1.21.5
Co-authored-by: Bjarne Koll <git@lynxplay.dev> Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com> Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Co-authored-by: MiniDigger | Martin <admin@minidigger.dev> Co-authored-by: Nassim Jahnke <nassim@njahnke.dev> Co-authored-by: Noah van der Aa <ndvdaa@gmail.com> Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Co-authored-by: Shane Freeder <theboyetronic@gmail.com> Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com> Co-authored-by: Tamion <70228790+notTamion@users.noreply.github.com> Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
This commit is contained in:
3
paper-generator/.editorconfig
Normal file
3
paper-generator/.editorconfig
Normal file
@ -0,0 +1,3 @@
|
||||
[*.java]
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
178
paper-generator/build.gradle.kts
Normal file
178
paper-generator/build.gradle.kts
Normal file
@ -0,0 +1,178 @@
|
||||
import io.papermc.paperweight.util.defaultJavaLauncher
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("io.papermc.paperweight.source-generator")
|
||||
}
|
||||
|
||||
paperweight {
|
||||
atFile.set(layout.projectDirectory.file("wideners.at"))
|
||||
}
|
||||
|
||||
val serverRuntimeClasspath by configurations.registering { // resolvable?
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraftJar(project(":paper-server", "mappedJarOutgoing"))
|
||||
implementation(project(":paper-server", "macheMinecraftLibraries"))
|
||||
|
||||
implementation("com.squareup:javapoet:1.13.0")
|
||||
implementation(project(":paper-api"))
|
||||
implementation("io.papermc.typewriter:typewriter:1.0.1") {
|
||||
isTransitive = false // paper-api already have everything
|
||||
}
|
||||
implementation("info.picocli:picocli:4.7.6")
|
||||
implementation("io.github.classgraph:classgraph:4.8.47")
|
||||
implementation("org.jetbrains:annotations:26.0.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
serverRuntimeClasspath(project(":paper-server", "runtimeConfiguration"))
|
||||
}
|
||||
|
||||
val gameVersion = providers.gradleProperty("mcVersion")
|
||||
|
||||
val rewriteApi = tasks.registerGenerationTask("rewriteApi", true, "api", {
|
||||
bootstrapTags = true
|
||||
sourceSet = rootProject.layout.projectDirectory.dir("paper-api")
|
||||
}) {
|
||||
description = "Rewrite existing API classes"
|
||||
classpath(sourceSets.main.map { it.runtimeClasspath })
|
||||
}
|
||||
|
||||
val rewriteImpl = tasks.registerGenerationTask("rewriteImpl", true, "impl", {
|
||||
sourceSet = rootProject.layout.projectDirectory.dir("paper-server")
|
||||
serverClassPath.from(serverRuntimeClasspath)
|
||||
}) {
|
||||
description = "Rewrite existing implementation classes"
|
||||
classpath(sourceSets.main.map { it.runtimeClasspath })
|
||||
}
|
||||
|
||||
tasks.register("rewrite") {
|
||||
group = "generation"
|
||||
description = "Rewrite existing API classes and its implementation"
|
||||
dependsOn(rewriteApi, rewriteImpl)
|
||||
}
|
||||
|
||||
|
||||
val generateApi = tasks.registerGenerationTask("generateApi", false, "api", {
|
||||
bootstrapTags = true
|
||||
sourceSet = rootProject.layout.projectDirectory.dir("paper-api")
|
||||
}) {
|
||||
description = "Generate new API classes"
|
||||
classpath(sourceSets.main.map { it.runtimeClasspath })
|
||||
}
|
||||
|
||||
val generateImpl = tasks.registerGenerationTask("generateImpl", false, "impl", {
|
||||
sourceSet = rootProject.layout.projectDirectory.dir("paper-server")
|
||||
}) {
|
||||
description = "Generate new implementation classes"
|
||||
classpath(sourceSets.main.map { it.runtimeClasspath })
|
||||
}
|
||||
|
||||
tasks.register("generate") {
|
||||
group = "generation"
|
||||
description = "Generate new API classes and its implementation"
|
||||
dependsOn(generateApi, generateImpl)
|
||||
}
|
||||
|
||||
if (providers.gradleProperty("updatingMinecraft").getOrElse("false").toBoolean()) {
|
||||
val scanOldGeneratedSourceCode by tasks.registering(JavaExec::class) {
|
||||
group = "verification"
|
||||
description = "Scan source code to detect outdated generated code"
|
||||
javaLauncher = javaToolchains.defaultJavaLauncher(project)
|
||||
mainClass.set("io.papermc.generator.rewriter.utils.ScanOldGeneratedSourceCode")
|
||||
classpath(sourceSets.main.map { it.runtimeClasspath })
|
||||
|
||||
val projectDirs = listOf("paper-api", "paper-server").map { rootProject.layout.projectDirectory.dir(it) }
|
||||
args(projectDirs.map { it.asFile.absolutePath })
|
||||
val workDirs = projectDirs.map { it.dir("src/main/java") }
|
||||
|
||||
workDirs.forEach { inputs.dir(it) }
|
||||
inputs.property("gameVersion", gameVersion)
|
||||
outputs.dirs(workDirs)
|
||||
}
|
||||
tasks.check {
|
||||
dependsOn(scanOldGeneratedSourceCode)
|
||||
}
|
||||
}
|
||||
|
||||
fun TaskContainer.registerGenerationTask(
|
||||
name: String,
|
||||
rewrite: Boolean,
|
||||
side: String,
|
||||
args: (GenerationArgumentProvider.() -> Unit)? = null,
|
||||
block: JavaExec.() -> Unit
|
||||
): TaskProvider<JavaExec> = register<JavaExec>(name) {
|
||||
group = "generation"
|
||||
dependsOn(project.tasks.test)
|
||||
javaLauncher = project.javaToolchains.defaultJavaLauncher(project)
|
||||
inputs.property("gameVersion", gameVersion)
|
||||
inputs.dir(layout.projectDirectory.dir("src/main/java")).withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
mainClass.set("io.papermc.generator.Main")
|
||||
systemProperty("paper.updatingMinecraft", providers.gradleProperty("updatingMinecraft").getOrElse("false").toBoolean())
|
||||
|
||||
val provider = objects.newInstance<GenerationArgumentProvider>()
|
||||
provider.side = side
|
||||
provider.rewrite = rewrite
|
||||
if (args != null) {
|
||||
args(provider)
|
||||
}
|
||||
argumentProviders.add(provider)
|
||||
|
||||
val targetDir = if (rewrite) "src/main/java" else "src/generated/java"
|
||||
outputs.dir(provider.sourceSet.dir(targetDir))
|
||||
|
||||
block(this)
|
||||
}
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
abstract class GenerationArgumentProvider : CommandLineArgumentProvider {
|
||||
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
@get:InputDirectory
|
||||
abstract val sourceSet: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val rewrite: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val side: Property<String>
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val serverClassPath: ConfigurableFileCollection
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val bootstrapTags: Property<Boolean>
|
||||
|
||||
init {
|
||||
bootstrapTags.convention(false)
|
||||
}
|
||||
|
||||
override fun asArguments(): Iterable<String> {
|
||||
val args = mutableListOf<String>()
|
||||
|
||||
args.add("--sourceset=${sourceSet.get().asFile.absolutePath}")
|
||||
args.add("--side=${side.get()}")
|
||||
args.add("--classpath=${serverClassPath.asPath}")
|
||||
|
||||
if (rewrite.get()) {
|
||||
args.add("--rewrite")
|
||||
}
|
||||
|
||||
if (bootstrapTags.get()) {
|
||||
args.add(("--bootstrap-tags"))
|
||||
}
|
||||
return args.toList()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
group = "io.papermc.paper"
|
||||
version = "1.0-SNAPSHOT"
|
||||
@ -0,0 +1,23 @@
|
||||
package io.papermc.generator;
|
||||
|
||||
import io.papermc.generator.registry.RegistryBootstrapper;
|
||||
import io.papermc.generator.types.SourceGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataBootstrapper;
|
||||
import io.papermc.generator.types.goal.MobGoalGenerator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.minecraft.Util;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface Generators {
|
||||
|
||||
List<SourceGenerator> API = Collections.unmodifiableList(Util.make(new ArrayList<>(), list -> {
|
||||
RegistryBootstrapper.bootstrap(list);
|
||||
list.add(new MobGoalGenerator("VanillaGoal", "com.destroystokyo.paper.entity.ai"));
|
||||
// todo extract fields for registry based api
|
||||
}));
|
||||
|
||||
List<SourceGenerator> SERVER = Collections.unmodifiableList(Util.make(new ArrayList<>(), CraftBlockDataBootstrapper::bootstrap));
|
||||
}
|
||||
145
paper-generator/src/main/java/io/papermc/generator/Main.java
Normal file
145
paper-generator/src/main/java/io/papermc/generator/Main.java
Normal file
@ -0,0 +1,145 @@
|
||||
package io.papermc.generator;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.generator.rewriter.registration.PaperPatternSourceSetRewriter;
|
||||
import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter;
|
||||
import io.papermc.generator.types.SourceGenerator;
|
||||
import io.papermc.generator.utils.experimental.ExperimentalCollector;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.core.Registry;
|
||||
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.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.tags.TagKey;
|
||||
import net.minecraft.tags.TagLoader;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.apache.commons.io.file.PathUtils;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.slf4j.Logger;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "generator",
|
||||
description = "Rewrite and generate API classes and its implementation for Paper"
|
||||
)
|
||||
public class Main implements Callable<Integer> {
|
||||
|
||||
@CommandLine.Option(names = {"--sourceset"}, required = true)
|
||||
Path sourceSet;
|
||||
|
||||
@CommandLine.Option(names = {"-cp", "--classpath"}, split = ":", required = true)
|
||||
Set<Path> classpath;
|
||||
|
||||
@CommandLine.Option(names = {"--rewrite"})
|
||||
boolean isRewrite;
|
||||
|
||||
@CommandLine.Option(names = {"--side"}, required = true)
|
||||
String side;
|
||||
|
||||
@CommandLine.Option(names = {"--bootstrap-tags"})
|
||||
boolean tagBootstrap;
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static RegistryAccess.@MonotonicNonNull Frozen REGISTRY_ACCESS;
|
||||
public static @MonotonicNonNull Map<TagKey<?>, String> EXPERIMENTAL_TAGS;
|
||||
|
||||
public static CompletableFuture<Void> bootStrap(boolean withTags) {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
Bootstrap.validate();
|
||||
|
||||
PackRepository resourceRepository = ServerPacksSource.createVanillaTrustedRepository();
|
||||
resourceRepository.reload();
|
||||
MultiPackResourceManager resourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, resourceRepository.getAvailablePacks().stream().map(Pack::open).toList());
|
||||
LayeredRegistryAccess<RegistryLayer> layers = RegistryLayer.createRegistryAccess();
|
||||
List<Registry.PendingTags<?>> pendingTags = TagLoader.loadTagsForExistingRegistries(resourceManager, layers.getLayer(RegistryLayer.STATIC));
|
||||
List<HolderLookup.RegistryLookup<?>> worldGenLayer = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLDGEN), pendingTags);
|
||||
RegistryAccess.Frozen frozenWorldgenRegistries = RegistryDataLoader.load(resourceManager, worldGenLayer, RegistryDataLoader.WORLDGEN_REGISTRIES);
|
||||
layers = layers.replaceFrom(RegistryLayer.WORLDGEN, frozenWorldgenRegistries);
|
||||
REGISTRY_ACCESS = layers.compositeAccess().freeze();
|
||||
if (withTags) {
|
||||
return ReloadableServerResources.loadResources(
|
||||
resourceManager,
|
||||
layers,
|
||||
pendingTags,
|
||||
FeatureFlags.VANILLA_SET,
|
||||
Commands.CommandSelection.DEDICATED,
|
||||
0,
|
||||
MoreExecutors.directExecutor(),
|
||||
MoreExecutors.directExecutor()
|
||||
).whenComplete((result, ex) -> {
|
||||
if (ex != null) {
|
||||
resourceManager.close();
|
||||
}
|
||||
}).thenAccept(resources -> {
|
||||
resources.updateStaticRegistryTags();
|
||||
EXPERIMENTAL_TAGS = ExperimentalCollector.collectTags(resourceManager);
|
||||
});
|
||||
} else {
|
||||
EXPERIMENTAL_TAGS = Map.of();
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() {
|
||||
bootStrap(this.tagBootstrap).join();
|
||||
|
||||
try {
|
||||
if (this.isRewrite) {
|
||||
rewrite(this.sourceSet, this.classpath, this.side.equals("api") ? Rewriters.API : Rewriters.SERVER);
|
||||
} else {
|
||||
generate(this.sourceSet, this.side.equals("api") ? Generators.API : Generators.SERVER);
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void rewrite(Path sourceSet, Set<Path> classpath, Consumer<PatternSourceSetRewriter> rewriters) throws IOException {
|
||||
PatternSourceSetRewriter sourceSetRewriter = new PaperPatternSourceSetRewriter(classpath);
|
||||
rewriters.accept(sourceSetRewriter);
|
||||
sourceSetRewriter.apply(sourceSet.resolve("src/main/java"));
|
||||
}
|
||||
|
||||
private static void generate(Path sourceSet, Collection<SourceGenerator> generators) throws IOException {
|
||||
Path output = sourceSet.resolve("src/generated/java");
|
||||
if (Files.exists(output)) {
|
||||
PathUtils.deleteDirectory(output);
|
||||
}
|
||||
|
||||
for (SourceGenerator generator : generators) {
|
||||
generator.writeToFile(output);
|
||||
}
|
||||
LOGGER.info("Files written to {}", output.toAbsolutePath());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.exit(new CommandLine(new Main()).execute(args));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
package io.papermc.generator;
|
||||
|
||||
import io.papermc.generator.registry.RegistryBootstrapper;
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter;
|
||||
import io.papermc.generator.rewriter.types.Types;
|
||||
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.FeatureFlagRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryTagRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.TagRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.BlockTypeRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.CraftBlockDataMapping;
|
||||
import io.papermc.generator.rewriter.types.simple.CraftBlockEntityStateMapping;
|
||||
import io.papermc.generator.rewriter.types.simple.CraftPotionUtilRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.EntityTypeRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.MapPaletteRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.MaterialRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.MemoryKeyRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.PaperFeatureFlagMapping;
|
||||
import io.papermc.generator.rewriter.types.simple.StatisticRewriter;
|
||||
import io.papermc.generator.rewriter.types.simple.trial.VillagerProfessionRewriter;
|
||||
import io.papermc.generator.types.goal.MobGoalNames;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
|
||||
import io.papermc.typewriter.preset.EnumCloneRewriter;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.item.Rarity;
|
||||
import org.bukkit.Art;
|
||||
import org.bukkit.FeatureFlag;
|
||||
import org.bukkit.Fluid;
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.JukeboxSong;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.MusicInstrument;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.BlockType;
|
||||
import org.bukkit.block.banner.PatternType;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.entity.Boat;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.Chicken;
|
||||
import org.bukkit.entity.Cow;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Fox;
|
||||
import org.bukkit.entity.Frog;
|
||||
import org.bukkit.entity.Panda;
|
||||
import org.bukkit.entity.Pig;
|
||||
import org.bukkit.entity.Salmon;
|
||||
import org.bukkit.entity.Sniffer;
|
||||
import org.bukkit.entity.TropicalFish;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.Wolf;
|
||||
import org.bukkit.entity.memory.MemoryKey;
|
||||
import org.bukkit.generator.structure.Structure;
|
||||
import org.bukkit.generator.structure.StructureType;
|
||||
import org.bukkit.inventory.ItemRarity;
|
||||
import org.bukkit.inventory.ItemType;
|
||||
import org.bukkit.inventory.meta.trim.TrimMaterial;
|
||||
import org.bukkit.inventory.meta.trim.TrimPattern;
|
||||
import org.bukkit.inventory.recipe.CookingBookCategory;
|
||||
import org.bukkit.inventory.recipe.CraftingBookCategory;
|
||||
import org.bukkit.map.MapCursor;
|
||||
import org.bukkit.map.MapPalette;
|
||||
import org.bukkit.potion.PotionType;
|
||||
import org.bukkit.scoreboard.DisplaySlot;
|
||||
import org.bukkit.tag.DamageTypeTags;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.papermc.generator.rewriter.registration.PaperPatternSourceSetRewriter.composite;
|
||||
import static io.papermc.generator.rewriter.registration.RewriterHolder.holder;
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@NullMarked
|
||||
public final class Rewriters {
|
||||
|
||||
public static void bootstrap(PatternSourceSetRewriter apiSourceSet, PatternSourceSetRewriter serverSourceSet) {
|
||||
bootstrapApi(apiSourceSet);
|
||||
bootstrapServer(serverSourceSet);
|
||||
}
|
||||
|
||||
public static final Consumer<PatternSourceSetRewriter> API = Rewriters::bootstrapApi;
|
||||
public static final Consumer<PatternSourceSetRewriter> SERVER = Rewriters::bootstrapServer;
|
||||
|
||||
private static void bootstrapApi(PatternSourceSetRewriter sourceSet) {
|
||||
sourceSet
|
||||
.register("PotionType", PotionType.class, new EnumRegistryRewriter<>(Registries.POTION))
|
||||
.register("EntityType", EntityType.class, new EntityTypeRewriter())
|
||||
.register("DisplaySlot", DisplaySlot.class, new EnumCloneRewriter<>(net.minecraft.world.scores.DisplaySlot.class) {
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(net.minecraft.world.scores.DisplaySlot slot) {
|
||||
final String name;
|
||||
if (slot == net.minecraft.world.scores.DisplaySlot.LIST) {
|
||||
name = "PLAYER_LIST";
|
||||
} else {
|
||||
name = Formatting.formatKeyAsField(slot.getSerializedName());
|
||||
}
|
||||
|
||||
return EnumValue.builder(name).argument(quoted(slot.getSerializedName()));
|
||||
}
|
||||
})
|
||||
.register("SnifferState", Sniffer.State.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.sniffer.Sniffer.State.class))
|
||||
.register("PandaGene", Panda.Gene.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.Panda.Gene.class) {
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(net.minecraft.world.entity.animal.Panda.Gene gene) {
|
||||
return super.rewriteEnumValue(gene).argument(String.valueOf(gene.isRecessive()));
|
||||
}
|
||||
})
|
||||
.register("CookingBookCategory", CookingBookCategory.class, new EnumCloneRewriter<>(net.minecraft.world.item.crafting.CookingBookCategory.class))
|
||||
.register("CraftingBookCategory", CraftingBookCategory.class, new EnumCloneRewriter<>(net.minecraft.world.item.crafting.CraftingBookCategory.class))
|
||||
.register("TropicalFishPattern", TropicalFish.Pattern.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.TropicalFish.Pattern.class))
|
||||
.register("BoatStatus", Boat.Status.class, new EnumCloneRewriter<>(net.minecraft.world.entity.vehicle.Boat.Status.class))
|
||||
.register("FoxType", Fox.Type.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.Fox.Variant.class))
|
||||
.register("SalmonVariant", Salmon.Variant.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.Salmon.Variant.class))
|
||||
.register("ItemUseAnimation", ItemUseAnimation.class, new EnumCloneRewriter<>(net.minecraft.world.item.ItemUseAnimation.class))
|
||||
.register("ItemRarity", ItemRarity.class, new EnumCloneRewriter<>(Rarity.class) {
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Rarity rarity) {
|
||||
return super.rewriteEnumValue(rarity).argument(
|
||||
"%s.%s".formatted(NamedTextColor.class.getSimpleName(), rarity.color().name())
|
||||
);
|
||||
}
|
||||
})
|
||||
.register(Material.class, composite(
|
||||
holder("Blocks", new MaterialRewriter.Blocks()),
|
||||
//holder("Material#isTransparent", MaterialRewriter.IsTransparent()),
|
||||
|
||||
holder("Items", new MaterialRewriter.Items())
|
||||
))
|
||||
.register(Statistic.class, composite(
|
||||
holder("StatisticCustom", new StatisticRewriter.Custom()),
|
||||
holder("StatisticType", new StatisticRewriter.Type())
|
||||
))
|
||||
.register(Villager.class, composite(
|
||||
holder("VillagerType", Villager.Type.class, new RegistryFieldRewriter<>(Registries.VILLAGER_TYPE, "getType")),
|
||||
holder("VillagerProfession", Villager.Profession.class, new VillagerProfessionRewriter())
|
||||
))
|
||||
.register("JukeboxSong", JukeboxSong.class, new RegistryFieldRewriter<>(Registries.JUKEBOX_SONG, "get") {
|
||||
@Override
|
||||
protected String rewriteFieldName(Holder.Reference<net.minecraft.world.item.JukeboxSong> reference) {
|
||||
String keyedName = super.rewriteFieldName(reference);
|
||||
if (!SourceVersion.isIdentifier(keyedName)) {
|
||||
// fallback to field names for invalid identifier (happens for 5, 11, 13 etc.)
|
||||
return RegistryEntries.byRegistryKey(Registries.JUKEBOX_SONG).getFieldNames().get(reference.key());
|
||||
}
|
||||
return keyedName;
|
||||
}
|
||||
})
|
||||
.register("DamageTypeTags", DamageTypeTags.class, new RegistryTagRewriter<>(Registries.DAMAGE_TYPE, DamageType.class))
|
||||
.register("MapCursorType", MapCursor.Type.class, new RegistryFieldRewriter<>(Registries.MAP_DECORATION_TYPE, "getType"))
|
||||
.register("Structure", Structure.class, new RegistryFieldRewriter<>(Registries.STRUCTURE, "getStructure"))
|
||||
.register("StructureType", StructureType.class, new RegistryFieldRewriter<>(Registries.STRUCTURE_TYPE, "getStructureType"))
|
||||
.register("TrimPattern", TrimPattern.class, new RegistryFieldRewriter<>(Registries.TRIM_PATTERN, "getTrimPattern"))
|
||||
.register("TrimMaterial", TrimMaterial.class, new RegistryFieldRewriter<>(Registries.TRIM_MATERIAL, "getTrimMaterial"))
|
||||
.register("DamageType", DamageType.class, new RegistryFieldRewriter<>(Registries.DAMAGE_TYPE, "getDamageType"))
|
||||
.register("GameEvent", GameEvent.class, new RegistryFieldRewriter<>(Registries.GAME_EVENT, "getEvent"))
|
||||
.register("MusicInstrument", MusicInstrument.class, new RegistryFieldRewriter<>(Registries.INSTRUMENT, "getInstrument"))
|
||||
.register("WolfVariant", Wolf.Variant.class, new RegistryFieldRewriter<>(Registries.WOLF_VARIANT, "getVariant"))
|
||||
.register("WolfSoundVariant", Wolf.SoundVariant.class, new RegistryFieldRewriter<>(Registries.WOLF_SOUND_VARIANT, "getSoundVariant"))
|
||||
.register("CatType", Cat.Type.class, new RegistryFieldRewriter<>(Registries.CAT_VARIANT, "getType"))
|
||||
.register("FrogVariant", Frog.Variant.class, new RegistryFieldRewriter<>(Registries.FROG_VARIANT, "getVariant"))
|
||||
.register("PatternType", PatternType.class, new RegistryFieldRewriter<>(Registries.BANNER_PATTERN, "getType"))
|
||||
.register("Biome", Biome.class, new RegistryFieldRewriter<>(Registries.BIOME, "getBiome"))
|
||||
.register("Fluid", Fluid.class, new RegistryFieldRewriter<>(Registries.FLUID, "getFluid"))
|
||||
// .register("Attribute", Attribute.class, new RegistryFieldRewriter<>(Registries.ATTRIBUTE, "getAttribute")) - disable for now (javadocs)
|
||||
.register("Sound", Sound.class, new RegistryFieldRewriter<>(Registries.SOUND_EVENT, "getSound"))
|
||||
.register("Art", Art.class, new RegistryFieldRewriter<>(Registries.PAINTING_VARIANT, "getArt"))
|
||||
.register("ChickenVariant", Chicken.Variant.class, new RegistryFieldRewriter<>(Registries.CHICKEN_VARIANT, "getVariant"))
|
||||
.register("CowVariant", Cow.Variant.class, new RegistryFieldRewriter<>(Registries.COW_VARIANT, "getVariant"))
|
||||
.register("PigVariant", Pig.Variant.class, new RegistryFieldRewriter<>(Registries.PIG_VARIANT, "getVariant"))
|
||||
.register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter())
|
||||
// .register("DataComponentTypes", DataComponentTypes.class, new DataComponentTypesRewriter()) - disable for now
|
||||
// .register("ItemType", ItemType.class, new ItemTypeRewriter()) - disable for now, lynx want the generic type
|
||||
.register("BlockType", BlockType.class, new BlockTypeRewriter())
|
||||
.register("FeatureFlag", FeatureFlag.class, new FeatureFlagRewriter())
|
||||
.register("Tag", Tag.class, new TagRewriter())
|
||||
.register("MapPalette#colors", MapPalette.class, new MapPaletteRewriter());
|
||||
RegistryBootstrapper.bootstrapApi(sourceSet);
|
||||
}
|
||||
|
||||
private static void bootstrapServer(PatternSourceSetRewriter sourceSet) {
|
||||
sourceSet
|
||||
.register("CraftBlockData#MAP", Types.CRAFT_BLOCK_DATA, new CraftBlockDataMapping())
|
||||
.register("CraftBlockEntityStates", Types.CRAFT_BLOCK_STATES, new CraftBlockEntityStateMapping())
|
||||
.register(Types.CRAFT_STATISTIC, composite(
|
||||
holder("CraftStatisticCustom", new StatisticRewriter.CraftCustom()),
|
||||
holder("CraftStatisticType", new StatisticRewriter.CraftType())
|
||||
))
|
||||
.register(Types.CRAFT_POTION_UTIL, composite(
|
||||
holder("CraftPotionUtil#upgradeable", new CraftPotionUtilRewriter("strong")),
|
||||
holder("CraftPotionUtil#extendable", new CraftPotionUtilRewriter("long"))
|
||||
))
|
||||
.register("PaperFeatureFlagProviderImpl#FLAGS", Types.PAPER_FEATURE_FLAG_PROVIDER_IMPL, new PaperFeatureFlagMapping())
|
||||
.register("MobGoalHelper#bukkitMap", Types.MOB_GOAL_HELPER, new SearchReplaceRewriter() {
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
for (Map.Entry<Class<? extends Mob>, Class<? extends org.bukkit.entity.Mob>> entry : MobGoalNames.bukkitMap.entrySet()) {
|
||||
builder.append(metadata.indent()).append("bukkitMap.put(%s.class, %s.class);".formatted(
|
||||
entry.getKey().getCanonicalName(), this.importCollector.getShortName(entry.getValue())
|
||||
));
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
});
|
||||
RegistryBootstrapper.bootstrapServer(sourceSet);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package io.papermc.generator.registry;
|
||||
|
||||
import io.papermc.generator.rewriter.registration.PatternSourceSetRewriter;
|
||||
import io.papermc.generator.rewriter.types.Types;
|
||||
import io.papermc.generator.rewriter.types.registry.PaperRegistriesRewriter;
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryEventsRewriter;
|
||||
import io.papermc.generator.types.SourceGenerator;
|
||||
import io.papermc.generator.types.registry.GeneratedKeyType;
|
||||
import io.papermc.generator.types.registry.GeneratedTagKeyType;
|
||||
import io.papermc.paper.registry.event.RegistryEvents;
|
||||
import java.util.List;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class RegistryBootstrapper {
|
||||
|
||||
private static final String PAPER_REGISTRY_PACKAGE = "io.papermc.paper.registry";
|
||||
|
||||
public static void bootstrap(List<SourceGenerator> generators) {
|
||||
// typed/tag keys
|
||||
RegistryEntries.forEach(entry -> {
|
||||
generators.add(new GeneratedKeyType<>(PAPER_REGISTRY_PACKAGE + ".keys", entry));
|
||||
if (entry.registry().listTags().findAny().isPresent()) {
|
||||
generators.add(new GeneratedTagKeyType(entry, PAPER_REGISTRY_PACKAGE + ".keys.tags"));
|
||||
}
|
||||
});
|
||||
|
||||
// todo remove once entity type is a registry
|
||||
generators.add(new GeneratedTagKeyType(RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE), PAPER_REGISTRY_PACKAGE + ".keys.tags"));
|
||||
}
|
||||
|
||||
public static void bootstrap(PatternSourceSetRewriter apiSourceSet, PatternSourceSetRewriter serverSourceSet) {
|
||||
bootstrapApi(apiSourceSet);
|
||||
bootstrapServer(serverSourceSet);
|
||||
}
|
||||
|
||||
public static void bootstrapApi(PatternSourceSetRewriter sourceSet) {
|
||||
sourceSet.register("RegistryEvents", RegistryEvents.class, new RegistryEventsRewriter());
|
||||
}
|
||||
|
||||
public static void bootstrapServer(PatternSourceSetRewriter sourceSet) {
|
||||
sourceSet.register("RegistryDefinitions", Types.PAPER_REGISTRIES, new PaperRegistriesRewriter());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,215 @@
|
||||
package io.papermc.generator.registry;
|
||||
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.paper.datacomponent.DataComponentType;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
|
||||
import io.papermc.paper.registry.data.DamageTypeRegistryEntry;
|
||||
import io.papermc.paper.registry.data.EnchantmentRegistryEntry;
|
||||
import io.papermc.paper.registry.data.GameEventRegistryEntry;
|
||||
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.animal.CatVariants;
|
||||
import net.minecraft.world.entity.animal.ChickenVariants;
|
||||
import net.minecraft.world.entity.animal.CowVariants;
|
||||
import net.minecraft.world.entity.animal.PigVariants;
|
||||
import net.minecraft.world.entity.animal.frog.FrogVariants;
|
||||
import net.minecraft.world.entity.animal.wolf.WolfSoundVariants;
|
||||
import net.minecraft.world.entity.animal.wolf.WolfVariants;
|
||||
import net.minecraft.world.entity.decoration.PaintingVariants;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import net.minecraft.world.entity.npc.VillagerType;
|
||||
import net.minecraft.world.item.Instruments;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.JukeboxSongs;
|
||||
import net.minecraft.world.item.alchemy.Potions;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import net.minecraft.world.item.equipment.trim.TrimMaterials;
|
||||
import net.minecraft.world.item.equipment.trim.TrimPatterns;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.level.saveddata.maps.MapDecorationTypes;
|
||||
import org.bukkit.Art;
|
||||
import org.bukkit.Fluid;
|
||||
import org.bukkit.GameEvent;
|
||||
import org.bukkit.JukeboxSong;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.MusicInstrument;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.BlockType;
|
||||
import org.bukkit.block.banner.PatternType;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.Chicken;
|
||||
import org.bukkit.entity.Cow;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Frog;
|
||||
import org.bukkit.entity.Pig;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.Wolf;
|
||||
import org.bukkit.entity.memory.MemoryKey;
|
||||
import org.bukkit.generator.structure.Structure;
|
||||
import org.bukkit.generator.structure.StructureType;
|
||||
import org.bukkit.inventory.ItemType;
|
||||
import org.bukkit.inventory.MenuType;
|
||||
import org.bukkit.inventory.meta.trim.TrimMaterial;
|
||||
import org.bukkit.inventory.meta.trim.TrimPattern;
|
||||
import org.bukkit.map.MapCursor;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.potion.PotionType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class RegistryEntries {
|
||||
|
||||
// CraftBukkit entry where implementation start by "Craft"
|
||||
private static <T> RegistryEntry<T> entry(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass, Class<? extends Keyed> apiClass) {
|
||||
return entry(registryKey, holderElementsClass, apiClass, "Craft");
|
||||
}
|
||||
|
||||
private static <T> RegistryEntry<T> entry(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass, Class<? extends Keyed> apiClass, String implPrefix) {
|
||||
String name = io.papermc.typewriter.util.ClassHelper.retrieveFullNestedName(apiClass);
|
||||
RegistryKeyField<T> registryKeyField = (RegistryKeyField<T>) REGISTRY_KEY_FIELDS.get(registryKey);
|
||||
String[] classes = name.split("\\.");
|
||||
if (classes.length == 0) {
|
||||
return new RegistryEntry<>(registryKey, registryKeyField, holderElementsClass, apiClass, implPrefix.concat(apiClass.getSimpleName()));
|
||||
}
|
||||
|
||||
String implName = Arrays.stream(classes).map(implPrefix::concat).collect(Collectors.joining("."));
|
||||
return new RegistryEntry<>(registryKey, registryKeyField, holderElementsClass, apiClass, implName);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static <T> RegistryEntry<T> inconsistentEntry(ResourceKey<? extends Registry<T>> registryKey, Class<?> holderElementsClass, Class<? extends Keyed> apiClass, String implClass) {
|
||||
return new RegistryEntry<>(registryKey, (RegistryKeyField<T>) REGISTRY_KEY_FIELDS.get(registryKey), holderElementsClass, apiClass, implClass);
|
||||
}
|
||||
|
||||
private static final Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> REGISTRY_KEY_FIELDS;
|
||||
static {
|
||||
Map<ResourceKey<? extends Registry<?>>, RegistryKeyField<?>> registryKeyFields = new IdentityHashMap<>();
|
||||
try {
|
||||
for (Field field : Registries.class.getDeclaredFields()) {
|
||||
if (!ResourceKey.class.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
|
||||
Type elementType = ClassHelper.getNestedTypeParameter(field.getGenericType(), ResourceKey.class, Registry.class, null);
|
||||
if (elementType != null) {
|
||||
registryKeyFields.put(((ResourceKey<? extends Registry<?>>) field.get(null)), new RegistryKeyField<>(ClassHelper.eraseType(elementType), field.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
REGISTRY_KEY_FIELDS = Collections.unmodifiableMap(registryKeyFields);
|
||||
}
|
||||
|
||||
public static final Set<Class<?>> REGISTRY_CLASS_NAME_BASED_ON_API = Set.of(
|
||||
BlockType.class,
|
||||
ItemType.class
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> BUILT_IN = List.of(
|
||||
entry(Registries.GAME_EVENT, net.minecraft.world.level.gameevent.GameEvent.class, GameEvent.class).apiRegistryBuilder(GameEventRegistryEntry.Builder.class, "PaperGameEventRegistryEntry.PaperBuilder"),
|
||||
entry(Registries.STRUCTURE_TYPE, net.minecraft.world.level.levelgen.structure.StructureType.class, StructureType.class),
|
||||
entry(Registries.MOB_EFFECT, MobEffects.class, PotionEffectType.class),
|
||||
entry(Registries.BLOCK, Blocks.class, BlockType.class),
|
||||
entry(Registries.ITEM, Items.class, ItemType.class),
|
||||
entry(Registries.VILLAGER_PROFESSION, VillagerProfession.class, Villager.Profession.class),
|
||||
entry(Registries.VILLAGER_TYPE, VillagerType.class, Villager.Type.class),
|
||||
entry(Registries.MAP_DECORATION_TYPE, MapDecorationTypes.class, MapCursor.Type.class),
|
||||
entry(Registries.MENU, net.minecraft.world.inventory.MenuType.class, MenuType.class),
|
||||
entry(Registries.ATTRIBUTE, Attributes.class, Attribute.class),
|
||||
entry(Registries.FLUID, Fluids.class, Fluid.class),
|
||||
entry(Registries.SOUND_EVENT, SoundEvents.class, Sound.class).allowDirect().apiRegistryField("SOUNDS"),
|
||||
entry(Registries.DATA_COMPONENT_TYPE, DataComponents.class, DataComponentType.class, "Paper").preload(DataComponentTypes.class).apiAccessName("of")
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> DATA_DRIVEN = List.of(
|
||||
entry(Registries.BIOME, Biomes.class, Biome.class).delayed(),
|
||||
entry(Registries.STRUCTURE, BuiltinStructures.class, Structure.class).delayed(),
|
||||
entry(Registries.TRIM_MATERIAL, TrimMaterials.class, TrimMaterial.class).allowDirect().delayed(),
|
||||
entry(Registries.TRIM_PATTERN, TrimPatterns.class, TrimPattern.class).allowDirect().delayed(),
|
||||
entry(Registries.DAMAGE_TYPE, DamageTypes.class, DamageType.class).apiRegistryBuilder(DamageTypeRegistryEntry.Builder.class, "PaperDamageTypeRegistryEntry.PaperBuilder").delayed(),
|
||||
entry(Registries.WOLF_VARIANT, WolfVariants.class, Wolf.Variant.class).delayed(),
|
||||
entry(Registries.WOLF_SOUND_VARIANT, WolfSoundVariants.class, Wolf.SoundVariant.class),
|
||||
entry(Registries.ENCHANTMENT, Enchantments.class, Enchantment.class).apiRegistryBuilder(EnchantmentRegistryEntry.Builder.class, "PaperEnchantmentRegistryEntry.PaperBuilder").serializationUpdater("ENCHANTMENT_RENAME").delayed(),
|
||||
entry(Registries.JUKEBOX_SONG, JukeboxSongs.class, JukeboxSong.class).delayed(),
|
||||
entry(Registries.BANNER_PATTERN, BannerPatterns.class, PatternType.class).allowDirect().apiRegistryBuilder(BannerPatternRegistryEntry.Builder.class, "PaperBannerPatternRegistryEntry.PaperBuilder").delayed(),
|
||||
entry(Registries.PAINTING_VARIANT, PaintingVariants.class, Art.class).allowDirect().apiRegistryBuilder(PaintingVariantRegistryEntry.Builder.class, "PaperPaintingVariantRegistryEntry.PaperBuilder").apiRegistryField("ART").delayed(),
|
||||
entry(Registries.INSTRUMENT, Instruments.class, MusicInstrument.class).allowDirect().delayed(),
|
||||
entry(Registries.CAT_VARIANT, CatVariants.class, Cat.Type.class).delayed(),
|
||||
entry(Registries.FROG_VARIANT, FrogVariants.class, Frog.Variant.class).delayed(),
|
||||
entry(Registries.CHICKEN_VARIANT, ChickenVariants.class, Chicken.Variant.class),
|
||||
entry(Registries.COW_VARIANT, CowVariants.class, Cow.Variant.class),
|
||||
entry(Registries.PIG_VARIANT, PigVariants.class, Pig.Variant.class)
|
||||
);
|
||||
|
||||
public static final List<RegistryEntry<?>> API_ONLY = List.of(
|
||||
entry(Registries.ENTITY_TYPE, net.minecraft.world.entity.EntityType.class, EntityType.class),
|
||||
entry(Registries.PARTICLE_TYPE, ParticleTypes.class, Particle.class),
|
||||
entry(Registries.POTION, Potions.class, PotionType.class),
|
||||
entry(Registries.MEMORY_MODULE_TYPE, MemoryModuleType.class, MemoryKey.class)
|
||||
);
|
||||
|
||||
public static final Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> BY_REGISTRY_KEY;
|
||||
static {
|
||||
Map<ResourceKey<? extends Registry<?>>, RegistryEntry<?>> byRegistryKey = new IdentityHashMap<>(BUILT_IN.size() + DATA_DRIVEN.size() + API_ONLY.size());
|
||||
forEach(entry -> {
|
||||
byRegistryKey.put(entry.registryKey(), entry);
|
||||
}, RegistryEntries.BUILT_IN, RegistryEntries.DATA_DRIVEN, RegistryEntries.API_ONLY);
|
||||
BY_REGISTRY_KEY = Collections.unmodifiableMap(byRegistryKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> RegistryEntry<T> byRegistryKey(ResourceKey<? extends Registry<T>> registryKey) {
|
||||
return (RegistryEntry<T>) Objects.requireNonNull(BY_REGISTRY_KEY.get(registryKey));
|
||||
}
|
||||
|
||||
// real registries
|
||||
public static void forEach(Consumer<RegistryEntry<?>> callback) {
|
||||
forEach(callback, RegistryEntries.BUILT_IN, RegistryEntries.DATA_DRIVEN);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static void forEach(Consumer<RegistryEntry<?>> callback, List<RegistryEntry<?>>... datas) {
|
||||
for (List<RegistryEntry<?>> data : datas) {
|
||||
for (RegistryEntry<?> entry : data) {
|
||||
callback.accept(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RegistryEntries() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
package io.papermc.generator.registry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public final class RegistryEntry<T> {
|
||||
|
||||
private final ResourceKey<? extends Registry<T>> registryKey;
|
||||
private final RegistryKeyField<T> registryKeyField;
|
||||
private final Class<T> elementClass;
|
||||
private final Class<?> holderElementsClass;
|
||||
private boolean allowDirect;
|
||||
|
||||
private final Class<? extends Keyed> apiClass; // TODO remove Keyed
|
||||
private Class<?> preloadClass;
|
||||
private final String implClass;
|
||||
|
||||
private @Nullable Class<?> apiRegistryBuilder;
|
||||
private @Nullable String apiRegistryBuilderImpl;
|
||||
|
||||
private @Nullable String fieldRename;
|
||||
private boolean delayed;
|
||||
private String apiAccessName = ConstantDescs.INIT_NAME;
|
||||
private Optional<String> apiRegistryField = Optional.empty();
|
||||
|
||||
private @Nullable Map<ResourceKey<T>, String> fieldNames;
|
||||
|
||||
public RegistryEntry(ResourceKey<? extends Registry<T>> registryKey, RegistryKeyField<T> registryKeyField, Class<?> holderElementsClass, Class<? extends Keyed> apiClass, String implClass) {
|
||||
this.registryKey = registryKey;
|
||||
this.registryKeyField = registryKeyField;
|
||||
this.elementClass = registryKeyField.elementClass();
|
||||
this.holderElementsClass = holderElementsClass;
|
||||
this.apiClass = apiClass;
|
||||
this.preloadClass = apiClass;
|
||||
this.implClass = implClass;
|
||||
}
|
||||
|
||||
public ResourceKey<? extends Registry<T>> registryKey() {
|
||||
return this.registryKey;
|
||||
}
|
||||
|
||||
public Registry<T> registry() {
|
||||
return Main.REGISTRY_ACCESS.lookupOrThrow(this.registryKey);
|
||||
}
|
||||
|
||||
public String registryKeyField() {
|
||||
return this.registryKeyField.name();
|
||||
}
|
||||
|
||||
public Class<? extends Keyed> apiClass() {
|
||||
return this.apiClass;
|
||||
}
|
||||
|
||||
public String implClass() {
|
||||
return this.implClass;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> allowDirect() {
|
||||
this.allowDirect = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> delayed() {
|
||||
this.delayed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> preload(Class<?> klass) {
|
||||
this.preloadClass = klass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> apiAccessName(String name) {
|
||||
Preconditions.checkArgument(SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name), "Invalid accessor name");
|
||||
this.apiAccessName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> serializationUpdater(String fieldName) {
|
||||
this.fieldRename = fieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean canAllowDirect() {
|
||||
return this.allowDirect;
|
||||
}
|
||||
|
||||
public boolean isDelayed() {
|
||||
return this.delayed;
|
||||
}
|
||||
|
||||
public String apiAccessName() {
|
||||
return this.apiAccessName;
|
||||
}
|
||||
|
||||
public Class<?> preloadClass() {
|
||||
return this.preloadClass;
|
||||
}
|
||||
|
||||
public @Nullable String fieldRename() {
|
||||
return this.fieldRename;
|
||||
}
|
||||
|
||||
public @Nullable Class<?> apiRegistryBuilder() {
|
||||
return this.apiRegistryBuilder;
|
||||
}
|
||||
|
||||
public @Nullable String apiRegistryBuilderImpl() {
|
||||
return this.apiRegistryBuilderImpl;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> apiRegistryBuilder(Class<?> builderClass, String builderImplClass) {
|
||||
this.apiRegistryBuilder = builderClass;
|
||||
this.apiRegistryBuilderImpl = builderImplClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<String> apiRegistryField() {
|
||||
return this.apiRegistryField;
|
||||
}
|
||||
|
||||
public RegistryEntry<T> apiRegistryField(String registryField) {
|
||||
this.apiRegistryField = Optional.of(registryField);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String keyClassName() {
|
||||
if (RegistryEntries.REGISTRY_CLASS_NAME_BASED_ON_API.contains(this.apiClass)) {
|
||||
return this.apiClass.getSimpleName();
|
||||
}
|
||||
|
||||
return this.elementClass.getSimpleName();
|
||||
}
|
||||
|
||||
public boolean allowCustomKeys() {
|
||||
return this.apiRegistryBuilder != null || RegistryEntries.DATA_DRIVEN.contains(this);
|
||||
}
|
||||
|
||||
private <TO> Map<ResourceKey<T>, TO> getFields(Map<ResourceKey<T>, TO> map, Function<Field, @Nullable TO> transform) {
|
||||
Registry<T> registry = this.registry();
|
||||
try {
|
||||
for (Field field : this.holderElementsClass.getDeclaredFields()) {
|
||||
if (!ResourceKey.class.isAssignableFrom(field.getType()) && !Holder.Reference.class.isAssignableFrom(field.getType()) && !this.elementClass.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
|
||||
ResourceKey<T> key = null;
|
||||
if (this.elementClass.isAssignableFrom(field.getType())) {
|
||||
key = registry.getResourceKey(this.elementClass.cast(field.get(null))).orElseThrow();
|
||||
} else {
|
||||
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1 &&
|
||||
complexType.getActualTypeArguments()[0] == this.elementClass) {
|
||||
|
||||
if (Holder.Reference.class.isAssignableFrom(field.getType())) {
|
||||
key = ((Holder.Reference<T>) field.get(null)).key();
|
||||
} else {
|
||||
key = (ResourceKey<T>) field.get(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != null) {
|
||||
TO value = transform.apply(field);
|
||||
if (value != null) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<ResourceKey<T>, String> getFieldNames() {
|
||||
if (this.fieldNames == null) {
|
||||
this.fieldNames = this.getFields(Field::getName);
|
||||
}
|
||||
return this.fieldNames;
|
||||
}
|
||||
|
||||
public <TO> Map<ResourceKey<T>, TO> getFields(Function<Field, @Nullable TO> transform) {
|
||||
return Collections.unmodifiableMap(this.getFields(new IdentityHashMap<>(), transform));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegistryEntry[" +
|
||||
"registryKey=" + this.registryKey + ", " +
|
||||
"registryKeyField=" + this.registryKeyField + ", " +
|
||||
"apiClass=" + this.apiClass + ", " +
|
||||
"implClass=" + this.implClass + ", " +
|
||||
']';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package io.papermc.generator.registry;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record RegistryKeyField<T>(Class<T> elementClass, String name) {
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package io.papermc.generator.rewriter.registration;
|
||||
|
||||
import io.papermc.generator.rewriter.utils.Annotations;
|
||||
import io.papermc.generator.types.SimpleGenerator;
|
||||
import io.papermc.paper.generated.GeneratedFrom;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.SourceFile;
|
||||
import io.papermc.typewriter.context.IndentUnit;
|
||||
import io.papermc.typewriter.context.SourcesMetadata;
|
||||
import io.papermc.typewriter.registration.SourceSetRewriterImpl;
|
||||
import io.papermc.typewriter.replace.CompositeRewriter;
|
||||
import io.papermc.typewriter.replace.ReplaceOptions;
|
||||
import io.papermc.typewriter.replace.ReplaceOptionsLike;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.SharedConstants;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public class PaperPatternSourceSetRewriter extends SourceSetRewriterImpl<PatternSourceSetRewriter> implements PatternSourceSetRewriter {
|
||||
|
||||
private static final String COMMENT_MARKER_FORMAT = "%s generate - %s"; // {0} = Start|End {1} = pattern
|
||||
private static final IndentUnit INDENT_UNIT = IndentUnit.parse(SimpleGenerator.INDENT_UNIT);
|
||||
|
||||
public PaperPatternSourceSetRewriter() {
|
||||
this(Collections.emptySet());
|
||||
}
|
||||
|
||||
public PaperPatternSourceSetRewriter(Set<Path> classpath) {
|
||||
super(SourcesMetadata.of(INDENT_UNIT, b -> b.classpath(classpath))); // let the runtime java version since it will always be in sync with what paperweight use
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public SourcesMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
private static ReplaceOptionsLike getOptions(String pattern, @Nullable ClassNamed targetClass) {
|
||||
return ReplaceOptions.between(
|
||||
COMMENT_MARKER_FORMAT.formatted("Start", pattern),
|
||||
COMMENT_MARKER_FORMAT.formatted("End", pattern)
|
||||
)
|
||||
.generatedComment(Annotations.annotationStyle(GeneratedFrom.class) + " " + SharedConstants.getCurrentVersion().getId())
|
||||
.targetClass(targetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatternSourceSetRewriter register(String pattern, ClassNamed targetClass, SearchReplaceRewriter rewriter) {
|
||||
return super.register(SourceFile.of(targetClass.topLevel()), rewriter.withOptions(getOptions(pattern, targetClass)).customName(pattern));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatternSourceSetRewriter register(ClassNamed mainClass, CompositeRewriter rewriter) {
|
||||
return super.register(SourceFile.of(mainClass), rewriter);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public static CompositeRewriter composite(RewriterHolder... holders) {
|
||||
return CompositeRewriter.bind(Arrays.stream(holders)
|
||||
.map(holder -> holder.transform(PaperPatternSourceSetRewriter::getOptions))
|
||||
.toArray(SearchReplaceRewriter[]::new));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package io.papermc.generator.rewriter.registration;
|
||||
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.registration.SourceSetRewriter;
|
||||
import io.papermc.typewriter.replace.CompositeRewriter;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface PatternSourceSetRewriter extends SourceSetRewriter<PatternSourceSetRewriter> {
|
||||
|
||||
default PatternSourceSetRewriter register(String pattern, Class<?> targetClass, SearchReplaceRewriter rewriter) {
|
||||
return register(pattern, new ClassNamed(targetClass), rewriter);
|
||||
}
|
||||
|
||||
PatternSourceSetRewriter register(String pattern, ClassNamed targetClass, SearchReplaceRewriter rewriter);
|
||||
|
||||
default PatternSourceSetRewriter register(Class<?> mainClass, CompositeRewriter rewriter) {
|
||||
return this.register(new ClassNamed(mainClass), rewriter);
|
||||
}
|
||||
|
||||
PatternSourceSetRewriter register(ClassNamed mainClass, CompositeRewriter rewriter);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package io.papermc.generator.rewriter.registration;
|
||||
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.replace.ReplaceOptionsLike;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.function.BiFunction;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public record RewriterHolder(String pattern, @Nullable ClassNamed targetClass, SearchReplaceRewriter rewriter) {
|
||||
|
||||
@Contract(value = "_, _, _ -> new", pure = true)
|
||||
public static RewriterHolder holder(String pattern, @Nullable Class<?> targetClass, SearchReplaceRewriter rewriter) {
|
||||
return new RewriterHolder(pattern, targetClass == null ? null : new ClassNamed(targetClass), rewriter);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public static RewriterHolder holder(String pattern, SearchReplaceRewriter rewriter) {
|
||||
return holder(pattern, null, rewriter);
|
||||
}
|
||||
|
||||
public SearchReplaceRewriter transform(BiFunction<String, @Nullable ClassNamed, ReplaceOptionsLike> patternMapper) {
|
||||
return this.rewriter.withOptions(patternMapper.apply(this.pattern, this.targetClass)).customName(this.pattern);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package io.papermc.generator.rewriter.types;
|
||||
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Types {
|
||||
|
||||
public static final String BASE_PACKAGE = "org.bukkit.craftbukkit";
|
||||
|
||||
public static final ClassNamed CRAFT_BLOCK_DATA = ClassNamed.of(BASE_PACKAGE + ".block.data", "CraftBlockData");
|
||||
|
||||
public static final ClassNamed CRAFT_BLOCK_STATES = ClassNamed.of(BASE_PACKAGE + ".block", "CraftBlockStates");
|
||||
|
||||
public static final ClassNamed CRAFT_STATISTIC = ClassNamed.of(BASE_PACKAGE, "CraftStatistic");
|
||||
|
||||
public static final ClassNamed CRAFT_POTION_UTIL = ClassNamed.of(BASE_PACKAGE + ".potion", "CraftPotionUtil");
|
||||
|
||||
public static final ClassNamed FIELD_RENAME = ClassNamed.of(BASE_PACKAGE + ".legacy", "FieldRename");
|
||||
|
||||
public static final ClassNamed PAPER_REGISTRIES = ClassNamed.of("io.papermc.paper.registry", "PaperRegistries");
|
||||
|
||||
public static final ClassNamed PAPER_FEATURE_FLAG_PROVIDER_IMPL = ClassNamed.of("io.papermc.paper.world.flag", "PaperFeatureFlagProviderImpl");
|
||||
|
||||
public static final ClassNamed PAPER_SIMPLE_REGISTRY = ClassNamed.of("io.papermc.paper.registry", "PaperSimpleRegistry");
|
||||
|
||||
public static final ClassNamed MOB_GOAL_HELPER = ClassNamed.of("com.destroystokyo.paper.entity.ai", "MobGoalHelper");
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.rewriter.utils.Annotations;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.generator.utils.experimental.ExperimentalCollector;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.typewriter.preset.EnumRewriter;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.flag.FeatureElement;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@NullMarked
|
||||
@ApiStatus.Obsolete
|
||||
public class EnumRegistryRewriter<T> extends EnumRewriter<Holder.Reference<T>> {
|
||||
|
||||
private final Supplier<Registry<T>> registry;
|
||||
private final Supplier<Map<ResourceKey<T>, SingleFlagHolder>> experimentalKeys;
|
||||
private final boolean isFilteredRegistry;
|
||||
private final boolean hasKeyArgument;
|
||||
|
||||
public EnumRegistryRewriter(ResourceKey<? extends Registry<T>> registryKey) {
|
||||
this(registryKey, true);
|
||||
}
|
||||
|
||||
protected EnumRegistryRewriter(ResourceKey<? extends Registry<T>> registryKey, boolean hasKeyArgument) {
|
||||
this.registry = Suppliers.memoize(() -> Main.REGISTRY_ACCESS.lookupOrThrow(registryKey));
|
||||
this.experimentalKeys = Suppliers.memoize(() -> ExperimentalCollector.collectDataDrivenElementIds(this.registry.get()));
|
||||
this.isFilteredRegistry = FeatureElement.FILTERED_REGISTRIES.contains(registryKey);
|
||||
this.hasKeyArgument = hasKeyArgument;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Holder.Reference<T>> getValues() {
|
||||
return this.registry.get().listElements().sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath()))::iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<T> reference) {
|
||||
EnumValue.Builder value = EnumValue.builder(Formatting.formatKeyAsField(reference.key().location().getPath()));
|
||||
if (this.hasKeyArgument) {
|
||||
value.argument(quoted(reference.key().location().getPath()));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendEnumValue(Holder.Reference<T> reference, StringBuilder builder, String indent, boolean reachEnd) {
|
||||
// experimental annotation
|
||||
SingleFlagHolder requiredFeature = this.getRequiredFeature(reference);
|
||||
if (requiredFeature != null) {
|
||||
Annotations.experimentalAnnotations(builder, indent, this.importCollector, requiredFeature);
|
||||
}
|
||||
|
||||
super.appendEnumValue(reference, builder, indent, reachEnd);
|
||||
}
|
||||
|
||||
protected @Nullable SingleFlagHolder getRequiredFeature(Holder.Reference<T> reference) {
|
||||
if (this.isFilteredRegistry) {
|
||||
// built-in registry
|
||||
FeatureElement element = (FeatureElement) reference.value();
|
||||
if (FeatureFlags.isExperimental(element.requiredFeatures())) {
|
||||
return SingleFlagHolder.fromSet(element.requiredFeatures());
|
||||
}
|
||||
} else {
|
||||
// data-driven registry
|
||||
return this.experimentalKeys.get().get(reference.key());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.SourceFile;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlag;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static io.papermc.generator.rewriter.utils.Annotations.annotation;
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@NullMarked
|
||||
public class FeatureFlagRewriter extends SearchReplaceRewriter {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
@Override
|
||||
public boolean registerFor(SourceFile file) {
|
||||
try {
|
||||
org.bukkit.FeatureFlag.class.getDeclaredMethod("create", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.error("Fetch method not found, skipping the rewriter for feature flag", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.registerFor(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
Iterator<Map.Entry<ResourceLocation, FeatureFlag>> flagIterator = FeatureFlags.REGISTRY.names.entrySet().stream().sorted(Formatting.alphabeticKeyOrder(entry -> entry.getKey().getPath())).iterator();
|
||||
while (flagIterator.hasNext()) {
|
||||
Map.Entry<ResourceLocation, FeatureFlag> entry = flagIterator.next();
|
||||
ResourceLocation name = entry.getKey();
|
||||
if (FeatureFlags.isExperimental(FeatureFlagSet.of(entry.getValue()))) {
|
||||
builder.append(metadata.indent()).append(annotation(ApiStatus.Experimental.class, this.importCollector)).append('\n');
|
||||
}
|
||||
|
||||
builder.append(metadata.indent());
|
||||
|
||||
builder.append(org.bukkit.FeatureFlag.class.getSimpleName()).append(' ').append(Formatting.formatKeyAsField(name.getPath()));
|
||||
builder.append(" = ");
|
||||
builder.append("create(%s)".formatted(quoted(name.getPath())));
|
||||
builder.append(';');
|
||||
|
||||
builder.append('\n');
|
||||
if (flagIterator.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Iterator;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.bukkit.FeatureFlag;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class PaperFeatureFlagMapping extends SearchReplaceRewriter {
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
Iterator<ResourceLocation> flagIterator = FeatureFlags.REGISTRY.toNames(FeatureFlags.REGISTRY.allFlags()).stream().sorted(Formatting.alphabeticKeyOrder(ResourceLocation::getPath)).iterator();
|
||||
|
||||
while (flagIterator.hasNext()) {
|
||||
ResourceLocation name = flagIterator.next();
|
||||
String keyedName = Formatting.formatKeyAsField(name.getPath());
|
||||
builder.append(metadata.indent());
|
||||
builder.append("%s.%s, %s.%s".formatted(FeatureFlag.class.getSimpleName(), keyedName, FeatureFlags.class.getSimpleName(), keyedName));
|
||||
if (flagIterator.hasNext()) {
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.registry.RegistryEntry;
|
||||
import io.papermc.generator.rewriter.types.Types;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Registry;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class PaperRegistriesRewriter extends SearchReplaceRewriter {
|
||||
|
||||
private void appendEntry(String indent, StringBuilder builder, RegistryEntry<?> entry, boolean canBeDelayed, boolean apiOnly) {
|
||||
builder.append(indent);
|
||||
builder.append("start");
|
||||
builder.append('(');
|
||||
builder.append(Registries.class.getSimpleName()).append('.').append(entry.registryKeyField());
|
||||
builder.append(", ");
|
||||
builder.append(RegistryKey.class.getSimpleName()).append('.').append(entry.registryKeyField());
|
||||
builder.append(").");
|
||||
if (apiOnly) {
|
||||
builder.append("apiOnly(");
|
||||
if (entry.apiClass().isEnum()) {
|
||||
builder.append(this.importCollector.getShortName(Types.PAPER_SIMPLE_REGISTRY)).append("::").append(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, entry.registryKey().location().getPath()));
|
||||
} else {
|
||||
builder.append("() -> ");
|
||||
builder.append(Registry.class.getCanonicalName()).append('.').append(entry.apiRegistryField().orElse(entry.registryKeyField()));
|
||||
}
|
||||
builder.append(')');
|
||||
} else {
|
||||
builder.append("craft(");
|
||||
builder.append(this.importCollector.getShortName(entry.preloadClass())).append(".class");
|
||||
builder.append(", ");
|
||||
|
||||
builder.append(this.importCollector.getShortName(this.getImplClassName(entry))).append("::").append(entry.apiAccessName().equals(ConstantDescs.INIT_NAME) ? "new" : entry.apiAccessName());
|
||||
|
||||
if (entry.canAllowDirect()) {
|
||||
builder.append(", ");
|
||||
builder.append(Boolean.TRUE.toString());
|
||||
}
|
||||
builder.append(')');
|
||||
|
||||
if (entry.fieldRename() != null) {
|
||||
builder.append(".serializationUpdater(").append(Types.FIELD_RENAME.simpleName()).append('.').append(entry.fieldRename()).append(")");
|
||||
}
|
||||
|
||||
if (entry.apiRegistryBuilderImpl() != null) {
|
||||
builder.append(".writable(");
|
||||
builder.append(this.importCollector.getShortName(this.classNamedView.findFirst(entry.apiRegistryBuilderImpl()).resolve(this.classResolver))).append("::new");
|
||||
builder.append(')');
|
||||
} else {
|
||||
builder.append(".build()");
|
||||
}
|
||||
}
|
||||
if (canBeDelayed && entry.isDelayed()) {
|
||||
builder.append(".delayed()");
|
||||
}
|
||||
builder.append(',');
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
builder.append(metadata.indent()).append("// built-in");
|
||||
builder.append('\n');
|
||||
|
||||
for (RegistryEntry<?> entry : RegistryEntries.BUILT_IN) {
|
||||
appendEntry(metadata.indent(), builder, entry, false, false);
|
||||
}
|
||||
|
||||
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('\n');
|
||||
|
||||
for (RegistryEntry<?> entry : RegistryEntries.API_ONLY) {
|
||||
appendEntry(metadata.indent(), builder, entry, false, true);
|
||||
}
|
||||
|
||||
builder.deleteCharAt(builder.length() - 2); // delete extra comma...
|
||||
}
|
||||
|
||||
private ClassNamed getImplClassName(RegistryEntry<?> entry) {
|
||||
try (Stream<ClassNamed> stream = this.classNamedView.find(entry.implClass())) {
|
||||
return stream.map(klass -> klass.resolve(this.classResolver))
|
||||
.filter(klass -> Keyed.class.isAssignableFrom(klass.knownClass())) // todo check handleable/holderable once keyed is gone
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.event.RegistryEventProvider;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static javax.lang.model.element.Modifier.FINAL;
|
||||
import static javax.lang.model.element.Modifier.PUBLIC;
|
||||
import static javax.lang.model.element.Modifier.STATIC;
|
||||
|
||||
@NullMarked
|
||||
public class RegistryEventsRewriter extends SearchReplaceRewriter {
|
||||
|
||||
@Override
|
||||
public void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
RegistryEntries.forEach(entry -> {
|
||||
if (entry.apiRegistryBuilder() != null) {
|
||||
builder.append(metadata.indent());
|
||||
builder.append("%s %s %s ".formatted(PUBLIC, STATIC, FINAL));
|
||||
builder.append(RegistryEventProvider.class.getSimpleName());
|
||||
builder.append("<").append(this.importCollector.getShortName(entry.apiClass())).append(", ").append(this.importCollector.getShortName(entry.apiRegistryBuilder())).append('>');
|
||||
builder.append(' ');
|
||||
builder.append(entry.registryKeyField());
|
||||
builder.append(" = ");
|
||||
builder.append("create(").append(RegistryKey.class.getSimpleName()).append('.').append(entry.registryKeyField()).append(");");
|
||||
builder.append('\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.rewriter.utils.Annotations;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.generator.utils.experimental.ExperimentalCollector;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.SourceFile;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.flag.FeatureElement;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
import static javax.lang.model.element.Modifier.FINAL;
|
||||
import static javax.lang.model.element.Modifier.PUBLIC;
|
||||
import static javax.lang.model.element.Modifier.STATIC;
|
||||
|
||||
@NullMarked
|
||||
public class RegistryFieldRewriter<T> extends SearchReplaceRewriter {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final ResourceKey<? extends Registry<T>> registryKey;
|
||||
private final boolean isFilteredRegistry;
|
||||
private final @Nullable String fetchMethod;
|
||||
|
||||
protected @MonotonicNonNull ClassNamed fieldClass;
|
||||
private @MonotonicNonNull Supplier<Map<ResourceKey<T>, SingleFlagHolder>> experimentalKeys;
|
||||
|
||||
public RegistryFieldRewriter(ResourceKey<? extends Registry<T>> registryKey, @Nullable String fetchMethod) {
|
||||
this.registryKey = registryKey;
|
||||
this.isFilteredRegistry = FeatureElement.FILTERED_REGISTRIES.contains(registryKey);
|
||||
this.fetchMethod = fetchMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerFor(SourceFile file) {
|
||||
this.fieldClass = this.options.targetClass().orElse(file.mainClass());
|
||||
Preconditions.checkState(this.fieldClass.knownClass() != null, "This rewriter can't run without knowing the field class at runtime!");
|
||||
|
||||
if (this.fetchMethod != null) {
|
||||
try {
|
||||
this.fieldClass.knownClass().getDeclaredMethod(this.fetchMethod, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.error("Fetch method not found, skipping the rewriter for registry fields of {}", this.registryKey, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.registerFor(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
boolean isInterface = Objects.requireNonNull(this.fieldClass.knownClass()).isInterface();
|
||||
Registry<T> registry = Main.REGISTRY_ACCESS.lookupOrThrow(this.registryKey);
|
||||
this.experimentalKeys = Suppliers.memoize(() -> ExperimentalCollector.collectDataDrivenElementIds(registry));
|
||||
Iterator<Holder.Reference<T>> referenceIterator = registry.listElements().filter(this::canPrintField).sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath())).iterator();
|
||||
|
||||
while (referenceIterator.hasNext()) {
|
||||
Holder.Reference<T> reference = referenceIterator.next();
|
||||
|
||||
this.rewriteJavadocs(reference, metadata.replacedContent(), metadata.indent(), builder);
|
||||
|
||||
SingleFlagHolder requiredFeature = this.getRequiredFeature(reference);
|
||||
if (requiredFeature != null) {
|
||||
Annotations.experimentalAnnotations(builder, metadata.indent(), this.importCollector, requiredFeature);
|
||||
}
|
||||
|
||||
builder.append(metadata.indent());
|
||||
if (!isInterface) {
|
||||
builder.append("%s %s %s ".formatted(PUBLIC, STATIC, FINAL));
|
||||
}
|
||||
|
||||
builder.append(this.rewriteFieldType(reference)).append(' ').append(this.rewriteFieldName(reference));
|
||||
builder.append(" = ");
|
||||
builder.append(this.rewriteFieldValue(reference));
|
||||
builder.append(';');
|
||||
|
||||
builder.append('\n');
|
||||
if (referenceIterator.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void rewriteJavadocs(Holder.Reference<T> reference, String replacedContent, String indent, StringBuilder builder) {
|
||||
}
|
||||
|
||||
protected boolean canPrintField(Holder.Reference<T> reference) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String rewriteFieldType(Holder.Reference<T> reference) {
|
||||
return this.fieldClass.simpleName();
|
||||
}
|
||||
|
||||
protected String rewriteFieldName(Holder.Reference<T> reference) {
|
||||
return Formatting.formatKeyAsField(reference.key().location().getPath());
|
||||
}
|
||||
|
||||
protected String rewriteFieldValue(Holder.Reference<T> reference) {
|
||||
return "%s(%s)".formatted(this.fetchMethod, quoted(reference.key().location().getPath()));
|
||||
}
|
||||
|
||||
protected @Nullable SingleFlagHolder getRequiredFeature(Holder.Reference<T> reference) {
|
||||
if (this.isFilteredRegistry) {
|
||||
// built-in registry
|
||||
FeatureElement element = (FeatureElement) reference.value();
|
||||
if (FeatureFlags.isExperimental(element.requiredFeatures())) {
|
||||
return SingleFlagHolder.fromSet(element.requiredFeatures());
|
||||
}
|
||||
} else {
|
||||
// data-driven registry
|
||||
return this.experimentalKeys.get().get(reference.key());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.rewriter.utils.Annotations;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.SourceFile;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Iterator;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Tag;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
import static javax.lang.model.element.Modifier.FINAL;
|
||||
import static javax.lang.model.element.Modifier.PUBLIC;
|
||||
import static javax.lang.model.element.Modifier.STATIC;
|
||||
|
||||
@NullMarked
|
||||
@ApiStatus.Obsolete
|
||||
public class RegistryTagRewriter<T> extends SearchReplaceRewriter {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final ResourceKey<? extends Registry<T>> registryKey;
|
||||
private final Class<? extends Keyed> apiClass;
|
||||
private final String fetchMethod = "getTag";
|
||||
|
||||
public RegistryTagRewriter(ResourceKey<? extends Registry<T>> registryKey, Class<? extends Keyed> apiClass) {
|
||||
this.registryKey = registryKey;
|
||||
this.apiClass = apiClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerFor(SourceFile file) {
|
||||
ClassNamed holderClass = this.options.targetClass().orElse(file.mainClass());
|
||||
Preconditions.checkState(holderClass.knownClass() != null, "This rewriter can't run without knowing the field class at runtime!");
|
||||
try {
|
||||
holderClass.knownClass().getDeclaredMethod(this.fetchMethod, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.error("Fetch method not found, skipping the rewriter for registry tag fields of {}", this.registryKey, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.registerFor(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
Registry<T> registry = Main.REGISTRY_ACCESS.lookupOrThrow(this.registryKey);
|
||||
Iterator<? extends TagKey<T>> keyIterator = registry.listTagIds().sorted(Formatting.alphabeticKeyOrder(reference -> reference.location().getPath())).iterator();
|
||||
|
||||
while (keyIterator.hasNext()) {
|
||||
TagKey<T> tagKey = keyIterator.next();
|
||||
|
||||
String featureFlagName = Main.EXPERIMENTAL_TAGS.get(tagKey);
|
||||
if (featureFlagName != null) {
|
||||
Annotations.experimentalAnnotations(builder, metadata.indent(), this.importCollector, SingleFlagHolder.fromName(featureFlagName));
|
||||
}
|
||||
|
||||
builder.append(metadata.indent());
|
||||
builder.append("%s %s %s ".formatted(PUBLIC, STATIC, FINAL));
|
||||
|
||||
builder.append("%s<%s>".formatted(Tag.class.getSimpleName(), this.apiClass.getSimpleName())).append(' ').append(this.rewriteFieldName(tagKey));
|
||||
builder.append(" = ");
|
||||
builder.append(this.rewriteFieldValue(tagKey));
|
||||
builder.append(';');
|
||||
|
||||
builder.append('\n');
|
||||
if (keyIterator.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String rewriteFieldName(TagKey<T> tagKey) {
|
||||
return Formatting.formatKeyAsField(tagKey.location().getPath());
|
||||
}
|
||||
|
||||
protected String rewriteFieldValue(TagKey<T> tagKey) {
|
||||
return "%s(%s)".formatted(this.fetchMethod, quoted(tagKey.location().getPath()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package io.papermc.generator.rewriter.types.registry;
|
||||
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.rewriter.utils.Annotations;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
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.entity.EntityType;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@NullMarked
|
||||
@ApiStatus.Obsolete
|
||||
public class TagRewriter extends SearchReplaceRewriter {
|
||||
|
||||
public record TagRegistry(String legacyFolderName, Class<? extends Keyed> apiType, ResourceKey<? extends Registry<?>> registryKey) { // TODO remove Keyed
|
||||
}
|
||||
|
||||
private static final TagRegistry[] SUPPORTED_REGISTRIES = { // 1.21 folder name are normalized to registry key but api will stay as is
|
||||
new TagRegistry("blocks", Material.class, Registries.BLOCK),
|
||||
new TagRegistry("items", Material.class, Registries.ITEM),
|
||||
new TagRegistry("fluids", Fluid.class, Registries.FLUID),
|
||||
new TagRegistry("entity_types", EntityType.class, Registries.ENTITY_TYPE),
|
||||
new TagRegistry("game_events", GameEvent.class, Registries.GAME_EVENT)
|
||||
// new TagRegistry("damage_types", DamageType.class, Registries.DAMAGE_TYPE) - separate in DamageTypeTags
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
for (int i = 0, len = SUPPORTED_REGISTRIES.length; i < len; i++) {
|
||||
final TagRegistry tagRegistry = SUPPORTED_REGISTRIES[i];
|
||||
|
||||
final ResourceKey<? extends Registry<?>> registryKey = tagRegistry.registryKey();
|
||||
final Registry<?> registry = Main.REGISTRY_ACCESS.lookupOrThrow(registryKey);
|
||||
|
||||
final String fieldPrefix = Formatting.formatTagFieldPrefix(tagRegistry.legacyFolderName(), registryKey);
|
||||
final String registryFieldName = "REGISTRY_" + tagRegistry.legacyFolderName().toUpperCase(Locale.ENGLISH);
|
||||
|
||||
if (i != 0) {
|
||||
builder.append('\n'); // extra line before the registry field
|
||||
}
|
||||
|
||||
// registry name field
|
||||
builder.append(metadata.indent());
|
||||
builder.append("%s %s = %s;".formatted(String.class.getSimpleName(), registryFieldName, quoted(tagRegistry.legacyFolderName())));
|
||||
|
||||
builder.append('\n');
|
||||
builder.append('\n');
|
||||
|
||||
Iterator<? extends TagKey<?>> keyIterator = registry.listTagIds().sorted(Formatting.alphabeticKeyOrder(tagKey -> tagKey.location().getPath())).iterator();
|
||||
|
||||
while (keyIterator.hasNext()) {
|
||||
TagKey<?> tagKey = keyIterator.next();
|
||||
final String keyPath = tagKey.location().getPath();
|
||||
final String fieldName = fieldPrefix + Formatting.formatKeyAsField(keyPath);
|
||||
|
||||
// tag field
|
||||
String featureFlagName = Main.EXPERIMENTAL_TAGS.get(tagKey);
|
||||
if (featureFlagName != null) {
|
||||
Annotations.experimentalAnnotations(builder, metadata.indent(), this.importCollector, SingleFlagHolder.fromName(featureFlagName));
|
||||
}
|
||||
|
||||
builder.append(metadata.indent());
|
||||
builder.append("%s<%s>".formatted(this.source.mainClass().simpleName(), this.importCollector.getShortName(tagRegistry.apiType()))).append(' ').append(fieldName);
|
||||
builder.append(" = ");
|
||||
builder.append("%s.getTag(%s, %s.minecraft(%s), %s.class)".formatted(Bukkit.class.getSimpleName(), registryFieldName, NamespacedKey.class.getSimpleName(), quoted(keyPath), tagRegistry.apiType().getSimpleName())); // assume type is imported properly
|
||||
builder.append(';');
|
||||
|
||||
builder.append('\n');
|
||||
if (keyIterator.hasNext()) {
|
||||
builder.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.typewriter.util.ClassHelper;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.block.BlockType;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class BlockTypeRewriter extends RegistryFieldRewriter<Block> {
|
||||
|
||||
public BlockTypeRewriter() {
|
||||
super(Registries.BLOCK, "getBlockType");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldType(Holder.Reference<Block> reference) {
|
||||
Class<? extends BlockData> blockData = BlockStateMapping.getBestSuitedApiClass(reference.value().getClass());
|
||||
if (blockData == null) {
|
||||
blockData = BlockData.class;
|
||||
}
|
||||
|
||||
return "%s<%s>".formatted(ClassHelper.retrieveFullNestedName(BlockType.Typed.class), this.importCollector.getShortName(blockData));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.rewriter.types.Types;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class CraftBlockDataMapping extends SearchReplaceRewriter {
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
BlockStateMapping.MAPPING.entrySet().stream().sorted(Comparator.comparing(entry -> entry.getKey().getCanonicalName())).forEach(entry -> {
|
||||
builder.append(metadata.indent());
|
||||
builder.append("register(%s.class, %s.block.impl.%s::new);".formatted(entry.getKey().getCanonicalName(), Types.BASE_PACKAGE, entry.getValue().implName()));
|
||||
builder.append('\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.utils.BlockEntityMapping;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
|
||||
public class CraftBlockEntityStateMapping extends SearchReplaceRewriter {
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
BlockEntityMapping.MAPPING.entrySet().stream().sorted(Formatting.alphabeticKeyOrder(entry -> entry.getKey().location().getPath())).forEach(entry -> {
|
||||
builder.append(metadata.indent());
|
||||
builder.append("register(%s.%s, %s.class, %s::new);".formatted(
|
||||
BlockEntityType.class.getSimpleName(), Formatting.formatKeyAsField(entry.getKey().location().getPath()),
|
||||
entry.getValue(), entry.getValue()));
|
||||
builder.append('\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.util.Locale;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.bukkit.potion.PotionType;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class CraftPotionUtilRewriter extends SearchReplaceRewriter {
|
||||
|
||||
private final String statePrefix;
|
||||
|
||||
public CraftPotionUtilRewriter(String statePrefix) {
|
||||
this.statePrefix = statePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
String upperStatePrefix = this.statePrefix.toUpperCase(Locale.ENGLISH);
|
||||
BuiltInRegistries.POTION.keySet().stream()
|
||||
.filter(key -> BuiltInRegistries.POTION.containsKey(key.withPath(path -> this.statePrefix + "_" + path)))
|
||||
.sorted(Formatting.alphabeticKeyOrder(ResourceLocation::getPath)).forEach(key -> {
|
||||
String keyedName = Formatting.formatKeyAsField(key.getPath());
|
||||
builder.append(metadata.indent());
|
||||
builder.append(".put(%s.%s, %s.%s_%s)".formatted(PotionType.class.getSimpleName(), keyedName, PotionType.class.getSimpleName(), upperStatePrefix, keyedName));
|
||||
builder.append('\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
|
||||
import io.papermc.generator.types.goal.MobGoalNames;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import io.papermc.typewriter.util.ClassResolver;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
public class EntityTypeRewriter extends EnumRegistryRewriter<EntityType<?>> {
|
||||
|
||||
private static final Map<ResourceKey<EntityType<?>>, Class<? extends Entity>> ENTITY_GENERIC_TYPES =
|
||||
RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE).getFields(field -> {
|
||||
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1) {
|
||||
return (Class<? extends Entity>) complexType.getActualTypeArguments()[0];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
private static final Map<String, String> CLASS_RENAMES = ImmutableMap.<String, String>builder()
|
||||
.put("ExperienceBottle", "ThrownExpBottle")
|
||||
.put("EyeOfEnder", "EnderSignal")
|
||||
.put("EndCrystal", "EnderCrystal")
|
||||
.put("FireworkRocket", "Firework")
|
||||
.put("FishingBobber", "FishHook")
|
||||
.put("LeashKnot", "LeashHitch")
|
||||
.put("LightningBolt", "LightningStrike")
|
||||
.put("Tnt", "TNTPrimed")
|
||||
.put("ChestMinecart", "StorageMinecart")
|
||||
.put("CommandBlockMinecart", "CommandMinecart")
|
||||
.put("TntMinecart", "ExplosiveMinecart")
|
||||
.put("FurnaceMinecart", "PoweredMinecart")
|
||||
.buildOrThrow();
|
||||
|
||||
@Deprecated
|
||||
private static final Object2IntMap<EntityType<?>> LEGACY_ID = Util.make(new Object2IntOpenHashMap<>(), map -> {
|
||||
map.put(EntityType.ITEM, 1);
|
||||
map.put(EntityType.EXPERIENCE_ORB, 2);
|
||||
map.put(EntityType.AREA_EFFECT_CLOUD, 3);
|
||||
map.put(EntityType.ELDER_GUARDIAN, 4);
|
||||
map.put(EntityType.WITHER_SKELETON, 5);
|
||||
map.put(EntityType.STRAY, 6);
|
||||
map.put(EntityType.EGG, 7);
|
||||
map.put(EntityType.LEASH_KNOT, 8);
|
||||
map.put(EntityType.PAINTING, 9);
|
||||
map.put(EntityType.ARROW, 10);
|
||||
map.put(EntityType.SNOWBALL, 11);
|
||||
map.put(EntityType.FIREBALL, 12);
|
||||
map.put(EntityType.SMALL_FIREBALL, 13);
|
||||
map.put(EntityType.ENDER_PEARL, 14);
|
||||
map.put(EntityType.EYE_OF_ENDER, 15);
|
||||
map.put(EntityType.SPLASH_POTION, 16);
|
||||
map.put(EntityType.EXPERIENCE_BOTTLE, 17);
|
||||
map.put(EntityType.ITEM_FRAME, 18);
|
||||
map.put(EntityType.WITHER_SKULL, 19);
|
||||
map.put(EntityType.TNT, 20);
|
||||
map.put(EntityType.FALLING_BLOCK, 21);
|
||||
map.put(EntityType.FIREWORK_ROCKET, 22);
|
||||
map.put(EntityType.HUSK, 23);
|
||||
map.put(EntityType.SPECTRAL_ARROW, 24);
|
||||
map.put(EntityType.SHULKER_BULLET, 25);
|
||||
map.put(EntityType.DRAGON_FIREBALL, 26);
|
||||
map.put(EntityType.ZOMBIE_VILLAGER, 27);
|
||||
map.put(EntityType.SKELETON_HORSE, 28);
|
||||
map.put(EntityType.ZOMBIE_HORSE, 29);
|
||||
map.put(EntityType.ARMOR_STAND, 30);
|
||||
map.put(EntityType.DONKEY, 31);
|
||||
map.put(EntityType.MULE, 32);
|
||||
map.put(EntityType.EVOKER_FANGS, 33);
|
||||
map.put(EntityType.EVOKER, 34);
|
||||
map.put(EntityType.VEX, 35);
|
||||
map.put(EntityType.VINDICATOR, 36);
|
||||
map.put(EntityType.ILLUSIONER, 37);
|
||||
|
||||
map.put(EntityType.COMMAND_BLOCK_MINECART, 40);
|
||||
map.put(EntityType.MINECART, 42);
|
||||
map.put(EntityType.CHEST_MINECART, 43);
|
||||
map.put(EntityType.FURNACE_MINECART, 44);
|
||||
map.put(EntityType.TNT_MINECART, 45);
|
||||
map.put(EntityType.HOPPER_MINECART, 46);
|
||||
map.put(EntityType.SPAWNER_MINECART, 47);
|
||||
|
||||
map.put(EntityType.CREEPER, 50);
|
||||
map.put(EntityType.SKELETON, 51);
|
||||
map.put(EntityType.SPIDER, 52);
|
||||
map.put(EntityType.GIANT, 53);
|
||||
map.put(EntityType.ZOMBIE, 54);
|
||||
map.put(EntityType.SLIME, 55);
|
||||
map.put(EntityType.GHAST, 56);
|
||||
map.put(EntityType.ZOMBIFIED_PIGLIN, 57);
|
||||
map.put(EntityType.ENDERMAN, 58);
|
||||
map.put(EntityType.CAVE_SPIDER, 59);
|
||||
map.put(EntityType.SILVERFISH, 60);
|
||||
map.put(EntityType.BLAZE, 61);
|
||||
map.put(EntityType.MAGMA_CUBE, 62);
|
||||
map.put(EntityType.ENDER_DRAGON, 63);
|
||||
map.put(EntityType.WITHER, 64);
|
||||
map.put(EntityType.BAT, 65);
|
||||
map.put(EntityType.WITCH, 66);
|
||||
map.put(EntityType.ENDERMITE, 67);
|
||||
map.put(EntityType.GUARDIAN, 68);
|
||||
map.put(EntityType.SHULKER, 69);
|
||||
|
||||
map.put(EntityType.PIG, 90);
|
||||
map.put(EntityType.SHEEP, 91);
|
||||
map.put(EntityType.COW, 92);
|
||||
map.put(EntityType.CHICKEN, 93);
|
||||
map.put(EntityType.SQUID, 94);
|
||||
map.put(EntityType.WOLF, 95);
|
||||
map.put(EntityType.MOOSHROOM, 96);
|
||||
map.put(EntityType.SNOW_GOLEM, 97);
|
||||
map.put(EntityType.OCELOT, 98);
|
||||
map.put(EntityType.IRON_GOLEM, 99);
|
||||
map.put(EntityType.HORSE, 100);
|
||||
map.put(EntityType.RABBIT, 101);
|
||||
map.put(EntityType.POLAR_BEAR, 102);
|
||||
map.put(EntityType.LLAMA, 103);
|
||||
map.put(EntityType.LLAMA_SPIT, 104);
|
||||
map.put(EntityType.PARROT, 105);
|
||||
|
||||
map.put(EntityType.VILLAGER, 120);
|
||||
|
||||
map.put(EntityType.END_CRYSTAL, 200);
|
||||
});
|
||||
|
||||
private static final ClassResolver runtime = new ClassResolver(EntityTypeRewriter.class.getClassLoader());
|
||||
|
||||
public EntityTypeRewriter() {
|
||||
super(Registries.ENTITY_TYPE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<EntityType<?>> reference) {
|
||||
String path = reference.key().location().getPath();
|
||||
List<String> arguments = new ArrayList<>(4);
|
||||
arguments.add(quoted(path));
|
||||
arguments.add(toBukkitClass(reference).concat(".class"));
|
||||
arguments.add(Integer.toString(LEGACY_ID.getOrDefault(reference.value(), -1)));
|
||||
|
||||
if (!reference.value().canSummon()) {
|
||||
arguments.add(Boolean.FALSE.toString());
|
||||
}
|
||||
return super.rewriteEnumValue(reference).arguments(arguments);
|
||||
}
|
||||
|
||||
private String toBukkitClass(Holder.Reference<EntityType<?>> reference) {
|
||||
Class<? extends Entity> internalClass = ENTITY_GENERIC_TYPES.get(reference.key());
|
||||
if (Mob.class.isAssignableFrom(internalClass)) {
|
||||
return this.importCollector.getShortName(MobGoalNames.bukkitMap.get((Class<? extends Mob>) internalClass));
|
||||
}
|
||||
|
||||
String className = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, reference.key().location().getPath()); // use the key instead of the internal class name since name match a bit more
|
||||
ClassNamed resolvedClass = this.classNamedView.findFirst(CLASS_RENAMES.getOrDefault(className, className)).resolve(runtime);
|
||||
Preconditions.checkArgument(org.bukkit.entity.Entity.class.isAssignableFrom(resolvedClass.knownClass()), "Generic type must be an entity");
|
||||
return this.importCollector.getShortName(this.classNamedView.findFirst(CLASS_RENAMES.getOrDefault(className, className)).resolve(runtime));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.typewriter.util.ClassHelper;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.bukkit.inventory.ItemType;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
@Deprecated // bad generic
|
||||
public class ItemTypeRewriter extends RegistryFieldRewriter<Item> {
|
||||
|
||||
public ItemTypeRewriter() {
|
||||
super(Registries.ITEM, "getItemType");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldType(Holder.Reference<Item> reference) {
|
||||
if (reference.value().equals(Items.AIR)) {
|
||||
return super.rewriteFieldType(reference);
|
||||
}
|
||||
|
||||
return "%s<%s>".formatted(ClassHelper.retrieveFullNestedName(ItemType.Typed.class), ItemMeta.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import java.awt.Color;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class MapPaletteRewriter extends SearchReplaceRewriter {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final boolean UPDATING = Boolean.getBoolean("paper.updatingMinecraft");
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
int count = 0;
|
||||
for (@Nullable MapColor mapColor : MapColor.MATERIAL_COLORS) {
|
||||
if (mapColor == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (MapColor.Brightness brightness : MapColor.Brightness.values()) {
|
||||
builder.append(metadata.indent());
|
||||
Color color = new Color(mapColor.calculateARGBColor(brightness), true);
|
||||
if (color.getAlpha() != 0xFF) {
|
||||
builder.append("new %s(0x%08X, true),".formatted(color.getClass().getSimpleName(), color.getRGB()));
|
||||
} else {
|
||||
builder.append("new %s(0x%06X),".formatted(color.getClass().getSimpleName(), color.getRGB() & 0x00FFFFFF));
|
||||
}
|
||||
builder.append('\n');
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (UPDATING) {
|
||||
LOGGER.warn("There are {} map colors, check CraftMapView#render for possible change and update md5 hash in CraftMapColorCache", count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.WallSignBlock;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.asCode;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class MaterialRewriter {
|
||||
|
||||
// blocks
|
||||
|
||||
public static class Blocks extends EnumRegistryRewriter<Block> {
|
||||
|
||||
public Blocks() {
|
||||
super(Registries.BLOCK, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Holder.Reference<Block>> getValues() {
|
||||
return BuiltInRegistries.BLOCK.listElements().filter(reference -> !reference.value().equals(net.minecraft.world.level.block.Blocks.AIR))
|
||||
.sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath()))::iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<Block> reference) {
|
||||
EnumValue.Builder value = super.rewriteEnumValue(reference);
|
||||
Block block = reference.value();
|
||||
if (BlockStateMapping.MAPPING.containsKey(block.getClass())) {
|
||||
// some block can also be represented as item in that enum
|
||||
// doing a double job
|
||||
Optional<Item> equivalentItem = BuiltInRegistries.ITEM.getOptional(reference.key().location());
|
||||
|
||||
if (equivalentItem.isEmpty() && block instanceof WallSignBlock) {
|
||||
// wall sign block stack size is 16 for some reason like the sign item?
|
||||
// but that rule doesn't work for the wall hanging sign block??
|
||||
equivalentItem = Optional.of(block.asItem());
|
||||
}
|
||||
|
||||
Class<?> blockData = BlockStateMapping.getBestSuitedApiClass(block.getClass());
|
||||
if (blockData == null) {
|
||||
blockData = BlockData.class;
|
||||
}
|
||||
if (equivalentItem.isPresent() && equivalentItem.get().getDefaultMaxStackSize() != Item.DEFAULT_MAX_STACK_SIZE) {
|
||||
return value.arguments(Integer.toString(-1), Integer.toString(equivalentItem.get().getDefaultMaxStackSize()), this.importCollector.getShortName(blockData).concat(".class"));
|
||||
}
|
||||
return value.arguments(Integer.toString(-1), this.importCollector.getShortName(blockData).concat(".class"));
|
||||
}
|
||||
return value.argument(Integer.toString(-1)); // id not needed for non legacy material
|
||||
}
|
||||
}
|
||||
|
||||
/* todo test is broken
|
||||
public static class IsTransparent extends SwitchCaseRewriter {
|
||||
|
||||
public IsTransparent() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<String> getCases() {
|
||||
return BuiltInRegistries.BLOCK.holders().filter(reference -> reference.value().defaultBlockState().useShapeForLightOcclusion())
|
||||
.map(reference -> reference.key().location().getPath().toUpperCase(Locale.ENGLISH)).sorted(Formatting.ALPHABETIC_KEY_ORDER)::iterator;
|
||||
}
|
||||
}*/
|
||||
|
||||
// items
|
||||
|
||||
public static class Items extends EnumRegistryRewriter<Item> {
|
||||
|
||||
public Items() {
|
||||
super(Registries.ITEM, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Holder.Reference<Item>> getValues() {
|
||||
return BuiltInRegistries.ITEM.listElements().filter(reference -> BuiltInRegistries.BLOCK.getOptional(reference.key().location()).isEmpty() || reference.value().equals(net.minecraft.world.item.Items.AIR))
|
||||
.sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath()))::iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<Item> reference) {
|
||||
EnumValue.Builder value = super.rewriteEnumValue(reference);
|
||||
Item item = reference.value();
|
||||
int maxStackSize = item.getDefaultMaxStackSize();
|
||||
if (maxStackSize != Item.DEFAULT_MAX_STACK_SIZE) {
|
||||
return value.arguments(asCode(-1, maxStackSize));
|
||||
}
|
||||
|
||||
return value.argument(Integer.toString(-1)); // id not needed for non legacy material
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import com.google.gson.internal.Primitives;
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ai.behavior.PositionTracker;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@Deprecated
|
||||
public class MemoryKeyRewriter extends RegistryFieldRewriter<MemoryModuleType<?>> {
|
||||
|
||||
private static final Map<ResourceKey<MemoryModuleType<?>>, Class<?>> MEMORY_GENERIC_TYPES = RegistryEntries.byRegistryKey(Registries.MEMORY_MODULE_TYPE).getFields(field -> {
|
||||
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1) {
|
||||
return ClassHelper.eraseType(complexType.getActualTypeArguments()[0]);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
public MemoryKeyRewriter() {
|
||||
super(Registries.MEMORY_MODULE_TYPE, null);
|
||||
}
|
||||
|
||||
// this api is not implemented and is not backed by a proper registry
|
||||
private static final Set<Class<?>> IGNORED_TYPES = Set.of(
|
||||
NearestVisibleLivingEntities.class,
|
||||
WalkTarget.class,
|
||||
PositionTracker.class,
|
||||
Path.class,
|
||||
DamageSource.class,
|
||||
Vec3.class,
|
||||
BlockPos.class,
|
||||
Unit.class,
|
||||
Void.class
|
||||
);
|
||||
|
||||
private static final Set<Class<?>> IGNORED_SUB_TYPES = Set.of(
|
||||
Iterable.class,
|
||||
Map.class,
|
||||
Entity.class
|
||||
);
|
||||
|
||||
private static final Map<Class<?>, Class<?>> API_BRIDGE = Map.of(
|
||||
GlobalPos.class, Location.class
|
||||
);
|
||||
|
||||
private static final Map<String, String> FIELD_RENAMES = Map.of(
|
||||
"LIKED_NOTEBLOCK", "LIKED_NOTEBLOCK_POSITION"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected boolean canPrintField(Holder.Reference<MemoryModuleType<?>> reference) {
|
||||
Class<?> memoryType = MEMORY_GENERIC_TYPES.get(reference.key());
|
||||
if (IGNORED_TYPES.contains(memoryType)) {
|
||||
return false;
|
||||
}
|
||||
for (Class<?> subType : IGNORED_SUB_TYPES) {
|
||||
if (subType.isAssignableFrom(memoryType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private @MonotonicNonNull Class<?> apiMemoryType;
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldType(Holder.Reference<MemoryModuleType<?>> reference) {
|
||||
Class<?> memoryType = MEMORY_GENERIC_TYPES.get(reference.key());
|
||||
|
||||
if (!Primitives.isWrapperType(memoryType) && API_BRIDGE.containsKey(memoryType)) {
|
||||
this.apiMemoryType = API_BRIDGE.get(memoryType);
|
||||
} else {
|
||||
this.apiMemoryType = memoryType;
|
||||
}
|
||||
|
||||
return "%s<%s>".formatted(this.fieldClass.simpleName(), this.importCollector.getShortName(this.apiMemoryType));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldName(Holder.Reference<MemoryModuleType<?>> reference) {
|
||||
String keyedName = super.rewriteFieldName(reference);
|
||||
return FIELD_RENAMES.getOrDefault(keyedName, keyedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldValue(Holder.Reference<MemoryModuleType<?>> reference) {
|
||||
return "new %s<>(%s.minecraft(%s), %s.class)".formatted(
|
||||
this.fieldClass.simpleName(),
|
||||
NamespacedKey.class.getSimpleName(),
|
||||
quoted(reference.key().location().getPath()),
|
||||
this.apiMemoryType.getSimpleName() // assume the type is already import (see above in rewriteFieldType)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.papermc.generator.rewriter.types.registry.EnumRegistryRewriter;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.typewriter.preset.model.EnumValue;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.stats.StatType;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.Statistic;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class StatisticRewriter {
|
||||
|
||||
private static final Map<String, String> FIELD_RENAMES = ImmutableMap.<String, String>builder()
|
||||
.put("DROP", "DROP_COUNT")
|
||||
.put("DROPPED", "DROP")
|
||||
.put("PICKED_UP", "PICKUP")
|
||||
.put("PLAY_TIME", "PLAY_ONE_MINUTE")
|
||||
.put("CROUCH_TIME", "SNEAK_TIME")
|
||||
.put("MINED", "MINE_BLOCK")
|
||||
.put("USED", "USE_ITEM")
|
||||
.put("BROKEN", "BREAK_ITEM")
|
||||
.put("CRAFTED", "CRAFT_ITEM")
|
||||
.put("KILLED", "KILL_ENTITY")
|
||||
.put("KILLED_BY", "ENTITY_KILLED_BY")
|
||||
.put("EAT_CAKE_SLICE", "CAKE_SLICES_EATEN")
|
||||
.put("FILL_CAULDRON", "CAULDRON_FILLED")
|
||||
.put("USE_CAULDRON", "CAULDRON_USED")
|
||||
.put("CLEAN_ARMOR", "ARMOR_CLEANED")
|
||||
.put("CLEAN_BANNER", "BANNER_CLEANED")
|
||||
.put("INTERACT_WITH_BREWINGSTAND", "BREWINGSTAND_INTERACTION")
|
||||
.put("INTERACT_WITH_BEACON", "BEACON_INTERACTION")
|
||||
.put("INSPECT_DROPPER", "DROPPER_INSPECTED")
|
||||
.put("INSPECT_HOPPER", "HOPPER_INSPECTED")
|
||||
.put("INSPECT_DISPENSER", "DISPENSER_INSPECTED")
|
||||
.put("PLAY_NOTEBLOCK", "NOTEBLOCK_PLAYED")
|
||||
.put("TUNE_NOTEBLOCK", "NOTEBLOCK_TUNED")
|
||||
.put("POT_FLOWER", "FLOWER_POTTED")
|
||||
.put("TRIGGER_TRAPPED_CHEST", "TRAPPED_CHEST_TRIGGERED")
|
||||
.put("OPEN_ENDERCHEST", "ENDERCHEST_OPENED")
|
||||
.put("ENCHANT_ITEM", "ITEM_ENCHANTED")
|
||||
.put("PLAY_RECORD", "RECORD_PLAYED")
|
||||
.put("INTERACT_WITH_FURNACE", "FURNACE_INTERACTION")
|
||||
.put("INTERACT_WITH_CRAFTING_TABLE", "CRAFTING_TABLE_INTERACTION")
|
||||
.put("OPEN_CHEST", "CHEST_OPENED")
|
||||
.put("OPEN_SHULKER_BOX", "SHULKER_BOX_OPENED")
|
||||
.buildOrThrow();
|
||||
|
||||
public static class Custom extends EnumRegistryRewriter<ResourceLocation> {
|
||||
|
||||
public Custom() {
|
||||
super(Registries.CUSTOM_STAT, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<ResourceLocation> reference) {
|
||||
return super.rewriteEnumValue(reference).rename(name -> FIELD_RENAMES.getOrDefault(name, name));
|
||||
}
|
||||
}
|
||||
|
||||
public static class CraftCustom extends EnumRegistryRewriter<ResourceLocation> {
|
||||
|
||||
private static final Map<String, String> INTERNAL_FIELD_RENAMES = Map.of(
|
||||
"SNEAK_TIME", "CROUCH_TIME"
|
||||
);
|
||||
|
||||
public CraftCustom() {
|
||||
super(Registries.CUSTOM_STAT, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<ResourceLocation> reference) {
|
||||
String keyedName = Formatting.formatKeyAsField(reference.key().location().getPath());
|
||||
|
||||
return super.rewriteEnumValue(reference)
|
||||
.rename(name -> FIELD_RENAMES.getOrDefault(name, name))
|
||||
.argument("%s.%s".formatted(Stats.class.getSimpleName(), INTERNAL_FIELD_RENAMES.getOrDefault(keyedName, keyedName)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Type extends EnumRegistryRewriter<StatType<?>> {
|
||||
|
||||
private static final Map<Class<?>, String> TYPE_MAPPING = Map.of(
|
||||
Item.class, "ITEM",
|
||||
Block.class, "BLOCK",
|
||||
EntityType.class, "ENTITY"
|
||||
);
|
||||
|
||||
private static final Map<StatType<?>, Class<?>> FIELD_GENERIC_TYPE;
|
||||
|
||||
static {
|
||||
final Map<StatType<?>, Class<?>> map = new IdentityHashMap<>();
|
||||
|
||||
try {
|
||||
for (Field field : Stats.class.getDeclaredFields()) {
|
||||
if (field.getType() != StatType.class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
|
||||
java.lang.reflect.Type genericType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
|
||||
map.put((StatType<?>) field.get(null), ClassHelper.eraseType(genericType));
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
FIELD_GENERIC_TYPE = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public Type() {
|
||||
super(Registries.STAT_TYPE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Holder.Reference<StatType<?>>> getValues() {
|
||||
return BuiltInRegistries.STAT_TYPE.listElements().filter(reference -> reference.value() != Stats.CUSTOM)
|
||||
.sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath()))::iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<StatType<?>> reference) {
|
||||
Class<?> genericType = FIELD_GENERIC_TYPE.get(reference.value());
|
||||
if (!TYPE_MAPPING.containsKey(genericType)) {
|
||||
throw new IllegalStateException("Unable to translate stat type generic " + genericType.getCanonicalName() + " into the api!");
|
||||
}
|
||||
|
||||
return super.rewriteEnumValue(reference)
|
||||
.rename(name -> FIELD_RENAMES.getOrDefault(name, name))
|
||||
.argument("%s.%s".formatted(Statistic.Type.class.getSimpleName(), TYPE_MAPPING.get(genericType))); // find a more direct way?
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class CraftType extends EnumRegistryRewriter<StatType<?>> {
|
||||
|
||||
public CraftType() {
|
||||
super(Registries.STAT_TYPE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Holder.Reference<StatType<?>>> getValues() {
|
||||
return BuiltInRegistries.STAT_TYPE.listElements().filter(reference -> reference.value() != Stats.CUSTOM)
|
||||
.sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath()))::iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumValue.Builder rewriteEnumValue(Holder.Reference<StatType<?>> reference) {
|
||||
return super.rewriteEnumValue(reference)
|
||||
.rename(name -> FIELD_RENAMES.getOrDefault(name, name))
|
||||
.argument("%s.withDefaultNamespace(%s)".formatted(ResourceLocation.class.getSimpleName(), quoted(reference.key().location().getPath())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.generator.rewriter.types.simple;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@ -0,0 +1,261 @@
|
||||
package io.papermc.generator.rewriter.types.simple.trial;
|
||||
|
||||
import com.google.gson.internal.Primitives;
|
||||
import com.mojang.serialization.Codec;
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.paper.datacomponent.item.BlockItemDataProperties;
|
||||
import io.papermc.paper.datacomponent.item.ItemAdventurePredicate;
|
||||
import io.papermc.paper.datacomponent.item.ItemArmorTrim;
|
||||
import io.papermc.typewriter.ClassNamed;
|
||||
import io.papermc.typewriter.parser.Lexer;
|
||||
import io.papermc.typewriter.parser.sequence.SequenceTokens;
|
||||
import io.papermc.typewriter.parser.sequence.TokenTaskBuilder;
|
||||
import io.papermc.typewriter.parser.token.CharSequenceBlockToken;
|
||||
import io.papermc.typewriter.parser.token.CharSequenceToken;
|
||||
import io.papermc.typewriter.parser.token.TokenType;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.AdventureModePredicate;
|
||||
import net.minecraft.world.item.Instrument;
|
||||
import net.minecraft.world.item.Rarity;
|
||||
import net.minecraft.world.item.component.BlockItemStateProperties;
|
||||
import net.minecraft.world.item.component.FireworkExplosion;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.equipment.trim.ArmorTrim;
|
||||
import org.bukkit.FireworkEffect;
|
||||
import org.bukkit.MusicInstrument;
|
||||
import org.bukkit.inventory.ItemRarity;
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.index.qual.Positive;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static io.papermc.generator.utils.Formatting.quoted;
|
||||
|
||||
public class DataComponentTypesRewriter extends RegistryFieldRewriter<DataComponentType<?>> {
|
||||
|
||||
public DataComponentTypesRewriter() {
|
||||
super(Registries.DATA_COMPONENT_TYPE, null);
|
||||
}
|
||||
|
||||
private static final Set<TokenType> FORMAT_TOKENS = EnumSet.of(
|
||||
TokenType.COMMENT,
|
||||
TokenType.SINGLE_COMMENT
|
||||
);
|
||||
|
||||
private @MonotonicNonNull Map<String, CharSequenceBlockToken> javadocsPerConstant;
|
||||
|
||||
private Map<String, CharSequenceBlockToken> parseConstantJavadocs(String content) {
|
||||
Map<String, CharSequenceBlockToken> map = new HashMap<>();
|
||||
|
||||
Lexer lex = new Lexer(content.toCharArray());
|
||||
lex.checkMarkdownDocComments = !this.sourcesMetadata.canSkipMarkdownDocComments();
|
||||
SequenceTokens.wrap(lex, FORMAT_TOKENS)
|
||||
.group(action -> {
|
||||
ProtoConstant constant = new ProtoConstant();
|
||||
action
|
||||
.map(TokenType.JAVADOC, token -> {
|
||||
constant.javadocs(((CharSequenceBlockToken) token));
|
||||
}, TokenTaskBuilder::asOptional)
|
||||
.skip(TokenType.PUBLIC).skip(TokenType.STATIC).skip(TokenType.FINAL)
|
||||
.skipQualifiedName(Predicate.isEqual(TokenType.JAVADOC))
|
||||
.skipClosure(TokenType.LT, TokenType.GT, true, TokenTaskBuilder::asOptional) // skip generic
|
||||
.map(TokenType.IDENTIFIER, token -> {
|
||||
constant.name(((CharSequenceToken) token).value());
|
||||
})
|
||||
.skip(TokenType.IDENTIFIER)
|
||||
.skipClosure(TokenType.LPAREN, TokenType.RPAREN, true)
|
||||
.map(TokenType.SECO, $ -> {
|
||||
if (constant.isComplete()) {
|
||||
map.put(constant.name(), constant.javadocs());
|
||||
}
|
||||
});
|
||||
}, TokenTaskBuilder::asRepeatable)
|
||||
.executeOrThrow();
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static final Set<DataComponentType<?>> UNSUPPORTED_TYPES = Set.of(
|
||||
DataComponents.CUSTOM_DATA,
|
||||
DataComponents.CREATIVE_SLOT_LOCK,
|
||||
DataComponents.DEBUG_STICK_STATE,
|
||||
DataComponents.ENTITY_DATA,
|
||||
DataComponents.BUCKET_ENTITY_DATA,
|
||||
DataComponents.BLOCK_ENTITY_DATA,
|
||||
DataComponents.BEES,
|
||||
DataComponents.LOCK
|
||||
);
|
||||
|
||||
private static final Map<ResourceKey<DataComponentType<?>>, Type> COMPONENT_GENERIC_TYPES = RegistryEntries.byRegistryKey(Registries.DATA_COMPONENT_TYPE).getFields(field -> {
|
||||
if (field.getGenericType() instanceof ParameterizedType complexType && complexType.getActualTypeArguments().length == 1) {
|
||||
return complexType.getActualTypeArguments()[0];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
private static final Map<Class<?>, Class<?>> API_BRIDGE = Map.of(
|
||||
Component.class, net.kyori.adventure.text.Component.class,
|
||||
ResourceLocation.class, Key.class,
|
||||
Instrument.class, MusicInstrument.class,
|
||||
FireworkExplosion.class, FireworkEffect.class,
|
||||
Rarity.class, ItemRarity.class,
|
||||
ArmorTrim.class, ItemArmorTrim.class,
|
||||
// renames
|
||||
BlockItemStateProperties.class, BlockItemDataProperties.class,
|
||||
AdventureModePredicate.class, ItemAdventurePredicate.class
|
||||
);
|
||||
|
||||
@Deprecated
|
||||
private static final Map<String, String> FIELD_RENAMES = Map.of(
|
||||
"BLOCK_STATE", "BLOCK_DATA"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
this.javadocsPerConstant = parseConstantJavadocs(metadata.replacedContent());
|
||||
super.insert(metadata, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canPrintField(Holder.Reference<DataComponentType<?>> reference) {
|
||||
return !UNSUPPORTED_TYPES.contains(reference.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void rewriteJavadocs(Holder.Reference<DataComponentType<?>> reference, String replacedContent, String indent, StringBuilder builder) {
|
||||
String constantName = this.rewriteFieldName(reference);
|
||||
if (this.javadocsPerConstant.containsKey(constantName)) {
|
||||
CharSequenceBlockToken token = this.javadocsPerConstant.get(constantName);
|
||||
builder.append(indent).append(replacedContent, token.pos(), token.endPos()).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValued;
|
||||
|
||||
private Class<?> handleParameterizedType(Type type) {
|
||||
if (type instanceof ParameterizedType complexType) {
|
||||
Type[] args = complexType.getActualTypeArguments();
|
||||
if (args.length != 1) {
|
||||
throw new UnsupportedOperationException("Unsupported type " + complexType);
|
||||
}
|
||||
|
||||
Class<?> baseClass = ClassHelper.eraseType(complexType);
|
||||
if (baseClass == Holder.class) {
|
||||
return ClassHelper.eraseType(args[0]);
|
||||
}
|
||||
if (baseClass == ResourceKey.class) {
|
||||
Class<?> componentClass = ClassHelper.eraseType(args[0]);
|
||||
if (componentClass == Recipe.class) {
|
||||
return ResourceLocation.class; // special case recipe registry is not really a thing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unsupported type " + type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldType(Holder.Reference<DataComponentType<?>> reference) {
|
||||
Type componentType = COMPONENT_GENERIC_TYPES.get(reference.key());
|
||||
this.isValued = componentType != Unit.class;
|
||||
if (this.isValued) {
|
||||
Class<?> componentClass = null;
|
||||
UnaryOperator<String> tryToWrap = UnaryOperator.identity();
|
||||
if (!reference.value().isTransient()) {
|
||||
final Class<? extends Annotation> annotation = getEquivalentAnnotation(reference.value().codecOrThrow());
|
||||
if (annotation != null) {
|
||||
tryToWrap = value -> "@%s %s".formatted(this.importCollector.getShortName(annotation), value);
|
||||
}
|
||||
}
|
||||
|
||||
if (componentType instanceof Class<?> clazz) {
|
||||
componentClass = clazz;
|
||||
} else if (componentType instanceof ParameterizedType complexType) {
|
||||
Type[] args = complexType.getActualTypeArguments();
|
||||
if (args.length != 1) {
|
||||
throw new UnsupportedOperationException("Unsupported type " + componentType);
|
||||
}
|
||||
|
||||
Class<?> baseClass = ClassHelper.eraseType(complexType);
|
||||
if (baseClass == List.class) {
|
||||
tryToWrap = value -> "%s<%s>".formatted(this.importCollector.getShortName(List.class), value);
|
||||
componentClass = this.handleParameterizedType(args[0]);
|
||||
} else {
|
||||
componentClass = this.handleParameterizedType(complexType);
|
||||
}
|
||||
}
|
||||
|
||||
if (componentClass == null) {
|
||||
throw new UnsupportedOperationException("Unsupported type " + componentType);
|
||||
}
|
||||
|
||||
Class<?> apiComponentClass = null;
|
||||
if (Primitives.isWrapperType(componentClass)) {
|
||||
apiComponentClass = componentClass;
|
||||
} else if (API_BRIDGE.containsKey(componentClass)) {
|
||||
apiComponentClass = API_BRIDGE.get(componentClass);
|
||||
}
|
||||
|
||||
final ClassNamed finalClass;
|
||||
if (apiComponentClass == null) {
|
||||
finalClass = this.classNamedView.tryFindFirst(io.papermc.typewriter.util.ClassHelper.retrieveFullNestedName(componentClass)).orElse(null);
|
||||
} else {
|
||||
finalClass = new ClassNamed(apiComponentClass);
|
||||
}
|
||||
return "%s.%s<%s>".formatted(
|
||||
io.papermc.paper.datacomponent.DataComponentType.class.getSimpleName(),
|
||||
io.papermc.paper.datacomponent.DataComponentType.Valued.class.getSimpleName(),
|
||||
tryToWrap.apply(Optional.ofNullable(finalClass).map(this.importCollector::getShortName).orElse(componentClass.getSimpleName()))
|
||||
);
|
||||
} else {
|
||||
return "%s.%s".formatted(
|
||||
io.papermc.paper.datacomponent.DataComponentType.class.getSimpleName(),
|
||||
io.papermc.paper.datacomponent.DataComponentType.NonValued.class.getSimpleName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Class<? extends Annotation> getEquivalentAnnotation(Codec<?> codec) {
|
||||
Class<? extends Annotation> annotation = null; // int range maybe?
|
||||
if (codec == ExtraCodecs.POSITIVE_INT || codec == ExtraCodecs.POSITIVE_FLOAT) {
|
||||
annotation = Positive.class;
|
||||
} else if (codec == ExtraCodecs.NON_NEGATIVE_INT || codec == ExtraCodecs.NON_NEGATIVE_FLOAT) {
|
||||
annotation = NonNegative.class;
|
||||
}
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldName(Holder.Reference<DataComponentType<?>> reference) {
|
||||
String keyedName = super.rewriteFieldName(reference);
|
||||
return FIELD_RENAMES.getOrDefault(keyedName, keyedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String rewriteFieldValue(Holder.Reference<DataComponentType<?>> reference) {
|
||||
return "%s(%s)".formatted(this.isValued ? "valued" : "unvalued", quoted(reference.key().location().getPath()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package io.papermc.generator.rewriter.types.simple.trial;
|
||||
|
||||
import io.papermc.typewriter.parser.token.CharSequenceBlockToken;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
class ProtoConstant {
|
||||
|
||||
private @MonotonicNonNull String name;
|
||||
private @MonotonicNonNull CharSequenceBlockToken token;
|
||||
|
||||
public void name(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void javadocs(CharSequenceBlockToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public CharSequenceBlockToken javadocs() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return this.name != null && this.token != null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package io.papermc.generator.rewriter.types.simple.trial;
|
||||
|
||||
import io.papermc.generator.rewriter.types.registry.RegistryFieldRewriter;
|
||||
import io.papermc.typewriter.parser.Lexer;
|
||||
import io.papermc.typewriter.parser.sequence.SequenceTokens;
|
||||
import io.papermc.typewriter.parser.sequence.TokenTaskBuilder;
|
||||
import io.papermc.typewriter.parser.token.CharSequenceBlockToken;
|
||||
import io.papermc.typewriter.parser.token.CharSequenceToken;
|
||||
import io.papermc.typewriter.parser.token.TokenType;
|
||||
import io.papermc.typewriter.replace.SearchMetadata;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
public class VillagerProfessionRewriter extends RegistryFieldRewriter<VillagerProfession> {
|
||||
|
||||
public VillagerProfessionRewriter() {
|
||||
super(Registries.VILLAGER_PROFESSION, "getProfession");
|
||||
}
|
||||
|
||||
private static final Set<TokenType> FORMAT_TOKENS = EnumSet.of(
|
||||
TokenType.COMMENT,
|
||||
TokenType.SINGLE_COMMENT
|
||||
);
|
||||
|
||||
private @MonotonicNonNull Map<String, CharSequenceBlockToken> javadocsPerConstant;
|
||||
|
||||
private Map<String, CharSequenceBlockToken> parseConstantJavadocs(String content) {
|
||||
Map<String, CharSequenceBlockToken> map = new HashMap<>();
|
||||
|
||||
Lexer lex = new Lexer(content.toCharArray());
|
||||
lex.checkMarkdownDocComments = !this.sourcesMetadata.canSkipMarkdownDocComments();
|
||||
SequenceTokens.wrap(lex, FORMAT_TOKENS)
|
||||
.group(action -> {
|
||||
ProtoConstant constant = new ProtoConstant();
|
||||
action
|
||||
.map(TokenType.JAVADOC, token -> {
|
||||
constant.javadocs(((CharSequenceBlockToken) token));
|
||||
}, TokenTaskBuilder::asOptional)
|
||||
.skipQualifiedName(Predicate.isEqual(TokenType.JAVADOC))
|
||||
.map(TokenType.IDENTIFIER, token -> {
|
||||
constant.name(((CharSequenceToken) token).value());
|
||||
})
|
||||
.skip(TokenType.IDENTIFIER)
|
||||
.skipClosure(TokenType.LPAREN, TokenType.RPAREN, true)
|
||||
.map(TokenType.SECO, $ -> {
|
||||
if (constant.isComplete()) {
|
||||
map.put(constant.name(), constant.javadocs());
|
||||
}
|
||||
});
|
||||
}, TokenTaskBuilder::asRepeatable)
|
||||
.executeOrThrow();
|
||||
/*
|
||||
for enums:
|
||||
Set<TokenType> endMarkers = Set.of(TokenType.CO, TokenType.SECO); // move to static
|
||||
SequenceTokens.wrap(lex, FORMAT_TOKENS)
|
||||
.group(action -> {
|
||||
ProtoConstant constant = new ProtoConstant();
|
||||
action
|
||||
.map(TokenType.JAVADOC, token -> {
|
||||
constant.javadocs(((CharSequenceBlockToken) token).value());
|
||||
}, TokenTaskBuilder::asOptional)
|
||||
.map(TokenType.IDENTIFIER, token -> {
|
||||
constant.name(((CharSequenceToken) token).value());
|
||||
})
|
||||
.skipClosure(TokenType.LPAREN, TokenType.RPAREN, true)
|
||||
.skipClosure(TokenType.LSCOPE, TokenType.RSCOPE, true)
|
||||
.map(endMarkers::contains, $ -> {
|
||||
// this part will probably fail for the last entry for enum without end (,;)
|
||||
if (constant.isComplete()) {
|
||||
map.put(constant.name(), constant.javadocs());
|
||||
}
|
||||
});
|
||||
}, TokenTaskBuilder::asRepeatable)
|
||||
.executeOrThrow();
|
||||
*/
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void insert(SearchMetadata metadata, StringBuilder builder) {
|
||||
this.javadocsPerConstant = parseConstantJavadocs(metadata.replacedContent());
|
||||
super.insert(metadata, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void rewriteJavadocs(Holder.Reference<VillagerProfession> reference, String replacedContent, String indent, StringBuilder builder) {
|
||||
String constantName = this.rewriteFieldName(reference);
|
||||
if (this.javadocsPerConstant.containsKey(constantName)) {
|
||||
CharSequenceBlockToken token = this.javadocsPerConstant.get(constantName);
|
||||
builder.append(indent).append(replacedContent, token.pos(), token.endPos()).append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
package io.papermc.generator.rewriter.types.simple.trial;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@ -0,0 +1,48 @@
|
||||
package io.papermc.generator.rewriter.utils;
|
||||
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.typewriter.context.ImportCollector;
|
||||
import io.papermc.typewriter.util.ClassHelper;
|
||||
import java.lang.annotation.Annotation;
|
||||
import org.bukkit.MinecraftExperimental;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Annotations {
|
||||
|
||||
public static String annotation(Class<? extends Annotation> clazz, ImportCollector collector) {
|
||||
return "@%s".formatted(collector.getShortName(clazz));
|
||||
}
|
||||
|
||||
public static String annotationStyle(Class<? extends Annotation> clazz) {
|
||||
return "@%s".formatted(ClassHelper.retrieveFullNestedName(clazz));
|
||||
}
|
||||
|
||||
public static String annotation(Class<? extends Annotation> clazz, ImportCollector collector, String param, String value) {
|
||||
String annotation = annotation(clazz, collector);
|
||||
if (value.isEmpty()) {
|
||||
return annotation;
|
||||
}
|
||||
return "%s(%s = %s)".formatted(annotation, param, value);
|
||||
}
|
||||
|
||||
public static String annotation(Class<? extends Annotation> clazz, ImportCollector collector, String value) {
|
||||
String annotation = annotation(clazz, collector);
|
||||
if (value.isEmpty()) {
|
||||
return annotation;
|
||||
}
|
||||
return "%s(%s)".formatted(annotation, value);
|
||||
}
|
||||
|
||||
public static void experimentalAnnotations(StringBuilder builder, String indent, ImportCollector importCollector, SingleFlagHolder requiredFeature) {
|
||||
builder.append(indent).append(annotation(MinecraftExperimental.class, importCollector, "%s.%s".formatted(
|
||||
importCollector.getShortName(MinecraftExperimental.Requires.class, false), requiredFeature.asAnnotationMember().name()
|
||||
))).append('\n');
|
||||
|
||||
builder.append(indent).append(annotation(ApiStatus.Experimental.class, importCollector)).append('\n');
|
||||
}
|
||||
|
||||
private Annotations() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package io.papermc.generator.rewriter.utils;
|
||||
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.Rewriters;
|
||||
import io.papermc.generator.rewriter.registration.PaperPatternSourceSetRewriter;
|
||||
import io.papermc.paper.generated.GeneratedFrom;
|
||||
import io.papermc.typewriter.SourceFile;
|
||||
import io.papermc.typewriter.SourceRewriter;
|
||||
import io.papermc.typewriter.context.FileMetadata;
|
||||
import io.papermc.typewriter.context.IndentUnit;
|
||||
import io.papermc.typewriter.parser.StringReader;
|
||||
import io.papermc.typewriter.replace.CommentMarker;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriter;
|
||||
import io.papermc.typewriter.replace.SearchReplaceRewriterBase;
|
||||
import net.minecraft.SharedConstants;
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.papermc.typewriter.replace.CommentMarker.EMPTY_MARKER;
|
||||
|
||||
public class ScanOldGeneratedSourceCode {
|
||||
|
||||
private static final String CURRENT_VERSION;
|
||||
|
||||
static {
|
||||
Main.bootStrap(false);
|
||||
CURRENT_VERSION = SharedConstants.getCurrentVersion().getId();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
PaperPatternSourceSetRewriter apiSourceSet = new PaperPatternSourceSetRewriter();
|
||||
PaperPatternSourceSetRewriter serverSourceSet = new PaperPatternSourceSetRewriter();
|
||||
|
||||
Rewriters.bootstrap(apiSourceSet, serverSourceSet);
|
||||
|
||||
checkOutdated(apiSourceSet, Path.of(args[0], "src/main/java"));
|
||||
checkOutdated(serverSourceSet, Path.of(args[1], "src/main/java"));
|
||||
}
|
||||
|
||||
private static void checkOutdated(PaperPatternSourceSetRewriter sourceSetRewriter, Path sourceSet) throws IOException {
|
||||
IndentUnit globalIndentUnit = sourceSetRewriter.getMetadata().indentUnit();
|
||||
for (Map.Entry<SourceFile, SourceRewriter> entry : sourceSetRewriter.getRewriters().entrySet()) {
|
||||
SourceRewriter rewriter = entry.getValue();
|
||||
if (!(rewriter instanceof SearchReplaceRewriterBase srt) ||
|
||||
srt.getRewriters().stream().noneMatch(SearchReplaceRewriter::hasGeneratedComment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SourceFile file = entry.getKey();
|
||||
IndentUnit indentUnit = file.metadata().flatMap(FileMetadata::indentUnit).orElse(globalIndentUnit);
|
||||
Set<SearchReplaceRewriter> rewriters = new HashSet<>(srt.getRewriters());
|
||||
try (LineNumberReader reader = new LineNumberReader(Files.newBufferedReader(sourceSet.resolve(file.path()), StandardCharsets.UTF_8))) {
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CommentMarker marker = srt.searchStartMarker(new StringReader(line), indentUnit, rewriters);
|
||||
if (marker != EMPTY_MARKER) {
|
||||
int startIndentSize = marker.indentSize();
|
||||
if (startIndentSize % indentUnit.size() != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextLine = reader.readLine();
|
||||
if (nextLine == null) {
|
||||
break;
|
||||
}
|
||||
if (nextLine.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringReader nextLineIterator = new StringReader(nextLine);
|
||||
int indentSize = nextLineIterator.skipChars(indentUnit.character());
|
||||
if (indentSize != startIndentSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String generatedComment = "// %s ".formatted(Annotations.annotationStyle(GeneratedFrom.class));
|
||||
if (nextLineIterator.trySkipString(generatedComment) && nextLineIterator.canRead()) {
|
||||
String generatedVersion = nextLineIterator.getRemaining();
|
||||
if (!CURRENT_VERSION.equals(generatedVersion)) {
|
||||
throw new AssertionError(
|
||||
"Code at line %d in %s is marked as being generated in version %s when the current version is %s".formatted(
|
||||
reader.getLineNumber(), file.mainClass().canonicalName(), generatedVersion, CURRENT_VERSION)
|
||||
);
|
||||
}
|
||||
|
||||
if (!marker.owner().getOptions().multipleOperation()) {
|
||||
if (rewriters.remove(marker.owner()) && rewriters.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package io.papermc.generator.types;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import io.papermc.generator.utils.Annotations;
|
||||
import java.util.Arrays;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static javax.lang.model.element.Modifier.PUBLIC;
|
||||
|
||||
@NullMarked
|
||||
public abstract class OverriddenClassGenerator<T> extends SimpleGenerator {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
protected final Class<? extends T> baseClass;
|
||||
protected boolean printWarningOnMissingOverride;
|
||||
|
||||
protected OverriddenClassGenerator(Class<T> baseClass, String className, String packageName) {
|
||||
super(className, packageName);
|
||||
this.baseClass = baseClass;
|
||||
}
|
||||
|
||||
public Class<? extends T> getBaseClass() {
|
||||
return this.baseClass;
|
||||
}
|
||||
|
||||
public MethodSpec.Builder createMethod(String name, Class<?>... parameterTypes) {
|
||||
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name)
|
||||
.addModifiers(PUBLIC);
|
||||
if (methodExists(name, parameterTypes)) {
|
||||
methodBuilder.addAnnotation(Annotations.OVERRIDE);
|
||||
} else {
|
||||
if (this.printWarningOnMissingOverride) {
|
||||
LOGGER.warn("Method {}#{}{} didn't override a known api method!", this.className, name, Arrays.toString(parameterTypes));
|
||||
}
|
||||
}
|
||||
return methodBuilder;
|
||||
}
|
||||
|
||||
protected boolean methodExists(String name, Class<?>... parameterTypes) {
|
||||
try {
|
||||
this.baseClass.getMethod(name, parameterTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package io.papermc.generator.types;
|
||||
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.utils.Annotations;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class SimpleEnumGenerator<T extends Enum<T>> extends SimpleGenerator {
|
||||
|
||||
private final Class<T> enumClass;
|
||||
|
||||
public SimpleEnumGenerator(Class<T> enumClass, String packageName) {
|
||||
super(enumClass.getSimpleName(), packageName);
|
||||
this.enumClass = enumClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeSpec getTypeSpec() {
|
||||
TypeSpec.Builder typeBuilder = TypeSpec.enumBuilder(this.enumClass.getSimpleName())
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotations(Annotations.CLASS_HEADER);
|
||||
|
||||
for (T enumValue : this.enumClass.getEnumConstants()) {
|
||||
typeBuilder.addEnumConstant(enumValue.name());
|
||||
}
|
||||
|
||||
return typeBuilder.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package io.papermc.generator.types;
|
||||
|
||||
import com.squareup.javapoet.JavaFile;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public abstract class SimpleGenerator implements SourceGenerator {
|
||||
|
||||
public static final String INDENT_UNIT = " ";
|
||||
|
||||
protected final String className;
|
||||
protected final String packageName;
|
||||
|
||||
protected SimpleGenerator(String className, String packageName) {
|
||||
this.className = className;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
protected abstract TypeSpec getTypeSpec();
|
||||
|
||||
protected JavaFile.Builder file(JavaFile.Builder builder) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToFile(Path parent) throws IOException {
|
||||
JavaFile.Builder builder = JavaFile.builder(this.packageName, this.getTypeSpec());
|
||||
this.file(builder)
|
||||
.indent(INDENT_UNIT)
|
||||
.skipJavaLangImports(true);
|
||||
|
||||
builder.build().writeTo(parent, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package io.papermc.generator.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface SourceGenerator {
|
||||
|
||||
void writeToFile(Path parent) throws IOException;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package io.papermc.generator.types;
|
||||
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Types {
|
||||
|
||||
public static final String BASE_PACKAGE = "org.bukkit.craftbukkit";
|
||||
|
||||
public static final ClassName CRAFT_BLOCK_DATA = ClassName.get(BASE_PACKAGE + ".block.data", "CraftBlockData");
|
||||
|
||||
public static final ClassName CRAFT_BLOCK = ClassName.get(BASE_PACKAGE + ".block", "CraftBlock");
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package io.papermc.generator.types.craftblockdata;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.generator.types.SourceGenerator;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class CraftBlockDataBootstrapper {
|
||||
|
||||
public static void bootstrap(List<SourceGenerator> generators) {
|
||||
for (Map.Entry<Class<? extends Block>, BlockStateMapping.BlockData> entry : BlockStateMapping.MAPPING.entrySet()) {
|
||||
Class<? extends BlockData> api = BlockStateMapping.getBestSuitedApiClass(entry.getValue());
|
||||
Preconditions.checkState(api != null, "Unknown custom BlockData api class for " + entry.getKey().getCanonicalName());
|
||||
|
||||
generators.add(new CraftBlockDataGenerator<>(entry.getKey(), entry.getValue(), api));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,243 @@
|
||||
package io.papermc.generator.types.craftblockdata;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.OverriddenClassGenerator;
|
||||
import io.papermc.generator.types.Types;
|
||||
import io.papermc.generator.types.craftblockdata.property.PropertyMaker;
|
||||
import io.papermc.generator.types.craftblockdata.property.PropertyWriter;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.Converters;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataPropertyMaker;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.VirtualField;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.converter.DataConverter;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.converter.DataConverters;
|
||||
import io.papermc.generator.utils.Annotations;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.generator.utils.CommonVariable;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Rail;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.papermc.generator.utils.NamingManager.keywordGet;
|
||||
import static io.papermc.generator.utils.NamingManager.keywordGetSet;
|
||||
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;
|
||||
|
||||
@NullMarked
|
||||
public class CraftBlockDataGenerator<T extends BlockData> extends OverriddenClassGenerator<T> {
|
||||
|
||||
private final Class<? extends Block> blockClass;
|
||||
private final BlockStateMapping.BlockData blockData;
|
||||
|
||||
protected CraftBlockDataGenerator(Class<? extends Block> blockClass, BlockStateMapping.BlockData blockData, Class<T> baseClass) {
|
||||
super(baseClass, blockData.implName(), Types.BASE_PACKAGE + ".block.impl");
|
||||
this.blockClass = blockClass;
|
||||
this.blockData = blockData;
|
||||
this.printWarningOnMissingOverride = true;
|
||||
}
|
||||
|
||||
// default keywords: get/set
|
||||
// for single boolean property: get = is
|
||||
// for indexed boolean property: get = has
|
||||
private static final Map<Property<?>, NamingManager.AccessKeyword> FLUENT_KEYWORD = ImmutableMap.<Property<?>, NamingManager.AccessKeyword>builder()
|
||||
.put(BlockStateProperties.ATTACH_FACE, keywordGetSet("getAttached", "setAttached")) // todo remove this once switch methods are gone
|
||||
.put(BlockStateProperties.EYE, keywordGet("has"))
|
||||
.put(BlockStateProperties.BERRIES, keywordGet("has")) // spigot method rename
|
||||
// data holder keywords is only needed for the first property they hold
|
||||
.put(ChiseledBookShelfBlock.SLOT_OCCUPIED_PROPERTIES.getFirst(), keywordGet("is"))
|
||||
.buildOrThrow();
|
||||
|
||||
private static final Map<Property<?>, BiConsumer<ParameterSpec, MethodSpec.Builder>> SETTER_PRECONDITIONS = Map.of(
|
||||
BlockStateProperties.FACING, (param, method) -> {
|
||||
method.addStatement("$T.checkArgument($N.isCartesian(), $S)", Preconditions.class, param, "Invalid face, only cartesian face are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.HORIZONTAL_FACING, (param, method) -> {
|
||||
method.addStatement("$1T.checkArgument($2N.isCartesian() && $2N.getModY() == 0, $3S)", Preconditions.class, param, "Invalid face, only cartesian horizontal face are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.FACING_HOPPER, (param, method) -> {
|
||||
method.addStatement("$1T.checkArgument($2N.isCartesian() && $2N != $3T.UP, $4S)", Preconditions.class, param, BlockFace.class, "Invalid face, only cartesian face (excluding UP) are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.VERTICAL_DIRECTION, (param, method) -> {
|
||||
method.addStatement("$T.checkArgument($N.getModY() != 0, $S)", Preconditions.class, param, "Invalid face, only vertical face are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.ROTATION_16, (param, method) -> {
|
||||
method.addStatement("$1T.checkArgument($2N != $3T.SELF && $2N.getModY() == 0, $4S)", Preconditions.class, param, BlockFace.class, "Invalid face, only horizontal face are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.HORIZONTAL_AXIS, (param, method) -> {
|
||||
method.addStatement("$1T.checkArgument($2N == $3T.X || $2N == $3T.Z, $4S)", Preconditions.class, param, Axis.class, "Invalid axis, only horizontal axis are allowed for this property!");
|
||||
},
|
||||
BlockStateProperties.RAIL_SHAPE_STRAIGHT, (param, method) -> {
|
||||
method.addStatement("$1T.checkArgument($2N != $3T.NORTH_EAST && $2N != $3T.NORTH_WEST && $2N != $3T.SOUTH_EAST && $2N != $3T.SOUTH_WEST, $4S)", Preconditions.class, param, Rail.Shape.class, "Invalid rail shape, only straight rail are allowed for this property!");
|
||||
}
|
||||
);
|
||||
|
||||
private TypeSpec.Builder propertyHolder() {
|
||||
TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(this.className)
|
||||
.addModifiers(PUBLIC)
|
||||
.addAnnotation(Annotations.GENERATED_FROM)
|
||||
.superclass(Types.CRAFT_BLOCK_DATA)
|
||||
.addSuperinterface(this.baseClass);
|
||||
|
||||
ParameterSpec parameter = ParameterSpec.builder(BlockState.class, "state").build();
|
||||
MethodSpec constructor = MethodSpec.constructorBuilder()
|
||||
.addModifiers(PUBLIC)
|
||||
.addParameter(parameter)
|
||||
.addStatement("super($N)", parameter)
|
||||
.build();
|
||||
|
||||
typeBuilder.addMethod(constructor);
|
||||
return typeBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeSpec getTypeSpec() {
|
||||
TypeSpec.Builder typeBuilder = this.propertyHolder();
|
||||
|
||||
for (Property<?> property : this.blockData.properties()) {
|
||||
Pair<Class<?>, String> fieldName = PropertyWriter.referenceFieldFromVar(this.blockClass, property, this.blockData.propertyFields());
|
||||
|
||||
PropertyMaker propertyMaker = PropertyMaker.make(property);
|
||||
|
||||
final String varName;
|
||||
if (this.blockData.propertyFields().containsKey(property)) {
|
||||
// get the name from the local class or fallback to the generic BlockStateProperties constant name if not found
|
||||
varName = this.blockData.propertyFields().get(property).getName();
|
||||
} else {
|
||||
varName = fieldName.right();
|
||||
}
|
||||
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(propertyMaker.getPropertyType(), varName, PRIVATE, STATIC, FINAL)
|
||||
.initializer("$T.$L", fieldName.left(), fieldName.right());
|
||||
FieldSpec field = fieldBuilder.build();
|
||||
|
||||
typeBuilder.addField(field);
|
||||
|
||||
ConverterBase converter = Converters.getOrDefault(property, propertyMaker);
|
||||
Class<?> apiClass = converter.getApiType();
|
||||
|
||||
NamingManager.AccessKeyword accessKeyword = null;
|
||||
if (apiClass == Boolean.TYPE) {
|
||||
accessKeyword = keywordGet("is");
|
||||
}
|
||||
accessKeyword = FLUENT_KEYWORD.getOrDefault(property, accessKeyword);
|
||||
NamingManager propertyNaming = new NamingManager(accessKeyword, CaseFormat.LOWER_UNDERSCORE, property.getName());
|
||||
|
||||
// get
|
||||
{
|
||||
MethodSpec.Builder methodBuilder = createMethod(propertyNaming.simpleGetterName(name -> !name.startsWith("is_") && !name.startsWith("has_")));
|
||||
converter.convertGetter(methodBuilder, field);
|
||||
methodBuilder.returns(apiClass);
|
||||
|
||||
typeBuilder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
// set
|
||||
{
|
||||
String paramName = propertyNaming.paramName(apiClass);
|
||||
ParameterSpec parameter = ParameterSpec.builder(apiClass, paramName, FINAL).build();
|
||||
|
||||
MethodSpec.Builder methodBuilder = createMethod(propertyNaming.simpleSetterName(name -> !name.startsWith("is_")), apiClass).addParameter(parameter);
|
||||
if (!apiClass.isPrimitive()) {
|
||||
methodBuilder.addStatement("$T.checkArgument($N != null, $S)", Preconditions.class, parameter, "%s cannot be null!".formatted(paramName));
|
||||
}
|
||||
if (SETTER_PRECONDITIONS.containsKey(property)) {
|
||||
SETTER_PRECONDITIONS.get(property).accept(parameter, methodBuilder);
|
||||
}
|
||||
converter.convertSetter(methodBuilder, field, parameter);
|
||||
|
||||
typeBuilder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
// extra
|
||||
propertyMaker.addExtras(typeBuilder, field, this, propertyNaming);
|
||||
}
|
||||
|
||||
for (Map.Entry<Either<Field, VirtualField>, Collection<Property<?>>> complexFields : this.blockData.complexPropertyFields().asMap().entrySet()) {
|
||||
Either<Field, VirtualField> fieldData = complexFields.getKey();
|
||||
Collection<Property<?>> properties = complexFields.getValue();
|
||||
Property<?> firstProperty = properties.iterator().next();
|
||||
|
||||
PropertyMaker propertyMaker = PropertyMaker.make(firstProperty);
|
||||
ConverterBase propertyConverter = Converters.getOrDefault(firstProperty, propertyMaker);
|
||||
|
||||
DataPropertyMaker dataPropertyMaker = DataPropertyMaker.make(properties, this.blockClass, fieldData);
|
||||
|
||||
FieldSpec field = dataPropertyMaker.getOrCreateField(this.blockData.propertyFields()).build();
|
||||
typeBuilder.addField(field);
|
||||
|
||||
DataConverter converter = DataConverters.getOrThrow(dataPropertyMaker.getType());
|
||||
Class<?> apiClass = propertyConverter.getApiType();
|
||||
|
||||
NamingManager.AccessKeyword accessKeyword = null;
|
||||
if (apiClass == Boolean.TYPE) {
|
||||
accessKeyword = NamingManager.keywordGet("has");
|
||||
}
|
||||
accessKeyword = FLUENT_KEYWORD.getOrDefault(firstProperty, accessKeyword);
|
||||
NamingManager baseNaming = new NamingManager(accessKeyword, CaseFormat.UPPER_UNDERSCORE, dataPropertyMaker.getBaseName());
|
||||
|
||||
ParameterSpec indexParameter = ParameterSpec.builder(dataPropertyMaker.getIndexClass(), dataPropertyMaker.getIndexClass() == Integer.TYPE ? CommonVariable.INDEX : baseNaming.paramName(dataPropertyMaker.getIndexClass()), FINAL).build();
|
||||
|
||||
// get
|
||||
{
|
||||
MethodSpec.Builder methodBuilder = createMethod(baseNaming.simpleGetterName(name -> true), dataPropertyMaker.getIndexClass())
|
||||
.addParameter(indexParameter);
|
||||
if (!dataPropertyMaker.getIndexClass().isPrimitive()) {
|
||||
methodBuilder.addStatement("$T.checkArgument($N != null, $S)", Preconditions.class, indexParameter, "%s cannot be null!".formatted(indexParameter.name));
|
||||
}
|
||||
converter.convertGetter(propertyConverter, methodBuilder, field, indexParameter);
|
||||
methodBuilder.returns(apiClass);
|
||||
|
||||
typeBuilder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
// set
|
||||
{
|
||||
String paramName = baseNaming.paramName(apiClass);
|
||||
ParameterSpec parameter = ParameterSpec.builder(apiClass, paramName, FINAL).build();
|
||||
|
||||
MethodSpec.Builder methodBuilder = createMethod(baseNaming.simpleSetterName(name -> true), dataPropertyMaker.getIndexClass(), apiClass)
|
||||
.addParameter(indexParameter)
|
||||
.addParameter(parameter);
|
||||
if (!dataPropertyMaker.getIndexClass().isPrimitive()) {
|
||||
methodBuilder.addStatement("$T.checkArgument($N != null, $S)", Preconditions.class, indexParameter, "%s cannot be null!".formatted(indexParameter.name));
|
||||
}
|
||||
if (!apiClass.isPrimitive()) {
|
||||
methodBuilder.addStatement("$T.checkArgument($N != null, $S)", Preconditions.class, parameter, "%s cannot be null!".formatted(paramName));
|
||||
}
|
||||
if (SETTER_PRECONDITIONS.containsKey(firstProperty)) {
|
||||
SETTER_PRECONDITIONS.get(firstProperty).accept(parameter, methodBuilder);
|
||||
}
|
||||
converter.convertSetter(propertyConverter, methodBuilder, field, indexParameter, parameter);
|
||||
|
||||
typeBuilder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
// extra
|
||||
dataPropertyMaker.addExtras(typeBuilder, field, indexParameter, propertyConverter, this, baseNaming);
|
||||
}
|
||||
|
||||
return typeBuilder.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package io.papermc.generator.types.craftblockdata.property;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class EnumPropertyWriter<T extends Enum<T> & StringRepresentable> extends PropertyWriter<T> {
|
||||
|
||||
protected EnumPropertyWriter(EnumProperty<T> property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeName getPropertyType() {
|
||||
if (this.property.getClass() == EnumProperty.class) { // exact match
|
||||
return ParameterizedTypeName.get(this.property.getClass(), this.property.getValueClass());
|
||||
}
|
||||
return super.getPropertyType();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> processApiType() {
|
||||
Class<?> apiClass = this.property.getValueClass();
|
||||
apiClass = BlockStateMapping.ENUM_BRIDGE.get(apiClass);
|
||||
if (apiClass == null) {
|
||||
throw new IllegalStateException("Unknown enum type for " + this.property);
|
||||
}
|
||||
return apiClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertGetter(MethodSpec.Builder method, FieldSpec field) {
|
||||
method.addStatement("return " + this.rawGetExprent().formatted("$N"), field, this.getApiType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawGetExprent() {
|
||||
return "this.get(%s, $T.class)";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package io.papermc.generator.types.craftblockdata.property;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.Converters;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class IntegerPropertyWriter extends PropertyWriter<Integer> {
|
||||
|
||||
protected IntegerPropertyWriter(IntegerProperty property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, CraftBlockDataGenerator<?> generator, NamingManager naming) {
|
||||
if (Converters.has(this.property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IntegerProperty property = (IntegerProperty) this.property;
|
||||
|
||||
if (property.min != 0 || property.getName().equals(BlockStateProperties.LEVEL.getName())) { // special case (levelled: composter)
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(naming.getterName(name -> true).pre("Minimum").concat());
|
||||
methodBuilder.addStatement("return $N.min", field);
|
||||
methodBuilder.returns(this.getApiType());
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
{
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(naming.getterName(name -> true).pre("Maximum").concat());
|
||||
methodBuilder.addStatement("return $N.max", field);
|
||||
methodBuilder.returns(this.getApiType());
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package io.papermc.generator.types.craftblockdata.property;
|
||||
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import io.papermc.generator.types.craftblockdata.property.appender.AppenderBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface PropertyMaker extends ConverterBase, AppenderBase {
|
||||
|
||||
TypeName getPropertyType();
|
||||
|
||||
static <T extends Comparable<T>> PropertyMaker make(Property<T> property) {
|
||||
if (property instanceof IntegerProperty intProperty) {
|
||||
return new IntegerPropertyWriter(intProperty);
|
||||
}
|
||||
if (property instanceof EnumProperty<?> enumProperty) {
|
||||
return new EnumPropertyWriter<>(enumProperty);
|
||||
}
|
||||
return new PropertyWriter<>(property);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package io.papermc.generator.types.craftblockdata.property;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.appender.PropertyAppenders;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class PropertyWriter<T extends Comparable<T>> implements PropertyMaker {
|
||||
|
||||
protected final Property<T> property;
|
||||
private final Supplier<Class<?>> apiClassSupplier;
|
||||
|
||||
protected PropertyWriter(Property<T> property) {
|
||||
this.property = property;
|
||||
this.apiClassSupplier = Suppliers.memoize(this::processApiType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeName getPropertyType() {
|
||||
return TypeName.get(this.property.getClass());
|
||||
}
|
||||
|
||||
protected Class<?> processApiType() {
|
||||
Class<T> apiClass = this.property.getValueClass();
|
||||
if (Primitives.isWrapperType(apiClass)) {
|
||||
apiClass = Primitives.unwrap(apiClass);
|
||||
}
|
||||
return apiClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getApiType() {
|
||||
return this.apiClassSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawSetExprent() {
|
||||
return "this.set(%s, $N)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawGetExprent() {
|
||||
return "this.get(%s)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, CraftBlockDataGenerator<?> generator, NamingManager naming) {
|
||||
PropertyAppenders.ifPresent(this.property, appender -> appender.addExtras(builder, field, generator, naming));
|
||||
}
|
||||
|
||||
public static Pair<Class<?>, String> referenceField(Class<? extends Block> from, Property<?> property, Map<Property<?>, Field> fields) {
|
||||
Class<?> fieldAccess = from;
|
||||
Field field = fields.get(property);
|
||||
if (field == null || !Modifier.isPublic(field.getModifiers())) {
|
||||
fieldAccess = BlockStateProperties.class;
|
||||
field = BlockStateMapping.FALLBACK_GENERIC_FIELDS.get(property);
|
||||
}
|
||||
return Pair.of(fieldAccess, field.getName());
|
||||
}
|
||||
|
||||
public static Pair<Class<?>, String> referenceFieldFromVar(Class<? extends Block> from, Property<?> property, Map<Property<?>, Field> fields) {
|
||||
Class<?> fieldAccess = from;
|
||||
Field field = fields.get(property);
|
||||
Field genericField = BlockStateMapping.FALLBACK_GENERIC_FIELDS.get(property);
|
||||
if (field == null || !Modifier.isPublic(field.getModifiers()) || !genericField.getType().equals(field.getType())) {
|
||||
// field type can differ from BlockStateProperties constants (that's the case for the shulker box (#FACING) and the vault (#STATE)) ref: 1.20.5
|
||||
// in that case fallback to the more accurate type to avoid compile error
|
||||
fieldAccess = BlockStateProperties.class;
|
||||
field = genericField;
|
||||
}
|
||||
return Pair.of(fieldAccess, field.getName());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.appender;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface AppenderBase {
|
||||
|
||||
void addExtras(TypeSpec.Builder builder, FieldSpec field, CraftBlockDataGenerator<?> generator, NamingManager naming);
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.appender;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.util.Set;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class EnumValuesAppender<T extends Enum<T> & StringRepresentable, A extends Enum<A>> implements PropertyAppender<T, A> {
|
||||
|
||||
private final EnumProperty<T> property;
|
||||
private final Class<A> apiType;
|
||||
private final String methodName;
|
||||
|
||||
public EnumValuesAppender(EnumProperty<T> property, Class<A> apiType, String methodName) {
|
||||
this.property = property;
|
||||
this.apiType = apiType;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumProperty<T> getProperty() {
|
||||
return this.property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<A> getApiType() {
|
||||
return this.apiType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, CraftBlockDataGenerator<?> generator, NamingManager naming) {
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(this.methodName);
|
||||
methodBuilder.addStatement("return this.getValues($N, $T.class)", field, this.apiType);
|
||||
methodBuilder.returns(ParameterizedTypeName.get(Set.class, this.apiType));
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.appender;
|
||||
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface PropertyAppender<T extends Comparable<T>, A> extends AppenderBase {
|
||||
|
||||
Property<T> getProperty();
|
||||
|
||||
Class<A> getApiType();
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.appender;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.Rail;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class PropertyAppenders {
|
||||
|
||||
private static final Map<Property<?>, AppenderBase> APPENDERS = Stream.of(
|
||||
new EnumValuesAppender<>(BlockStateProperties.AXIS, Axis.class, "getAxes"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.HORIZONTAL_AXIS, Axis.class, "getAxes"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.FACING, BlockFace.class, "getFaces"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.HORIZONTAL_FACING, BlockFace.class, "getFaces"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.FACING_HOPPER, BlockFace.class, "getFaces"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.RAIL_SHAPE, Rail.Shape.class, "getShapes"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.RAIL_SHAPE_STRAIGHT, Rail.Shape.class, "getShapes"),
|
||||
new EnumValuesAppender<>(BlockStateProperties.VERTICAL_DIRECTION, BlockFace.class, "getVerticalDirections")
|
||||
).collect(Collectors.toUnmodifiableMap(PropertyAppender::getProperty, key -> key));
|
||||
|
||||
public static void ifPresent(Property<?> property, Consumer<AppenderBase> callback) {
|
||||
if (APPENDERS.containsKey(property)) {
|
||||
callback.accept(APPENDERS.get(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.converter;
|
||||
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface Converter<T extends Comparable<T>, A> extends ConverterBase {
|
||||
|
||||
Property<T> getProperty();
|
||||
|
||||
@Override
|
||||
Class<A> getApiType();
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface ConverterBase {
|
||||
|
||||
Class<?> getApiType();
|
||||
|
||||
default void convertSetter(MethodSpec.Builder method, FieldSpec field, ParameterSpec parameter) {
|
||||
method.addStatement(this.rawSetExprent().formatted("$N"), field, parameter);
|
||||
}
|
||||
|
||||
String rawSetExprent(); // this go on two layers which can be hard to follow refactor?
|
||||
|
||||
default void convertGetter(MethodSpec.Builder method, FieldSpec field) {
|
||||
method.addStatement("return " + this.rawGetExprent().formatted("$N"), field);
|
||||
}
|
||||
|
||||
String rawGetExprent();
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.converter;
|
||||
|
||||
import io.papermc.generator.types.craftblockdata.property.PropertyMaker;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Converters {
|
||||
|
||||
private static final Map<Property<?>, ConverterBase> CONVERTERS = Stream.of(
|
||||
new RotationConverter(),
|
||||
new NoteConverter()
|
||||
).collect(Collectors.toUnmodifiableMap(Converter::getProperty, key -> key));
|
||||
|
||||
public static ConverterBase getOrDefault(Property<?> property, PropertyMaker maker) {
|
||||
return CONVERTERS.getOrDefault(property, maker);
|
||||
}
|
||||
|
||||
public static boolean has(Property<?> property) {
|
||||
return CONVERTERS.containsKey(property);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.bukkit.Note;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class NoteConverter implements Converter<Integer, Note> {
|
||||
|
||||
@Override
|
||||
public Property<Integer> getProperty() {
|
||||
return BlockStateProperties.NOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Note> getApiType() {
|
||||
return Note.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawSetExprent() {
|
||||
return "this.set(%s, (int) $N.getId())";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertGetter(MethodSpec.Builder method, FieldSpec field) {
|
||||
method.addStatement("return " + this.rawGetExprent().formatted("$N"), this.getApiType(), field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawGetExprent() {
|
||||
return "new $T(this.get(%s))";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.block.state.properties.RotationSegment;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class RotationConverter implements Converter<Integer, BlockFace> {
|
||||
|
||||
private static final String DIRECTION_VAR = "dir";
|
||||
private static final String ANGLE_VAR = "angle";
|
||||
|
||||
@Override
|
||||
public Property<Integer> getProperty() {
|
||||
return BlockStateProperties.ROTATION_16;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<BlockFace> getApiType() {
|
||||
return BlockFace.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertSetter(MethodSpec.Builder method, FieldSpec field, ParameterSpec parameter) {
|
||||
method.addStatement("$T $L = $N.getDirection()", Vector.class, DIRECTION_VAR, parameter);
|
||||
method.addStatement("$1T $2L = ($1T) -$3T.toDegrees($3T.atan2($4L.getX(), $4L.getZ()))", Float.TYPE, ANGLE_VAR, Math.class, DIRECTION_VAR);
|
||||
method.addStatement(this.rawSetExprent().formatted("$N", ANGLE_VAR), field, RotationSegment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawSetExprent() {
|
||||
return "this.set(%s, $T.convertToSegment(%s))";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawGetExprent() {
|
||||
return "CraftBlockData.ROTATION_CYCLE[this.get(%s)]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
public enum DataHolderType {
|
||||
MAP,
|
||||
LIST,
|
||||
ARRAY
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.appender.DataAppender;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface DataPropertyMaker extends DataAppender {
|
||||
|
||||
FieldSpec.Builder getOrCreateField(Map<Property<?>, Field> fields);
|
||||
|
||||
Class<?> getIndexClass();
|
||||
|
||||
@Override
|
||||
DataHolderType getType();
|
||||
|
||||
String getBaseName();
|
||||
|
||||
static DataPropertyMaker make(Collection<? extends Property<?>> properties, Class<? extends Block> blockClass, Either<Field, VirtualField> fieldData) {
|
||||
return fieldData.map(
|
||||
field -> new DataPropertyWriter(field, properties, blockClass),
|
||||
virtualField -> new VirtualDataPropertyWriter(virtualField, properties, blockClass)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.CodeBlock;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.Types;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.appender.DataAppenders;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import io.papermc.generator.utils.CommonVariable;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
|
||||
import net.minecraft.world.level.block.MossyCarpetBlock;
|
||||
import net.minecraft.world.level.block.WallBlock;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static javax.lang.model.element.Modifier.FINAL;
|
||||
import static javax.lang.model.element.Modifier.PRIVATE;
|
||||
import static javax.lang.model.element.Modifier.STATIC;
|
||||
|
||||
@NullMarked
|
||||
public class DataPropertyWriter extends DataPropertyWriterBase {
|
||||
|
||||
private record FieldKey(Class<? extends Block> blockClass, String fieldName) {
|
||||
}
|
||||
|
||||
private static FieldKey key(Class<? extends Block> blockClass, String fieldName) {
|
||||
return new FieldKey(blockClass, fieldName);
|
||||
}
|
||||
|
||||
private static final Map<String, String> FIELD_TO_BASE_NAME = Map.of(
|
||||
"PROPERTY_BY_DIRECTION", "FACE"
|
||||
);
|
||||
|
||||
private static final Map<FieldKey, String> FIELD_TO_BASE_NAME_SPECIFICS = Map.of(
|
||||
key(ChiseledBookShelfBlock.class, "SLOT_OCCUPIED_PROPERTIES"), "SLOT_OCCUPIED",
|
||||
key(MossyCarpetBlock.class, "PROPERTY_BY_DIRECTION"), "HEIGHT",
|
||||
key(WallBlock.class, "PROPERTY_BY_DIRECTION"), "HEIGHT"
|
||||
);
|
||||
|
||||
protected final Field field;
|
||||
protected @MonotonicNonNull DataHolderType type;
|
||||
protected @MonotonicNonNull Class<?> indexClass, internalIndexClass;
|
||||
protected @MonotonicNonNull TypeName fieldType;
|
||||
|
||||
protected DataPropertyWriter(Field field, Collection<? extends Property<?>> properties, Class<? extends Block> blockClass) {
|
||||
super(properties, blockClass);
|
||||
this.field = field;
|
||||
this.computeTypes(field);
|
||||
}
|
||||
|
||||
protected void computeTypes(Field field) {
|
||||
this.fieldType = TypeName.get(field.getGenericType());
|
||||
|
||||
if (field.getType().isArray()) {
|
||||
this.type = DataHolderType.ARRAY;
|
||||
this.indexClass = Integer.TYPE;
|
||||
} else if (List.class.isAssignableFrom(field.getType())) {
|
||||
this.type = DataHolderType.LIST;
|
||||
this.indexClass = Integer.TYPE;
|
||||
} else if (Map.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType complexType) {
|
||||
this.type = DataHolderType.MAP;
|
||||
this.internalIndexClass = ClassHelper.eraseType(complexType.getActualTypeArguments()[0]);
|
||||
if (this.internalIndexClass.isEnum()) {
|
||||
this.indexClass = BlockStateMapping.ENUM_BRIDGE.getOrDefault(this.internalIndexClass, (Class<? extends Enum<?>>) this.internalIndexClass);
|
||||
this.fieldType = ParameterizedTypeName.get(
|
||||
ClassName.get(field.getType()),
|
||||
ClassName.get(this.indexClass),
|
||||
ClassName.get(complexType.getActualTypeArguments()[1])
|
||||
);
|
||||
} else {
|
||||
this.indexClass = this.internalIndexClass;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Don't know how to turn " + field + " into api");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldSpec.Builder getOrCreateField(Map<Property<?>, Field> fields) {
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(this.fieldType, this.field.getName(), PRIVATE, STATIC, FINAL);
|
||||
if (Modifier.isPublic(this.field.getModifiers())) {
|
||||
// accessible phew
|
||||
if (this.type == DataHolderType.MAP &&
|
||||
this.internalIndexClass == Direction.class && this.indexClass == BlockFace.class) { // Direction -> BlockFace
|
||||
// convert the key manually only this one is needed for now
|
||||
fieldBuilder.initializer("$[$1T.$2L.entrySet().stream()\n.collect($3T.toMap($4L -> $5T.notchToBlockFace($4L.getKey()), $4L -> $4L.getValue()))$]",
|
||||
this.blockClass, this.field.getName(), Collectors.class, CommonVariable.MAP_ENTRY, Types.CRAFT_BLOCK);
|
||||
} else {
|
||||
fieldBuilder.initializer("$T.$L", this.blockClass, this.field.getName());
|
||||
}
|
||||
} else {
|
||||
if (this.type == DataHolderType.ARRAY || this.type == DataHolderType.LIST) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
this.createSyntheticCollection(code, this.type == DataHolderType.ARRAY, fields);
|
||||
fieldBuilder.initializer(code.build());
|
||||
} else if (this.type == DataHolderType.MAP) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
this.createSyntheticMap(code, this.indexClass, fields);
|
||||
fieldBuilder.initializer(code.build());
|
||||
}
|
||||
}
|
||||
return fieldBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIndexClass() {
|
||||
return this.indexClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseName() {
|
||||
String constantName = this.field.getName();
|
||||
|
||||
FieldKey key = key(this.blockClass, constantName);
|
||||
if (FIELD_TO_BASE_NAME_SPECIFICS.containsKey(key)) {
|
||||
return FIELD_TO_BASE_NAME_SPECIFICS.get(key);
|
||||
}
|
||||
|
||||
if (FIELD_TO_BASE_NAME.containsKey(constantName)) {
|
||||
return FIELD_TO_BASE_NAME.get(constantName);
|
||||
}
|
||||
return stripFieldAccessKeyword(constantName);
|
||||
}
|
||||
|
||||
private static final List<String> CUSTOM_KEYWORD = List.of("HAS", "IS", "CAN");
|
||||
|
||||
private String stripFieldAccessKeyword(String name) {
|
||||
for (String keyword : CUSTOM_KEYWORD) {
|
||||
if (name.startsWith(keyword + "_")) {
|
||||
return name.substring(keyword.length() + 1);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase childConverter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming) {
|
||||
DataAppenders.ifPresent(this.type, appender -> appender.addExtras(builder, field, indexParameter, childConverter, generator, baseNaming));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
import com.squareup.javapoet.CodeBlock;
|
||||
import io.papermc.generator.types.craftblockdata.property.PropertyWriter;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public abstract class DataPropertyWriterBase implements DataPropertyMaker {
|
||||
|
||||
protected final Collection<? extends Property<?>> properties;
|
||||
protected final Class<? extends Block> blockClass;
|
||||
|
||||
protected DataPropertyWriterBase(Collection<? extends Property<?>> properties, Class<? extends Block> blockClass) {
|
||||
this.properties = properties;
|
||||
this.blockClass = blockClass;
|
||||
}
|
||||
|
||||
protected void createSyntheticCollection(CodeBlock.Builder code, boolean isArray, Map<Property<?>, Field> fields) {
|
||||
if (isArray) {
|
||||
code.add("{\n");
|
||||
} else {
|
||||
code.add("$T.of(\n", List.class);
|
||||
}
|
||||
code.indent();
|
||||
Iterator<? extends Property<?>> it = this.properties.iterator();
|
||||
while (it.hasNext()) {
|
||||
Property<?> property = it.next();
|
||||
Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields);
|
||||
code.add("$T.$L", fieldName.left(), fieldName.right());
|
||||
if (it.hasNext()) {
|
||||
code.add(",");
|
||||
}
|
||||
code.add("\n");
|
||||
}
|
||||
code.unindent().add(isArray ? "}" : ")");
|
||||
}
|
||||
|
||||
protected void createSyntheticMap(CodeBlock.Builder code, Class<?> indexClass, Map<Property<?>, Field> fields) {
|
||||
// assume indexClass is an enum with its values matching the property names
|
||||
code.add("$T.of(\n", Map.class).indent();
|
||||
Iterator<? extends Property<?>> it = this.properties.iterator();
|
||||
while (it.hasNext()) {
|
||||
Property<?> property = it.next();
|
||||
String name = Formatting.formatKeyAsField(property.getName());
|
||||
Pair<Class<?>, String> fieldName = PropertyWriter.referenceField(this.blockClass, property, fields);
|
||||
code.add("$T.$L, $T.$L", indexClass, name, fieldName.left(), fieldName.right());
|
||||
if (it.hasNext()) {
|
||||
code.add(",");
|
||||
}
|
||||
code.add("\n");
|
||||
}
|
||||
code.unindent().add(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Class<?> getIndexClass();
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
import com.squareup.javapoet.ArrayTypeName;
|
||||
import com.squareup.javapoet.CodeBlock;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static javax.lang.model.element.Modifier.FINAL;
|
||||
import static javax.lang.model.element.Modifier.PRIVATE;
|
||||
import static javax.lang.model.element.Modifier.STATIC;
|
||||
|
||||
@NullMarked
|
||||
public class VirtualDataPropertyWriter extends DataPropertyWriterBase {
|
||||
|
||||
private final VirtualField virtualField;
|
||||
protected @MonotonicNonNull Class<?> indexClass;
|
||||
protected @MonotonicNonNull TypeName fieldType;
|
||||
|
||||
protected VirtualDataPropertyWriter(VirtualField virtualField, Collection<? extends Property<?>> properties, Class<? extends Block> blockClass) {
|
||||
super(properties, blockClass);
|
||||
this.virtualField = virtualField;
|
||||
this.computeTypes(virtualField);
|
||||
}
|
||||
|
||||
protected void computeTypes(VirtualField virtualField) {
|
||||
switch (virtualField.holderType()) {
|
||||
case ARRAY -> {
|
||||
this.indexClass = Integer.TYPE;
|
||||
this.fieldType = ArrayTypeName.of(virtualField.valueType());
|
||||
}
|
||||
case LIST -> {
|
||||
this.indexClass = Integer.TYPE;
|
||||
this.fieldType = ParameterizedTypeName.get(List.class, virtualField.valueType());
|
||||
}
|
||||
case MAP -> {
|
||||
if (virtualField.keyClass() != null) {
|
||||
this.indexClass = virtualField.keyClass();
|
||||
} else {
|
||||
this.indexClass = this.properties.iterator().next().getValueClass();
|
||||
if (this.indexClass.isEnum()) {
|
||||
this.indexClass = BlockStateMapping.ENUM_BRIDGE.getOrDefault(this.indexClass, (Class<? extends Enum<?>>) this.indexClass);
|
||||
}
|
||||
}
|
||||
this.fieldType = ParameterizedTypeName.get(Map.class, this.indexClass, virtualField.valueType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldSpec.Builder getOrCreateField(Map<Property<?>, Field> fields) {
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(this.fieldType, this.virtualField.name(), PRIVATE, STATIC, FINAL);
|
||||
if (this.getType() == DataHolderType.ARRAY || this.getType() == DataHolderType.LIST) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
this.createSyntheticCollection(code, this.getType() == DataHolderType.ARRAY, fields);
|
||||
fieldBuilder.initializer(code.build());
|
||||
} else if (this.getType() == DataHolderType.MAP) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
this.createSyntheticMap(code, this.indexClass, fields);
|
||||
fieldBuilder.initializer(code.build());
|
||||
}
|
||||
|
||||
return fieldBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIndexClass() {
|
||||
return this.indexClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return this.virtualField.holderType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseName() {
|
||||
return this.virtualField.baseName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase converter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public record VirtualField(
|
||||
String name,
|
||||
Type valueType,
|
||||
DataHolderType holderType,
|
||||
String baseName,
|
||||
@Nullable Class<?> keyClass,
|
||||
Collection<? extends Property<?>> values
|
||||
) {
|
||||
|
||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||
public static <T extends Property<? extends Comparable<?>>> VirtualField.FieldValue<T> createCollection(String name, TypeToken<T> valueType, boolean isArray, String baseName) {
|
||||
return new VirtualField.FieldValue<>(name, valueType, isArray ? DataHolderType.ARRAY : DataHolderType.LIST, baseName, null);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||
public static <T extends Property<? extends Comparable<?>>> VirtualField.FieldValue<T> createCollection(String name, Class<T> valueType, boolean isArray, String baseName) {
|
||||
return createCollection(name, TypeToken.of(valueType), isArray, baseName);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||
public static <T extends Property<? extends Comparable<?>>> VirtualField.FieldValue<T> createMap(String name, Class<?> keyClass, TypeToken<T> valueType, String baseName) {
|
||||
return new VirtualField.FieldValue<>(name, valueType, DataHolderType.MAP, baseName, keyClass);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||
public static <T extends Property<? extends Comparable<?>>> VirtualField.FieldValue<T> createMap(String name, Class<?> keyClass, Class<T> valueType, String baseName) {
|
||||
return createMap(name, keyClass, TypeToken.of(valueType), baseName);
|
||||
}
|
||||
|
||||
public static class FieldValue<T extends Property<? extends Comparable<?>>> {
|
||||
|
||||
private final String name;
|
||||
private final DataHolderType holderType;
|
||||
private final TypeToken<T> valueTypeToken;
|
||||
private final String baseName;
|
||||
private final @Nullable Class<?> keyClass;
|
||||
|
||||
private @Nullable Collection<T> values;
|
||||
|
||||
public FieldValue(String name, TypeToken<T> valueTypeToken, DataHolderType holderType, String baseName, @Nullable Class<?> keyClass) {
|
||||
this.name = name;
|
||||
this.valueTypeToken = valueTypeToken;
|
||||
this.holderType = holderType;
|
||||
this.baseName = baseName;
|
||||
this.keyClass = keyClass;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
public FieldValue<T> withValues(Collection<T> properties) {
|
||||
this.values = List.copyOf(properties);
|
||||
return this;
|
||||
}
|
||||
|
||||
public VirtualField make() {
|
||||
Preconditions.checkState(this.values != null && !this.values.isEmpty(), "The field should doesn't have any content");
|
||||
return new VirtualField(this.name, this.valueTypeToken.getType(), this.holderType, this.baseName, this.keyClass, this.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.appender;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import io.papermc.generator.utils.CommonVariable;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class ArrayAppender implements DataAppender {
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase childConverter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming) {
|
||||
if (childConverter.getApiType() == Boolean.TYPE) {
|
||||
String collectVarName = baseNaming.getVariableNameWrapper().post("s").concat();
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(baseNaming.getMethodNameWrapper().post("s").concat());
|
||||
methodBuilder.addStatement("$T $L = $T.builder()", ParameterizedTypeName.get(ImmutableSet.Builder.class, Integer.class), collectVarName, ImmutableSet.class);
|
||||
methodBuilder.beginControlFlow("for (int $1L = 0, len = $2N.length; $1L < len; $1L++)", CommonVariable.INDEX, field);
|
||||
{
|
||||
methodBuilder.beginControlFlow("if (" + childConverter.rawGetExprent().formatted("$N[$N]") + ")", field, indexParameter);
|
||||
{
|
||||
methodBuilder.addStatement("$L.add($L)", collectVarName, CommonVariable.INDEX);
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
methodBuilder.addStatement("return $L.build()", collectVarName);
|
||||
methodBuilder.returns(ParameterizedTypeName.get(Set.class, Integer.class));
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
{
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(baseNaming.getMethodNameWrapper().pre("Maximum").post("s").concat());
|
||||
methodBuilder.addStatement("return $N.length", field);
|
||||
methodBuilder.returns(Integer.TYPE);
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.appender;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface DataAppender {
|
||||
|
||||
DataHolderType getType();
|
||||
|
||||
void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase converter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.appender;
|
||||
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class DataAppenders {
|
||||
|
||||
private static final Map<DataHolderType, DataAppender> APPENDERS = Stream.of(
|
||||
new ArrayAppender(),
|
||||
new ListAppender(),
|
||||
new MapAppender()
|
||||
).collect(Collectors.toUnmodifiableMap(DataAppender::getType, key -> key));
|
||||
|
||||
public static void ifPresent(DataHolderType type, Consumer<DataAppender> callback) {
|
||||
if (APPENDERS.containsKey(type)) {
|
||||
callback.accept(APPENDERS.get(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.appender;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import io.papermc.generator.utils.CommonVariable;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class ListAppender implements DataAppender {
|
||||
|
||||
private static final Map<String, String> METHOD_BASE_RENAMES = Map.of(
|
||||
"SlotOccupied", "OccupiedSlot"
|
||||
);
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase childConverter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming) {
|
||||
NamingManager.NameWrapper methodName = NamingManager.NameWrapper.wrap("get", METHOD_BASE_RENAMES.getOrDefault(baseNaming.getMethodBaseName(), baseNaming.getMethodBaseName()));
|
||||
|
||||
if (childConverter.getApiType() == Boolean.TYPE) {
|
||||
String collectVarName = baseNaming.getVariableNameWrapper().post("s").concat();
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(methodName.post("s").concat());
|
||||
methodBuilder.addStatement("$T $L = $T.builder()", ParameterizedTypeName.get(ImmutableSet.Builder.class, Integer.class), collectVarName, ImmutableSet.class);
|
||||
methodBuilder.beginControlFlow("for (int $1L = 0, size = $2N.size(); $1L < size; $1L++)", CommonVariable.INDEX, field);
|
||||
{
|
||||
methodBuilder.beginControlFlow("if (" + childConverter.rawGetExprent().formatted("$N.get($N)") + ")", field, indexParameter);
|
||||
{
|
||||
methodBuilder.addStatement("$L.add($L)", collectVarName, CommonVariable.INDEX);
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
methodBuilder.addStatement("return $L.build()", collectVarName);
|
||||
methodBuilder.returns(ParameterizedTypeName.get(Set.class, Integer.class));
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
{
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(methodName.pre("Maximum").post("s").concat());
|
||||
methodBuilder.addStatement("return $N.size()", field);
|
||||
methodBuilder.returns(Integer.TYPE);
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.appender;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.types.craftblockdata.CraftBlockDataGenerator;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import io.papermc.generator.utils.CommonVariable;
|
||||
import io.papermc.generator.utils.NamingManager;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.MultipleFacing;
|
||||
import org.bukkit.block.data.type.RedstoneWire;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class MapAppender implements DataAppender {
|
||||
|
||||
private static final Map<String, String> INDEX_NAMES = ImmutableMap.<String, String>builder()
|
||||
.put(BlockFace.class.getSimpleName(), "Face")
|
||||
.buildOrThrow();
|
||||
|
||||
// no real rule here some has some don't mossy carpet and wall could have it
|
||||
private static final Set<Class<? extends BlockData>> HAS_ALLOWED_METHOD = Set.of(
|
||||
MultipleFacing.class,
|
||||
RedstoneWire.class
|
||||
);
|
||||
|
||||
private static boolean supportsExtraMethod(Class<? extends BlockData> clazz) {
|
||||
for (Class<? extends BlockData> supported : HAS_ALLOWED_METHOD) {
|
||||
if (supported.isAssignableFrom(clazz)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtras(TypeSpec.Builder builder, FieldSpec field, ParameterSpec indexParameter, ConverterBase childConverter, CraftBlockDataGenerator<?> generator, NamingManager baseNaming) {
|
||||
if (childConverter.getApiType() == Boolean.TYPE) {
|
||||
String collectVarName = baseNaming.getVariableNameWrapper().post("s").concat();
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(baseNaming.getMethodNameWrapper().post("s").concat());
|
||||
methodBuilder.addStatement("$T $L = $T.builder()", ParameterizedTypeName.get(ClassName.get(ImmutableSet.Builder.class), indexParameter.type), collectVarName, ImmutableSet.class);
|
||||
methodBuilder.beginControlFlow("for ($T $N : $N.entrySet())", ParameterizedTypeName.get(ClassName.get(Map.Entry.class), indexParameter.type, ClassName.get(BooleanProperty.class)), CommonVariable.MAP_ENTRY, field);
|
||||
{
|
||||
methodBuilder.beginControlFlow("if (" + childConverter.rawGetExprent().formatted("$L.getValue()") + ")", CommonVariable.MAP_ENTRY);
|
||||
{
|
||||
methodBuilder.addStatement("$L.add($N.getKey())", collectVarName, CommonVariable.MAP_ENTRY);
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
methodBuilder.addStatement("return $L.build()", collectVarName);
|
||||
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(Set.class), indexParameter.type));
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
|
||||
if (supportsExtraMethod(generator.getBaseClass()) &&
|
||||
indexParameter.type instanceof ClassName className && !className.isPrimitive() && !className.isBoxedPrimitive()) {
|
||||
NamingManager.NameWrapper indexNaming = NamingManager.NameWrapper.wrap("get", INDEX_NAMES.getOrDefault(className.simpleName(), className.simpleName()));
|
||||
|
||||
MethodSpec.Builder methodBuilder = generator.createMethod(indexNaming.pre("Allowed").post("s").concat());
|
||||
methodBuilder.addStatement("return $T.unmodifiableSet($N.keySet())", Collections.class, field);
|
||||
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(Set.class), className));
|
||||
|
||||
builder.addMethod(methodBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import io.papermc.generator.types.craftblockdata.property.EnumPropertyWriter;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class ArrayConverter implements DataConverter {
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertSetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter, ParameterSpec parameter) {
|
||||
method.addStatement(childConverter.rawSetExprent().formatted("$N[$N]"), field, indexParameter, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertGetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter) {
|
||||
if (childConverter instanceof EnumPropertyWriter<?> enumConverter) {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$N[$N]"), field, indexParameter, enumConverter.getApiType());
|
||||
} else {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$N[$N]"), field, indexParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface DataConverter {
|
||||
|
||||
DataHolderType getType();
|
||||
|
||||
void convertSetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter, ParameterSpec parameter);
|
||||
|
||||
void convertGetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.converter;
|
||||
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class DataConverters {
|
||||
|
||||
private static final Map<DataHolderType, DataConverter> CONVERTERS = Stream.of(
|
||||
new ArrayConverter(),
|
||||
new ListConverter(),
|
||||
new MapConverter()
|
||||
).collect(Collectors.toUnmodifiableMap(DataConverter::getType, key -> key));
|
||||
|
||||
public static DataConverter getOrThrow(DataHolderType type) {
|
||||
DataConverter converter = CONVERTERS.get(type);
|
||||
if (converter == null) {
|
||||
throw new IllegalStateException("Cannot handle data holder type: " + type);
|
||||
}
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.converter;
|
||||
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import io.papermc.generator.types.craftblockdata.property.EnumPropertyWriter;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class ListConverter implements DataConverter {
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertSetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter, ParameterSpec parameter) {
|
||||
method.addStatement(childConverter.rawSetExprent().formatted("$N.get($N)"), field, indexParameter, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertGetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter) {
|
||||
if (childConverter instanceof EnumPropertyWriter<?> enumConverter) {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$N.get($N)"), field, indexParameter, enumConverter.getApiType());
|
||||
} else {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$N.get($N)"), field, indexParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package io.papermc.generator.types.craftblockdata.property.holder.converter;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
import com.squareup.javapoet.MethodSpec;
|
||||
import com.squareup.javapoet.ParameterSpec;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import io.papermc.generator.types.craftblockdata.property.EnumPropertyWriter;
|
||||
import io.papermc.generator.types.craftblockdata.property.converter.ConverterBase;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.DataHolderType;
|
||||
import java.util.stream.Collectors;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class MapConverter implements DataConverter {
|
||||
|
||||
private static final String PROPERTY_VAR = "property";
|
||||
|
||||
@Override
|
||||
public DataHolderType getType() {
|
||||
return DataHolderType.MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertSetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter, ParameterSpec parameter) {
|
||||
method.addStatement("$T $L = $N.get($N)", ((ParameterizedTypeName) field.type).typeArguments.get(1), PROPERTY_VAR, field, indexParameter);
|
||||
method.addStatement("$T.checkArgument($N != null, $S, $N.keySet().stream().map($T::name).collect($T.joining($S)))",
|
||||
Preconditions.class, PROPERTY_VAR, "Invalid " + indexParameter.name + ", only %s are allowed!", field, Enum.class, Collectors.class, ", ");
|
||||
|
||||
method.addStatement(childConverter.rawSetExprent().formatted("$L"), PROPERTY_VAR, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertGetter(ConverterBase childConverter, MethodSpec.Builder method, FieldSpec field, ParameterSpec indexParameter) {
|
||||
method.addStatement("$T $L = $N.get($N)", ((ParameterizedTypeName) field.type).typeArguments.get(1), PROPERTY_VAR, field, indexParameter);
|
||||
method.addStatement("$T.checkArgument($N != null, $S, $N.keySet().stream().map($T::name).collect($T.joining($S)))",
|
||||
Preconditions.class, PROPERTY_VAR, "Invalid " + indexParameter.name + ", only %s are allowed!", field, Enum.class, Collectors.class, ", ");
|
||||
|
||||
if (childConverter instanceof EnumPropertyWriter<?> enumConverter) {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$L"), PROPERTY_VAR, enumConverter.getApiType());
|
||||
} else {
|
||||
method.addStatement("return " + childConverter.rawGetExprent().formatted("$L"), PROPERTY_VAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package io.papermc.generator.types.goal;
|
||||
|
||||
import com.destroystokyo.paper.entity.ai.GoalKey;
|
||||
import com.squareup.javapoet.ClassName;
|
||||
import com.squareup.javapoet.FieldSpec;
|
||||
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.github.classgraph.ClassGraph;
|
||||
import io.github.classgraph.ScanResult;
|
||||
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.typewriter.util.ClassHelper;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.GoalSelector;
|
||||
import net.minecraft.world.entity.ai.goal.WrappedGoal;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Mob;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
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;
|
||||
|
||||
@NullMarked
|
||||
public class MobGoalGenerator extends SimpleGenerator {
|
||||
|
||||
private static final String CLASS_HEADER = Javadocs.getVersionDependentClassHeader("keys", "Mob Goals");
|
||||
|
||||
public MobGoalGenerator(String className, String packageName) {
|
||||
super(className, packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeSpec getTypeSpec() {
|
||||
TypeVariableName type = TypeVariableName.get("T", Mob.class);
|
||||
TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(this.className)
|
||||
.addSuperinterface(ParameterizedTypeName.get(ClassName.get(com.destroystokyo.paper.entity.ai.Goal.class), type))
|
||||
.addModifiers(PUBLIC)
|
||||
.addTypeVariable(type)
|
||||
.addAnnotations(Annotations.CLASS_HEADER)
|
||||
.addJavadoc(CLASS_HEADER);
|
||||
|
||||
TypeName mobType = ParameterizedTypeName.get(ClassName.get(Class.class), type);
|
||||
|
||||
ParameterSpec keyParam = ParameterSpec.builder(String.class, "key", FINAL).build();
|
||||
ParameterSpec typeParam = ParameterSpec.builder(mobType, "type", FINAL).build();
|
||||
MethodSpec.Builder createMethod = MethodSpec.methodBuilder("create")
|
||||
.addModifiers(PRIVATE, STATIC)
|
||||
.addParameter(keyParam)
|
||||
.addParameter(typeParam)
|
||||
.addCode("return $T.of($N, $T.minecraft($N));", GoalKey.class, typeParam, NamespacedKey.class, keyParam)
|
||||
.addTypeVariable(type)
|
||||
.returns(ParameterizedTypeName.get(ClassName.get(GoalKey.class), type));
|
||||
|
||||
List<Class<Goal>> classes;
|
||||
try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) {
|
||||
classes = scanResult.getSubclasses(Goal.class.getName()).loadClasses(Goal.class);
|
||||
}
|
||||
|
||||
Stream<GoalKey<Mob>> vanillaGoals = classes.stream()
|
||||
.filter(clazz -> !java.lang.reflect.Modifier.isAbstract(clazz.getModifiers()))
|
||||
.filter(clazz -> !clazz.isAnonymousClass() || ClassHelper.getTopLevelClass(clazz) != GoalSelector.class)
|
||||
.filter(clazz -> !WrappedGoal.class.equals(clazz)) // TODO - properly fix
|
||||
.map(MobGoalNames::getKey)
|
||||
.sorted(Comparator.<GoalKey<?>, String>comparing(o -> o.getEntityClass().getSimpleName())
|
||||
.thenComparing(vanillaGoalKey -> vanillaGoalKey.getNamespacedKey().getKey())
|
||||
);
|
||||
|
||||
vanillaGoals.forEach(goalKey -> {
|
||||
String keyPath = goalKey.getNamespacedKey().getKey();
|
||||
String fieldName = Formatting.formatKeyAsField(keyPath);
|
||||
|
||||
TypeName typedKey = ParameterizedTypeName.get(GoalKey.class, goalKey.getEntityClass());
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(typedKey, fieldName, PUBLIC, STATIC, FINAL)
|
||||
.initializer("$N($S, $T.class)", createMethod.build(), keyPath, goalKey.getEntityClass());
|
||||
typeBuilder.addField(fieldBuilder.build());
|
||||
});
|
||||
|
||||
return typeBuilder.addMethod(createMethod.build()).build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
package io.papermc.generator.types.goal;
|
||||
|
||||
import com.destroystokyo.paper.entity.RangedEntity;
|
||||
import com.destroystokyo.paper.entity.ai.GoalKey;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import io.papermc.paper.entity.SchoolableFish;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.monster.RangedAttackMob;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.*;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class MobGoalNames { // todo sync with MobGoalHelper ideally this should not be duplicated
|
||||
|
||||
private static final Map<Class<? extends Goal>, Class<? extends Mob>> entityClassCache = new HashMap<>();
|
||||
public static final Map<Class<? extends net.minecraft.world.entity.Mob>, Class<? extends Mob>> bukkitMap = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
//<editor-fold defaultstate="collapsed" desc="bukkitMap Entities">
|
||||
bukkitMap.put(net.minecraft.world.entity.Mob.class, Mob.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.AgeableMob.class, Ageable.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.ambient.AmbientCreature.class, Ambient.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Animal.class, Animals.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.ambient.Bat.class, Bat.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Bee.class, Bee.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Blaze.class, Blaze.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Cat.class, Cat.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.CaveSpider.class, CaveSpider.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Chicken.class, Chicken.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Cod.class, Cod.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Cow.class, Cow.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.PathfinderMob.class, Creature.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Creeper.class, Creeper.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Dolphin.class, Dolphin.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Drowned.class, Drowned.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.boss.enderdragon.EnderDragon.class, EnderDragon.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.EnderMan.class, Enderman.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Endermite.class, Endermite.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Evoker.class, Evoker.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.AbstractFish.class, Fish.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.AbstractSchoolingFish.class, SchoolableFish.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.FlyingMob.class, Flying.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Fox.class, Fox.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Ghast.class, Ghast.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Giant.class, Giant.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.AbstractGolem.class, Golem.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Guardian.class, Guardian.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.ElderGuardian.class, ElderGuardian.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.Horse.class, Horse.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.AbstractHorse.class, AbstractHorse.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.AbstractChestedHorse.class, ChestedHorse.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.Donkey.class, Donkey.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.Mule.class, Mule.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.SkeletonHorse.class, SkeletonHorse.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.ZombieHorse.class, ZombieHorse.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.camel.Camel.class, org.bukkit.entity.Camel.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.AbstractIllager.class, Illager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Illusioner.class, Illusioner.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.SpellcasterIllager.class, Spellcaster.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.IronGolem.class, IronGolem.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.Llama.class, Llama.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.horse.TraderLlama.class, TraderLlama.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.MagmaCube.class, MagmaCube.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Monster.class, Monster.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.PatrollingMonster.class, Raider.class); // close enough
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.MushroomCow.class, MushroomCow.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Ocelot.class, Ocelot.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Panda.class, Panda.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Parrot.class, Parrot.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.ShoulderRidingEntity.class, Parrot.class); // close enough
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Phantom.class, Phantom.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Pig.class, Pig.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.ZombifiedPiglin.class, PigZombie.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Pillager.class, Pillager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.PolarBear.class, PolarBear.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Pufferfish.class, PufferFish.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Rabbit.class, Rabbit.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.raid.Raider.class, Raider.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Ravager.class, Ravager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Salmon.class, Salmon.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.sheep.Sheep.class, Sheep.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Shulker.class, Shulker.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Silverfish.class, Silverfish.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Skeleton.class, Skeleton.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.AbstractSkeleton.class, AbstractSkeleton.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Stray.class, Stray.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.WitherSkeleton.class, WitherSkeleton.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Slime.class, Slime.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.SnowGolem.class, Snowman.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Spider.class, Spider.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Squid.class, Squid.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.TamableAnimal.class, Tameable.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.TropicalFish.class, TropicalFish.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.Turtle.class, Turtle.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Vex.class, Vex.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.npc.Villager.class, Villager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.npc.AbstractVillager.class, AbstractVillager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.npc.WanderingTrader.class, WanderingTrader.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Vindicator.class, Vindicator.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.WaterAnimal.class, WaterMob.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Witch.class, Witch.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.boss.wither.WitherBoss.class, Wither.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.wolf.Wolf.class, Wolf.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Zombie.class, Zombie.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Husk.class, Husk.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.ZombieVillager.class, ZombieVillager.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.hoglin.Hoglin.class, Hoglin.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.piglin.Piglin.class, Piglin.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.piglin.AbstractPiglin.class, PiglinAbstract.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.piglin.PiglinBrute.class, PiglinBrute.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Strider.class, Strider.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Zoglin.class, Zoglin.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.GlowSquid.class, GlowSquid.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.axolotl.Axolotl.class, Axolotl.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.goat.Goat.class, Goat.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.frog.Frog.class, Frog.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.frog.Tadpole.class, Tadpole.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.warden.Warden.class, Warden.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.allay.Allay.class, Allay.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.sniffer.Sniffer.class, Sniffer.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.breeze.Breeze.class, Breeze.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.armadillo.Armadillo.class, Armadillo.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.Bogged.class, Bogged.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.monster.creaking.Creaking.class, Creaking.class);
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.AgeableWaterCreature.class, Squid.class); // close enough
|
||||
bukkitMap.put(net.minecraft.world.entity.animal.AbstractCow.class, AbstractCow.class);
|
||||
//</editor-fold>
|
||||
}
|
||||
|
||||
private static final Map<String, String> deobfuscationMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
// TODO these kinda should be checked on each release, in case obfuscation changes
|
||||
deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee");
|
||||
}
|
||||
|
||||
private static String getPathName(String name) {
|
||||
String pathName = name.substring(name.lastIndexOf('.') + 1);
|
||||
boolean needDeobfMap = false;
|
||||
|
||||
// inner classes
|
||||
int firstInnerDelimiter = pathName.indexOf('$');
|
||||
if (firstInnerDelimiter != -1) {
|
||||
String innerClassName = pathName.substring(firstInnerDelimiter + 1);
|
||||
for (String nestedClass : innerClassName.split("\\$")) {
|
||||
if (NumberUtils.isDigits(nestedClass)) {
|
||||
needDeobfMap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!needDeobfMap) {
|
||||
pathName = innerClassName;
|
||||
}
|
||||
pathName = pathName.replace('$', '_');
|
||||
// mapped, wooo!
|
||||
}
|
||||
|
||||
pathName = Formatting.stripWordOfCamelCaseName(pathName, "TargetGoal", true); // replace last? reverse search?
|
||||
pathName = Formatting.stripWordOfCamelCaseName(pathName, "Goal", true);
|
||||
pathName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, pathName);
|
||||
|
||||
if (needDeobfMap && !deobfuscationMap.containsKey(pathName)) {
|
||||
System.err.println("need to map " + name + " (" + pathName + ")");
|
||||
}
|
||||
|
||||
// did we rename this key?
|
||||
return deobfuscationMap.getOrDefault(pathName, pathName);
|
||||
}
|
||||
|
||||
public static <T extends Mob> GoalKey<T> getKey(Class<? extends Goal> goalClass) {
|
||||
String name = getPathName(goalClass.getName());
|
||||
return GoalKey.of(getEntity(goalClass), NamespacedKey.minecraft(name));
|
||||
}
|
||||
|
||||
private static <T extends Mob> Class<T> getEntity(Class<? extends Goal> goalClass) {
|
||||
//noinspection unchecked
|
||||
return (Class<T>) entityClassCache.computeIfAbsent(goalClass, key -> {
|
||||
for (Constructor<?> ctor : key.getDeclaredConstructors()) {
|
||||
for (Class<?> param : ctor.getParameterTypes()) {
|
||||
if (net.minecraft.world.entity.Mob.class.isAssignableFrom(param)) {
|
||||
//noinspection unchecked
|
||||
return toBukkitClass((Class<? extends net.minecraft.world.entity.Mob>) param);
|
||||
} else if (RangedAttackMob.class.isAssignableFrom(param)) {
|
||||
return RangedEntity.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Can't figure out applicable entity for mob goal " + goalClass); // maybe just return Mob?
|
||||
});
|
||||
}
|
||||
|
||||
private static Class<? extends Mob> toBukkitClass(Class<? extends net.minecraft.world.entity.Mob> nmsClass) {
|
||||
Class<? extends Mob> bukkitClass = bukkitMap.get(nmsClass);
|
||||
if (bukkitClass == null) {
|
||||
throw new RuntimeException("Can't figure out applicable bukkit entity for nms entity " + nmsClass); // maybe just return Mob?
|
||||
}
|
||||
return bukkitClass;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package io.papermc.generator.types.registry;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
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 io.papermc.generator.registry.RegistryEntry;
|
||||
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.generator.utils.experimental.ExperimentalCollector;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.flag.FeatureElement;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static com.squareup.javapoet.TypeSpec.classBuilder;
|
||||
import static io.papermc.generator.utils.Annotations.EXPERIMENTAL_API_ANNOTATION;
|
||||
import static io.papermc.generator.utils.Annotations.experimentalAnnotations;
|
||||
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;
|
||||
|
||||
@NullMarked
|
||||
public class GeneratedKeyType<T> extends SimpleGenerator {
|
||||
|
||||
private final RegistryEntry<T> entry;
|
||||
private final Supplier<Map<ResourceKey<T>, SingleFlagHolder>> experimentalKeys;
|
||||
private final boolean isFilteredRegistry;
|
||||
|
||||
public GeneratedKeyType(String packageName, RegistryEntry<T> entry) {
|
||||
super(entry.keyClassName().concat("Keys"), packageName);
|
||||
this.entry = entry;
|
||||
this.experimentalKeys = Suppliers.memoize(() -> ExperimentalCollector.collectDataDrivenElementIds(entry.registry()));
|
||||
this.isFilteredRegistry = FeatureElement.FILTERED_REGISTRIES.contains(entry.registryKey());
|
||||
}
|
||||
|
||||
private MethodSpec.Builder createMethod(TypeName returnType) {
|
||||
boolean publicCreateKeyMethod = this.entry.allowCustomKeys();
|
||||
|
||||
ParameterSpec keyParam = ParameterSpec.builder(Key.class, "key", FINAL).build();
|
||||
MethodSpec.Builder create = MethodSpec.methodBuilder("create")
|
||||
.addModifiers(publicCreateKeyMethod ? PUBLIC : PRIVATE, STATIC)
|
||||
.addParameter(keyParam)
|
||||
.addCode("return $T.create($T.$L, $N);", TypedKey.class, RegistryKey.class, this.entry.registryKeyField(), keyParam)
|
||||
.returns(returnType);
|
||||
if (publicCreateKeyMethod) {
|
||||
create.addJavadoc(Javadocs.CREATE_TYPED_KEY_JAVADOC, this.entry.apiClass(), this.entry.registryKey().location().toString());
|
||||
}
|
||||
return create;
|
||||
}
|
||||
|
||||
private TypeSpec.Builder keyHolderType() {
|
||||
return classBuilder(this.className)
|
||||
.addModifiers(PUBLIC, FINAL)
|
||||
.addJavadoc(Javadocs.getVersionDependentClassHeader("keys", "{@link $T#$L}"), RegistryKey.class, this.entry.registryKeyField())
|
||||
.addAnnotations(Annotations.CLASS_HEADER)
|
||||
.addMethod(MethodSpec.constructorBuilder()
|
||||
.addModifiers(PRIVATE)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeSpec getTypeSpec() {
|
||||
TypeName typedKeyType = ParameterizedTypeName.get(TypedKey.class, this.entry.apiClass());
|
||||
|
||||
TypeSpec.Builder typeBuilder = this.keyHolderType();
|
||||
MethodSpec.Builder createMethod = this.createMethod(typedKeyType);
|
||||
|
||||
boolean allExperimental = true;
|
||||
for (Holder.Reference<T> reference : this.entry.registry().listElements().sorted(Formatting.alphabeticKeyOrder(reference -> reference.key().location().getPath())).toList()) {
|
||||
ResourceKey<T> key = reference.key();
|
||||
String keyPath = key.location().getPath();
|
||||
String fieldName = Formatting.formatKeyAsField(keyPath);
|
||||
if (!SourceVersion.isIdentifier(fieldName) && this.entry.getFieldNames().containsKey(key)) {
|
||||
fieldName = this.entry.getFieldNames().get(key);
|
||||
}
|
||||
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(typedKeyType, fieldName, PUBLIC, STATIC, FINAL)
|
||||
.initializer("$N(key($S))", createMethod.build(), keyPath)
|
||||
.addJavadoc(Javadocs.getVersionDependentField("{@code $L}"), key.location().toString());
|
||||
|
||||
SingleFlagHolder requiredFeature = this.getRequiredFeature(reference);
|
||||
if (requiredFeature != null) {
|
||||
fieldBuilder.addAnnotations(experimentalAnnotations(requiredFeature));
|
||||
} else {
|
||||
allExperimental = false;
|
||||
}
|
||||
typeBuilder.addField(fieldBuilder.build());
|
||||
}
|
||||
|
||||
if (allExperimental) {
|
||||
typeBuilder.addAnnotation(EXPERIMENTAL_API_ANNOTATION);
|
||||
createMethod.addAnnotation(EXPERIMENTAL_API_ANNOTATION);
|
||||
}
|
||||
return typeBuilder.addMethod(createMethod.build()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JavaFile.Builder file(JavaFile.Builder builder) {
|
||||
return builder.addStaticImport(Key.class, "key");
|
||||
}
|
||||
|
||||
protected @Nullable SingleFlagHolder getRequiredFeature(Holder.Reference<T> reference) {
|
||||
if (this.isFilteredRegistry) {
|
||||
// built-in registry
|
||||
FeatureElement element = (FeatureElement) reference.value();
|
||||
if (FeatureFlags.isExperimental(element.requiredFeatures())) {
|
||||
return SingleFlagHolder.fromSet(element.requiredFeatures());
|
||||
}
|
||||
} else {
|
||||
// data-driven registry
|
||||
return this.experimentalKeys.get().get(reference.key());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
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.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
import com.squareup.javapoet.TypeSpec;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.registry.RegistryEntry;
|
||||
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.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.tag.TagKey;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static com.squareup.javapoet.TypeSpec.classBuilder;
|
||||
import static io.papermc.generator.utils.Annotations.EXPERIMENTAL_API_ANNOTATION;
|
||||
import static io.papermc.generator.utils.Annotations.experimentalAnnotations;
|
||||
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;
|
||||
|
||||
@NullMarked
|
||||
public class GeneratedTagKeyType extends SimpleGenerator {
|
||||
|
||||
private final RegistryEntry<?> entry;
|
||||
|
||||
public GeneratedTagKeyType(RegistryEntry<?> entry, String packageName) {
|
||||
super(entry.keyClassName().concat("TagKeys"), packageName);
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
private MethodSpec.Builder createMethod(TypeName returnType) {
|
||||
boolean publicCreateKeyMethod = true; // tag lifecycle event exists
|
||||
|
||||
ParameterSpec keyParam = ParameterSpec.builder(Key.class, "key", FINAL).build();
|
||||
MethodSpec.Builder create = MethodSpec.methodBuilder("create")
|
||||
.addModifiers(publicCreateKeyMethod ? PUBLIC : PRIVATE, STATIC)
|
||||
.addParameter(keyParam)
|
||||
.addCode("return $T.create($T.$L, $N);", TagKey.class, RegistryKey.class, this.entry.registryKeyField(), keyParam)
|
||||
.returns(returnType);
|
||||
if (publicCreateKeyMethod) {
|
||||
create.addAnnotation(EXPERIMENTAL_API_ANNOTATION); // TODO remove once not experimental
|
||||
create.addJavadoc(Javadocs.CREATED_TAG_KEY_JAVADOC, this.entry.apiClass(), this.entry.registryKey().location().toString());
|
||||
}
|
||||
return create;
|
||||
}
|
||||
|
||||
private TypeSpec.Builder keyHolderType() {
|
||||
return classBuilder(this.className)
|
||||
.addModifiers(PUBLIC, FINAL)
|
||||
.addJavadoc(Javadocs.getVersionDependentClassHeader("tag keys", "{@link $T#$L}"), RegistryKey.class, this.entry.registryKeyField())
|
||||
.addAnnotations(Annotations.CLASS_HEADER)
|
||||
.addMethod(MethodSpec.constructorBuilder()
|
||||
.addModifiers(PRIVATE)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeSpec getTypeSpec() {
|
||||
TypeName tagKeyType = ParameterizedTypeName.get(TagKey.class, this.entry.apiClass());
|
||||
|
||||
TypeSpec.Builder typeBuilder = this.keyHolderType();
|
||||
MethodSpec.Builder createMethod = this.createMethod(tagKeyType);
|
||||
|
||||
AtomicBoolean allExperimental = new AtomicBoolean(true);
|
||||
this.entry.registry().listTagIds().sorted(Formatting.alphabeticKeyOrder(tagKey -> tagKey.location().getPath())).forEach(tagKey -> {
|
||||
String fieldName = Formatting.formatKeyAsField(tagKey.location().getPath());
|
||||
FieldSpec.Builder fieldBuilder = FieldSpec.builder(tagKeyType, fieldName, PUBLIC, STATIC, FINAL)
|
||||
.initializer("$N(key($S))", createMethod.build(), tagKey.location().getPath())
|
||||
.addJavadoc(Javadocs.getVersionDependentField("{@code $L}"), "#" + tagKey.location());
|
||||
|
||||
String featureFlagName = Main.EXPERIMENTAL_TAGS.get(tagKey);
|
||||
if (featureFlagName != null) {
|
||||
fieldBuilder.addAnnotations(experimentalAnnotations(SingleFlagHolder.fromName(featureFlagName)));
|
||||
} else {
|
||||
allExperimental.set(false);
|
||||
}
|
||||
typeBuilder.addField(fieldBuilder.build());
|
||||
});
|
||||
if (allExperimental.get()) {
|
||||
typeBuilder.addAnnotation(EXPERIMENTAL_API_ANNOTATION);
|
||||
createMethod.addAnnotation(EXPERIMENTAL_API_ANNOTATION);
|
||||
} else {
|
||||
typeBuilder.addAnnotation(EXPERIMENTAL_API_ANNOTATION); // TODO experimental API
|
||||
}
|
||||
return typeBuilder.addMethod(createMethod.build()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JavaFile.Builder file(JavaFile.Builder builder) {
|
||||
return builder.addStaticImport(Key.class, "key");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import io.papermc.generator.utils.experimental.SingleFlagHolder;
|
||||
import io.papermc.paper.generated.GeneratedFrom;
|
||||
import java.util.List;
|
||||
import net.minecraft.SharedConstants;
|
||||
import org.bukkit.MinecraftExperimental;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Annotations {
|
||||
|
||||
public static List<AnnotationSpec> experimentalAnnotations(SingleFlagHolder requiredFeature) {
|
||||
AnnotationSpec.Builder builder = AnnotationSpec.builder(MinecraftExperimental.class);
|
||||
builder.addMember("value", "$T.$L", MinecraftExperimental.Requires.class, requiredFeature.asAnnotationMember().name());
|
||||
|
||||
return List.of(
|
||||
AnnotationSpec.builder(ApiStatus.Experimental.class).build(),
|
||||
builder.build()
|
||||
);
|
||||
}
|
||||
|
||||
public static AnnotationSpec suppressWarnings(String... values) {
|
||||
AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class);
|
||||
for (String value : values) {
|
||||
builder.addMember("value", "$S", value);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public static final AnnotationSpec EXPERIMENTAL_API_ANNOTATION = AnnotationSpec.builder(ApiStatus.Experimental.class).build();
|
||||
public static final AnnotationSpec NULL_MARKED = AnnotationSpec.builder(NullMarked.class).build();
|
||||
public static final AnnotationSpec OVERRIDE = AnnotationSpec.builder(Override.class).build();
|
||||
public static final AnnotationSpec GENERATED_FROM = AnnotationSpec.builder(GeneratedFrom.class)
|
||||
.addMember("value", "$S", SharedConstants.getCurrentVersion().getId())
|
||||
.build();
|
||||
public static final Iterable<AnnotationSpec> CLASS_HEADER = List.of(
|
||||
suppressWarnings("unused", "SpellCheckingInspection"),
|
||||
NULL_MARKED,
|
||||
GENERATED_FROM
|
||||
);
|
||||
|
||||
private Annotations() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class BlockEntityMapping {
|
||||
|
||||
// if this become painful/too weird like the blockdata just rename the impl directly again
|
||||
private static final Map<String, String> RENAMES = ImmutableMap.<String, String>builder()
|
||||
.put("CraftFurnace", "CraftFurnaceFurnace")
|
||||
.put("CraftMobSpawner", "CraftCreatureSpawner")
|
||||
.put("CraftPiston", "CraftMovingPiston")
|
||||
.put("CraftTrappedChest", "CraftChest") // not really a rename
|
||||
.buildOrThrow();
|
||||
|
||||
public static final Map<ResourceKey<BlockEntityType<?>>, String> MAPPING;
|
||||
|
||||
static {
|
||||
Map<ResourceKey<BlockEntityType<?>>, String> mapping = new IdentityHashMap<>();
|
||||
BuiltInRegistries.BLOCK_ENTITY_TYPE.registryKeySet().forEach(key -> {
|
||||
String name = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, key.location().getPath());
|
||||
String implName = "Craft".concat(name);
|
||||
|
||||
mapping.put(key, RENAMES.getOrDefault(implName, implName));
|
||||
});
|
||||
|
||||
MAPPING = Collections.unmodifiableMap(mapping);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,445 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import io.papermc.generator.types.craftblockdata.property.holder.VirtualField;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.FrontAndTop;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.level.block.AbstractFurnaceBlock;
|
||||
import net.minecraft.world.level.block.BigDripleafStemBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.CommandBlock;
|
||||
import net.minecraft.world.level.block.IronBarsBlock;
|
||||
import net.minecraft.world.level.block.MultifaceBlock;
|
||||
import net.minecraft.world.level.block.NoteBlock;
|
||||
import net.minecraft.world.level.block.PipeBlock;
|
||||
import net.minecraft.world.level.block.StructureBlock;
|
||||
import net.minecraft.world.level.block.TestBlock;
|
||||
import net.minecraft.world.level.block.TestInstanceBlock;
|
||||
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerState;
|
||||
import net.minecraft.world.level.block.entity.vault.VaultState;
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace;
|
||||
import net.minecraft.world.level.block.state.properties.BambooLeaves;
|
||||
import net.minecraft.world.level.block.state.properties.BedPart;
|
||||
import net.minecraft.world.level.block.state.properties.BellAttachType;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.ChestType;
|
||||
import net.minecraft.world.level.block.state.properties.ComparatorMode;
|
||||
import net.minecraft.world.level.block.state.properties.CreakingHeartState;
|
||||
import net.minecraft.world.level.block.state.properties.DoorHingeSide;
|
||||
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||
import net.minecraft.world.level.block.state.properties.DripstoneThickness;
|
||||
import net.minecraft.world.level.block.state.properties.Half;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.block.state.properties.PistonType;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.block.state.properties.RailShape;
|
||||
import net.minecraft.world.level.block.state.properties.RedstoneSide;
|
||||
import net.minecraft.world.level.block.state.properties.SculkSensorPhase;
|
||||
import net.minecraft.world.level.block.state.properties.SlabType;
|
||||
import net.minecraft.world.level.block.state.properties.StairsShape;
|
||||
import net.minecraft.world.level.block.state.properties.StructureMode;
|
||||
import net.minecraft.world.level.block.state.properties.TestBlockMode;
|
||||
import net.minecraft.world.level.block.state.properties.Tilt;
|
||||
import net.minecraft.world.level.block.state.properties.WallSide;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.Instrument;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Orientation;
|
||||
import org.bukkit.block.data.Ageable;
|
||||
import org.bukkit.block.data.AnaloguePowerable;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.Brushable;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.block.data.FaceAttachable;
|
||||
import org.bukkit.block.data.Hangable;
|
||||
import org.bukkit.block.data.Hatchable;
|
||||
import org.bukkit.block.data.Levelled;
|
||||
import org.bukkit.block.data.Lightable;
|
||||
import org.bukkit.block.data.MultipleFacing;
|
||||
import org.bukkit.block.data.Openable;
|
||||
import org.bukkit.block.data.Orientable;
|
||||
import org.bukkit.block.data.Powerable;
|
||||
import org.bukkit.block.data.Rail;
|
||||
import org.bukkit.block.data.Rotatable;
|
||||
import org.bukkit.block.data.Segmentable;
|
||||
import org.bukkit.block.data.Snowable;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
import org.bukkit.block.data.type.Bamboo;
|
||||
import org.bukkit.block.data.type.Bed;
|
||||
import org.bukkit.block.data.type.Bell;
|
||||
import org.bukkit.block.data.type.BigDripleaf;
|
||||
import org.bukkit.block.data.type.Chest;
|
||||
import org.bukkit.block.data.type.Comparator;
|
||||
import org.bukkit.block.data.type.CreakingHeart;
|
||||
import org.bukkit.block.data.type.Door;
|
||||
import org.bukkit.block.data.type.Dripleaf;
|
||||
import org.bukkit.block.data.type.Fence;
|
||||
import org.bukkit.block.data.type.Furnace;
|
||||
import org.bukkit.block.data.type.PointedDripstone;
|
||||
import org.bukkit.block.data.type.RedstoneRail;
|
||||
import org.bukkit.block.data.type.RedstoneWire;
|
||||
import org.bukkit.block.data.type.ResinClump;
|
||||
import org.bukkit.block.data.type.SculkSensor;
|
||||
import org.bukkit.block.data.type.Slab;
|
||||
import org.bukkit.block.data.type.Stairs;
|
||||
import org.bukkit.block.data.type.Switch;
|
||||
import org.bukkit.block.data.type.TechnicalPiston;
|
||||
import org.bukkit.block.data.type.TrialSpawner;
|
||||
import org.bukkit.block.data.type.Vault;
|
||||
import org.bukkit.block.data.type.Wall;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public final class BlockStateMapping {
|
||||
|
||||
public record BlockData(String implName, @Nullable Class<? extends org.bukkit.block.data.BlockData> api,
|
||||
Collection<? extends Property<?>> properties, Map<Property<?>, Field> propertyFields,
|
||||
Multimap<Either<Field, VirtualField>, Property<?>> complexPropertyFields) {
|
||||
}
|
||||
|
||||
private static final Map<String, String> API_RENAMES = ImmutableMap.<String, String>builder()
|
||||
.put("SnowLayer", "Snow")
|
||||
.put("StainedGlassPane", "GlassPane") // weird that this one implements glass pane but not the regular glass pane
|
||||
.put("CeilingHangingSign", "HangingSign")
|
||||
.put("RedStoneWire", "RedstoneWire")
|
||||
.put("TripWire", "Tripwire")
|
||||
.put("TripWireHook", "TripwireHook")
|
||||
.put("Tnt", "TNT")
|
||||
.put("BambooStalk", "Bamboo")
|
||||
.put("Farm", "Farmland")
|
||||
.put("ChiseledBookShelf", "ChiseledBookshelf")
|
||||
.put("UntintedParticleLeaves", "Leaves")
|
||||
.put("TintedParticleLeaves", "Leaves")
|
||||
.put("StandingSign", "Sign")
|
||||
.put("FenceGate", "Gate")
|
||||
.buildOrThrow();
|
||||
|
||||
private static final Set<Class<? extends Block>> BLOCK_SUFFIX_INTENDED = Set.of(
|
||||
CommandBlock.class,
|
||||
StructureBlock.class,
|
||||
NoteBlock.class,
|
||||
TestBlock.class,
|
||||
TestInstanceBlock.class
|
||||
);
|
||||
|
||||
// virtual data that doesn't exist as constant in the source but still organized this way in the api
|
||||
public static final ImmutableMultimap<Class<?>, VirtualField> VIRTUAL_NODES = ImmutableMultimap.<Class<?>, VirtualField>builder()
|
||||
.build();
|
||||
|
||||
public static final Map<Property<?>, Field> FALLBACK_GENERIC_FIELDS;
|
||||
|
||||
static {
|
||||
Map<Property<?>, Field> fallbackGenericFields = new HashMap<>();
|
||||
fetchProperties(BlockStateProperties.class, (field, property) -> fallbackGenericFields.put(property, field), null);
|
||||
FALLBACK_GENERIC_FIELDS = Collections.unmodifiableMap(fallbackGenericFields);
|
||||
}
|
||||
|
||||
public static final Map<Class<? extends Block>, BlockData> MAPPING;
|
||||
|
||||
static {
|
||||
Map<Class<? extends Block>, Collection<Property<?>>> specialBlocks = new IdentityHashMap<>();
|
||||
for (Block block : BuiltInRegistries.BLOCK) {
|
||||
if (!block.getStateDefinition().getProperties().isEmpty()) {
|
||||
specialBlocks.put(block.getClass(), block.getStateDefinition().getProperties());
|
||||
}
|
||||
}
|
||||
|
||||
Map<Class<? extends Block>, BlockData> map = new IdentityHashMap<>();
|
||||
for (Map.Entry<Class<? extends Block>, Collection<Property<?>>> entry : specialBlocks.entrySet()) {
|
||||
Class<? extends Block> specialBlock = entry.getKey();
|
||||
|
||||
Collection<Property<?>> properties = new ArrayList<>(entry.getValue());
|
||||
|
||||
Map<Property<?>, Field> propertyFields = new HashMap<>(properties.size());
|
||||
Multimap<Either<Field, VirtualField>, Property<?>> complexPropertyFields = ArrayListMultimap.create();
|
||||
|
||||
fetchProperties(specialBlock, (field, property) -> {
|
||||
if (properties.contains(property)) {
|
||||
propertyFields.put(property, field);
|
||||
}
|
||||
}, (field, property) -> {
|
||||
if (properties.remove(property)) { // handle those separately and only count if the property was in the state definition
|
||||
complexPropertyFields.put(Either.left(field), property);
|
||||
}
|
||||
});
|
||||
|
||||
// virtual nodes
|
||||
if (VIRTUAL_NODES.containsKey(specialBlock)) {
|
||||
for (VirtualField virtualField : VIRTUAL_NODES.get(specialBlock)) {
|
||||
for (Property<?> property : virtualField.values()) {
|
||||
if (properties.remove(property)) {
|
||||
complexPropertyFields.put(Either.right(virtualField), property);
|
||||
} else {
|
||||
throw new IllegalStateException("Unhandled virtual node " + virtualField.name() + " for " + property + " in " + specialBlock.getCanonicalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String apiName = formatApiName(specialBlock);
|
||||
String implName = "Craft".concat(apiName); // before renames
|
||||
|
||||
apiName = Formatting.stripWordOfCamelCaseName(apiName, "Base", true);
|
||||
apiName = API_RENAMES.getOrDefault(apiName, apiName);
|
||||
|
||||
Class<? extends org.bukkit.block.data.BlockData> api = ClassHelper.classOr("org.bukkit.block.data.type." + apiName, null);
|
||||
if (api == null) {
|
||||
Class<?> directParent = specialBlock.getSuperclass();
|
||||
if (specialBlocks.containsKey(directParent)) {
|
||||
// if the properties are the same then always consider the parent
|
||||
// check deeper in the tree?
|
||||
if (specialBlocks.get(directParent).equals(entry.getValue())) {
|
||||
String parentApiName = formatApiName(directParent);
|
||||
parentApiName = Formatting.stripWordOfCamelCaseName(parentApiName, "Base", true);
|
||||
parentApiName = API_RENAMES.getOrDefault(parentApiName, parentApiName);
|
||||
api = ClassHelper.classOr("org.bukkit.block.data.type." + parentApiName, api);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (api == null) { // todo remove this part
|
||||
if (AbstractFurnaceBlock.class.isAssignableFrom(specialBlock)) {
|
||||
api = Furnace.class; // for smoker and blast furnace
|
||||
} else if (specialBlock == BigDripleafStemBlock.class) {
|
||||
api = Dripleaf.class;
|
||||
} else if (specialBlock == IronBarsBlock.class) {
|
||||
api = Fence.class; // for glass pane (regular) and iron bars
|
||||
} else if (specialBlock == MultifaceBlock.class) {
|
||||
api = ResinClump.class;
|
||||
}
|
||||
}
|
||||
|
||||
map.put(specialBlock, new BlockData(implName, api, properties, propertyFields, complexPropertyFields));
|
||||
}
|
||||
MAPPING = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private static final Map<String, Class<? extends org.bukkit.block.data.BlockData>> NAME_TO_DATA = Map.of(
|
||||
BlockStateProperties.AGE_1.getName(), Ageable.class,
|
||||
BlockStateProperties.LEVEL.getName(), Levelled.class
|
||||
);
|
||||
|
||||
private static final Map<Property<?>, Class<? extends org.bukkit.block.data.BlockData>> PROPERTY_TO_DATA = ImmutableMap.<Property<?>, Class<? extends org.bukkit.block.data.BlockData>>builder()
|
||||
// levelled and ageable are done using the property name
|
||||
.put(BlockStateProperties.POWER, AnaloguePowerable.class)
|
||||
.put(BlockStateProperties.HALF, Bisected.class)
|
||||
.put(BlockStateProperties.DOUBLE_BLOCK_HALF, Bisected.class)
|
||||
.put(BlockStateProperties.DUSTED, Brushable.class)
|
||||
.put(BlockStateProperties.FACING, Directional.class)
|
||||
.put(BlockStateProperties.HORIZONTAL_FACING, Directional.class)
|
||||
.put(BlockStateProperties.ATTACH_FACE, FaceAttachable.class)
|
||||
.put(BlockStateProperties.HANGING, Hangable.class)
|
||||
.put(BlockStateProperties.HATCH, Hatchable.class)
|
||||
.put(BlockStateProperties.LIT, Lightable.class)
|
||||
// multiple facing is done by matching two or more pipe block properties
|
||||
.put(BlockStateProperties.OPEN, Openable.class)
|
||||
.put(BlockStateProperties.HORIZONTAL_AXIS, Orientable.class)
|
||||
.put(BlockStateProperties.AXIS, Orientable.class)
|
||||
.put(BlockStateProperties.POWERED, Powerable.class)
|
||||
.put(BlockStateProperties.RAIL_SHAPE, Rail.class)
|
||||
.put(BlockStateProperties.RAIL_SHAPE_STRAIGHT, Rail.class)
|
||||
.put(BlockStateProperties.ROTATION_16, Rotatable.class)
|
||||
.put(BlockStateProperties.SNOWY, Snowable.class)
|
||||
.put(BlockStateProperties.WATERLOGGED, Waterlogged.class)
|
||||
.put(BlockStateProperties.SEGMENT_AMOUNT, Segmentable.class)
|
||||
.buildOrThrow();
|
||||
|
||||
private static final Map<Property<?>, Class<? extends org.bukkit.block.data.BlockData>> MAIN_PROPERTY_TO_DATA = Map.of(
|
||||
BlockStateProperties.PISTON_TYPE, TechnicalPiston.class,
|
||||
BlockStateProperties.STAIRS_SHAPE, Stairs.class
|
||||
);
|
||||
|
||||
public static final Map<Class<? extends Enum<? extends StringRepresentable>>, Class<? extends Enum<?>>> ENUM_BRIDGE = ImmutableMap.<Class<? extends Enum<? extends StringRepresentable>>, Class<? extends Enum<?>>>builder()
|
||||
.put(DoorHingeSide.class, Door.Hinge.class)
|
||||
.put(SlabType.class, Slab.Type.class)
|
||||
.put(StructureMode.class, org.bukkit.block.data.type.StructureBlock.Mode.class)
|
||||
.put(DripstoneThickness.class, PointedDripstone.Thickness.class)
|
||||
.put(WallSide.class, Wall.Height.class)
|
||||
.put(BellAttachType.class, Bell.Attachment.class)
|
||||
.put(NoteBlockInstrument.class, Instrument.class)
|
||||
.put(StairsShape.class, Stairs.Shape.class)
|
||||
.put(Direction.class, BlockFace.class)
|
||||
.put(ComparatorMode.class, Comparator.Mode.class)
|
||||
.put(PistonType.class, TechnicalPiston.Type.class)
|
||||
.put(BedPart.class, Bed.Part.class)
|
||||
.put(Half.class, Bisected.Half.class)
|
||||
.put(AttachFace.class, FaceAttachable.AttachedFace.class)
|
||||
.put(RailShape.class, Rail.Shape.class)
|
||||
.put(SculkSensorPhase.class, SculkSensor.Phase.class)
|
||||
.put(DoubleBlockHalf.class, Bisected.Half.class)
|
||||
.put(Tilt.class, BigDripleaf.Tilt.class)
|
||||
.put(ChestType.class, Chest.Type.class)
|
||||
.put(RedstoneSide.class, RedstoneWire.Connection.class)
|
||||
.put(Direction.Axis.class, Axis.class)
|
||||
.put(BambooLeaves.class, Bamboo.Leaves.class)
|
||||
.put(TrialSpawnerState.class, TrialSpawner.State.class)
|
||||
.put(FrontAndTop.class, Orientation.class)
|
||||
.put(VaultState.class, Vault.State.class)
|
||||
.put(CreakingHeartState.class, CreakingHeart.State.class)
|
||||
.put(TestBlockMode.class, org.bukkit.block.data.type.TestBlock.Mode.class)
|
||||
.buildOrThrow();
|
||||
|
||||
public static @Nullable Class<? extends org.bukkit.block.data.BlockData> getBestSuitedApiClass(Class<?> block) {
|
||||
if (!MAPPING.containsKey(block)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getBestSuitedApiClass(MAPPING.get(block));
|
||||
}
|
||||
|
||||
public static @Nullable Class<? extends org.bukkit.block.data.BlockData> getBestSuitedApiClass(BlockData data) {
|
||||
if (data.api() != null) {
|
||||
return data.api();
|
||||
}
|
||||
|
||||
int pipeProps = 0;
|
||||
Set<Class<? extends org.bukkit.block.data.BlockData>> extensions = new LinkedHashSet<>();
|
||||
for (Property<?> property : data.properties()) {
|
||||
if (MAIN_PROPERTY_TO_DATA.containsKey(property)) {
|
||||
return MAIN_PROPERTY_TO_DATA.get(property);
|
||||
}
|
||||
|
||||
if (NAME_TO_DATA.containsKey(property.getName())) {
|
||||
extensions.add(NAME_TO_DATA.get(property.getName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PROPERTY_TO_DATA.containsKey(property)) {
|
||||
extensions.add(PROPERTY_TO_DATA.get(property));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PipeBlock.PROPERTY_BY_DIRECTION.containsValue(property)) {
|
||||
pipeProps++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!extensions.isEmpty()) {
|
||||
if (isExactly(extensions, Switch.class)) {
|
||||
return Switch.class;
|
||||
}
|
||||
if (isExactly(extensions, RedstoneRail.class)) {
|
||||
return RedstoneRail.class;
|
||||
}
|
||||
|
||||
return extensions.iterator().next();
|
||||
}
|
||||
|
||||
for (Property<?> property : data.complexPropertyFields().values()) {
|
||||
if (PipeBlock.PROPERTY_BY_DIRECTION.containsValue(property)) {
|
||||
pipeProps++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pipeProps >= 2) {
|
||||
return MultipleFacing.class;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isExactly(Set<Class<? extends org.bukkit.block.data.BlockData>> extensions, Class<? extends org.bukkit.block.data.BlockData> globClass) {
|
||||
return extensions.equals(ClassHelper.getAllInterfaces(globClass, org.bukkit.block.data.BlockData.class, new HashSet<>()));
|
||||
}
|
||||
|
||||
private static String formatApiName(Class<?> specialBlock) {
|
||||
String apiName = specialBlock.getSimpleName();
|
||||
if (!BLOCK_SUFFIX_INTENDED.contains(specialBlock)) {
|
||||
return apiName.substring(0, apiName.length() - "Block".length());
|
||||
}
|
||||
return apiName;
|
||||
}
|
||||
|
||||
private static boolean handleComplexType(Field field, BiConsumer<Field, Property<?>> complexCallback) throws IllegalAccessException {
|
||||
if (field.getType().isArray() && Property.class.isAssignableFrom(field.getType().getComponentType())) {
|
||||
if (!field.trySetAccessible()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Property<?> property : (Property<?>[]) field.get(null)) {
|
||||
complexCallback.accept(field, property);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (Iterable.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType complexType) {
|
||||
Type[] args = complexType.getActualTypeArguments();
|
||||
if (args.length == 1 && Property.class.isAssignableFrom(ClassHelper.eraseType(args[0]))) {
|
||||
if (!field.trySetAccessible()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Property<?> property : (Iterable<Property<?>>) field.get(null)) {
|
||||
complexCallback.accept(field, property);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (Map.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType complexType) {
|
||||
if (!field.trySetAccessible()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Type[] args = complexType.getActualTypeArguments();
|
||||
if (args.length == 2 && Property.class.isAssignableFrom(ClassHelper.eraseType(args[1]))) {
|
||||
for (Property<?> property : ((Map<?, Property<?>>) field.get(null)).values()) {
|
||||
complexCallback.accept(field, property);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void fetchProperties(Class<?> block, BiConsumer<Field, Property<?>> simpleCallback, @Nullable BiConsumer<Field, Property<?>> complexCallback) {
|
||||
try {
|
||||
for (Field field : block.getDeclaredFields()) {
|
||||
if (ClassHelper.isStaticConstant(field, 0)) {
|
||||
if (complexCallback != null && handleComplexType(field, complexCallback)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Property.class.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field.trySetAccessible()) {
|
||||
Property<?> property = ((Property<?>) field.get(null));
|
||||
simpleCallback.accept(field, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if (block.isInterface()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// look deeper
|
||||
if (block.getSuperclass() != null && block.getSuperclass() != Block.class) {
|
||||
fetchProperties(block.getSuperclass(), simpleCallback, complexCallback);
|
||||
}
|
||||
for (Class<?> ext : block.getInterfaces()) {
|
||||
fetchProperties(ext, simpleCallback, complexCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public final class ClassHelper {
|
||||
|
||||
public static Set<Class<?>> getAllInterfaces(Class<?> clazz, Class<?> ignored, Set<Class<?>> interfaces) {
|
||||
Class<?>[] classes = clazz.getInterfaces();
|
||||
interfaces.addAll(Arrays.asList(classes));
|
||||
for (Class<?> farClass : classes) {
|
||||
if (farClass == ignored) {
|
||||
continue;
|
||||
}
|
||||
getAllInterfaces(farClass, ignored, interfaces);
|
||||
}
|
||||
interfaces.remove(ignored);
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public static @Nullable Type getNestedTypeParameter(Type type, @Nullable Class<?>... classes) {
|
||||
for (Class<?> clazz : classes) {
|
||||
if (!(type instanceof ParameterizedType complexType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type[] types = complexType.getActualTypeArguments();
|
||||
if (types.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (clazz == null || eraseType(types[0]) == clazz) {
|
||||
type = types[0];
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public static Class<?> eraseType(Type type) {
|
||||
if (type instanceof Class<?> clazz) {
|
||||
return clazz;
|
||||
}
|
||||
if (type instanceof ParameterizedType complexType) {
|
||||
return eraseType(complexType.getRawType());
|
||||
}
|
||||
throw new UnsupportedOperationException("Don't know how to turn " + type + " into its erased type!");
|
||||
}
|
||||
|
||||
public static boolean isStaticConstant(Field field, int extraFlags) {
|
||||
int flags = extraFlags | Modifier.STATIC | Modifier.FINAL;
|
||||
return (field.getModifiers() & flags) == flags;
|
||||
}
|
||||
|
||||
public static <T> @Nullable Class<? extends T> classOr(String className, @Nullable Class<? extends T> defaultClass) {
|
||||
try {
|
||||
return (Class<? extends T>) Class.forName(className);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return defaultClass;
|
||||
}
|
||||
}
|
||||
|
||||
private ClassHelper() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class CommonVariable {
|
||||
|
||||
public static final String INDEX = "index";
|
||||
public static final String MAP_ENTRY = "entry";
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Formatting {
|
||||
|
||||
private static final Pattern ILLEGAL_FIELD_CHARACTERS = Pattern.compile("[.-/]");
|
||||
|
||||
public static String formatKeyAsField(String path) {
|
||||
return ILLEGAL_FIELD_CHARACTERS.matcher(path.toUpperCase(Locale.ENGLISH)).replaceAll("_");
|
||||
}
|
||||
|
||||
public static String formatTagFieldPrefix(String name, ResourceKey<? extends Registry<?>> registryKey) {
|
||||
if (registryKey == Registries.BLOCK) {
|
||||
return "";
|
||||
}
|
||||
if (registryKey == Registries.GAME_EVENT) {
|
||||
return "GAME_EVENT_"; // Paper doesn't follow the format (should be GAME_EVENTS_) (pre 1.21)
|
||||
}
|
||||
return name.toUpperCase(Locale.ENGLISH) + "_";
|
||||
}
|
||||
|
||||
public static Optional<String> formatTagKey(String tagDir, String resourcePath) {
|
||||
int tagsIndex = resourcePath.indexOf(tagDir);
|
||||
int dotIndex = resourcePath.lastIndexOf('.');
|
||||
if (tagsIndex == -1 || dotIndex == -1) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(resourcePath.substring(tagsIndex + tagDir.length() + 1, dotIndex)); // namespace/tags/registry_key/[tag_key].json
|
||||
}
|
||||
|
||||
public static String quoted(String value) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
|
||||
public static String[] asCode(int... values) {
|
||||
return IntStream.of(values).mapToObj(Integer::toString).toArray(String[]::new);
|
||||
}
|
||||
|
||||
public static String stripWordOfCamelCaseName(String name, String word, boolean onlyOnce) {
|
||||
String newName = name;
|
||||
int startIndex = 0;
|
||||
while (true) {
|
||||
int baseIndex = newName.indexOf(word, startIndex);
|
||||
if (baseIndex == -1) {
|
||||
return newName;
|
||||
}
|
||||
|
||||
if ((baseIndex > 0 && !Character.isLowerCase(newName.charAt(baseIndex - 1))) ||
|
||||
(baseIndex + word.length() < newName.length() && !Character.isUpperCase(newName.charAt(baseIndex + word.length())))) {
|
||||
startIndex = baseIndex + word.length();
|
||||
continue;
|
||||
}
|
||||
|
||||
newName = newName.substring(0, baseIndex) + newName.substring(baseIndex + word.length());
|
||||
startIndex = baseIndex;
|
||||
if (onlyOnce) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newName;
|
||||
}
|
||||
|
||||
public static final Comparator<String> ALPHABETIC_KEY_ORDER = alphabeticKeyOrder(path -> path);
|
||||
|
||||
public static <T> Comparator<T> alphabeticKeyOrder(Function<T, String> mapper) {
|
||||
return (o1, o2) -> {
|
||||
String path1 = mapper.apply(o1);
|
||||
String path2 = mapper.apply(o2);
|
||||
|
||||
OptionalInt trailingInt1 = tryParseTrailingInt(path1);
|
||||
OptionalInt trailingInt2 = tryParseTrailingInt(path2);
|
||||
|
||||
if (trailingInt1.isPresent() && trailingInt2.isPresent()) {
|
||||
return Integer.compare(trailingInt1.getAsInt(), trailingInt2.getAsInt());
|
||||
}
|
||||
|
||||
return path1.compareTo(path2);
|
||||
};
|
||||
}
|
||||
|
||||
private static OptionalInt tryParseTrailingInt(String path) {
|
||||
int delimiterIndex = path.lastIndexOf('_');
|
||||
if (delimiterIndex != -1) {
|
||||
String score = path.substring(delimiterIndex + 1);
|
||||
if (NumberUtils.isDigits(score)) {
|
||||
return OptionalInt.of(Integer.parseInt(score));
|
||||
}
|
||||
}
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
private Formatting() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class Javadocs {
|
||||
|
||||
public static String getVersionDependentClassHeader(String objectIdentifier, String headerIdentifier) {
|
||||
return """
|
||||
Vanilla %s for %s.
|
||||
|
||||
@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.
|
||||
""".formatted(objectIdentifier, headerIdentifier);
|
||||
}
|
||||
|
||||
public static String getVersionDependentField(String headerIdentifier) {
|
||||
return """
|
||||
%s
|
||||
|
||||
@apiNote This field is version-dependant and may be removed in future Minecraft versions
|
||||
""".formatted(headerIdentifier);
|
||||
}
|
||||
|
||||
public static final String CREATE_TYPED_KEY_JAVADOC = """
|
||||
Creates a typed key for {@link $T} in the registry {@code $L}.
|
||||
|
||||
@param key the value's key in the registry
|
||||
@return a new typed key
|
||||
""";
|
||||
|
||||
public static final String CREATED_TAG_KEY_JAVADOC = """
|
||||
Creates a tag key for {@link $T} in the registry {@code $L}.
|
||||
|
||||
@param key the tag key's key
|
||||
@return a new tag key
|
||||
""";
|
||||
|
||||
private Javadocs() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
package io.papermc.generator.utils;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public class NamingManager {
|
||||
|
||||
private final @Nullable AccessKeyword accessKeyword;
|
||||
private final String baseName;
|
||||
private final String lowerCamelName, upperCamelName;
|
||||
|
||||
public NamingManager(NamingManager.@Nullable AccessKeyword accessKeyword, CaseFormat format, String baseName) {
|
||||
this.accessKeyword = accessKeyword; // this is a little bit too restrictive for extra data hmm
|
||||
this.baseName = baseName;
|
||||
this.upperCamelName = format.to(CaseFormat.UPPER_CAMEL, baseName);
|
||||
this.lowerCamelName = format.to(CaseFormat.LOWER_CAMEL, baseName);
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return this.lowerCamelName;
|
||||
}
|
||||
|
||||
public String getMethodBaseName() {
|
||||
return this.upperCamelName;
|
||||
}
|
||||
|
||||
public NameWrapper getMethodNameWrapper() {
|
||||
return NameWrapper.wrap("get", this.upperCamelName);
|
||||
}
|
||||
|
||||
public NameWrapper getVariableNameWrapper() {
|
||||
return NameWrapper.wrap("", this.lowerCamelName);
|
||||
}
|
||||
|
||||
public NameWrapper getterName(Predicate<String> keywordPredicate) {
|
||||
return accessName(keywordPredicate, AccessKeyword::get, "get");
|
||||
}
|
||||
|
||||
public NameWrapper setterName(Predicate<String> keywordPredicate) {
|
||||
return accessName(keywordPredicate, AccessKeyword::set, "set");
|
||||
}
|
||||
|
||||
public String simpleGetterName(Predicate<String> keywordPredicate) {
|
||||
return getterName(keywordPredicate).concat();
|
||||
}
|
||||
|
||||
public String simpleSetterName(Predicate<String> keywordPredicate) {
|
||||
return setterName(keywordPredicate).concat();
|
||||
}
|
||||
|
||||
private NameWrapper accessName(Predicate<String> keywordPredicate, KeywordFetcher keywordFetcher, String fallbackKeyword) {
|
||||
final String name;
|
||||
String accessKeyword;
|
||||
if (keywordPredicate.test(this.baseName)) {
|
||||
accessKeyword = Optional.ofNullable(this.accessKeyword).flatMap(keywordFetcher::fetch).orElse(fallbackKeyword);
|
||||
name = this.upperCamelName;
|
||||
} else {
|
||||
accessKeyword = "";
|
||||
name = this.lowerCamelName;
|
||||
}
|
||||
return NameWrapper.wrap(accessKeyword, name);
|
||||
}
|
||||
|
||||
public String paramName(Class<?> type) {
|
||||
final String paramName;
|
||||
if (type.isPrimitive()) {
|
||||
paramName = this.lowerCamelName;
|
||||
} else {
|
||||
paramName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, type.getSimpleName());
|
||||
}
|
||||
return ensureValidName(paramName);
|
||||
}
|
||||
|
||||
public static String ensureValidName(String name) {
|
||||
if (!SourceVersion.isIdentifier(name) || SourceVersion.isKeyword(name)) {
|
||||
return "_" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static class NameWrapper {
|
||||
|
||||
private final String keyword;
|
||||
private final String name;
|
||||
private String preValue = "", postValue = "";
|
||||
|
||||
private NameWrapper(String keyword, String name) {
|
||||
this.keyword = keyword;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static NameWrapper wrap(String keyword, String name) {
|
||||
return new NameWrapper(keyword, name);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
public NameWrapper pre(String value) {
|
||||
this.preValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
public NameWrapper post(String value) {
|
||||
this.postValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String concat() {
|
||||
String finalName = this.keyword + this.preValue + this.name + this.postValue;
|
||||
this.preValue = this.postValue = ""; // reset
|
||||
return ensureValidName(finalName);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface KeywordFetcher {
|
||||
|
||||
Optional<String> fetch(AccessKeyword accessKeyword);
|
||||
}
|
||||
|
||||
public static AccessKeyword keywordGet(String keyword) {
|
||||
return new AccessKeyword(Optional.of(keyword), Optional.empty());
|
||||
}
|
||||
|
||||
public static AccessKeyword keywordSet(String keyword) {
|
||||
return new AccessKeyword(Optional.empty(), Optional.of(keyword));
|
||||
}
|
||||
|
||||
public static AccessKeyword keywordGetSet(String getter, String setter) {
|
||||
return new AccessKeyword(Optional.of(getter), Optional.of(setter));
|
||||
}
|
||||
|
||||
public record AccessKeyword(Optional<String> get, Optional<String> set) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package io.papermc.generator.utils.experimental;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import io.papermc.generator.Main;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderGetter;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record CollectingContext<T>(Set<ResourceKey<T>> registered,
|
||||
Registry<T> registry) implements BootstrapContext<T> {
|
||||
|
||||
@Override
|
||||
public Holder.Reference<T> register(ResourceKey<T> key, T value, Lifecycle lifecycle) {
|
||||
this.registered.add(key);
|
||||
return Holder.Reference.createStandAlone(this.registry, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> HolderGetter<S> lookup(ResourceKey<? extends Registry<? extends S>> key) {
|
||||
return Main.REGISTRY_ACCESS.lookupOrThrow(key);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package io.papermc.generator.utils.experimental;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.generator.Main;
|
||||
import io.papermc.generator.utils.Formatting;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.registries.TradeRebalanceRegistries;
|
||||
import net.minecraft.data.registries.VanillaRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.repository.BuiltInPackSource;
|
||||
import net.minecraft.server.packs.resources.MultiPackResourceManager;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@NullMarked
|
||||
public final class ExperimentalCollector {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private static final Map<ResourceKey<? extends Registry<?>>, RegistrySetBuilder.RegistryBootstrap<?>> VANILLA_REGISTRY_ENTRIES = VanillaRegistries.BUILDER.entries.stream()
|
||||
.collect(Collectors.toMap(RegistrySetBuilder.RegistryStub::key, RegistrySetBuilder.RegistryStub::bootstrap));
|
||||
|
||||
private static final Map<RegistrySetBuilder, SingleFlagHolder> EXPERIMENTAL_REGISTRY_FLAGS = Map.of(
|
||||
// Update for Experimental API
|
||||
TradeRebalanceRegistries.BUILDER, FlagHolders.TRADE_REBALANCE
|
||||
);
|
||||
|
||||
private static final Multimap<ResourceKey<? extends Registry<?>>, Map.Entry<SingleFlagHolder, RegistrySetBuilder.RegistryBootstrap<?>>> EXPERIMENTAL_REGISTRY_ENTRIES;
|
||||
static {
|
||||
EXPERIMENTAL_REGISTRY_ENTRIES = HashMultimap.create();
|
||||
for (Map.Entry<RegistrySetBuilder, SingleFlagHolder> entry : EXPERIMENTAL_REGISTRY_FLAGS.entrySet()) {
|
||||
for (RegistrySetBuilder.RegistryStub<?> stub : entry.getKey().entries) {
|
||||
EXPERIMENTAL_REGISTRY_ENTRIES.put(stub.key(), Map.entry(entry.getValue(), stub.bootstrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Map<ResourceKey<T>, SingleFlagHolder> collectDataDrivenElementIds(Registry<T> registry) {
|
||||
Collection<Map.Entry<SingleFlagHolder, RegistrySetBuilder.RegistryBootstrap<?>>> experimentalEntries = EXPERIMENTAL_REGISTRY_ENTRIES.get(registry.key());
|
||||
if (experimentalEntries.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<ResourceKey<T>, SingleFlagHolder> result = new IdentityHashMap<>();
|
||||
for (Map.Entry<SingleFlagHolder, RegistrySetBuilder.RegistryBootstrap<?>> experimentalEntry : experimentalEntries) {
|
||||
RegistrySetBuilder.RegistryBootstrap<T> experimentalBootstrap = (RegistrySetBuilder.RegistryBootstrap<T>) experimentalEntry.getValue();
|
||||
Set<ResourceKey<T>> experimental = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
CollectingContext<T> experimentalCollector = new CollectingContext<>(experimental, registry);
|
||||
experimentalBootstrap.run(experimentalCollector);
|
||||
result.putAll(experimental.stream().collect(Collectors.toMap(key -> key, key -> experimentalEntry.getKey())));
|
||||
}
|
||||
|
||||
RegistrySetBuilder.@Nullable RegistryBootstrap<T> vanillaBootstrap = (RegistrySetBuilder.RegistryBootstrap<T>) VANILLA_REGISTRY_ENTRIES.get(registry.key());
|
||||
if (vanillaBootstrap != null) {
|
||||
Set<ResourceKey<T>> vanilla = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
CollectingContext<T> vanillaCollector = new CollectingContext<>(vanilla, registry);
|
||||
vanillaBootstrap.run(vanillaCollector);
|
||||
result.keySet().removeAll(vanilla);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// collect all the tags by grabbing the json from the data-packs
|
||||
// another (probably) way is to hook into the data generator like the typed keys generator
|
||||
public static Map<TagKey<?>, String> collectTags(MultiPackResourceManager resourceManager) {
|
||||
Map<TagKey<?>, String> result = new IdentityHashMap<>();
|
||||
|
||||
// collect all vanilla tags
|
||||
Multimap<ResourceKey<? extends Registry<?>>, String> vanillaTags = HashMultimap.create();
|
||||
PackResources vanillaPack = resourceManager.listPacks()
|
||||
.filter(packResources -> packResources.packId().equals(BuiltInPackSource.VANILLA_ID))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
collectTagsFromPack(vanillaPack, (entry, path) -> vanillaTags.put(entry.key(), path));
|
||||
|
||||
// then distinct with other data-pack tags to know for sure newly created tags and so experimental one
|
||||
resourceManager.listPacks().forEach(pack -> {
|
||||
String packId = pack.packId();
|
||||
if (packId.equals(BuiltInPackSource.VANILLA_ID)) return;
|
||||
|
||||
collectTagsFromPack(pack, (entry, path) -> {
|
||||
if (vanillaTags.get(entry.key()).contains(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.put(entry.value().listTagIds()
|
||||
.filter(tagKey -> tagKey.location().getPath().equals(path))
|
||||
.findFirst()
|
||||
.orElseThrow(), packId);
|
||||
});
|
||||
});
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
private static void collectTagsFromPack(PackResources pack, BiConsumer<RegistryAccess.RegistryEntry<?>, String> output) {
|
||||
Set<String> namespaces = pack.getNamespaces(PackType.SERVER_DATA);
|
||||
|
||||
for (String namespace : namespaces) {
|
||||
Main.REGISTRY_ACCESS.registries().forEach(entry -> {
|
||||
// this is probably expensive but can't find another way around and data-pack loader has similar logic
|
||||
// the issue is that registry key can have parent/key but tag key can also have parent/key so parsing become a mess
|
||||
// without having at least one of the two values
|
||||
String tagDir = Registries.tagsDirPath(entry.key());
|
||||
pack.listResources(PackType.SERVER_DATA, namespace, tagDir, (id, supplier) -> {
|
||||
Formatting.formatTagKey(tagDir, id.getPath()).ifPresentOrElse(path -> output.accept(entry, path), () -> {
|
||||
LOGGER.warn("Unable to parse the path: {}/{}/{}.json in the data-pack {} into a tag key", namespace, tagDir, id.getPath(), pack.packId());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ExperimentalCollector() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package io.papermc.generator.utils.experimental;
|
||||
|
||||
import net.minecraft.world.flag.FeatureFlag;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface FlagHolder {
|
||||
|
||||
default FeatureFlagSet flagSet() {
|
||||
return FeatureFlags.REGISTRY.subset(this.flag());
|
||||
}
|
||||
|
||||
FeatureFlag flag();
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package io.papermc.generator.utils.experimental;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.world.flag.FeatureFlag;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.bukkit.MinecraftExperimental;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class FlagHolders {
|
||||
|
||||
public static final SingleFlagHolder TRADE_REBALANCE = SingleFlagHolder.fromValue(FeatureFlags.TRADE_REBALANCE);
|
||||
public static final SingleFlagHolder REDSTONE_EXPERIMENTS = SingleFlagHolder.fromValue(FeatureFlags.REDSTONE_EXPERIMENTS);
|
||||
public static final SingleFlagHolder MINECART_IMPROVEMENTS = SingleFlagHolder.fromValue(FeatureFlags.MINECART_IMPROVEMENTS);
|
||||
|
||||
static final Map<FeatureFlag, MinecraftExperimental.Requires> ANNOTATION_EQUIVALENT = Util.make(new HashMap<SingleFlagHolder, MinecraftExperimental.Requires>(), map -> {
|
||||
map.put(TRADE_REBALANCE, MinecraftExperimental.Requires.TRADE_REBALANCE);
|
||||
map.put(REDSTONE_EXPERIMENTS, MinecraftExperimental.Requires.REDSTONE_EXPERIMENTS);
|
||||
map.put(MINECART_IMPROVEMENTS, MinecraftExperimental.Requires.MINECART_IMPROVEMENTS);
|
||||
}).entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey().flag(), Map.Entry::getValue));
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package io.papermc.generator.utils.experimental;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlag;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.bukkit.MinecraftExperimental;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record SingleFlagHolder(FeatureFlag flag) implements FlagHolder { // todo support multiple flags?
|
||||
|
||||
private static final Map<String, FeatureFlag> FEATURE_FLAG_CACHE = new HashMap<>();
|
||||
private static final Map<FeatureFlag, ResourceLocation> FEATURE_FLAG_NAME = HashBiMap.create(FeatureFlags.REGISTRY.names).inverse();
|
||||
|
||||
static SingleFlagHolder fromValue(FeatureFlag flag) {
|
||||
return new SingleFlagHolder(flag);
|
||||
}
|
||||
|
||||
public static SingleFlagHolder fromSet(FeatureFlagSet standaloneSet) {
|
||||
Preconditions.checkArgument(Long.bitCount(standaloneSet.mask) == 1, "Flag set size must be equals to 1.");
|
||||
|
||||
for (FeatureFlag flag : FeatureFlags.REGISTRY.names.values()) {
|
||||
if (standaloneSet.contains(flag)) {
|
||||
return fromValue(flag);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public static SingleFlagHolder fromName(String name) {
|
||||
return fromValue(FEATURE_FLAG_CACHE.computeIfAbsent(name, key -> {
|
||||
return FeatureFlags.REGISTRY.names.get(ResourceLocation.withDefaultNamespace(key));
|
||||
}));
|
||||
}
|
||||
|
||||
public MinecraftExperimental.Requires asAnnotationMember() {
|
||||
MinecraftExperimental.Requires annotationMember = FlagHolders.ANNOTATION_EQUIVALENT.get(this.flag);
|
||||
if (annotationMember == null) {
|
||||
throw new UnsupportedOperationException("Don't know that feature flag: " + FEATURE_FLAG_NAME.get(this.flag));
|
||||
}
|
||||
return annotationMember;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package io.papermc.generator;
|
||||
|
||||
import io.papermc.generator.utils.BlockStateMapping;
|
||||
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.Map;
|
||||
import java.util.Set;
|
||||
import io.papermc.generator.utils.ClassHelper;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
|
||||
import net.minecraft.world.level.block.MossyCarpetBlock;
|
||||
import net.minecraft.world.level.block.PipeBlock;
|
||||
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;
|
||||
|
||||
public class BlockStatePropertyTest {
|
||||
|
||||
private static Set<Class<? extends Comparable<?>>> ENUM_PROPERTY_VALUES;
|
||||
|
||||
@BeforeAll
|
||||
public static void getAllProperties() {
|
||||
// bootstrap
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
Bootstrap.validate();
|
||||
|
||||
// get all properties
|
||||
Set<Class<? extends Comparable<?>>> enumPropertyValues = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
try {
|
||||
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
|
||||
if (ClassHelper.isStaticConstant(field, Modifier.PUBLIC)) {
|
||||
if (!EnumProperty.class.isAssignableFrom(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
enumPropertyValues.add(((EnumProperty<?>) field.get(null)).getValueClass());
|
||||
}
|
||||
}
|
||||
ENUM_PROPERTY_VALUES = Collections.unmodifiableSet(enumPropertyValues);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferences() throws NoSuchFieldException, IllegalAccessException {
|
||||
// if renamed should change DataPropertyWriter#FIELD_TO_BASE_NAME/FIELD_TO_BASE_NAME_SPECIFICS
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticVarHandle(ChiseledBookShelfBlock.class, "SLOT_OCCUPIED_PROPERTIES", List.class);
|
||||
lookup.findStaticVarHandle(PipeBlock.class, "PROPERTY_BY_DIRECTION", Map.class);
|
||||
MethodHandles.privateLookupIn(MossyCarpetBlock.class, lookup).findStaticVarHandle(MossyCarpetBlock.class, "PROPERTY_BY_DIRECTION", Map.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBridge() {
|
||||
Set<String> missingApiEquivalents = new HashSet<>();
|
||||
for (Class<? extends Comparable<?>> value : ENUM_PROPERTY_VALUES) {
|
||||
if (!BlockStateMapping.ENUM_BRIDGE.containsKey(value)) {
|
||||
missingApiEquivalents.add(value.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertTrue(missingApiEquivalents.isEmpty(), () -> "Missing some api equivalent in the block state mapping enum bridge (BlockStateMapping#ENUM_BRIDGE) : " + String.join(", ", missingApiEquivalents));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
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 {
|
||||
|
||||
@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.bukkitMap.containsKey(nmsClass)) {
|
||||
missingClasses.add(nmsClass.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(missingClasses.isEmpty(), () -> "Missing some entity classes in the bukkit map: " + String.join(", ", missingClasses));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package io.papermc.generator;
|
||||
|
||||
import io.papermc.generator.registry.RegistryEntries;
|
||||
import io.papermc.generator.registry.RegistryEntry;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class RegistryMigrationTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
Bootstrap.validate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuiltInToDataDriven() {
|
||||
Set<String> migratedRegistries = new HashSet<>();
|
||||
for (RegistryEntry<?> entry : RegistryEntries.BUILT_IN) {
|
||||
ResourceKey<? extends Registry<?>> key = entry.registryKey();
|
||||
if (!BuiltInRegistries.REGISTRY.containsKey(key.location())) {
|
||||
migratedRegistries.add(key.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertTrue(migratedRegistries.isEmpty(), () -> "Some registries have become data-driven: %s".formatted(String.join(", ", migratedRegistries)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataDrivenToBuiltIn() { // shouldn't really happen but just in case
|
||||
Set<String> migratedRegistries = new HashSet<>();
|
||||
for (RegistryEntry<?> entry : RegistryEntries.DATA_DRIVEN) {
|
||||
ResourceKey<? extends Registry<?>> key = entry.registryKey();
|
||||
if (BuiltInRegistries.REGISTRY.containsKey(key.location())) {
|
||||
migratedRegistries.add(key.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertTrue(migratedRegistries.isEmpty(), () -> "Some registries have become built-in: %s".formatted(String.join(", ", migratedRegistries)));
|
||||
}
|
||||
}
|
||||
10
paper-generator/wideners.at
Normal file
10
paper-generator/wideners.at
Normal file
@ -0,0 +1,10 @@
|
||||
public net/minecraft/world/level/material/MapColor MATERIAL_COLORS
|
||||
public net/minecraft/world/level/block/state/properties/IntegerProperty min
|
||||
|
||||
# for auto-marking experimental stuff
|
||||
public net/minecraft/core/RegistrySetBuilder entries
|
||||
public net/minecraft/core/RegistrySetBuilder$RegistryStub
|
||||
public net/minecraft/data/registries/VanillaRegistries BUILDER
|
||||
public net/minecraft/data/registries/TradeRebalanceRegistries BUILDER
|
||||
public net/minecraft/world/flag/FeatureFlagRegistry names
|
||||
public net/minecraft/world/flag/FeatureFlagSet mask
|
||||
Reference in New Issue
Block a user