-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
and generate maps to allow entity metadata syncher validation
- Loading branch information
1 parent
ce0a041
commit 44df8ba
Showing
14 changed files
with
4,578 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Uncomment to enable the 'paper-server-generator' project | ||
// include(":paper-server-generator") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import io.papermc.paperweight.PaperweightSourceGeneratorHelper | ||
import io.papermc.paperweight.extension.PaperweightSourceGeneratorExt | ||
|
||
plugins { | ||
java | ||
} | ||
|
||
plugins.apply(PaperweightSourceGeneratorHelper::class) | ||
|
||
extensions.configure(PaperweightSourceGeneratorExt::class) { | ||
atFile.set(projectDir.toPath().resolve("wideners.at").toFile()) | ||
} | ||
|
||
dependencies { | ||
implementation("com.squareup:javapoet:1.13.0") | ||
implementation(project(":paper-api")) | ||
implementation(project(":paper-api-generator")) | ||
implementation("io.github.classgraph:classgraph:4.8.112") | ||
implementation("org.jetbrains:annotations:24.0.1") | ||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") | ||
testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||
} | ||
|
||
tasks.register<JavaExec>("generate") { | ||
dependsOn(tasks.check) | ||
mainClass.set("io.papermc.generator.Main") | ||
classpath(sourceSets.main.map { it.runtimeClasspath }) | ||
args(projectDir.toPath().resolve("generated").toString()) | ||
} | ||
|
||
tasks.test { | ||
useJUnitPlatform() | ||
} | ||
|
||
group = "io.papermc.paper" | ||
version = "1.0-SNAPSHOT" |
3,117 changes: 3,117 additions & 0 deletions
3,117
paper-server-generator/generated/io/papermc/paper/entity/meta/EntityMetaWatcher.java
Large diffs are not rendered by default.
Oops, something went wrong.
307 changes: 307 additions & 0 deletions
307
paper-server-generator/generated/io/papermc/paper/entity/meta/EntityTypeToEntityClass.java
Large diffs are not rendered by default.
Oops, something went wrong.
98 changes: 98 additions & 0 deletions
98
paper-server-generator/src/main/java/io/papermc/generator/Main.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package io.papermc.generator; | ||
|
||
import com.google.common.util.concurrent.MoreExecutors; | ||
import com.mojang.logging.LogUtils; | ||
|
||
import io.papermc.generator.types.EntityMetaWatcherGenerator; | ||
import io.papermc.generator.types.EntityTypeToEntityClassGenerator; | ||
import io.papermc.generator.types.SourceGenerator; | ||
import io.papermc.generator.utils.TagCollector; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
import java.util.Map; | ||
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.slf4j.Logger; | ||
|
||
public final class Main { | ||
|
||
private static final Logger LOGGER = LogUtils.getLogger(); | ||
public static final RegistryAccess.Frozen REGISTRY_ACCESS; | ||
public static final Map<TagKey<?>, String> EXPERIMENTAL_TAGS; | ||
|
||
static { | ||
SharedConstants.tryDetectVersion(); | ||
Bootstrap.bootStrap(); | ||
final PackRepository resourceRepository = ServerPacksSource.createVanillaTrustedRepository(); | ||
resourceRepository.reload(); | ||
final MultiPackResourceManager resourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, resourceRepository.getAvailablePacks().stream().map(Pack::open).toList()); | ||
LayeredRegistryAccess<RegistryLayer> layers = RegistryLayer.createRegistryAccess(); | ||
final List<Registry.PendingTags<?>> pendingTags = TagLoader.loadTagsForExistingRegistries(resourceManager, layers.getLayer(RegistryLayer.STATIC)); | ||
final List<HolderLookup.RegistryLookup<?>> worldGenLayer = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLDGEN), pendingTags); | ||
final RegistryAccess.Frozen frozenWorldgenRegistries = RegistryDataLoader.load(resourceManager, worldGenLayer, RegistryDataLoader.WORLDGEN_REGISTRIES); | ||
layers = layers.replaceFrom(RegistryLayer.WORLDGEN, frozenWorldgenRegistries); | ||
REGISTRY_ACCESS = layers.compositeAccess().freeze(); | ||
final ReloadableServerResources reloadableServerResources = ReloadableServerResources.loadResources( | ||
resourceManager, | ||
layers, | ||
pendingTags, | ||
FeatureFlags.VANILLA_SET, | ||
Commands.CommandSelection.DEDICATED, | ||
0, | ||
MoreExecutors.directExecutor(), | ||
MoreExecutors.directExecutor() | ||
).join(); | ||
reloadableServerResources.updateStaticRegistryTags(); | ||
EXPERIMENTAL_TAGS = TagCollector.grabExperimental(resourceManager); | ||
} | ||
|
||
private Main() { | ||
} | ||
|
||
public static void main(final String[] args) { | ||
LOGGER.info("Running SERVER generators..."); | ||
|
||
SourceGenerator[] SERVER = { | ||
new EntityMetaWatcherGenerator("EntityMetaWatcher", "io.papermc.paper.entity.meta"), | ||
new EntityTypeToEntityClassGenerator("EntityTypeToEntityClass", "io.papermc.paper.entity.meta"), | ||
}; | ||
|
||
generate(Paths.get(args[0]), SERVER); | ||
} | ||
|
||
private static void generate(Path output, SourceGenerator[] generators) { | ||
try { | ||
if (Files.exists(output)) { | ||
PathUtils.deleteDirectory(output); | ||
} | ||
Files.createDirectories(output); | ||
|
||
for (final SourceGenerator generator : generators) { | ||
generator.writeToFile(output); | ||
} | ||
|
||
LOGGER.info("Files written to {}", output.toAbsolutePath()); | ||
} catch (final Exception ex) { | ||
throw new RuntimeException(ex); | ||
} | ||
} | ||
} |
181 changes: 181 additions & 0 deletions
181
...server-generator/src/main/java/io/papermc/generator/types/EntityMetaWatcherGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package io.papermc.generator.types; | ||
|
||
import com.squareup.javapoet.ClassName; | ||
import com.squareup.javapoet.FieldSpec; | ||
import com.squareup.javapoet.JavaFile; | ||
import com.squareup.javapoet.MethodSpec; | ||
import com.squareup.javapoet.ParameterizedTypeName; | ||
import com.squareup.javapoet.TypeSpec; | ||
import com.squareup.javapoet.WildcardTypeName; | ||
import io.github.classgraph.ClassGraph; | ||
import io.github.classgraph.ScanResult; | ||
import io.papermc.generator.utils.Annotations; | ||
import io.papermc.generator.utils.ReflectionHelper; | ||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
import java.util.Comparator; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
import java.util.stream.Collectors; | ||
import javax.lang.model.element.Modifier; | ||
import net.minecraft.network.syncher.EntityDataAccessor; | ||
import net.minecraft.network.syncher.EntityDataSerializer; | ||
import net.minecraft.network.syncher.EntityDataSerializers; | ||
import net.minecraft.world.entity.Entity; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.checkerframework.checker.nullness.qual.NonNull; | ||
import org.checkerframework.checker.nullness.qual.Nullable; | ||
import org.checkerframework.framework.qual.DefaultQualifier; | ||
|
||
@DefaultQualifier(NonNull.class) | ||
public class EntityMetaWatcherGenerator extends SimpleGenerator { | ||
|
||
private static final ParameterizedTypeName GENERIC_ENTITY_DATA_SERIALIZER = ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(Long.class), ParameterizedTypeName.get(ClassName.get(EntityDataSerializer.class), WildcardTypeName.subtypeOf(Object.class))); | ||
private static final ParameterizedTypeName ENTITY_CLASS = ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(Entity.class)); | ||
private static final ParameterizedTypeName OUTER_MAP_TYPE = ParameterizedTypeName.get(ClassName.get(Map.class), ENTITY_CLASS, GENERIC_ENTITY_DATA_SERIALIZER); | ||
|
||
public EntityMetaWatcherGenerator(String className, String packageName) { | ||
super(className, packageName); | ||
} | ||
|
||
@Override | ||
protected TypeSpec getTypeSpec() { | ||
Map<EntityDataSerializer<?>, String> dataAccessorStringMap = serializerMap(); | ||
|
||
List<Class<?>> classes; | ||
try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) { | ||
classes = scanResult.getSubclasses(net.minecraft.world.entity.Entity.class.getName()).loadClasses(); | ||
} | ||
|
||
classes = classes.stream() | ||
.filter(clazz -> !java.lang.reflect.Modifier.isAbstract(clazz.getModifiers())) | ||
.toList(); | ||
|
||
record Pair(Class<?> clazz, List<? extends EntityDataAccessor<?>> metaResults) {} | ||
|
||
final List<Pair> list = classes.stream() | ||
.map(clazz -> new Pair( | ||
clazz, | ||
ReflectionHelper.getAllForAllParents(clazz, EntityMetaWatcherGenerator::doFilter) | ||
.stream() | ||
.map(this::createData) | ||
.filter(Objects::nonNull) | ||
.toList() | ||
) | ||
) | ||
.toList(); | ||
|
||
Map<Class<?>, List<? extends EntityDataAccessor<?>>> vanillaNames = new TreeMap<>(Comparator.comparing(Class::getSimpleName)); | ||
vanillaNames.putAll(list.stream() | ||
.collect(Collectors.toMap(pair -> pair.clazz, pair -> pair.metaResults))); | ||
|
||
TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(this.className) | ||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL) | ||
.addAnnotations(Annotations.CLASS_HEADER); | ||
|
||
generateIdAccessorMethods(vanillaNames, dataAccessorStringMap, typeBuilder); | ||
generateClassToTypeMap(typeBuilder, vanillaNames.keySet()); | ||
generateIsValidAccessorForEntity(typeBuilder); | ||
|
||
return typeBuilder.build(); | ||
} | ||
|
||
private void generateIsValidAccessorForEntity(TypeSpec.Builder builder) { | ||
var methodBuilder = MethodSpec.methodBuilder("isValidForClass") | ||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) | ||
.returns(boolean.class) | ||
.addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(Entity.class)), "clazz") | ||
.addParameter(EntityDataAccessor.class, "accessor") | ||
.addStatement("Map<Long, EntityDataSerializer<?>> serializerMap = VALID_ENTITY_META_MAP.get(clazz)") | ||
.beginControlFlow("if(serializerMap == null)") | ||
.addStatement("return false") | ||
.endControlFlow() | ||
.addStatement("var serializer = serializerMap.get(accessor.id())") | ||
.addStatement("return serializer != null && serializer == accessor.serializer()"); | ||
|
||
builder.addMethod(methodBuilder.build()); | ||
} | ||
|
||
private void generateClassToTypeMap(TypeSpec.Builder typeBuilder, Set<Class<?>> classes){ | ||
typeBuilder.addField( | ||
FieldSpec.builder(OUTER_MAP_TYPE, "VALID_ENTITY_META_MAP", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) | ||
.initializer("initialize()") | ||
.build() | ||
); | ||
|
||
MethodSpec.Builder builder = MethodSpec.methodBuilder("initialize") | ||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) | ||
.returns(OUTER_MAP_TYPE) | ||
.addStatement("$T result = new $T<>()", OUTER_MAP_TYPE, ClassName.get(HashMap.class)); | ||
|
||
classes.forEach(aClass -> { | ||
String name = StringUtils.uncapitalize(aClass.getSimpleName()); | ||
if(!name.isBlank()) { | ||
builder.addStatement("result.put($T.class, $L())", aClass, name); | ||
} | ||
}); | ||
|
||
typeBuilder.addMethod(builder.addStatement("return $T.copyOf(result)", Map.class).build()); | ||
} | ||
|
||
private static void generateIdAccessorMethods(Map<Class<?>, List<? extends EntityDataAccessor<?>>> vanillaNames, Map<EntityDataSerializer<?>, String> dataAccessorStringMap, TypeSpec.Builder typeBuilder) { | ||
for (final Map.Entry<Class<?>, List<? extends EntityDataAccessor<?>>> perClassResults : vanillaNames.entrySet()) { | ||
if (perClassResults.getKey().getSimpleName().isBlank()) { | ||
continue; | ||
} | ||
var simpleName = perClassResults.getKey().getSimpleName(); | ||
|
||
ClassName hashMap = ClassName.get(HashMap.class); | ||
|
||
MethodSpec.Builder builder = MethodSpec.methodBuilder(StringUtils.uncapitalize(simpleName)) | ||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) | ||
.returns(GENERIC_ENTITY_DATA_SERIALIZER) | ||
.addStatement("$T result = new $T<>()", GENERIC_ENTITY_DATA_SERIALIZER, hashMap); | ||
|
||
perClassResults.getValue().forEach(result -> { | ||
builder.addStatement("result.put($LL, $T.$L)", result.id(), EntityDataSerializers.class, dataAccessorStringMap.get(result.serializer())); | ||
}); | ||
|
||
var method = builder.addStatement("return $T.copyOf(result)", Map.class) | ||
.build(); | ||
|
||
typeBuilder.addMethod(method); | ||
} | ||
} | ||
|
||
private @Nullable EntityDataAccessor<?> createData(Field field) { | ||
try { | ||
field.setAccessible(true); | ||
return (EntityDataAccessor<?>) field.get(null); | ||
} catch (IllegalAccessException e) { | ||
return null; | ||
} | ||
} | ||
|
||
private static boolean doFilter(Field field) { | ||
return java.lang.reflect.Modifier.isStatic(field.getModifiers()) && field.getType().isAssignableFrom(EntityDataAccessor.class); | ||
} | ||
|
||
@Override | ||
protected JavaFile.Builder file(JavaFile.Builder builder) { | ||
return builder.skipJavaLangImports(true); | ||
} | ||
|
||
private Map<EntityDataSerializer<?>, String> serializerMap(){ | ||
return Arrays.stream(EntityDataSerializers.class.getDeclaredFields()) | ||
.filter(field -> field.getType() == EntityDataSerializer.class) | ||
.map(field -> { | ||
try { | ||
return Map.entry((EntityDataSerializer<?>)field.get(0), field.getName()); | ||
} catch (IllegalAccessException e) { | ||
return null; | ||
} | ||
}) | ||
.filter(Objects::nonNull) | ||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
} | ||
} |
Oops, something went wrong.