mirror of https://github.com/PaperMC/Paper.git
disguise api
and generate maps to allow entity metadata syncher validation
This commit is contained in:
parent
8f7ac62905
commit
d3cef031db
|
@ -0,0 +1,2 @@
|
|||
// Uncomment to enable the 'paper-server-generator' project
|
||||
include(":paper-server-generator")
|
|
@ -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-server"))
|
||||
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"
|
File diff suppressed because it is too large
Load Diff
|
@ -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")
|
||||
};
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
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);
|
||||
// LOGGER.info("Running Server generators...");
|
||||
// generate(Paths.get(args[1]), 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package io.papermc.generator.utils;
|
||||
|
||||
import com.squareup.javapoet.AnnotationSpec;
|
||||
import java.util.List;
|
||||
|
||||
import io.papermc.paper.generated.GeneratedFrom;
|
||||
import net.minecraft.SharedConstants;
|
||||
import org.bukkit.MinecraftExperimental;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class Annotations {
|
||||
|
||||
public static List<AnnotationSpec> experimentalAnnotations(final String version) {
|
||||
return List.of(
|
||||
AnnotationSpec.builder(ApiStatus.Experimental.class).build(),
|
||||
AnnotationSpec.builder(MinecraftExperimental.class)
|
||||
.addMember("value", "$S", version)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public static AnnotationSpec deprecatedVersioned(final @Nullable String version, boolean forRemoval) {
|
||||
AnnotationSpec.Builder annotationSpec = AnnotationSpec.builder(Deprecated.class);
|
||||
if (forRemoval) {
|
||||
annotationSpec.addMember("forRemoval", "$L", forRemoval);
|
||||
}
|
||||
if (version != null) {
|
||||
annotationSpec.addMember("since", "$S", version);
|
||||
}
|
||||
|
||||
return annotationSpec.build();
|
||||
}
|
||||
|
||||
public static AnnotationSpec scheduledRemoval(final @Nullable String version) {
|
||||
return AnnotationSpec.builder(ApiStatus.ScheduledForRemoval.class)
|
||||
.addMember("inVersion", "$S", version)
|
||||
.build();
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public static final AnnotationSpec EXPERIMENTAL_API_ANNOTATION = AnnotationSpec.builder(ApiStatus.Experimental.class).build();
|
||||
public static final AnnotationSpec NOT_NULL = AnnotationSpec.builder(NotNull.class).build();
|
||||
private static final AnnotationSpec SUPPRESS_WARNINGS = AnnotationSpec.builder(SuppressWarnings.class)
|
||||
.addMember("value", "$S", "unused")
|
||||
.addMember("value", "$S", "SpellCheckingInspection")
|
||||
.build();
|
||||
private static final AnnotationSpec GENERATED_FROM = AnnotationSpec.builder(GeneratedFrom.class)
|
||||
.addMember("value", "$S", SharedConstants.getCurrentVersion().getName())
|
||||
.build();
|
||||
public static final Iterable<AnnotationSpec> CLASS_HEADER = List.of(
|
||||
SUPPRESS_WARNINGS,
|
||||
GENERATED_FROM
|
||||
);
|
||||
|
||||
private Annotations() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.papermc.generator.utils;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import io.papermc.generator.Main;
|
||||
import java.util.List;
|
||||
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.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public record CollectingContext<T>(List<ResourceKey<T>> registered,
|
||||
Registry<T> registry) implements BootstrapContext<T> {
|
||||
|
||||
@Override
|
||||
public Holder.Reference<T> register(final ResourceKey<T> resourceKey, final @NonNull T t, final Lifecycle lifecycle) {
|
||||
this.registered.add(resourceKey);
|
||||
return Holder.Reference.createStandAlone(this.registry.holderOwner(), resourceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> HolderGetter<S> lookup(final ResourceKey<? extends Registry<? extends S>> resourceKey) {
|
||||
return Main.REGISTRY_ACCESS.registryOrThrow(resourceKey).asLookup();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.papermc.generator.utils;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class Formatting {
|
||||
|
||||
public static String formatKeyAsField(Key key) {
|
||||
return key.value().toUpperCase(Locale.ENGLISH).replaceAll("[.-/]", "_"); // replace invalid field name chars
|
||||
}
|
||||
|
||||
private Formatting() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package io.papermc.generator.utils;
|
||||
|
||||
public final class Javadocs {
|
||||
|
||||
public static String getVersionDependentClassHeader(String headerIdentifier) {
|
||||
return """
|
||||
Vanilla keys 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(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);
|
||||
}
|
||||
|
||||
private Javadocs() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package io.papermc.generator.utils;
|
||||
|
||||
import io.papermc.generator.types.EntityMetaWatcherGenerator;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class ReflectionHelper {
|
||||
private ReflectionHelper(){}
|
||||
|
||||
public static List<Field> getAllForAllParents(Class<?> clazz, Predicate<Field> filter) {
|
||||
List<Field> allClasses = new ArrayList<>(forClass(clazz, filter));
|
||||
for (final Class<?> aClass : allParents(clazz)) {
|
||||
allClasses.addAll(forClass(aClass, filter));
|
||||
}
|
||||
return allClasses;
|
||||
}
|
||||
|
||||
public static List<Class<?>> allParents(Class<?> clazz){
|
||||
List<Class<?>> allClasses = new ArrayList<>();
|
||||
Class<?> current = clazz;
|
||||
while (current.getSuperclass() != null) {
|
||||
var toAdd = current.getSuperclass();
|
||||
if (net.minecraft.world.entity.Entity.class.isAssignableFrom(toAdd)) {
|
||||
allClasses.add(toAdd);
|
||||
}
|
||||
current = toAdd;
|
||||
}
|
||||
Collections.reverse(allClasses);
|
||||
return allClasses;
|
||||
}
|
||||
|
||||
public static List<Field> forClass(Class<?> clazz, Predicate<Field> filter) {
|
||||
return Arrays.stream(clazz.getDeclaredFields()).filter(filter).toList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
public net/minecraft/server/WorldLoader loadAndReplaceLayer(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/server/RegistryLayer;Ljava/util/List;)Lnet/minecraft/core/LayeredRegistryAccess;
|
||||
|
||||
# for auto-marking experimental stuff
|
||||
public net/minecraft/core/RegistrySetBuilder entries
|
||||
public net/minecraft/core/RegistrySetBuilder$RegistryStub
|
||||
public net/minecraft/data/registries/UpdateOneTwentyOneRegistries BUILDER
|
||||
public net/minecraft/data/registries/VanillaRegistries BUILDER
|
|
@ -0,0 +1,158 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yannick Lamprecht <yannicklamprecht@live.de>
|
||||
Date: Wed, 27 Dec 2023 14:51:59 +0100
|
||||
Subject: [PATCH] add disguise api
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/SkinParts.java b/src/main/java/com/destroystokyo/paper/SkinParts.java
|
||||
index 4a0c39405d4fbed457787e3c6ded4cc6591bc8c2..7db62131bccaa218954fb56c5cdfe71f48aa59a1 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/SkinParts.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/SkinParts.java
|
||||
@@ -17,4 +17,23 @@ public interface SkinParts {
|
||||
boolean hasHatsEnabled();
|
||||
|
||||
int getRaw();
|
||||
+
|
||||
+ interface Builder {
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withCape();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withJacket();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withLeftSleeve();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withRightSleeve();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withLeftPants();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withRightPants();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ Builder withHat();
|
||||
+ @org.jetbrains.annotations.NotNull
|
||||
+ SkinParts build();
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/disguise/DisguiseData.java b/src/main/java/io/papermc/paper/disguise/DisguiseData.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..916ea0127e75cff32d48c9f7d0b9a62d0e291e74
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/disguise/DisguiseData.java
|
||||
@@ -0,0 +1,25 @@
|
||||
+package io.papermc.paper.disguise;
|
||||
+
|
||||
+import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
+import org.bukkit.entity.EntityType;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+public sealed interface DisguiseData permits DisguiseData.OriginalDisguise, EntityTypeDisguise, PlayerDisguise {
|
||||
+ static @NotNull DisguiseData original() {
|
||||
+ return new OriginalDisguise();
|
||||
+ }
|
||||
+
|
||||
+ static @NotNull PlayerDisguise.Builder player(@NotNull PlayerProfile playerProfile) {
|
||||
+ return new PlayerDisguise.Builder(playerProfile);
|
||||
+ }
|
||||
+
|
||||
+ static @NotNull EntityTypeDisguise entity(@NotNull EntityType entityType) {
|
||||
+ return new EntityTypeDisguise(entityType);
|
||||
+ }
|
||||
+
|
||||
+ static @NotNull OriginalDisguise reset() {
|
||||
+ return new OriginalDisguise();
|
||||
+ }
|
||||
+
|
||||
+ record OriginalDisguise() implements DisguiseData{}
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/disguise/EntityTypeDisguise.java b/src/main/java/io/papermc/paper/disguise/EntityTypeDisguise.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4db3e283418a328f92fb538cd93461f4770e5b0c
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/disguise/EntityTypeDisguise.java
|
||||
@@ -0,0 +1,11 @@
|
||||
+package io.papermc.paper.disguise;
|
||||
+
|
||||
+import java.util.Objects;
|
||||
+import org.bukkit.entity.EntityType;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+public record EntityTypeDisguise(@NotNull EntityType entityType) implements DisguiseData {
|
||||
+ public EntityTypeDisguise {
|
||||
+ Objects.requireNonNull(entityType, "type cannot be null");
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/disguise/PlayerDisguise.java b/src/main/java/io/papermc/paper/disguise/PlayerDisguise.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..a328ce26a6c7aa2fc875a571e9bb9857e59db624
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/disguise/PlayerDisguise.java
|
||||
@@ -0,0 +1,41 @@
|
||||
+package io.papermc.paper.disguise;
|
||||
+
|
||||
+import com.destroystokyo.paper.SkinParts;
|
||||
+import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
+import java.util.Objects;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
+
|
||||
+public record PlayerDisguise(@NotNull PlayerProfile playerProfile, boolean listed, @Nullable SkinParts skinParts) implements DisguiseData {
|
||||
+
|
||||
+ public PlayerDisguise {
|
||||
+ Objects.requireNonNull(playerProfile, "profile cannot be null");
|
||||
+ }
|
||||
+ public static @NotNull Builder builder(@NotNull PlayerProfile playerProfile) {
|
||||
+ return new Builder(playerProfile);
|
||||
+ }
|
||||
+
|
||||
+ public static class Builder {
|
||||
+ private final PlayerProfile playerProfile;
|
||||
+ private boolean listed;
|
||||
+ private SkinParts skinParts;
|
||||
+
|
||||
+ public Builder(@NotNull PlayerProfile playerProfile) {
|
||||
+ this.playerProfile = playerProfile;
|
||||
+ }
|
||||
+
|
||||
+ public @NotNull Builder listed(boolean listed) {
|
||||
+ this.listed = listed;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @NotNull Builder skinParts(@NotNull SkinParts skinParts) {
|
||||
+ this.skinParts = skinParts;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @NotNull PlayerDisguise build() {
|
||||
+ return new PlayerDisguise(playerProfile, listed, skinParts);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
|
||||
index 27084402cf0e46dcd171074629b7c4156e48aa44..de92d1c1133a02bd0518f5985efe20f38157473e 100644
|
||||
--- a/src/main/java/org/bukkit/Server.java
|
||||
+++ b/src/main/java/org/bukkit/Server.java
|
||||
@@ -2537,4 +2537,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*/
|
||||
boolean isOwnedByCurrentRegion(@NotNull Entity entity);
|
||||
// Paper end - Folia region threading API
|
||||
+ // Paper start - add disguise api
|
||||
+ /**
|
||||
+ * Creates a new skinparts builder used for overriding skin settintgs
|
||||
+ * @return a new builder for skin parts
|
||||
+ */
|
||||
+ com.destroystokyo.paper.SkinParts.@NotNull Builder newSkinPartsBuilder();
|
||||
+ // Paper end - add disguise api
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
|
||||
index 62e3793903905b94eb1a120345015149abb33713..fd73fa94b1bd7418c5f87254bab093c27d68b8e0 100644
|
||||
--- a/src/main/java/org/bukkit/entity/Entity.java
|
||||
+++ b/src/main/java/org/bukkit/entity/Entity.java
|
||||
@@ -1155,4 +1155,8 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
||||
*/
|
||||
@NotNull String getScoreboardEntryName();
|
||||
// Paper end - entity scoreboard name
|
||||
+ // Paper start - disguise api
|
||||
+ @NotNull io.papermc.paper.disguise.DisguiseData getDisguiseData();
|
||||
+ void setDisguiseData(@NotNull io.papermc.paper.disguise.DisguiseData disguiseData);
|
||||
+ // Paper end - disguise api
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yannick Lamprecht <yannicklamprecht@live.de>
|
||||
Date: Mon, 11 Mar 2024 00:52:56 +0100
|
||||
Subject: [PATCH] add paper server generator dependency
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 5d448d8a7cf6626a11791f30ad52baf41a099272..32392d95d34ac62a78c0eb1d6c1dd701dd76e9ad 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -63,6 +63,22 @@ dependencies {
|
||||
// Paper end - Remap reflection
|
||||
}
|
||||
|
||||
+// Paper start
|
||||
+val generatedServerPath: java.nio.file.Path = rootProject.projectDir.toPath().resolve("paper-server-generator/generated")
|
||||
+idea {
|
||||
+ module {
|
||||
+ generatedSourceDirs.add(generatedServerPath.toFile())
|
||||
+ }
|
||||
+}
|
||||
+sourceSets {
|
||||
+ main {
|
||||
+ java {
|
||||
+ srcDir(generatedServerPath)
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+// Paper end
|
||||
+
|
||||
paperweight {
|
||||
craftBukkitPackageVersion.set("v1_20_R4") // also needs to be updated in MappingEnvironment
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yannick Lamprecht <yannicklamprecht@live.de>
|
||||
Date: Sat, 16 Mar 2024 22:58:19 +0100
|
||||
Subject: [PATCH] add disguise api
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperSkinParts.java b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
|
||||
index b6f4400df3d8ec7e06a996de54f8cabba57885e1..ce28a4e9597b8c7a2d535eaac34d8babd67ed226 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
|
||||
@@ -71,4 +71,59 @@ public class PaperSkinParts implements SkinParts {
|
||||
.add("hats=" + hasHatsEnabled())
|
||||
.toString();
|
||||
}
|
||||
+
|
||||
+ public static SkinParts.Builder builder(){
|
||||
+ return new Builder();
|
||||
+ }
|
||||
+
|
||||
+ public static class Builder implements SkinParts.Builder{
|
||||
+ private int raw = 0;
|
||||
+
|
||||
+ private static final int CAPE = 0x01;
|
||||
+ private static final int JACKET = 0x02;
|
||||
+ private static final int LEFT_SLEEVE = 0x04;
|
||||
+ private static final int RIGHT_SLEEVE = 0x08;
|
||||
+ private static final int LEFT_PANTS = 0x10;
|
||||
+ private static final int RIGHT_PANTS = 0x20;
|
||||
+ private static final int HAT = 0x40;
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withCape(){
|
||||
+ raw |= CAPE;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withJacket(){
|
||||
+ raw |= JACKET;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withLeftSleeve(){
|
||||
+ raw |= LEFT_SLEEVE;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withRightSleeve(){
|
||||
+ raw |= RIGHT_SLEEVE;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withLeftPants(){
|
||||
+ raw |= LEFT_PANTS;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withRightPants(){
|
||||
+ raw |= RIGHT_PANTS;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull Builder withHat(){
|
||||
+ raw |= HAT;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public @org.jetbrains.annotations.NotNull SkinParts build(){
|
||||
+ return new PaperSkinParts(raw);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/disguise/DisguiseUtil.java b/src/main/java/io/papermc/paper/disguise/DisguiseUtil.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..30b770ff2990a298cb19efcd82eef2266e68e85d
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/disguise/DisguiseUtil.java
|
||||
@@ -0,0 +1,92 @@
|
||||
+package io.papermc.paper.disguise;
|
||||
+
|
||||
+import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
+import java.util.EnumSet;
|
||||
+import java.util.List;
|
||||
+import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
+import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
+import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
+import net.minecraft.network.syncher.SynchedEntityData;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import net.minecraft.world.entity.player.Player;
|
||||
+import net.minecraft.world.phys.Vec3;
|
||||
+import org.bukkit.Bukkit;
|
||||
+
|
||||
+public final class DisguiseUtil {
|
||||
+ private DisguiseUtil(){}
|
||||
+
|
||||
+ public static boolean tryDisguise(ServerPlayer player, Entity entity, ClientboundAddEntityPacket clientboundAddEntityPacket) {
|
||||
+ return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||
+ case DisguiseData.OriginalDisguise disguise -> false;
|
||||
+ case io.papermc.paper.disguise.EntityTypeDisguise(var type) -> {
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundAddEntityPacket(
|
||||
+ clientboundAddEntityPacket.getId(),
|
||||
+ clientboundAddEntityPacket.getUUID(),
|
||||
+ clientboundAddEntityPacket.getX(),
|
||||
+ clientboundAddEntityPacket.getY(),
|
||||
+ clientboundAddEntityPacket.getZ(),
|
||||
+ clientboundAddEntityPacket.getXRot(),
|
||||
+ clientboundAddEntityPacket.getYRot(),
|
||||
+ org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(type),
|
||||
+ 0,
|
||||
+ Vec3.ZERO.add(clientboundAddEntityPacket.getX(), clientboundAddEntityPacket.getY(), clientboundAddEntityPacket.getZ()).scale(1/8000.0D),
|
||||
+ clientboundAddEntityPacket.getYHeadRot()
|
||||
+ ));
|
||||
+
|
||||
+ yield true;
|
||||
+ }
|
||||
+ case io.papermc.paper.disguise.PlayerDisguise(var playerProfile, var listed, var skinParts) -> {
|
||||
+ PlayerProfile adapted = Bukkit.createProfile(entity.getUUID(), entity.hasCustomName()? entity.getBukkitEntity().getCustomName(): playerProfile.getName());
|
||||
+ adapted.setProperties(playerProfile.getProperties());
|
||||
+ net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Entry playerUpdate =
|
||||
+ new net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
+ entity.getUUID(),
|
||||
+ com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(adapted),
|
||||
+ listed,
|
||||
+ 0,
|
||||
+ net.minecraft.world.level.GameType.DEFAULT_MODE,
|
||||
+ entity.getCustomName(),
|
||||
+ null
|
||||
+ );
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket(EnumSet.of(net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER), playerUpdate));
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED), playerUpdate));
|
||||
+
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundAddEntityPacket(
|
||||
+ clientboundAddEntityPacket.getId(),
|
||||
+ clientboundAddEntityPacket.getUUID(),
|
||||
+ clientboundAddEntityPacket.getX(),
|
||||
+ clientboundAddEntityPacket.getY(),
|
||||
+ clientboundAddEntityPacket.getZ(),
|
||||
+ clientboundAddEntityPacket.getXRot(),
|
||||
+ clientboundAddEntityPacket.getYRot(),
|
||||
+ net.minecraft.world.entity.EntityType.PLAYER,
|
||||
+ 0,
|
||||
+ Vec3.ZERO.add(clientboundAddEntityPacket.getX(), clientboundAddEntityPacket.getY(), clientboundAddEntityPacket.getZ()).scale(1/8000.0D),
|
||||
+ clientboundAddEntityPacket.getYHeadRot()
|
||||
+ ));
|
||||
+ if(skinParts != null) {
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(
|
||||
+ clientboundAddEntityPacket.getId(),
|
||||
+ List.of(new SynchedEntityData.DataItem<>(Player.DATA_PLAYER_MODE_CUSTOMISATION, (byte) skinParts.getRaw()).value())
|
||||
+ ));
|
||||
+ }
|
||||
+ yield true;
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ public static boolean shouldSkip(Entity entity, EntityDataAccessor<?> dataAccessor) {
|
||||
+ return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||
+ case DisguiseData.OriginalDisguise original -> false;
|
||||
+ case EntityTypeDisguise entityTypeDisguise -> !io.papermc.paper.entity.meta.EntityMetaWatcher.isValidForClass(
|
||||
+ org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(entityTypeDisguise.entityType()).getBaseClass(),
|
||||
+ dataAccessor
|
||||
+ );
|
||||
+ case PlayerDisguise playerDisguise -> !io.papermc.paper.entity.meta.EntityMetaWatcher.isValidForClass(
|
||||
+ ServerPlayer.class,
|
||||
+ dataAccessor
|
||||
+ );
|
||||
+ };
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
index 0f99733660f91280e4c6262cf75b3c9cae86f65a..e8194dcc0bdeae6604d6a958c81807681114ed8b 100644
|
||||
--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
@@ -100,6 +100,7 @@ public class SynchedEntityData {
|
||||
|
||||
if (datawatcher_item.isDirty()) {
|
||||
datawatcher_item.setDirty(false);
|
||||
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, datawatcher_item.getAccessor())) continue; // Paper - disguise api
|
||||
list.add(datawatcher_item.value());
|
||||
}
|
||||
}
|
||||
@@ -116,7 +117,7 @@ public class SynchedEntityData {
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
SynchedEntityData.DataItem<?> datawatcher_item = adatawatcher_item[j];
|
||||
-
|
||||
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, datawatcher_item.getAccessor())) continue; // Paper - disguise api
|
||||
if (!datawatcher_item.isSetToDefault()) {
|
||||
if (list == null) {
|
||||
list = new ArrayList();
|
||||
@@ -135,7 +136,7 @@ public class SynchedEntityData {
|
||||
while (iterator.hasNext()) {
|
||||
SynchedEntityData.DataValue<?> datawatcher_c = (SynchedEntityData.DataValue) iterator.next();
|
||||
SynchedEntityData.DataItem<?> datawatcher_item = this.itemsById[datawatcher_c.id];
|
||||
-
|
||||
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, datawatcher_item.getAccessor())) continue; // Paper - disguise api
|
||||
this.assignValue(datawatcher_item, datawatcher_c);
|
||||
this.entity.onSyncedDataUpdated(datawatcher_item.getAccessor());
|
||||
}
|
||||
@@ -158,6 +159,7 @@ public class SynchedEntityData {
|
||||
public List<SynchedEntityData.DataValue<?>> packAll() {
|
||||
final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
|
||||
for (final DataItem<?> dataItem : this.itemsById) {
|
||||
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, dataItem.getAccessor())) continue; // Paper - disguise api
|
||||
list.add(dataItem.value());
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index 4f103f731623a8570238a6867fda1c5f83fca4e4..eb2b634da88ad5fba0e19fa5c3ff021263fb72ed 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -308,7 +308,11 @@ public class ServerEntity {
|
||||
Packet<ClientGamePacketListener> packet = this.entity.getAddEntityPacket();
|
||||
|
||||
this.yHeadRotp = Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F);
|
||||
+ // Paper start - disguise api
|
||||
+ if(!io.papermc.paper.disguise.DisguiseUtil.tryDisguise(player, entity, (net.minecraft.network.protocol.game.ClientboundAddEntityPacket) packet)){
|
||||
sender.accept(packet);
|
||||
+ }
|
||||
+ // Paper end - disguise api
|
||||
if (this.trackedDataValues != null) {
|
||||
sender.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), this.trackedDataValues));
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 05e304f9fc8d0291fa779da589bd060ef4165b49..9165271e974103b9c53a3319959c11b164f38a20 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -379,6 +379,12 @@ public final class CraftServer implements Server {
|
||||
return io.papermc.paper.util.TickThread.isTickThreadFor(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandleRaw());
|
||||
}
|
||||
// Paper end - Folia reagion threading API
|
||||
+ // Paper start - add disguise api
|
||||
+ @Override
|
||||
+ public com.destroystokyo.paper.SkinParts.@org.jetbrains.annotations.NotNull Builder newSkinPartsBuilder() {
|
||||
+ return com.destroystokyo.paper.PaperSkinParts.builder();
|
||||
+ }
|
||||
+ // Paper end - add disguise api
|
||||
|
||||
static {
|
||||
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index a2d336ceb52b63db5c03432ee7bc94dc6a742b82..ba8a0c525505d0211666304c698e19312ae07470 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -1280,4 +1280,16 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
return this.getHandle().getScoreboardName();
|
||||
}
|
||||
// Paper end - entity scoreboard name
|
||||
+ // Paper start - disguise api
|
||||
+ private io.papermc.paper.disguise.DisguiseData disguiseData = io.papermc.paper.disguise.DisguiseData.original();
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull io.papermc.paper.disguise.DisguiseData getDisguiseData() {
|
||||
+ return disguiseData;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setDisguiseData(@org.jetbrains.annotations.NotNull io.papermc.paper.disguise.DisguiseData disguiseData) {
|
||||
+ this.disguiseData = disguiseData;
|
||||
+ }
|
||||
+ // Paper end - disguise api
|
||||
}
|
|
@ -41,6 +41,7 @@ for (name in listOf("Paper-API", "Paper-Server", "Paper-MojangAPI")) {
|
|||
|
||||
optionalInclude("test-plugin")
|
||||
optionalInclude("paper-api-generator")
|
||||
optionalInclude("paper-server-generator")
|
||||
|
||||
fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) {
|
||||
val settingsFile = file("$name.settings.gradle.kts")
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
package io.papermc.testplugin;
|
||||
|
||||
import com.destroystokyo.paper.MaterialTags;
|
||||
import io.papermc.paper.disguise.DisguiseData;
|
||||
import io.papermc.paper.disguise.EntityTypeDisguise;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Allay;
|
||||
import org.bukkit.entity.Blaze;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scoreboard.Team;
|
||||
|
||||
public final class TestPlugin extends JavaPlugin implements Listener {
|
||||
|
||||
|
@ -9,4 +26,49 @@ public final class TestPlugin extends JavaPlugin implements Listener {
|
|||
public void onEnable() {
|
||||
this.getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEntityTrack(PlayerInteractEvent playerInteractEvent) {
|
||||
if (playerInteractEvent.getAction().isLeftClick() || playerInteractEvent.getAction() == Action.RIGHT_CLICK_AIR) {
|
||||
return;
|
||||
}
|
||||
if (!playerInteractEvent.hasItem()) {
|
||||
return;
|
||||
}
|
||||
var stack = playerInteractEvent.getItem();
|
||||
if (stack == null || !MaterialTags.SPAWN_EGGS.isTagged(stack)) {
|
||||
return;
|
||||
}
|
||||
if (playerInteractEvent.getInteractionPoint() == null) {
|
||||
return;
|
||||
}
|
||||
var player = playerInteractEvent.getPlayer();
|
||||
|
||||
switch (stack.getType()) {
|
||||
case BLAZE_SPAWN_EGG -> spawnOtherEntity(Blaze.class, DisguiseData.entity(EntityType.BEE), playerInteractEvent.getInteractionPoint());
|
||||
case ALLAY_SPAWN_EGG -> spawnPlayerLikeEntity(player, Allay.class, playerInteractEvent.getInteractionPoint());
|
||||
};
|
||||
playerInteractEvent.setCancelled(true);
|
||||
}
|
||||
|
||||
private void spawnOtherEntity(Class<? extends Entity> entityClass, EntityTypeDisguise entityTypeDisguise, Location location) {
|
||||
location.getWorld().spawn(location, entityClass, entity -> {
|
||||
entity.setDisguiseData(entityTypeDisguise);
|
||||
});
|
||||
}
|
||||
|
||||
private void spawnPlayerLikeEntity(Player player, Class<? extends Entity> entityClass, Location location) {
|
||||
player.getWorld().spawn(location, entityClass, entity -> {
|
||||
entity.customName(Component.text("Gollum"));
|
||||
entity.setDisguiseData(DisguiseData
|
||||
.player(player.getPlayerProfile()).listed(false)
|
||||
.skinParts(Bukkit.getServer().newSkinPartsBuilder().withJacket().withCape().build())
|
||||
.build());
|
||||
Team team = player.getServer().getScoreboardManager().getMainScoreboard().registerNewTeam(RandomStringUtils.random(10, true, true));
|
||||
team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.ALWAYS);
|
||||
team.addEntry(entity.getScoreboardEntryName());
|
||||
team.prefix(Component.text("Happy Coding ", NamedTextColor.GOLD));
|
||||
team.suffix(Component.text(" :D", NamedTextColor.RED));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue