mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-26 14:30:17 +01:00
disguise api
This commit is contained in:
parent
63c94c90d9
commit
1cf4fed272
30 changed files with 4757 additions and 0 deletions
|
@ -17,4 +17,15 @@ public interface SkinParts {
|
||||||
boolean hasHatsEnabled();
|
boolean hasHatsEnabled();
|
||||||
|
|
||||||
int getRaw();
|
int getRaw();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withCape(boolean cape);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withJacket(boolean jacket);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withLeftSleeve(boolean leftSleeve);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withRightSleeve(boolean rightSleeve);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withLeftPants(boolean leftPants);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withRightPants(boolean rightPants);
|
||||||
|
@org.jetbrains.annotations.NotNull Builder withHat(boolean hat);
|
||||||
|
@org.jetbrains.annotations.NotNull SkinParts build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package io.papermc.paper.disguise;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the data used to disguise an entity as another.
|
||||||
|
* Also supports disguising an entity as a player commonly known as `FakePlayer`.
|
||||||
|
*/
|
||||||
|
@NullMarked
|
||||||
|
public sealed interface DisguiseData permits DisguiseData.OriginalDisguise, EntityTypeDisguise, PlayerDisguise {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an original disguise data that can be used to reset disguising.
|
||||||
|
* <p>
|
||||||
|
* The original instance is set by default when a new entity is spawned
|
||||||
|
* and represents the state of no disguise should be made.
|
||||||
|
* <p>
|
||||||
|
* Same as {@link #reset()}
|
||||||
|
*
|
||||||
|
* @return an original disguise data
|
||||||
|
*/
|
||||||
|
static DisguiseData original() {
|
||||||
|
return reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link PlayerDisguise.Builder} where you can configure certain properties of the fake player appearance.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param playerProfile a already completed player profile that will be the fake players skin
|
||||||
|
* @return a builder to configure certain attributes
|
||||||
|
*/
|
||||||
|
static PlayerDisguise.Builder player(PlayerProfile playerProfile) {
|
||||||
|
return new PlayerDisguise.Builder(playerProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link EntityTypeDisguise.Builder} to allow disguising your entity as the given {@link EntityType}.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param entityType the entity type as which the entity should appear as.
|
||||||
|
* @return an entity disguise
|
||||||
|
*/
|
||||||
|
static EntityTypeDisguise.Builder entity(EntityType entityType) {
|
||||||
|
return new EntityTypeDisguise.Builder(entityType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alias for {@link #original()} to cover certain views on it.
|
||||||
|
*
|
||||||
|
* @see #original()
|
||||||
|
*
|
||||||
|
* @return an original disguise data
|
||||||
|
*/
|
||||||
|
static OriginalDisguise reset() {
|
||||||
|
return new OriginalDisguise();
|
||||||
|
}
|
||||||
|
|
||||||
|
record OriginalDisguise() implements DisguiseData{
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public OriginalDisguise() {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.papermc.paper.disguise;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public record EntityTypeDisguise(EntityType entityType) implements DisguiseData {
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public EntityTypeDisguise {
|
||||||
|
Objects.requireNonNull(entityType, "type cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the builder to configure certain appearance settings.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private final EntityType entityType;
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public Builder(EntityType entityType) {
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the disguise
|
||||||
|
*
|
||||||
|
* @return the built disguise
|
||||||
|
*/
|
||||||
|
public EntityTypeDisguise build() {
|
||||||
|
return new EntityTypeDisguise(entityType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package io.papermc.paper.disguise;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.SkinParts;
|
||||||
|
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public record PlayerDisguise(PlayerProfile playerProfile, boolean listed, boolean showHead,
|
||||||
|
@Nullable SkinParts skinParts) implements DisguiseData {
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public PlayerDisguise {
|
||||||
|
Objects.requireNonNull(playerProfile, "profile cannot be null");
|
||||||
|
}
|
||||||
|
public static Builder builder(PlayerProfile playerProfile) {
|
||||||
|
return new Builder(playerProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the builder to configure certain appearance settings.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private final PlayerProfile playerProfile;
|
||||||
|
private boolean listed;
|
||||||
|
private boolean showHead;
|
||||||
|
@Nullable
|
||||||
|
private SkinParts skinParts;
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public Builder(PlayerProfile playerProfile) {
|
||||||
|
this.playerProfile = playerProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines if the fake player will be shown in player list.
|
||||||
|
*
|
||||||
|
* @param listed true, if the player should be listed else false
|
||||||
|
* @return the builder instance
|
||||||
|
*/
|
||||||
|
public Builder listed(boolean listed) {
|
||||||
|
this.listed = listed;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines which skin parts should be enabled for the fake player.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param showHead defines if the fake players head should be shown in the player list.
|
||||||
|
* @return the builder instance
|
||||||
|
*/
|
||||||
|
public Builder showHead(boolean showHead) {
|
||||||
|
this.showHead = showHead;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines which skin parts should be enabled for the fake player.
|
||||||
|
* <p>
|
||||||
|
* Use {@link Server#newSkinPartsBuilder()} to get a fresh builder instance for configuration.
|
||||||
|
*
|
||||||
|
* @param skinParts the skin parts that should be shown.
|
||||||
|
* @return the builder instance
|
||||||
|
*/
|
||||||
|
public Builder skinParts(SkinParts skinParts) {
|
||||||
|
this.skinParts = skinParts;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the disguise
|
||||||
|
*
|
||||||
|
* @return the built disguise
|
||||||
|
*/
|
||||||
|
public PlayerDisguise build() {
|
||||||
|
return new PlayerDisguise(playerProfile, listed, showHead, skinParts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2607,4 +2607,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||||
*/
|
*/
|
||||||
void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value);
|
void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value);
|
||||||
// Paper end - API to check if the server is sleeping
|
// Paper end - API to check if the server is sleeping
|
||||||
|
// Paper start - add disguise api
|
||||||
|
/**
|
||||||
|
* Creates a new skinparts builder used for overriding skin settings
|
||||||
|
* @return a new builder for skin parts
|
||||||
|
*/
|
||||||
|
com.destroystokyo.paper.SkinParts.@NotNull Builder newSkinPartsBuilder();
|
||||||
|
// Paper end - add disguise api
|
||||||
}
|
}
|
||||||
|
|
|
@ -1172,4 +1172,34 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
||||||
*/
|
*/
|
||||||
void broadcastHurtAnimation(@NotNull java.util.Collection<Player> players);
|
void broadcastHurtAnimation(@NotNull java.util.Collection<Player> players);
|
||||||
// Paper end - broadcast hurt animation
|
// Paper end - broadcast hurt animation
|
||||||
|
// Paper start - disguise api
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current {@link io.papermc.paper.disguise.DisguiseData} of the entity.
|
||||||
|
*
|
||||||
|
* @return {@link io.papermc.paper.disguise.DisguiseData.OriginalDisguise} if entity is not disguised.
|
||||||
|
* Otherwise, one of {@link io.papermc.paper.disguise.EntityTypeDisguise} or {@link io.papermc.paper.disguise.PlayerDisguise}
|
||||||
|
*/
|
||||||
|
@NotNull io.papermc.paper.disguise.DisguiseData getDisguiseData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current {@link io.papermc.paper.disguise.DisguiseData} of the entity.
|
||||||
|
* <p>
|
||||||
|
* Following {@link io.papermc.paper.disguise.DisguiseData} can be set:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link io.papermc.paper.disguise.PlayerDisguise} use {@link io.papermc.paper.disguise.DisguiseData#player(com.destroystokyo.paper.profile.PlayerProfile)}.
|
||||||
|
* It returns a builder where you are able to configure additional settings</li>
|
||||||
|
* <li>{@link io.papermc.paper.disguise.EntityTypeDisguise} use {@link io.papermc.paper.disguise.DisguiseData#entity(EntityType)}</li>
|
||||||
|
* <li>{@link io.papermc.paper.disguise.DisguiseData.OriginalDisguise} use {@link io.papermc.paper.disguise.DisguiseData#original()} or {@link io.papermc.paper.disguise.DisguiseData#reset()} to reset it again to the original state</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* The following entities are not supported:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link ExperienceOrb}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param disguiseData the {@link io.papermc.paper.disguise.DisguiseData} that will be set.
|
||||||
|
*/
|
||||||
|
void setDisguiseData(@NotNull io.papermc.paper.disguise.DisguiseData disguiseData);
|
||||||
|
// Paper end - disguise api
|
||||||
}
|
}
|
||||||
|
|
2
paper-server-generator.settings.gradle.kts
Normal file
2
paper-server-generator.settings.gradle.kts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Uncomment to enable the 'paper-server-generator' project
|
||||||
|
// include(":paper-server-generator")
|
37
paper-server-generator/build.gradle.kts
Normal file
37
paper-server-generator/build.gradle.kts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import io.papermc.paperweight.util.defaultJavaLauncher
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("io.papermc.paperweight.source-generator")
|
||||||
|
}
|
||||||
|
|
||||||
|
paperweight {
|
||||||
|
atFile.set(layout.projectDirectory.file("wideners.at"))
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
minecraftJar(project(":paper-server", "mappedJarOutgoing"))
|
||||||
|
implementation(project(":paper-server", "macheMinecraftLibraries"))
|
||||||
|
|
||||||
|
implementation("com.squareup:javapoet:1.13.0")
|
||||||
|
implementation(project(":paper-api"))
|
||||||
|
implementation("io.github.classgraph:classgraph:4.8.47")
|
||||||
|
implementation("org.jetbrains:annotations:24.1.0")
|
||||||
|
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())
|
||||||
|
javaLauncher = javaToolchains.defaultJavaLauncher(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.papermc.paper"
|
||||||
|
version = "1.0-SNAPSHOT"
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,307 @@
|
||||||
|
package io.papermc.paper.entity.meta;
|
||||||
|
|
||||||
|
import io.papermc.paper.generated.GeneratedFrom;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.minecraft.world.entity.AreaEffectCloud;
|
||||||
|
import net.minecraft.world.entity.Display;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.ExperienceOrb;
|
||||||
|
import net.minecraft.world.entity.GlowSquid;
|
||||||
|
import net.minecraft.world.entity.Interaction;
|
||||||
|
import net.minecraft.world.entity.LightningBolt;
|
||||||
|
import net.minecraft.world.entity.Marker;
|
||||||
|
import net.minecraft.world.entity.OminousItemSpawner;
|
||||||
|
import net.minecraft.world.entity.ambient.Bat;
|
||||||
|
import net.minecraft.world.entity.animal.Bee;
|
||||||
|
import net.minecraft.world.entity.animal.Cat;
|
||||||
|
import net.minecraft.world.entity.animal.Chicken;
|
||||||
|
import net.minecraft.world.entity.animal.Cod;
|
||||||
|
import net.minecraft.world.entity.animal.Cow;
|
||||||
|
import net.minecraft.world.entity.animal.Dolphin;
|
||||||
|
import net.minecraft.world.entity.animal.Fox;
|
||||||
|
import net.minecraft.world.entity.animal.IronGolem;
|
||||||
|
import net.minecraft.world.entity.animal.MushroomCow;
|
||||||
|
import net.minecraft.world.entity.animal.Ocelot;
|
||||||
|
import net.minecraft.world.entity.animal.Panda;
|
||||||
|
import net.minecraft.world.entity.animal.Parrot;
|
||||||
|
import net.minecraft.world.entity.animal.Pig;
|
||||||
|
import net.minecraft.world.entity.animal.PolarBear;
|
||||||
|
import net.minecraft.world.entity.animal.Pufferfish;
|
||||||
|
import net.minecraft.world.entity.animal.Rabbit;
|
||||||
|
import net.minecraft.world.entity.animal.Salmon;
|
||||||
|
import net.minecraft.world.entity.animal.Sheep;
|
||||||
|
import net.minecraft.world.entity.animal.SnowGolem;
|
||||||
|
import net.minecraft.world.entity.animal.Squid;
|
||||||
|
import net.minecraft.world.entity.animal.TropicalFish;
|
||||||
|
import net.minecraft.world.entity.animal.Turtle;
|
||||||
|
import net.minecraft.world.entity.animal.Wolf;
|
||||||
|
import net.minecraft.world.entity.animal.allay.Allay;
|
||||||
|
import net.minecraft.world.entity.animal.armadillo.Armadillo;
|
||||||
|
import net.minecraft.world.entity.animal.axolotl.Axolotl;
|
||||||
|
import net.minecraft.world.entity.animal.camel.Camel;
|
||||||
|
import net.minecraft.world.entity.animal.frog.Frog;
|
||||||
|
import net.minecraft.world.entity.animal.frog.Tadpole;
|
||||||
|
import net.minecraft.world.entity.animal.goat.Goat;
|
||||||
|
import net.minecraft.world.entity.animal.horse.Donkey;
|
||||||
|
import net.minecraft.world.entity.animal.horse.Horse;
|
||||||
|
import net.minecraft.world.entity.animal.horse.Llama;
|
||||||
|
import net.minecraft.world.entity.animal.horse.Mule;
|
||||||
|
import net.minecraft.world.entity.animal.horse.SkeletonHorse;
|
||||||
|
import net.minecraft.world.entity.animal.horse.TraderLlama;
|
||||||
|
import net.minecraft.world.entity.animal.horse.ZombieHorse;
|
||||||
|
import net.minecraft.world.entity.animal.sniffer.Sniffer;
|
||||||
|
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
|
||||||
|
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||||
|
import net.minecraft.world.entity.boss.wither.WitherBoss;
|
||||||
|
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||||
|
import net.minecraft.world.entity.decoration.GlowItemFrame;
|
||||||
|
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||||
|
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
|
||||||
|
import net.minecraft.world.entity.decoration.Painting;
|
||||||
|
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.entity.item.PrimedTnt;
|
||||||
|
import net.minecraft.world.entity.monster.Blaze;
|
||||||
|
import net.minecraft.world.entity.monster.Bogged;
|
||||||
|
import net.minecraft.world.entity.monster.CaveSpider;
|
||||||
|
import net.minecraft.world.entity.monster.Creeper;
|
||||||
|
import net.minecraft.world.entity.monster.Drowned;
|
||||||
|
import net.minecraft.world.entity.monster.ElderGuardian;
|
||||||
|
import net.minecraft.world.entity.monster.EnderMan;
|
||||||
|
import net.minecraft.world.entity.monster.Endermite;
|
||||||
|
import net.minecraft.world.entity.monster.Evoker;
|
||||||
|
import net.minecraft.world.entity.monster.Ghast;
|
||||||
|
import net.minecraft.world.entity.monster.Giant;
|
||||||
|
import net.minecraft.world.entity.monster.Guardian;
|
||||||
|
import net.minecraft.world.entity.monster.Husk;
|
||||||
|
import net.minecraft.world.entity.monster.Illusioner;
|
||||||
|
import net.minecraft.world.entity.monster.MagmaCube;
|
||||||
|
import net.minecraft.world.entity.monster.Phantom;
|
||||||
|
import net.minecraft.world.entity.monster.Pillager;
|
||||||
|
import net.minecraft.world.entity.monster.Ravager;
|
||||||
|
import net.minecraft.world.entity.monster.Shulker;
|
||||||
|
import net.minecraft.world.entity.monster.Silverfish;
|
||||||
|
import net.minecraft.world.entity.monster.Skeleton;
|
||||||
|
import net.minecraft.world.entity.monster.Slime;
|
||||||
|
import net.minecraft.world.entity.monster.Spider;
|
||||||
|
import net.minecraft.world.entity.monster.Stray;
|
||||||
|
import net.minecraft.world.entity.monster.Strider;
|
||||||
|
import net.minecraft.world.entity.monster.Vex;
|
||||||
|
import net.minecraft.world.entity.monster.Vindicator;
|
||||||
|
import net.minecraft.world.entity.monster.Witch;
|
||||||
|
import net.minecraft.world.entity.monster.WitherSkeleton;
|
||||||
|
import net.minecraft.world.entity.monster.Zoglin;
|
||||||
|
import net.minecraft.world.entity.monster.Zombie;
|
||||||
|
import net.minecraft.world.entity.monster.ZombieVillager;
|
||||||
|
import net.minecraft.world.entity.monster.ZombifiedPiglin;
|
||||||
|
import net.minecraft.world.entity.monster.breeze.Breeze;
|
||||||
|
import net.minecraft.world.entity.monster.creaking.Creaking;
|
||||||
|
import net.minecraft.world.entity.monster.hoglin.Hoglin;
|
||||||
|
import net.minecraft.world.entity.monster.piglin.Piglin;
|
||||||
|
import net.minecraft.world.entity.monster.piglin.PiglinBrute;
|
||||||
|
import net.minecraft.world.entity.monster.warden.Warden;
|
||||||
|
import net.minecraft.world.entity.npc.Villager;
|
||||||
|
import net.minecraft.world.entity.npc.WanderingTrader;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.entity.projectile.Arrow;
|
||||||
|
import net.minecraft.world.entity.projectile.DragonFireball;
|
||||||
|
import net.minecraft.world.entity.projectile.EvokerFangs;
|
||||||
|
import net.minecraft.world.entity.projectile.EyeOfEnder;
|
||||||
|
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||||
|
import net.minecraft.world.entity.projectile.FishingHook;
|
||||||
|
import net.minecraft.world.entity.projectile.LargeFireball;
|
||||||
|
import net.minecraft.world.entity.projectile.LlamaSpit;
|
||||||
|
import net.minecraft.world.entity.projectile.ShulkerBullet;
|
||||||
|
import net.minecraft.world.entity.projectile.SmallFireball;
|
||||||
|
import net.minecraft.world.entity.projectile.Snowball;
|
||||||
|
import net.minecraft.world.entity.projectile.SpectralArrow;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrownEgg;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrownEnderpearl;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrownExperienceBottle;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrownPotion;
|
||||||
|
import net.minecraft.world.entity.projectile.ThrownTrident;
|
||||||
|
import net.minecraft.world.entity.projectile.WitherSkull;
|
||||||
|
import net.minecraft.world.entity.projectile.windcharge.BreezeWindCharge;
|
||||||
|
import net.minecraft.world.entity.projectile.windcharge.WindCharge;
|
||||||
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
|
import net.minecraft.world.entity.vehicle.ChestBoat;
|
||||||
|
import net.minecraft.world.entity.vehicle.ChestRaft;
|
||||||
|
import net.minecraft.world.entity.vehicle.Minecart;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartChest;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartCommandBlock;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartFurnace;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartHopper;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartSpawner;
|
||||||
|
import net.minecraft.world.entity.vehicle.MinecartTNT;
|
||||||
|
import net.minecraft.world.entity.vehicle.Raft;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
@SuppressWarnings({
|
||||||
|
"unused",
|
||||||
|
"SpellCheckingInspection"
|
||||||
|
})
|
||||||
|
@GeneratedFrom("1.21.4")
|
||||||
|
@NullMarked
|
||||||
|
public final class EntityTypeToEntityClass {
|
||||||
|
private static final Map<EntityType, Class<? extends Entity>> ENTITY_TYPE_TO_CLASS = initialize();
|
||||||
|
|
||||||
|
private static final Map<EntityType, Class<? extends Entity>> initialize() {
|
||||||
|
Map<EntityType, Class<? extends Entity>> result = new HashMap<>();
|
||||||
|
result.put(EntityType.ACACIA_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.ACACIA_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.ALLAY, Allay.class);
|
||||||
|
result.put(EntityType.AREA_EFFECT_CLOUD, AreaEffectCloud.class);
|
||||||
|
result.put(EntityType.ARMADILLO, Armadillo.class);
|
||||||
|
result.put(EntityType.ARMOR_STAND, ArmorStand.class);
|
||||||
|
result.put(EntityType.ARROW, Arrow.class);
|
||||||
|
result.put(EntityType.AXOLOTL, Axolotl.class);
|
||||||
|
result.put(EntityType.BAMBOO_CHEST_RAFT, ChestRaft.class);
|
||||||
|
result.put(EntityType.BAMBOO_RAFT, Raft.class);
|
||||||
|
result.put(EntityType.BAT, Bat.class);
|
||||||
|
result.put(EntityType.BEE, Bee.class);
|
||||||
|
result.put(EntityType.BIRCH_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.BIRCH_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.BLAZE, Blaze.class);
|
||||||
|
result.put(EntityType.BLOCK_DISPLAY, Display.BlockDisplay.class);
|
||||||
|
result.put(EntityType.BOGGED, Bogged.class);
|
||||||
|
result.put(EntityType.BREEZE, Breeze.class);
|
||||||
|
result.put(EntityType.BREEZE_WIND_CHARGE, BreezeWindCharge.class);
|
||||||
|
result.put(EntityType.CAMEL, Camel.class);
|
||||||
|
result.put(EntityType.CAT, Cat.class);
|
||||||
|
result.put(EntityType.CAVE_SPIDER, CaveSpider.class);
|
||||||
|
result.put(EntityType.CHERRY_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.CHERRY_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.CHEST_MINECART, MinecartChest.class);
|
||||||
|
result.put(EntityType.CHICKEN, Chicken.class);
|
||||||
|
result.put(EntityType.COD, Cod.class);
|
||||||
|
result.put(EntityType.COMMAND_BLOCK_MINECART, MinecartCommandBlock.class);
|
||||||
|
result.put(EntityType.COW, Cow.class);
|
||||||
|
result.put(EntityType.CREAKING, Creaking.class);
|
||||||
|
result.put(EntityType.CREEPER, Creeper.class);
|
||||||
|
result.put(EntityType.DARK_OAK_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.DARK_OAK_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.DOLPHIN, Dolphin.class);
|
||||||
|
result.put(EntityType.DONKEY, Donkey.class);
|
||||||
|
result.put(EntityType.DRAGON_FIREBALL, DragonFireball.class);
|
||||||
|
result.put(EntityType.DROWNED, Drowned.class);
|
||||||
|
result.put(EntityType.EGG, ThrownEgg.class);
|
||||||
|
result.put(EntityType.ELDER_GUARDIAN, ElderGuardian.class);
|
||||||
|
result.put(EntityType.ENDERMAN, EnderMan.class);
|
||||||
|
result.put(EntityType.ENDERMITE, Endermite.class);
|
||||||
|
result.put(EntityType.ENDER_DRAGON, EnderDragon.class);
|
||||||
|
result.put(EntityType.ENDER_PEARL, ThrownEnderpearl.class);
|
||||||
|
result.put(EntityType.END_CRYSTAL, EndCrystal.class);
|
||||||
|
result.put(EntityType.EVOKER, Evoker.class);
|
||||||
|
result.put(EntityType.EVOKER_FANGS, EvokerFangs.class);
|
||||||
|
result.put(EntityType.EXPERIENCE_BOTTLE, ThrownExperienceBottle.class);
|
||||||
|
result.put(EntityType.EXPERIENCE_ORB, ExperienceOrb.class);
|
||||||
|
result.put(EntityType.EYE_OF_ENDER, EyeOfEnder.class);
|
||||||
|
result.put(EntityType.FALLING_BLOCK, FallingBlockEntity.class);
|
||||||
|
result.put(EntityType.FIREBALL, LargeFireball.class);
|
||||||
|
result.put(EntityType.FIREWORK_ROCKET, FireworkRocketEntity.class);
|
||||||
|
result.put(EntityType.FOX, Fox.class);
|
||||||
|
result.put(EntityType.FROG, Frog.class);
|
||||||
|
result.put(EntityType.FURNACE_MINECART, MinecartFurnace.class);
|
||||||
|
result.put(EntityType.GHAST, Ghast.class);
|
||||||
|
result.put(EntityType.GIANT, Giant.class);
|
||||||
|
result.put(EntityType.GLOW_ITEM_FRAME, GlowItemFrame.class);
|
||||||
|
result.put(EntityType.GLOW_SQUID, GlowSquid.class);
|
||||||
|
result.put(EntityType.GOAT, Goat.class);
|
||||||
|
result.put(EntityType.GUARDIAN, Guardian.class);
|
||||||
|
result.put(EntityType.HOGLIN, Hoglin.class);
|
||||||
|
result.put(EntityType.HOPPER_MINECART, MinecartHopper.class);
|
||||||
|
result.put(EntityType.HORSE, Horse.class);
|
||||||
|
result.put(EntityType.HUSK, Husk.class);
|
||||||
|
result.put(EntityType.ILLUSIONER, Illusioner.class);
|
||||||
|
result.put(EntityType.INTERACTION, Interaction.class);
|
||||||
|
result.put(EntityType.IRON_GOLEM, IronGolem.class);
|
||||||
|
result.put(EntityType.ITEM, ItemEntity.class);
|
||||||
|
result.put(EntityType.ITEM_DISPLAY, Display.ItemDisplay.class);
|
||||||
|
result.put(EntityType.ITEM_FRAME, ItemFrame.class);
|
||||||
|
result.put(EntityType.JUNGLE_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.JUNGLE_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.LEASH_KNOT, LeashFenceKnotEntity.class);
|
||||||
|
result.put(EntityType.LIGHTNING_BOLT, LightningBolt.class);
|
||||||
|
result.put(EntityType.LLAMA, Llama.class);
|
||||||
|
result.put(EntityType.LLAMA_SPIT, LlamaSpit.class);
|
||||||
|
result.put(EntityType.MAGMA_CUBE, MagmaCube.class);
|
||||||
|
result.put(EntityType.MANGROVE_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.MANGROVE_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.MARKER, Marker.class);
|
||||||
|
result.put(EntityType.MINECART, Minecart.class);
|
||||||
|
result.put(EntityType.MOOSHROOM, MushroomCow.class);
|
||||||
|
result.put(EntityType.MULE, Mule.class);
|
||||||
|
result.put(EntityType.OAK_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.OAK_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.OCELOT, Ocelot.class);
|
||||||
|
result.put(EntityType.OMINOUS_ITEM_SPAWNER, OminousItemSpawner.class);
|
||||||
|
result.put(EntityType.PAINTING, Painting.class);
|
||||||
|
result.put(EntityType.PALE_OAK_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.PALE_OAK_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.PANDA, Panda.class);
|
||||||
|
result.put(EntityType.PARROT, Parrot.class);
|
||||||
|
result.put(EntityType.PHANTOM, Phantom.class);
|
||||||
|
result.put(EntityType.PIG, Pig.class);
|
||||||
|
result.put(EntityType.PIGLIN, Piglin.class);
|
||||||
|
result.put(EntityType.PIGLIN_BRUTE, PiglinBrute.class);
|
||||||
|
result.put(EntityType.PILLAGER, Pillager.class);
|
||||||
|
result.put(EntityType.POLAR_BEAR, PolarBear.class);
|
||||||
|
result.put(EntityType.POTION, ThrownPotion.class);
|
||||||
|
result.put(EntityType.PUFFERFISH, Pufferfish.class);
|
||||||
|
result.put(EntityType.RABBIT, Rabbit.class);
|
||||||
|
result.put(EntityType.RAVAGER, Ravager.class);
|
||||||
|
result.put(EntityType.SALMON, Salmon.class);
|
||||||
|
result.put(EntityType.SHEEP, Sheep.class);
|
||||||
|
result.put(EntityType.SHULKER, Shulker.class);
|
||||||
|
result.put(EntityType.SHULKER_BULLET, ShulkerBullet.class);
|
||||||
|
result.put(EntityType.SILVERFISH, Silverfish.class);
|
||||||
|
result.put(EntityType.SKELETON, Skeleton.class);
|
||||||
|
result.put(EntityType.SKELETON_HORSE, SkeletonHorse.class);
|
||||||
|
result.put(EntityType.SLIME, Slime.class);
|
||||||
|
result.put(EntityType.SMALL_FIREBALL, SmallFireball.class);
|
||||||
|
result.put(EntityType.SNIFFER, Sniffer.class);
|
||||||
|
result.put(EntityType.SNOWBALL, Snowball.class);
|
||||||
|
result.put(EntityType.SNOW_GOLEM, SnowGolem.class);
|
||||||
|
result.put(EntityType.SPAWNER_MINECART, MinecartSpawner.class);
|
||||||
|
result.put(EntityType.SPECTRAL_ARROW, SpectralArrow.class);
|
||||||
|
result.put(EntityType.SPIDER, Spider.class);
|
||||||
|
result.put(EntityType.SPRUCE_BOAT, Boat.class);
|
||||||
|
result.put(EntityType.SPRUCE_CHEST_BOAT, ChestBoat.class);
|
||||||
|
result.put(EntityType.SQUID, Squid.class);
|
||||||
|
result.put(EntityType.STRAY, Stray.class);
|
||||||
|
result.put(EntityType.STRIDER, Strider.class);
|
||||||
|
result.put(EntityType.TADPOLE, Tadpole.class);
|
||||||
|
result.put(EntityType.TEXT_DISPLAY, Display.TextDisplay.class);
|
||||||
|
result.put(EntityType.TNT, PrimedTnt.class);
|
||||||
|
result.put(EntityType.TNT_MINECART, MinecartTNT.class);
|
||||||
|
result.put(EntityType.TRADER_LLAMA, TraderLlama.class);
|
||||||
|
result.put(EntityType.TRIDENT, ThrownTrident.class);
|
||||||
|
result.put(EntityType.TROPICAL_FISH, TropicalFish.class);
|
||||||
|
result.put(EntityType.TURTLE, Turtle.class);
|
||||||
|
result.put(EntityType.VEX, Vex.class);
|
||||||
|
result.put(EntityType.VILLAGER, Villager.class);
|
||||||
|
result.put(EntityType.VINDICATOR, Vindicator.class);
|
||||||
|
result.put(EntityType.WANDERING_TRADER, WanderingTrader.class);
|
||||||
|
result.put(EntityType.WARDEN, Warden.class);
|
||||||
|
result.put(EntityType.WIND_CHARGE, WindCharge.class);
|
||||||
|
result.put(EntityType.WITCH, Witch.class);
|
||||||
|
result.put(EntityType.WITHER, WitherBoss.class);
|
||||||
|
result.put(EntityType.WITHER_SKELETON, WitherSkeleton.class);
|
||||||
|
result.put(EntityType.WITHER_SKULL, WitherSkull.class);
|
||||||
|
result.put(EntityType.WOLF, Wolf.class);
|
||||||
|
result.put(EntityType.ZOGLIN, Zoglin.class);
|
||||||
|
result.put(EntityType.ZOMBIE, Zombie.class);
|
||||||
|
result.put(EntityType.ZOMBIE_HORSE, ZombieHorse.class);
|
||||||
|
result.put(EntityType.ZOMBIE_VILLAGER, ZombieVillager.class);
|
||||||
|
result.put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.class);
|
||||||
|
result.put(EntityType.PLAYER, Player.class);
|
||||||
|
result.put(EntityType.FISHING_BOBBER, FishingHook.class);
|
||||||
|
return Map.copyOf(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Class<? extends Entity> getClassByEntityType(EntityType entityType) {
|
||||||
|
return ENTITY_TYPE_TO_CLASS.get(entityType);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package io.papermc.generator;
|
||||||
|
|
||||||
|
import io.papermc.generator.types.EntityMetaWatcherGenerator;
|
||||||
|
import io.papermc.generator.types.EntityTypeToEntityClassGenerator;
|
||||||
|
import io.papermc.generator.types.SourceGenerator;
|
||||||
|
|
||||||
|
public interface Generators {
|
||||||
|
|
||||||
|
SourceGenerator[] SERVER = {
|
||||||
|
new EntityMetaWatcherGenerator("EntityMetaWatcher", "io.papermc.paper.entity.meta"),
|
||||||
|
new EntityTypeToEntityClassGenerator("EntityTypeToEntityClass", "io.papermc.paper.entity.meta"),
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package io.papermc.generator;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
|
import io.papermc.generator.types.SourceGenerator;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import 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;
|
||||||
|
|
||||||
|
static {
|
||||||
|
SharedConstants.tryDetectVersion();
|
||||||
|
Bootstrap.bootStrap();
|
||||||
|
Bootstrap.validate();
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,182 @@
|
||||||
|
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(ParameterizedTypeName.get(ClassName.get(EntityDataSerializer.class), WildcardTypeName.subtypeOf(Object.class)), "entityDataSerializer")
|
||||||
|
.addParameter(int.class, "id")
|
||||||
|
.addStatement("Map<Long, EntityDataSerializer<?>> serializerMap = VALID_ENTITY_META_MAP.get(clazz)")
|
||||||
|
.beginControlFlow("if(serializerMap == null)")
|
||||||
|
.addStatement("return false")
|
||||||
|
.endControlFlow()
|
||||||
|
.addStatement("var serializer = serializerMap.get(id)")
|
||||||
|
.addStatement("return serializer != null && serializer == entityDataSerializer");
|
||||||
|
|
||||||
|
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().stream().sorted(Comparator.comparing(EntityDataAccessor::id)).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,95 @@
|
||||||
|
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.papermc.generator.utils.Annotations;
|
||||||
|
import io.papermc.generator.utils.ReflectionHelper;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public class EntityTypeToEntityClassGenerator extends SimpleGenerator {
|
||||||
|
|
||||||
|
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), ClassName.get(EntityType.class), ENTITY_CLASS);
|
||||||
|
|
||||||
|
|
||||||
|
public EntityTypeToEntityClassGenerator(String className, String packageName) {
|
||||||
|
super(className, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TypeSpec getTypeSpec() {
|
||||||
|
|
||||||
|
final List<TypeToClass> typeToClasses = ReflectionHelper.forClass(net.minecraft.world.entity.EntityType.class, field ->
|
||||||
|
java.lang.reflect.Modifier.isStatic(field.getModifiers()) &&
|
||||||
|
java.lang.reflect.Modifier.isPublic(field.getModifiers()) &&
|
||||||
|
java.lang.reflect.Modifier.isFinal(field.getModifiers()) &&
|
||||||
|
field.getType().isAssignableFrom(net.minecraft.world.entity.EntityType.class)
|
||||||
|
).stream().map(this::createFromField).toList();
|
||||||
|
|
||||||
|
TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(this.className)
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||||
|
.addAnnotations(Annotations.CLASS_HEADER);
|
||||||
|
|
||||||
|
generateEntityTypeToEntityClassMap(typeBuilder, typeToClasses);
|
||||||
|
generateGetClassByEntityType(typeBuilder);
|
||||||
|
|
||||||
|
return typeBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeToClass createFromField(Field field) {
|
||||||
|
return new TypeToClass(field.getName(), (Class<?>) ((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
record TypeToClass(String entityType, Class<?> className) {}
|
||||||
|
|
||||||
|
private void generateGetClassByEntityType(TypeSpec.Builder builder) {
|
||||||
|
var methodBuilder = MethodSpec.methodBuilder("getClassByEntityType")
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
|
||||||
|
.returns(ENTITY_CLASS)
|
||||||
|
.addParameter(ClassName.get(EntityType.class), "entityType")
|
||||||
|
.addStatement("return ENTITY_TYPE_TO_CLASS.get(entityType)");
|
||||||
|
|
||||||
|
builder.addMethod(methodBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateEntityTypeToEntityClassMap(TypeSpec.Builder typeBuilder, List<TypeToClass> classes) {
|
||||||
|
typeBuilder.addField(
|
||||||
|
FieldSpec.builder(OUTER_MAP_TYPE, "ENTITY_TYPE_TO_CLASS", 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 -> {
|
||||||
|
builder.addStatement("result.put(EntityType.$L, $T.class)", aClass.entityType, aClass.className);
|
||||||
|
});
|
||||||
|
|
||||||
|
typeBuilder.addMethod(builder.addStatement("return $T.copyOf(result)", Map.class).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JavaFile.Builder file(final JavaFile.Builder builder) {
|
||||||
|
return builder.skipJavaLangImports(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
JavaFile.Builder builder = JavaFile.builder(this.packageName, this.getTypeSpec());
|
||||||
|
this.file(builder)
|
||||||
|
.indent(" ")
|
||||||
|
.skipJavaLangImports(true);
|
||||||
|
|
||||||
|
builder.build().writeTo(parent, 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,65 @@
|
||||||
|
package io.papermc.generator.utils;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.AnnotationSpec;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
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.Nullable;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
public final class Annotations {
|
||||||
|
|
||||||
|
public static List<AnnotationSpec> experimentalAnnotations(final MinecraftExperimental.@Nullable Requires requiredFeatureFlag) {
|
||||||
|
final List<AnnotationSpec> annotationSpecs = new ArrayList<>();
|
||||||
|
annotationSpecs.add(AnnotationSpec.builder(ApiStatus.Experimental.class).build());
|
||||||
|
if (requiredFeatureFlag != null) {
|
||||||
|
annotationSpecs.add(AnnotationSpec.builder(MinecraftExperimental.class)
|
||||||
|
.addMember("value", "$T.$L", MinecraftExperimental.Requires.class, requiredFeatureFlag.name())
|
||||||
|
.build());
|
||||||
|
} else {
|
||||||
|
annotationSpecs.add(AnnotationSpec.builder(MinecraftExperimental.class).build());
|
||||||
|
}
|
||||||
|
return annotationSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnnotationSpec deprecatedVersioned(final @Nullable String version, final boolean forRemoval) {
|
||||||
|
final AnnotationSpec.Builder annotationSpec = AnnotationSpec.builder(Deprecated.class);
|
||||||
|
if (forRemoval) {
|
||||||
|
annotationSpec.addMember("forRemoval", "$L", true);
|
||||||
|
}
|
||||||
|
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 NULL_MARKED = AnnotationSpec.builder(NullMarked.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,
|
||||||
|
NULL_MARKED
|
||||||
|
);
|
||||||
|
|
||||||
|
private Annotations() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package io.papermc.generator.utils;
|
||||||
|
|
||||||
|
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.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public record CollectingContext<T>(Set<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, resourceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S> HolderGetter<S> lookup(final ResourceKey<? extends Registry<? extends S>> resourceKey) {
|
||||||
|
return Main.REGISTRY_ACCESS.lookupOrThrow(resourceKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
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.ROOT)).replaceAll("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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,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();
|
||||||
|
}
|
||||||
|
}
|
1
paper-server-generator/wideners.at
Normal file
1
paper-server-generator/wideners.at
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -6,6 +6,7 @@ plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
id("io.papermc.paperweight.core")
|
id("io.papermc.paperweight.core")
|
||||||
|
idea
|
||||||
}
|
}
|
||||||
|
|
||||||
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
||||||
|
@ -105,6 +106,22 @@ if (project.providers.gradleProperty("publishDevBundle").isPresent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val log4jPlugins = sourceSets.create("log4jPlugins")
|
val log4jPlugins = sourceSets.create("log4jPlugins")
|
||||||
configurations.named(log4jPlugins.compileClasspathConfigurationName) {
|
configurations.named(log4jPlugins.compileClasspathConfigurationName) {
|
||||||
extendsFrom(configurations.compileClasspath.get())
|
extendsFrom(configurations.compileClasspath.get())
|
||||||
|
|
174
paper-server/patches/features/0031-disguise-api.patch
Normal file
174
paper-server/patches/features/0031-disguise-api.patch
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
From ff31ccd6752ae4866da7efe41800d8051d0fb04a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Yannick Lamprecht <yannicklamprecht@live.de>
|
||||||
|
Date: Mon, 23 Dec 2024 14:31:39 +0100
|
||||||
|
Subject: [PATCH] disguise api
|
||||||
|
|
||||||
|
---
|
||||||
|
net/minecraft/network/syncher/SynchedEntityData.java | 4 ++++
|
||||||
|
net/minecraft/server/level/ServerEntity.java | 11 +++++++++--
|
||||||
|
net/minecraft/server/level/ServerPlayer.java | 4 +++-
|
||||||
|
net/minecraft/world/entity/Entity.java | 9 +++++++++
|
||||||
|
net/minecraft/world/entity/LivingEntity.java | 3 ++-
|
||||||
|
net/minecraft/world/entity/Entity.java | 8 ++++++++
|
||||||
|
6 files changed, 35 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/net/minecraft/network/syncher/SynchedEntityData.java b/net/minecraft/network/syncher/SynchedEntityData.java
|
||||||
|
index 7a83c00..7c25745 100644
|
||||||
|
--- a/net/minecraft/network/syncher/SynchedEntityData.java
|
||||||
|
+++ b/net/minecraft/network/syncher/SynchedEntityData.java
|
||||||
|
@@ -89,6 +89,7 @@ public class SynchedEntityData {
|
||||||
|
for (SynchedEntityData.DataItem<?> dataItem : this.itemsById) {
|
||||||
|
if (dataItem.isDirty()) {
|
||||||
|
dataItem.setDirty(false);
|
||||||
|
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, dataItem.getAccessor())) continue; // Paper - disguise api
|
||||||
|
list.add(dataItem.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -102,6 +103,7 @@ public class SynchedEntityData {
|
||||||
|
List<SynchedEntityData.DataValue<?>> list = null;
|
||||||
|
|
||||||
|
for (SynchedEntityData.DataItem<?> dataItem : this.itemsById) {
|
||||||
|
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, dataItem.getAccessor())) continue; // Paper - disguise api
|
||||||
|
if (!dataItem.isSetToDefault()) {
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
@@ -117,6 +119,7 @@ public class SynchedEntityData {
|
||||||
|
public void assignValues(List<SynchedEntityData.DataValue<?>> entries) {
|
||||||
|
for (SynchedEntityData.DataValue<?> dataValue : entries) {
|
||||||
|
SynchedEntityData.DataItem<?> dataItem = this.itemsById[dataValue.id];
|
||||||
|
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip((net.minecraft.world.entity.Entity) entity, dataItem.getAccessor())) continue; // Paper - disguise api
|
||||||
|
this.assignValue(dataItem, dataValue);
|
||||||
|
this.entity.onSyncedDataUpdated(dataItem.getAccessor());
|
||||||
|
}
|
||||||
|
@@ -184,6 +187,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/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||||
|
index a4da360..d1dbf46 100644
|
||||||
|
--- a/net/minecraft/server/level/ServerEntity.java
|
||||||
|
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||||
|
@@ -303,6 +303,7 @@ public class ServerEntity {
|
||||||
|
|
||||||
|
public void removePairing(ServerPlayer player) {
|
||||||
|
this.entity.stopSeenByPlayer(player);
|
||||||
|
+ io.papermc.paper.disguise.DisguiseUtil.tryDespawn(player, this.entity); // Paper - disguise api
|
||||||
|
player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -322,9 +323,13 @@ public class ServerEntity {
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet<ClientGamePacketListener> addEntityPacket = this.entity.getAddEntityPacket(this);
|
||||||
|
+ // Paper start - disguise api
|
||||||
|
+ if(!io.papermc.paper.disguise.DisguiseUtil.tryDisguise(player, entity, addEntityPacket)){
|
||||||
|
consumer.accept(addEntityPacket);
|
||||||
|
+ }
|
||||||
|
+ // Paper end - disguise api
|
||||||
|
if (this.trackedDataValues != null) {
|
||||||
|
- consumer.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), this.trackedDataValues));
|
||||||
|
+ consumer.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), io.papermc.paper.disguise.DisguiseUtil.filter(this.entity, this.trackedDataValues))); // Paper - disguise api
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean flag = this.trackDelta;
|
||||||
|
@@ -406,7 +411,7 @@ public class ServerEntity {
|
||||||
|
this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (this.entity instanceof LivingEntity) {
|
||||||
|
+ if (this.entity instanceof LivingEntity && !io.papermc.paper.disguise.DisguiseUtil.shouldSkipAttributeSending(this.entity)) { // Paper - disguise api
|
||||||
|
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
|
||||||
|
if (!attributesToSync.isEmpty()) {
|
||||||
|
// CraftBukkit start - Send scaled max health
|
||||||
|
@@ -414,7 +419,9 @@ public class ServerEntity {
|
||||||
|
serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false);
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
+ if(!io.papermc.paper.disguise.DisguiseUtil.shouldSkipAttributeSending(this.entity)) { // Paper start - disguise api
|
||||||
|
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
|
||||||
|
+ } // Paper end - disguise api
|
||||||
|
}
|
||||||
|
|
||||||
|
attributesToSync.clear();
|
||||||
|
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||||
|
index e350c6b..82251c1 100644
|
||||||
|
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||||
|
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||||
|
@@ -1663,7 +1663,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
- if (this.isSleeping()) {
|
||||||
|
+ if (this.isSleeping() && io.papermc.paper.disguise.DisguiseUtil.canSendAnimation(this) /* Paper - disguise api */) {
|
||||||
|
this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(this, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2169,11 +2169,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void crit(Entity entityHit) {
|
||||||
|
+ if(!io.papermc.paper.disguise.DisguiseUtil.canSendAnimation(this)) return; // Paper - disguise api
|
||||||
|
this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(entityHit, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void magicCrit(Entity entityHit) {
|
||||||
|
+ if(!io.papermc.paper.disguise.DisguiseUtil.canSendAnimation(this)) return; // Paper - disguise api
|
||||||
|
this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(entityHit, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||||
|
index 3cefe3d..cb11234 100644
|
||||||
|
--- a/net/minecraft/world/entity/Entity.java
|
||||||
|
+++ b/net/minecraft/world/entity/Entity.java
|
||||||
|
@@ -505,6 +505,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
// Paper end - optimise collisions
|
||||||
|
+ // Paper start - disguise api
|
||||||
|
+ public void clearPlayers() {
|
||||||
|
+ trackedEntity.moonrise$clearPlayers();
|
||||||
|
+ }
|
||||||
|
+ public void updatePlayers() {
|
||||||
|
+ trackedEntity.updatePlayers(((ServerLevel)level).players());
|
||||||
|
+ }
|
||||||
|
+ // Paper end - disguise api
|
||||||
|
// Paper start - optimise entity tracker
|
||||||
|
private net.minecraft.server.level.ChunkMap.TrackedEntity trackedEntity;
|
||||||
|
|
||||||
|
@@ -675,6 +683,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||||
|
|
||||||
|
final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
|
||||||
|
for (final EntityDataAccessor<?> key : keys) {
|
||||||
|
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkip(this, key)) continue; // Paper - disguise api
|
||||||
|
final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
|
||||||
|
values.add(synchedValue.value());
|
||||||
|
}
|
||||||
|
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||||
|
index 195e115..feca873 100644
|
||||||
|
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||||
|
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||||
|
@@ -1273,7 +1273,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||||
|
|
||||||
|
private void refreshDirtyAttributes() {
|
||||||
|
Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
|
||||||
|
-
|
||||||
|
+ if (io.papermc.paper.disguise.DisguiseUtil.shouldSkipAttributeSending(this)) return; // Paper - disguise api
|
||||||
|
for (AttributeInstance attributeInstance : attributesToUpdate) {
|
||||||
|
this.onAttributeUpdated(attributeInstance.getAttribute());
|
||||||
|
}
|
||||||
|
@@ -2477,6 +2477,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||||
|
this.swinging = true;
|
||||||
|
this.swingingArm = hand;
|
||||||
|
if (this.level() instanceof ServerLevel) {
|
||||||
|
+ if(!io.papermc.paper.disguise.DisguiseUtil.canSendAnimation(this)) return; // Paper - disguise api
|
||||||
|
ClientboundAnimatePacket clientboundAnimatePacket = new ClientboundAnimatePacket(this, hand == InteractionHand.MAIN_HAND ? 0 : 3);
|
||||||
|
ServerChunkCache chunkSource = ((ServerLevel)this.level()).getChunkSource();
|
||||||
|
if (updateSelf) {
|
||||||
|
--
|
||||||
|
2.46.1
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.destroystokyo.paper;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
|
import net.minecraft.world.entity.player.PlayerModelPart;
|
||||||
|
|
||||||
public class PaperSkinParts implements SkinParts {
|
public class PaperSkinParts implements SkinParts {
|
||||||
|
|
||||||
|
@ -71,4 +72,81 @@ public class PaperSkinParts implements SkinParts {
|
||||||
.add("hats=" + hasHatsEnabled())
|
.add("hats=" + hasHatsEnabled())
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SkinParts.Builder builder(){
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder implements SkinParts.Builder {
|
||||||
|
|
||||||
|
private boolean cape;
|
||||||
|
private boolean jacket;
|
||||||
|
private boolean leftSleeve;
|
||||||
|
private boolean rightSleeve;
|
||||||
|
private boolean leftPants;
|
||||||
|
private boolean rightPants;
|
||||||
|
private boolean hats;
|
||||||
|
|
||||||
|
private static final int CAPE = PlayerModelPart.CAPE.getMask();
|
||||||
|
private static final int JACKET = PlayerModelPart.JACKET.getMask();
|
||||||
|
private static final int LEFT_SLEEVE = PlayerModelPart.LEFT_SLEEVE.getMask();
|
||||||
|
private static final int RIGHT_SLEEVE = PlayerModelPart.RIGHT_SLEEVE.getMask();
|
||||||
|
private static final int LEFT_PANTS = PlayerModelPart.LEFT_PANTS_LEG.getMask();
|
||||||
|
private static final int RIGHT_PANTS = PlayerModelPart.RIGHT_PANTS_LEG.getMask();
|
||||||
|
private static final int HAT = PlayerModelPart.HAT.getMask();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withCape(boolean cape) {
|
||||||
|
this.cape = cape;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withJacket(boolean jacket) {
|
||||||
|
this.jacket = jacket;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withLeftSleeve(boolean leftSleeve) {
|
||||||
|
this.leftSleeve = leftSleeve;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withRightSleeve(boolean rightSleeve) {
|
||||||
|
this.rightSleeve = rightSleeve;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withLeftPants(boolean leftPants) {
|
||||||
|
this.leftPants = leftPants;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withRightPants(boolean rightPants) {
|
||||||
|
this.rightPants = rightPants;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @org.jetbrains.annotations.NotNull Builder withHat(boolean hat) {
|
||||||
|
this.hats = hat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @org.jetbrains.annotations.NotNull SkinParts build() {
|
||||||
|
int raw = 0;
|
||||||
|
if (cape) raw |= CAPE;
|
||||||
|
if (jacket) raw |= JACKET;
|
||||||
|
if (leftSleeve) raw |= LEFT_SLEEVE;
|
||||||
|
if (rightSleeve) raw |= RIGHT_SLEEVE;
|
||||||
|
if (leftPants) raw |= LEFT_PANTS;
|
||||||
|
if (rightPants) raw |= RIGHT_PANTS;
|
||||||
|
if (hats) raw |= HAT;
|
||||||
|
return new PaperSkinParts(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
package io.papermc.paper.disguise;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||||
|
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||||
|
import io.papermc.paper.entity.meta.EntityTypeToEntityClass;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||||
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
|
import net.minecraft.network.syncher.EntityDataSerializer;
|
||||||
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.craftbukkit.entity.CraftEntityType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Action;
|
||||||
|
import static net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Entry;
|
||||||
|
|
||||||
|
public final class DisguiseUtil {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DisguiseUtil.class);
|
||||||
|
|
||||||
|
private DisguiseUtil(){}
|
||||||
|
|
||||||
|
public static boolean tryDisguise(ServerPlayer player, Entity entity, Packet<?> packet) {
|
||||||
|
if(!(packet instanceof ClientboundAddEntityPacket clientboundAddEntityPacket)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||||
|
case DisguiseData.OriginalDisguise disguise -> false;
|
||||||
|
case io.papermc.paper.disguise.EntityTypeDisguise(var type) -> {
|
||||||
|
player.connection.send(create(clientboundAddEntityPacket, CraftEntityType.bukkitToMinecraft(type)));
|
||||||
|
yield true;
|
||||||
|
}
|
||||||
|
case PlayerDisguise(var playerProfile, var listed, var showHead, var skinParts) -> {
|
||||||
|
PlayerProfile adapted = Bukkit.createProfile(entity.getUUID(), playerProfile.getName());
|
||||||
|
adapted.setProperties(playerProfile.getProperties());
|
||||||
|
Entry playerUpdate = new Entry(
|
||||||
|
entity.getUUID(),
|
||||||
|
CraftPlayerProfile.asAuthlibCopy(adapted),
|
||||||
|
listed,
|
||||||
|
0,
|
||||||
|
net.minecraft.world.level.GameType.DEFAULT_MODE,
|
||||||
|
entity.getCustomName(),
|
||||||
|
showHead,
|
||||||
|
0,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
player.connection.send(new ClientboundPlayerInfoUpdatePacket(EnumSet.of(Action.ADD_PLAYER, Action.UPDATE_LISTED), playerUpdate));
|
||||||
|
player.connection.send(create(clientboundAddEntityPacket, net.minecraft.world.entity.EntityType.PLAYER));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only player disguise needs to be handled specially
|
||||||
|
* because the client doesn't forget the player profile otherwise.
|
||||||
|
* This would result in player being kicked cause the entities type mismatches the previously disguised one.
|
||||||
|
*/
|
||||||
|
public static void tryDespawn(ServerPlayer player, Entity entity) {
|
||||||
|
if(entity.getBukkitEntity().getDisguiseData() instanceof PlayerDisguise) {
|
||||||
|
player.connection.send(new ClientboundPlayerInfoRemovePacket(List.of(entity.getUUID())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientboundAddEntityPacket create(ClientboundAddEntityPacket packet, EntityType<?> entityType) {
|
||||||
|
return new net.minecraft.network.protocol.game.ClientboundAddEntityPacket(
|
||||||
|
packet.getId(),
|
||||||
|
packet.getUUID(),
|
||||||
|
packet.getX(),
|
||||||
|
packet.getY(),
|
||||||
|
packet.getZ(),
|
||||||
|
packet.getXRot(),
|
||||||
|
packet.getYRot(),
|
||||||
|
entityType,
|
||||||
|
0,
|
||||||
|
Vec3.ZERO.add(packet.getX(), packet.getY(), packet.getZ()).scale(1/8000.0D),
|
||||||
|
packet.getYHeadRot()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is used to skip entity meta that doesn't fit the disguised type.
|
||||||
|
* e.g. Player having a float at index 15 (additional hearts) and the server side entity is an Armorstand
|
||||||
|
* that has a byte at that index.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static boolean shouldSkip(Entity entity, EntityDataAccessor<?> dataAccessor) {
|
||||||
|
return shouldSkip(entity, dataAccessor.serializer(), dataAccessor.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldSkip(Entity entity, EntityDataSerializer<?> entityDataSerializer, int id) {
|
||||||
|
return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||||
|
case DisguiseData.OriginalDisguise original -> false;
|
||||||
|
case EntityTypeDisguise entityTypeDisguise -> !io.papermc.paper.entity.meta.EntityMetaWatcher.isValidForClass(
|
||||||
|
EntityTypeToEntityClass.getClassByEntityType(entityTypeDisguise.entityType()),
|
||||||
|
entityDataSerializer, id
|
||||||
|
);
|
||||||
|
case PlayerDisguise playerDisguise -> !io.papermc.paper.entity.meta.EntityMetaWatcher.isValidForClass(
|
||||||
|
ServerPlayer.class,
|
||||||
|
entityDataSerializer, id
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SynchedEntityData.DataValue<?>> filter(Entity entity, List<SynchedEntityData.DataValue<?>> values) {
|
||||||
|
List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
|
||||||
|
for (SynchedEntityData.DataValue<?> value : values) {
|
||||||
|
if (!shouldSkip(entity, value.serializer(), value.id())) {
|
||||||
|
list.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldSkipAttributeSending(Entity entity) {
|
||||||
|
return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||||
|
case DisguiseData.OriginalDisguise original -> false;
|
||||||
|
case EntityTypeDisguise entityTypeDisguise -> !entityTypeDisguise.entityType().hasDefaultAttributes();
|
||||||
|
case PlayerDisguise playerDisguise -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canSendAnimation(Entity entity) {
|
||||||
|
return switch (entity.getBukkitEntity().getDisguiseData()) {
|
||||||
|
case DisguiseData.OriginalDisguise original -> true;
|
||||||
|
case EntityTypeDisguise entityTypeDisguise -> LivingEntity.class.isAssignableFrom(EntityTypeToEntityClass.getClassByEntityType(entityTypeDisguise.entityType()));
|
||||||
|
case PlayerDisguise playerDisguise -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -395,6 +395,12 @@ public final class CraftServer implements Server {
|
||||||
return ca.spottedleaf.moonrise.common.util.TickThread.isTickThread();
|
return ca.spottedleaf.moonrise.common.util.TickThread.isTickThread();
|
||||||
}
|
}
|
||||||
// Paper end - Folia reagion threading API
|
// 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 {
|
static {
|
||||||
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
|
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
|
||||||
|
|
|
@ -1306,4 +1306,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Paper end - broadcast hurt animation
|
// Paper end - broadcast hurt animation
|
||||||
|
// 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) {
|
||||||
|
getHandle().clearPlayers();
|
||||||
|
this.disguiseData = disguiseData;
|
||||||
|
getHandle().updatePlayers();
|
||||||
|
}
|
||||||
|
// Paper end - disguise api
|
||||||
}
|
}
|
||||||
|
|
|
@ -2844,7 +2844,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||||
|
|
||||||
// SPIGOT-3813: Attributes before health
|
// SPIGOT-3813: Attributes before health
|
||||||
if (this.getHandle().connection != null) {
|
if (this.getHandle().connection != null) {
|
||||||
|
if(!io.papermc.paper.disguise.DisguiseUtil.shouldSkipAttributeSending(this.getHandle())){ // Paper start - disguise api
|
||||||
this.getHandle().connection.send(new ClientboundUpdateAttributesPacket(this.getHandle().getId(), set));
|
this.getHandle().connection.send(new ClientboundUpdateAttributesPacket(this.getHandle().getId(), set));
|
||||||
|
} // Paper end - disguise api
|
||||||
if (sendHealth) {
|
if (sendHealth) {
|
||||||
this.sendHealthUpdate();
|
this.sendHealthUpdate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ for (name in listOf("paper-api", "paper-server")) {
|
||||||
|
|
||||||
optionalInclude("test-plugin")
|
optionalInclude("test-plugin")
|
||||||
optionalInclude("paper-api-generator")
|
optionalInclude("paper-api-generator")
|
||||||
|
optionalInclude("paper-server-generator")
|
||||||
|
|
||||||
fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) {
|
fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) {
|
||||||
val settingsFile = file("$name.settings.gradle.kts")
|
val settingsFile = file("$name.settings.gradle.kts")
|
||||||
|
|
Loading…
Reference in a new issue