mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-25 22:10:21 +01:00
Expand on entity serialization API
This commit is contained in:
parent
d0d0efee02
commit
e40df834dc
7 changed files with 221 additions and 56 deletions
|
@ -0,0 +1,38 @@
|
||||||
|
package io.papermc.paper.entity;
|
||||||
|
|
||||||
|
import org.bukkit.UnsafeValues;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents flags for entity serialization.
|
||||||
|
*
|
||||||
|
* @see UnsafeValues#serializeEntity(Entity, EntitySerializationFlag... serializationFlags)
|
||||||
|
* @since 1.21.4
|
||||||
|
*/
|
||||||
|
public enum EntitySerializationFlag {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize entities that wouldn't be serialized normally
|
||||||
|
* (e.g. dead, despawned, non-persistent, etc.).
|
||||||
|
*
|
||||||
|
* @see Entity#isValid()
|
||||||
|
* @see Entity#isPersistent()
|
||||||
|
*/
|
||||||
|
FORCE,
|
||||||
|
/**
|
||||||
|
* Serialize misc non-saveable entities like lighting bolts, fishing bobbers, etc.
|
||||||
|
* <br>Note: players require a separate flag: {@link #PLAYER}.
|
||||||
|
*/
|
||||||
|
MISC,
|
||||||
|
/**
|
||||||
|
* Include passengers in the serialized data.
|
||||||
|
*/
|
||||||
|
PASSENGERS,
|
||||||
|
/**
|
||||||
|
* Allow serializing {@link Player}s.
|
||||||
|
* <p>Note: deserializing player data will always fail.
|
||||||
|
*/
|
||||||
|
PLAYER
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package org.bukkit;
|
package org.bukkit;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import io.papermc.paper.entity.EntitySerializationFlag;
|
||||||
import org.bukkit.advancement.Advancement;
|
import org.bukkit.advancement.Advancement;
|
||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.attribute.AttributeModifier;
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
@ -9,7 +10,9 @@ import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.damage.DamageEffect;
|
import org.bukkit.damage.DamageEffect;
|
||||||
import org.bukkit.damage.DamageSource;
|
import org.bukkit.damage.DamageSource;
|
||||||
import org.bukkit.damage.DamageType;
|
import org.bukkit.damage.DamageType;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.inventory.CreativeCategory;
|
import org.bukkit.inventory.CreativeCategory;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
@ -198,13 +201,81 @@ public interface UnsafeValues {
|
||||||
*/
|
*/
|
||||||
@NotNull ItemStack deserializeItemFromJson(@NotNull com.google.gson.JsonObject data) throws IllegalArgumentException;
|
@NotNull ItemStack deserializeItemFromJson(@NotNull com.google.gson.JsonObject data) throws IllegalArgumentException;
|
||||||
|
|
||||||
byte[] serializeEntity(org.bukkit.entity.Entity entity);
|
/**
|
||||||
|
* Serializes the provided entity.
|
||||||
|
*
|
||||||
|
* @param entity entity
|
||||||
|
* @return serialized entity data
|
||||||
|
* @see #serializeEntity(Entity, EntitySerializationFlag...)
|
||||||
|
* @see #deserializeEntity(byte[], World, boolean, boolean)
|
||||||
|
* @throws IllegalArgumentException if couldn't serialize the entity
|
||||||
|
* @since 1.17.1
|
||||||
|
*/
|
||||||
|
default byte @NotNull [] serializeEntity(@NotNull Entity entity) {
|
||||||
|
return serializeEntity(entity, new EntitySerializationFlag[0]);
|
||||||
|
}
|
||||||
|
|
||||||
default org.bukkit.entity.Entity deserializeEntity(byte[] data, World world) {
|
/**
|
||||||
|
* Serializes the provided entity.
|
||||||
|
*
|
||||||
|
* @param entity entity
|
||||||
|
* @param serializationFlags serialization flags
|
||||||
|
* @return serialized entity data
|
||||||
|
* @throws IllegalArgumentException if couldn't serialize the entity
|
||||||
|
* @see #deserializeEntity(byte[], World, boolean, boolean)
|
||||||
|
* @since 1.21.4
|
||||||
|
*/
|
||||||
|
byte @NotNull [] serializeEntity(@NotNull Entity entity, @NotNull EntitySerializationFlag... serializationFlags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the entity from data.
|
||||||
|
* <br>The entity's {@link java.util.UUID} as well as passengers will not be preserved.
|
||||||
|
*
|
||||||
|
* @param data serialized entity data
|
||||||
|
* @param world world
|
||||||
|
* @return deserialized entity
|
||||||
|
* @throws IllegalArgumentException if invalid serialized entity data provided
|
||||||
|
* @see #deserializeEntity(byte[], World, boolean, boolean)
|
||||||
|
* @see #serializeEntity(Entity, EntitySerializationFlag...)
|
||||||
|
* @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason)
|
||||||
|
* @since 1.17.1
|
||||||
|
*/
|
||||||
|
default @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world) {
|
||||||
return deserializeEntity(data, world, false);
|
return deserializeEntity(data, world, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
org.bukkit.entity.Entity deserializeEntity(byte[] data, World world, boolean preserveUUID);
|
/**
|
||||||
|
* Deserializes the entity from data.
|
||||||
|
* <br>The entity's passengers will not be preserved.
|
||||||
|
*
|
||||||
|
* @param data serialized entity data
|
||||||
|
* @param world world
|
||||||
|
* @param preserveUUID whether to preserve the entity's uuid
|
||||||
|
* @return deserialized entity
|
||||||
|
* @throws IllegalArgumentException if invalid serialized entity data provided
|
||||||
|
* @see #deserializeEntity(byte[], World, boolean, boolean)
|
||||||
|
* @see #serializeEntity(Entity, EntitySerializationFlag...)
|
||||||
|
* @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason)
|
||||||
|
* @since 1.17.1
|
||||||
|
*/
|
||||||
|
default @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world, boolean preserveUUID) {
|
||||||
|
return deserializeEntity(data, world, preserveUUID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the entity from data.
|
||||||
|
*
|
||||||
|
* @param data serialized entity data
|
||||||
|
* @param world world
|
||||||
|
* @param preserveUUID whether to preserve uuids of the entity and its passengers
|
||||||
|
* @param preservePassengers whether to preserve passengers
|
||||||
|
* @return deserialized entity
|
||||||
|
* @throws IllegalArgumentException if invalid serialized entity data provided
|
||||||
|
* @see #serializeEntity(Entity, EntitySerializationFlag...)
|
||||||
|
* @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason)
|
||||||
|
* @since 1.21.4
|
||||||
|
*/
|
||||||
|
@NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world, boolean preserveUUID, boolean preservePassengers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns the next EntityId available.
|
* Creates and returns the next EntityId available.
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.bukkit.World;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.PistonMoveReaction;
|
import org.bukkit.block.PistonMoveReaction;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||||
import org.bukkit.material.Directional;
|
import org.bukkit.material.Directional;
|
||||||
|
@ -1051,11 +1052,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
||||||
* <p>
|
* <p>
|
||||||
* Also, this method will fire the same events as a normal entity spawn.
|
* Also, this method will fire the same events as a normal entity spawn.
|
||||||
*
|
*
|
||||||
* @param location The location to spawn the entity at.
|
* @param location the location to spawn the entity at
|
||||||
* @return Whether the entity was successfully spawned.
|
* @return whether the entity was successfully spawned
|
||||||
|
* @since 1.17.1
|
||||||
*/
|
*/
|
||||||
public default boolean spawnAt(@NotNull Location location) {
|
default boolean spawnAt(@NotNull Location location) {
|
||||||
return spawnAt(location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
return spawnAt(location, CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1065,11 +1067,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
||||||
* <p>
|
* <p>
|
||||||
* Also, this method will fire the same events as a normal entity spawn.
|
* Also, this method will fire the same events as a normal entity spawn.
|
||||||
*
|
*
|
||||||
* @param location The location to spawn the entity at.
|
* @param location the location to spawn the entity at
|
||||||
* @param reason The reason for the entity being spawned.
|
* @param reason the reason for the entity being spawned
|
||||||
* @return Whether the entity was successfully spawned.
|
* @return whether the entity was successfully spawned
|
||||||
|
* @since 1.17.1
|
||||||
*/
|
*/
|
||||||
public boolean spawnAt(@NotNull Location location, @NotNull org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason);
|
boolean spawnAt(@NotNull Location location, @NotNull CreatureSpawnEvent.SpawnReason reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if entity is inside powdered snow.
|
* Check if entity is inside powdered snow.
|
||||||
|
|
|
@ -706,52 +706,44 @@
|
||||||
|
|
||||||
public void awardKillScore(Entity entity, DamageSource damageSource) {
|
public void awardKillScore(Entity entity, DamageSource damageSource) {
|
||||||
if (entity instanceof ServerPlayer) {
|
if (entity instanceof ServerPlayer) {
|
||||||
@@ -1752,34 +_,70 @@
|
@@ -1752,15 +_,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean saveAsPassenger(CompoundTag compound) {
|
public boolean saveAsPassenger(CompoundTag compound) {
|
||||||
|
- if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
||||||
+ // CraftBukkit start - allow excluding certain data when saving
|
+ // CraftBukkit start - allow excluding certain data when saving
|
||||||
+ return this.saveAsPassenger(compound, true);
|
+ // Paper start - Raw entity serialization API
|
||||||
|
+ return this.saveAsPassenger(compound, true, false, false);
|
||||||
+ }
|
+ }
|
||||||
+
|
+ public boolean saveAsPassenger(CompoundTag compound, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) {
|
||||||
+ public boolean saveAsPassenger(CompoundTag compound, boolean includeAll) {
|
+ // Paper end - Raw entity serialization API
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
+ if (this.removalReason != null && !this.removalReason.shouldSave() && !forceSerialization) { // Paper - Raw entity serialization API
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
String encodeId = this.getEncodeId();
|
- String encodeId = this.getEncodeId();
|
||||||
- if (encodeId == null) {
|
- if (encodeId == null) {
|
||||||
+ if (!this.persist || encodeId == null) { // CraftBukkit - persist flag
|
+ String encodeId = this.getEncodeId(includeNonSaveable); // Paper - Raw entity serialization API
|
||||||
|
+ if ((!this.persist && !forceSerialization) || encodeId == null) { // CraftBukkit - persist flag // Paper - Raw entity serialization API
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
compound.putString("id", encodeId);
|
compound.putString("id", encodeId);
|
||||||
- this.saveWithoutId(compound);
|
- this.saveWithoutId(compound);
|
||||||
+ this.saveWithoutId(compound, includeAll); // CraftBukkit - pass on includeAll
|
+ this.saveWithoutId(compound, includeAll, includeNonSaveable, forceSerialization); // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
@@ -1771,15 +_,37 @@
|
||||||
+
|
|
||||||
+ // Paper start - Entity serialization api
|
|
||||||
+ public boolean serializeEntity(CompoundTag compound) {
|
|
||||||
+ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers());
|
|
||||||
+ this.passengers = ImmutableList.of();
|
|
||||||
+ boolean result = save(compound);
|
|
||||||
+ this.passengers = ImmutableList.copyOf(pass);
|
|
||||||
+ return result;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - Entity serialization api
|
|
||||||
|
|
||||||
public boolean save(CompoundTag compound) {
|
|
||||||
return !this.isPassenger() && this.saveAsPassenger(compound);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundTag saveWithoutId(CompoundTag compound) {
|
public CompoundTag saveWithoutId(CompoundTag compound) {
|
||||||
+ // CraftBukkit start - allow excluding certain data when saving
|
+ // CraftBukkit start - allow excluding certain data when saving
|
||||||
+ return this.saveWithoutId(compound, true);
|
+ // Paper start - Raw entity serialization API
|
||||||
|
+ return this.saveWithoutId(compound, true, false, false);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ public CompoundTag saveWithoutId(CompoundTag compound, boolean includeAll) {
|
+ public CompoundTag saveWithoutId(CompoundTag compound, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) {
|
||||||
|
+ // Paper end - Raw entity serialization API
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
try {
|
try {
|
||||||
- if (this.vehicle != null) {
|
- if (this.vehicle != null) {
|
||||||
|
@ -827,7 +819,7 @@
|
||||||
for (Entity entity : this.getPassengers()) {
|
for (Entity entity : this.getPassengers()) {
|
||||||
CompoundTag compoundTag = new CompoundTag();
|
CompoundTag compoundTag = new CompoundTag();
|
||||||
- if (entity.saveAsPassenger(compoundTag)) {
|
- if (entity.saveAsPassenger(compoundTag)) {
|
||||||
+ if (entity.saveAsPassenger(compoundTag, includeAll)) { // CraftBukkit - pass on includeAll
|
+ if (entity.saveAsPassenger(compoundTag, includeAll, includeNonSaveable, forceSerialization)) { // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API
|
||||||
listTag.add(compoundTag);
|
listTag.add(compoundTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -935,19 +927,30 @@
|
||||||
} catch (Throwable var17) {
|
} catch (Throwable var17) {
|
||||||
CrashReport crashReport = CrashReport.forThrowable(var17, "Loading entity NBT");
|
CrashReport crashReport = CrashReport.forThrowable(var17, "Loading entity NBT");
|
||||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded");
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded");
|
||||||
@@ -1949,6 +_,12 @@
|
@@ -1944,10 +_,21 @@
|
||||||
return type.canSerialize() && key != null ? key.toString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public final String getEncodeId() {
|
||||||
|
+ // Paper start - Raw entity serialization API
|
||||||
|
+ return getEncodeId(false);
|
||||||
|
+ }
|
||||||
|
+ public final @Nullable String getEncodeId(boolean includeNonSaveable) {
|
||||||
|
+ // Paper end - Raw entity serialization API
|
||||||
|
EntityType<?> type = this.getType();
|
||||||
|
ResourceLocation key = EntityType.getKey(type);
|
||||||
|
- return type.canSerialize() && key != null ? key.toString() : null;
|
||||||
|
- }
|
||||||
|
+ return (type.canSerialize() || includeNonSaveable) && key != null ? key.toString() : null; // Paper - Raw entity serialization API
|
||||||
|
+ }
|
||||||
|
+
|
||||||
+ // CraftBukkit start - allow excluding certain data when saving
|
+ // CraftBukkit start - allow excluding certain data when saving
|
||||||
+ protected void addAdditionalSaveData(CompoundTag tag, boolean includeAll) {
|
+ protected void addAdditionalSaveData(CompoundTag tag, boolean includeAll) {
|
||||||
+ this.addAdditionalSaveData(tag);
|
+ this.addAdditionalSaveData(tag);
|
||||||
+ }
|
+ }
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
+
|
|
||||||
protected abstract void readAdditionalSaveData(CompoundTag tag);
|
protected abstract void readAdditionalSaveData(CompoundTag tag);
|
||||||
|
|
||||||
protected abstract void addAdditionalSaveData(CompoundTag tag);
|
|
||||||
@@ -1990,11 +_,61 @@
|
@@ -1990,11 +_,61 @@
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Pose;
|
import org.bukkit.entity.Pose;
|
||||||
import org.bukkit.entity.SpawnCategory;
|
import org.bukkit.entity.SpawnCategory;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityRemoveEvent;
|
import org.bukkit.event.entity.EntityRemoveEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||||
|
@ -935,7 +936,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||||
@Override
|
@Override
|
||||||
public String getAsString() {
|
public String getAsString() {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
if (!this.getHandle().saveAsPassenger(tag, false)) {
|
if (!this.getHandle().saveAsPassenger(tag, false, true, true)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -968,7 +969,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||||
|
|
||||||
private Entity copy(net.minecraft.world.level.Level level) {
|
private Entity copy(net.minecraft.world.level.Level level) {
|
||||||
CompoundTag compoundTag = new CompoundTag();
|
CompoundTag compoundTag = new CompoundTag();
|
||||||
this.getHandle().saveAsPassenger(compoundTag, false);
|
this.getHandle().saveAsPassenger(compoundTag, false, true, true);
|
||||||
|
|
||||||
return net.minecraft.world.entity.EntityType.loadEntityRecursive(compoundTag, level, EntitySpawnReason.LOAD, java.util.function.Function.identity());
|
return net.minecraft.world.entity.EntityType.loadEntityRecursive(compoundTag, level, EntitySpawnReason.LOAD, java.util.function.Function.identity());
|
||||||
}
|
}
|
||||||
|
@ -1204,17 +1205,21 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||||
}
|
}
|
||||||
// Paper end - tracked players API
|
// Paper end - tracked players API
|
||||||
|
|
||||||
// Paper start - raw entity serialization API
|
|
||||||
@Override
|
@Override
|
||||||
public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
public boolean spawnAt(Location location, CreatureSpawnEvent.SpawnReason reason) {
|
||||||
Preconditions.checkNotNull(location, "location cannot be null");
|
Preconditions.checkNotNull(location, "location cannot be null");
|
||||||
Preconditions.checkNotNull(reason, "reason cannot be null");
|
Preconditions.checkNotNull(reason, "reason cannot be null");
|
||||||
this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle());
|
this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle());
|
||||||
this.entity.setPos(location.getX(), location.getY(), location.getZ());
|
this.entity.setPos(location.getX(), location.getY(), location.getZ());
|
||||||
this.entity.setRot(location.getYaw(), location.getPitch());
|
this.entity.setRot(location.getYaw(), location.getPitch());
|
||||||
return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason);
|
boolean spawned = !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason);
|
||||||
|
if (spawned) {
|
||||||
|
for (org.bukkit.entity.Entity pass : getPassengers()) {
|
||||||
|
pass.spawnAt(getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spawned;
|
||||||
}
|
}
|
||||||
// Paper end - raw entity serialization API
|
|
||||||
|
|
||||||
// Paper start - entity powdered snow API
|
// Paper start - entity powdered snow API
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class CraftEntitySnapshot implements EntitySnapshot {
|
||||||
|
|
||||||
public static CraftEntitySnapshot create(CraftEntity entity) {
|
public static CraftEntitySnapshot create(CraftEntity entity) {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
if (!entity.getHandle().saveAsPassenger(tag, false)) {
|
if (!entity.getHandle().saveAsPassenger(tag, false, true, true)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -558,29 +558,74 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
|
public byte[] serializeEntity(org.bukkit.entity.Entity entity, io.papermc.paper.entity.EntitySerializationFlag... serializationFlags) {
|
||||||
Preconditions.checkNotNull(entity, "null cannot be serialized");
|
Preconditions.checkNotNull(entity, "null cannot be serialized");
|
||||||
Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized");
|
Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "Only CraftEntities can be serialized");
|
||||||
|
|
||||||
|
java.util.Set<io.papermc.paper.entity.EntitySerializationFlag> flags = java.util.Set.of(serializationFlags);
|
||||||
|
boolean forceSerialization = flags.contains(io.papermc.paper.entity.EntitySerializationFlag.FORCE);
|
||||||
|
Preconditions.checkArgument((entity.isValid() && entity.isPersistent()) || forceSerialization, "Cannot serialize invalid or non-persistent entity without the FORCE flag");
|
||||||
|
|
||||||
|
boolean includeNonSaveable;
|
||||||
|
net.minecraft.world.entity.Entity nmsEntity = ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle();
|
||||||
|
if (entity instanceof org.bukkit.entity.Player) {
|
||||||
|
includeNonSaveable = flags.contains(io.papermc.paper.entity.EntitySerializationFlag.PLAYER);
|
||||||
|
Preconditions.checkArgument(includeNonSaveable, "Cannot serialize Players without the PLAYER flag");
|
||||||
|
} else {
|
||||||
|
includeNonSaveable = flags.contains(io.papermc.paper.entity.EntitySerializationFlag.MISC);
|
||||||
|
Preconditions.checkArgument(nmsEntity.getType().canSerialize() || includeNonSaveable, String.format("Cannot serialize misc non-saveable entity (%s) without the MISC flag", entity.getType().name()));
|
||||||
|
}
|
||||||
|
|
||||||
net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag();
|
net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag();
|
||||||
((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
|
if (flags.contains(io.papermc.paper.entity.EntitySerializationFlag.PASSENGERS)) {
|
||||||
|
if (!nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization)) {
|
||||||
|
throw new IllegalArgumentException("Couldn't serialize entity");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
java.util.List<net.minecraft.world.entity.Entity> pass = new java.util.ArrayList<>(nmsEntity.getPassengers());
|
||||||
|
nmsEntity.passengers = com.google.common.collect.ImmutableList.of();
|
||||||
|
boolean serialized = nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization);
|
||||||
|
nmsEntity.passengers = com.google.common.collect.ImmutableList.copyOf(pass);
|
||||||
|
if (!serialized) {
|
||||||
|
throw new IllegalArgumentException("Couldn't serialize entity");
|
||||||
|
}
|
||||||
|
}
|
||||||
return serializeNbtToBytes(compound);
|
return serializeNbtToBytes(compound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) {
|
public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID, boolean preservePassengers) {
|
||||||
Preconditions.checkNotNull(data, "null cannot be deserialized");
|
Preconditions.checkNotNull(data, "null cannot be deserialized");
|
||||||
Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
|
Preconditions.checkArgument(data.length > 0, "Cannot deserialize empty data");
|
||||||
|
|
||||||
net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data);
|
net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data);
|
||||||
int dataVersion = compound.getInt("DataVersion");
|
int dataVersion = compound.getInt("DataVersion");
|
||||||
compound = ca.spottedleaf.moonrise.common.PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
|
compound = ca.spottedleaf.moonrise.common.PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
|
||||||
|
if (!preservePassengers) {
|
||||||
|
compound.remove("Passengers");
|
||||||
|
}
|
||||||
|
net.minecraft.world.entity.Entity nmsEntity = deserializeEntity(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), preserveUUID);
|
||||||
|
return nmsEntity.getBukkitEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private net.minecraft.world.entity.Entity deserializeEntity(net.minecraft.nbt.CompoundTag compound, net.minecraft.server.level.ServerLevel world, boolean preserveUUID) {
|
||||||
if (!preserveUUID) {
|
if (!preserveUUID) {
|
||||||
// Generate a new UUID so we don't have to worry about deserializing the same entity twice
|
// Generate a new UUID, so we don't have to worry about deserializing the same entity twice
|
||||||
compound.remove("UUID");
|
compound.remove("UUID");
|
||||||
}
|
}
|
||||||
return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), net.minecraft.world.entity.EntitySpawnReason.LOAD)
|
net.minecraft.world.entity.Entity nmsEntity = net.minecraft.world.entity.EntityType.create(compound, world, net.minecraft.world.entity.EntitySpawnReason.LOAD)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
|
.orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?"));
|
||||||
|
if (compound.contains("Passengers", Tag.TAG_LIST)) {
|
||||||
|
net.minecraft.nbt.ListTag passengersCompound = compound.getList("Passengers", Tag.TAG_COMPOUND);
|
||||||
|
for (Tag tag : passengersCompound) {
|
||||||
|
if (!(tag instanceof net.minecraft.nbt.CompoundTag serializedPassenger)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
net.minecraft.world.entity.Entity passengerEntity = deserializeEntity(serializedPassenger, world, preserveUUID);
|
||||||
|
passengerEntity.startRiding(nmsEntity, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nmsEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) {
|
private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) {
|
||||||
|
|
Loading…
Reference in a new issue