-
-
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
11b4ac7
commit 39e3f2f
Showing
19 changed files
with
4,134 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,35 @@ | ||
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("io.github.classgraph:classgraph:4.8.47") | ||
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,020 changes: 3,020 additions & 0 deletions
3,020
paper-server-generator/generated/io/papermc/paper/entity/meta/EntityMetaWatcher.java
Large diffs are not rendered by default.
Oops, something went wrong.
11 changes: 11 additions & 0 deletions
11
paper-server-generator/src/main/java/io/papermc/generator/Generators.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,11 @@ | ||
package io.papermc.generator; | ||
|
||
import io.papermc.generator.types.EntityMetaWatcherGenerator; | ||
import io.papermc.generator.types.SourceGenerator; | ||
|
||
public interface Generators { | ||
|
||
SourceGenerator[] SERVER = { | ||
new EntityMetaWatcherGenerator("EntityMetaWatcher", "io.papermc.paper.entity.meta") | ||
}; | ||
} |
64 changes: 64 additions & 0 deletions
64
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,64 @@ | ||
package io.papermc.generator; | ||
|
||
import com.mojang.logging.LogUtils; | ||
|
||
import io.papermc.generator.types.SourceGenerator; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import net.minecraft.SharedConstants; | ||
import net.minecraft.core.LayeredRegistryAccess; | ||
import net.minecraft.core.RegistryAccess; | ||
import net.minecraft.resources.RegistryDataLoader; | ||
import net.minecraft.server.Bootstrap; | ||
import net.minecraft.server.RegistryLayer; | ||
import net.minecraft.server.WorldLoader; | ||
import net.minecraft.server.packs.PackType; | ||
import net.minecraft.server.packs.repository.Pack; | ||
import net.minecraft.server.packs.repository.PackRepository; | ||
import net.minecraft.server.packs.repository.ServerPacksSource; | ||
import net.minecraft.server.packs.resources.MultiPackResourceManager; | ||
import 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; | ||
|
||
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(); | ||
layers = WorldLoader.loadAndReplaceLayer(resourceManager, layers, RegistryLayer.WORLDGEN, RegistryDataLoader.WORLDGEN_REGISTRIES); | ||
REGISTRY_ACCESS = layers.compositeAccess().freeze(); | ||
} | ||
|
||
private Main() { | ||
} | ||
|
||
public static void main(final String[] args) { | ||
LOGGER.info("Running API generators..."); | ||
generate(Paths.get(args[0]), Generators.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); | ||
} | ||
} | ||
} |
178 changes: 178 additions & 0 deletions
178
...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,178 @@ | ||
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.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
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 = 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)); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
paper-server-generator/src/main/java/io/papermc/generator/types/SimpleGenerator.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,37 @@ | ||
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.Files; | ||
import java.nio.file.Path; | ||
|
||
public abstract class SimpleGenerator implements SourceGenerator { | ||
|
||
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 abstract JavaFile.Builder file(JavaFile.Builder builder); | ||
|
||
@Override | ||
public void writeToFile(Path parent) throws IOException { | ||
Path packagePath = parent.resolve(this.packageName.replace('.', '/')); | ||
Files.createDirectories(packagePath); | ||
|
||
JavaFile.Builder builder = JavaFile.builder(this.packageName, this.getTypeSpec()) | ||
.indent(" "); | ||
this.file(builder); | ||
|
||
Files.writeString(packagePath.resolve(this.className + ".java"), this.file(builder).build().toString(), StandardCharsets.UTF_8); | ||
} | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
paper-server-generator/src/main/java/io/papermc/generator/types/SourceGenerator.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,9 @@ | ||
package io.papermc.generator.types; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
|
||
public interface SourceGenerator { | ||
|
||
void writeToFile(Path parent) throws IOException; | ||
} |
Oops, something went wrong.