mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 23:10:16 +01:00
Expand on entity serialization API (#11807)
This commit is contained in:
parent
0efd3012c9
commit
aac246ae29
8 changed files with 275 additions and 77 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;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import io.papermc.paper.entity.EntitySerializationFlag;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeModifier;
|
||||
|
@ -9,7 +10,9 @@ import org.bukkit.block.data.BlockData;
|
|||
import org.bukkit.damage.DamageEffect;
|
||||
import org.bukkit.damage.DamageSource;
|
||||
import org.bukkit.damage.DamageType;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.inventory.CreativeCategory;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
@ -198,13 +201,81 @@ public interface UnsafeValues {
|
|||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.bukkit.World;
|
|||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.PistonMoveReaction;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.material.Directional;
|
||||
|
@ -1072,11 +1073,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
|||
* <p>
|
||||
* Also, this method will fire the same events as a normal entity spawn.
|
||||
*
|
||||
* @param location The location to spawn the entity at.
|
||||
* @return Whether the entity was successfully spawned.
|
||||
* @param location the location to spawn the entity at
|
||||
* @return whether the entity was successfully spawned
|
||||
* @since 1.17.1
|
||||
*/
|
||||
public default boolean spawnAt(@NotNull Location location) {
|
||||
return spawnAt(location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||
default boolean spawnAt(@NotNull Location location) {
|
||||
return spawnAt(location, CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1086,11 +1088,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
|
|||
* <p>
|
||||
* Also, this method will fire the same events as a normal entity spawn.
|
||||
*
|
||||
* @param location The location to spawn the entity at.
|
||||
* @param reason The reason for the entity being spawned.
|
||||
* @return Whether the entity was successfully spawned.
|
||||
* @param location the location to spawn the entity at
|
||||
* @param reason the reason for the entity being 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.
|
||||
|
|
|
@ -26719,7 +26719,7 @@ index 2f49dbc919f7f5eea9abce6106723c72f5ae45fb..87d4291a3944f706a694536da6de0f28
|
|||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||
index 70f6d068b3f3665b282d9750310c883839120ab2..870b9efd445ddadb3725e88351555ad986ce7c72 100644
|
||||
index da793ad12565c36fffb26eb771ff68c76632caf7..db06f966077928419bfe469260f04d7dfda69f28 100644
|
||||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -91,6 +91,11 @@ public class ServerEntity {
|
||||
|
@ -27496,7 +27496,7 @@ index 192977dd661ee795ada13db895db770293e9b402..95a4e37a3c93f9b3c56c7a7376ed521c
|
|||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index ff5889f8fed0707a6654d9d21862e32e2ebc866d..e61fe83479f095e8addbd3e8f1d5179c998ae1eb 100644
|
||||
index 097ec55166b9e9269142be58992c29687122fe28..aeabb79512aabd7a9e8af1be72e1745f0e7eefe4 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -178,7 +178,7 @@ import net.minecraft.world.scores.Team;
|
||||
|
@ -28372,7 +28372,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
|
|||
+ // Paper end - block counting
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab6814aef9ee 100644
|
||||
index 189385600b9094291152035b17df869eaccc0428..25a1089a7376f0cbd96bb43b5c203640c88fc282 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -135,7 +135,7 @@ import net.minecraft.world.scores.ScoreHolder;
|
||||
|
@ -28725,7 +28725,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
}
|
||||
|
||||
private static float[] collectCandidateStepUpHeights(AABB box, List<VoxelShape> colliders, float deltaY, float maxUpStep) {
|
||||
@@ -2664,23 +2812,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -2662,23 +2810,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public boolean isInWall() {
|
||||
|
@ -28849,7 +28849,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
}
|
||||
|
||||
public InteractionResult interact(Player player, InteractionHand hand) {
|
||||
@@ -4104,15 +4339,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4102,15 +4337,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
public Iterable<Entity> getIndirectPassengers() {
|
||||
|
@ -28875,7 +28875,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
}
|
||||
|
||||
public int countPlayerPassengers() {
|
||||
@@ -4250,77 +4487,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4248,77 +4485,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return Mth.lerp(partialTick, this.yRotO, this.yRot);
|
||||
}
|
||||
|
||||
|
@ -29066,7 +29066,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
|
||||
public boolean touchingUnloadedChunk() {
|
||||
AABB aabb = this.getBoundingBox().inflate(1.0);
|
||||
@@ -4473,6 +4769,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4471,6 +4767,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.setPosRaw(x, y, z, false);
|
||||
}
|
||||
public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
|
||||
|
@ -29082,7 +29082,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
if (!checkPosition(this, x, y, z)) {
|
||||
return;
|
||||
}
|
||||
@@ -4603,6 +4908,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4601,6 +4906,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
@Override
|
||||
public final void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.Cause cause) {
|
||||
|
@ -29095,7 +29095,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause);
|
||||
// CraftBukkit end
|
||||
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
||||
@@ -4614,7 +4925,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4612,7 +4923,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stopRiding();
|
||||
}
|
||||
|
||||
|
@ -29104,7 +29104,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68
|
|||
this.levelCallback.onRemove(removalReason);
|
||||
this.onRemoval(removalReason);
|
||||
// Paper start - Folia schedulers
|
||||
@@ -4648,7 +4959,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -4646,7 +4957,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public boolean shouldBeSaved() {
|
||||
return (this.removalReason == null || this.removalReason.shouldSave())
|
||||
&& !this.isPassenger()
|
||||
|
|
|
@ -706,52 +706,44 @@
|
|||
|
||||
public void awardKillScore(Entity entity, DamageSource damageSource) {
|
||||
if (entity instanceof ServerPlayer) {
|
||||
@@ -1752,34 +_,70 @@
|
||||
@@ -1752,15 +_,22 @@
|
||||
}
|
||||
|
||||
public boolean saveAsPassenger(CompoundTag compound) {
|
||||
- if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
||||
+ // 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) {
|
||||
+ public boolean saveAsPassenger(CompoundTag compound, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) {
|
||||
+ // Paper end - Raw entity serialization API
|
||||
+ // CraftBukkit end
|
||||
if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
||||
+ if (this.removalReason != null && !this.removalReason.shouldSave() && !forceSerialization) { // Paper - Raw entity serialization API
|
||||
return false;
|
||||
} else {
|
||||
String encodeId = this.getEncodeId();
|
||||
- String encodeId = this.getEncodeId();
|
||||
- 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;
|
||||
} else {
|
||||
compound.putString("id", encodeId);
|
||||
- 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // 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);
|
||||
@@ -1771,15 +_,37 @@
|
||||
}
|
||||
|
||||
public CompoundTag saveWithoutId(CompoundTag compound) {
|
||||
+ // 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
|
||||
try {
|
||||
- if (this.vehicle != null) {
|
||||
|
@ -827,7 +819,7 @@
|
|||
for (Entity entity : this.getPassengers()) {
|
||||
CompoundTag compoundTag = new 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);
|
||||
}
|
||||
}
|
||||
|
@ -935,19 +927,30 @@
|
|||
} catch (Throwable var17) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var17, "Loading entity NBT");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded");
|
||||
@@ -1949,6 +_,12 @@
|
||||
return type.canSerialize() && key != null ? key.toString() : null;
|
||||
}
|
||||
@@ -1944,10 +_,21 @@
|
||||
|
||||
@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
|
||||
+ protected void addAdditionalSaveData(CompoundTag tag, boolean includeAll) {
|
||||
+ this.addAdditionalSaveData(tag);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
|
||||
protected abstract void readAdditionalSaveData(CompoundTag tag);
|
||||
|
||||
protected abstract void addAdditionalSaveData(CompoundTag tag);
|
||||
@@ -1990,11 +_,61 @@
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.bukkit.entity.EntityType;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.bukkit.entity.SpawnCategory;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityRemoveEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
@ -955,7 +956,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|||
@Override
|
||||
public String getAsString() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if (!this.getHandle().saveAsPassenger(tag, false)) {
|
||||
if (!this.getHandle().saveAsPassenger(tag, false, false, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -988,7 +989,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|||
|
||||
private Entity copy(net.minecraft.world.level.Level level) {
|
||||
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());
|
||||
}
|
||||
|
@ -1224,17 +1225,19 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|||
}
|
||||
// Paper end - tracked players API
|
||||
|
||||
// Paper start - raw entity serialization API
|
||||
@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(reason, "reason cannot be null");
|
||||
this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle());
|
||||
this.entity.setPos(location.getX(), location.getY(), location.getZ());
|
||||
this.entity.setRot(location.getYaw(), location.getPitch());
|
||||
return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason);
|
||||
final boolean spawned = !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason);
|
||||
if (!spawned) return false; // Do not attempt to spawn rest if root was not spawned in
|
||||
// Like net.minecraft.world.level.ServerLevelAccessor.addFreshEntityWithPassengers(net.minecraft.world.entity.Entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason)
|
||||
this.entity.getIndirectPassengers().forEach(e -> e.level().addFreshEntity(e, reason));
|
||||
return true;
|
||||
}
|
||||
// Paper end - raw entity serialization API
|
||||
|
||||
// Paper start - entity powdered snow API
|
||||
@Override
|
||||
|
|
|
@ -66,7 +66,7 @@ public class CraftEntitySnapshot implements EntitySnapshot {
|
|||
|
||||
public static CraftEntitySnapshot create(CraftEntity entity) {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if (!entity.getHandle().saveAsPassenger(tag, false)) {
|
||||
if (!entity.getHandle().saveAsPassenger(tag, false, false, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.bukkit.craftbukkit.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -13,24 +14,34 @@ import com.mojang.serialization.Dynamic;
|
|||
import com.mojang.serialization.JsonOps;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import io.papermc.paper.entity.EntitySerializationFlag;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.item.ItemParser;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.datafix.DataFixers;
|
||||
import net.minecraft.util.datafix.fixes.References;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.alchemy.Potion;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
@ -43,6 +54,7 @@ import org.bukkit.Material;
|
|||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.UnsafeValues;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeModifier;
|
||||
|
@ -51,10 +63,12 @@ import org.bukkit.block.data.BlockData;
|
|||
// import org.bukkit.craftbukkit.CraftFeatureFlag; // Paper
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.block.CraftBiome;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
|
||||
import org.bukkit.craftbukkit.damage.CraftDamageSourceBuilder;
|
||||
import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.legacy.CraftLegacy;
|
||||
import org.bukkit.craftbukkit.legacy.FieldRename;
|
||||
|
@ -513,7 +527,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|||
Preconditions.checkNotNull(item, "null cannot be serialized");
|
||||
Preconditions.checkArgument(item.getType() != Material.AIR, "air cannot be serialized");
|
||||
|
||||
return serializeNbtToBytes((net.minecraft.nbt.CompoundTag) (item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(MinecraftServer.getServer().registryAccess()));
|
||||
return serializeNbtToBytes((CompoundTag) (item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(MinecraftServer.getServer().registryAccess()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -521,9 +535,9 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|||
Preconditions.checkNotNull(data, "null cannot be deserialized");
|
||||
Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
|
||||
|
||||
net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data);
|
||||
CompoundTag compound = deserializeNbtFromBytes(data);
|
||||
final int dataVersion = compound.getInt("DataVersion");
|
||||
compound = ca.spottedleaf.moonrise.common.PlatformHooks.get().convertNBT(References.ITEM_STACK, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
|
||||
compound = PlatformHooks.get().convertNBT(References.ITEM_STACK, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
|
||||
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow());
|
||||
}
|
||||
|
||||
|
@ -558,32 +572,98 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
|
||||
public byte[] serializeEntity(org.bukkit.entity.Entity entity, EntitySerializationFlag... serializationFlags) {
|
||||
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 CraftEntity, "Only CraftEntities can be serialized");
|
||||
|
||||
net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag();
|
||||
((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
|
||||
Set<EntitySerializationFlag> flags = Set.of(serializationFlags);
|
||||
final boolean serializePassangers = flags.contains(EntitySerializationFlag.PASSENGERS);
|
||||
final boolean forceSerialization = flags.contains(EntitySerializationFlag.FORCE);
|
||||
final boolean allowPlayerSerialization = flags.contains(EntitySerializationFlag.PLAYER);
|
||||
final boolean allowMiscSerialization = flags.contains(EntitySerializationFlag.MISC);
|
||||
final boolean includeNonSaveable = allowPlayerSerialization || allowMiscSerialization;
|
||||
|
||||
net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
|
||||
(serializePassangers ? nmsEntity.getSelfAndPassengers() : Stream.of(nmsEntity)).forEach(e -> {
|
||||
// Ensure force flag is not needed
|
||||
Preconditions.checkArgument(
|
||||
(e.getBukkitEntity().isValid() && e.getBukkitEntity().isPersistent()) || forceSerialization,
|
||||
"Cannot serialize invalid or non-persistent entity %s(%s) without the FORCE flag",
|
||||
e.getType().toShortString(),
|
||||
e.getStringUUID()
|
||||
);
|
||||
|
||||
if (e instanceof Player) {
|
||||
// Ensure player flag is not needed
|
||||
Preconditions.checkArgument(
|
||||
allowPlayerSerialization,
|
||||
"Cannot serialize player(%s) without the PLAYER flag",
|
||||
e.getStringUUID()
|
||||
);
|
||||
} else {
|
||||
// Ensure player flag is not needed
|
||||
Preconditions.checkArgument(
|
||||
nmsEntity.getType().canSerialize() || allowMiscSerialization,
|
||||
"Cannot serialize misc non-saveable entity %s(%s) without the MISC flag",
|
||||
e.getType().toShortString(),
|
||||
e.getStringUUID()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
CompoundTag compound = new CompoundTag();
|
||||
if (serializePassangers) {
|
||||
if (!nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization)) {
|
||||
throw new IllegalArgumentException("Couldn't serialize entity");
|
||||
}
|
||||
} else {
|
||||
List<net.minecraft.world.entity.Entity> pass = new 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) {
|
||||
public org.bukkit.entity.Entity deserializeEntity(byte[] data, World world, boolean preserveUUID, boolean preservePassengers) {
|
||||
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);
|
||||
CompoundTag compound = deserializeNbtFromBytes(data);
|
||||
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
|
||||
if (!preserveUUID) {
|
||||
// Generate a new UUID so we don't have to worry about deserializing the same entity twice
|
||||
compound.remove("UUID");
|
||||
compound = PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
|
||||
if (!preservePassengers) {
|
||||
compound.remove("Passengers");
|
||||
}
|
||||
return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), net.minecraft.world.entity.EntitySpawnReason.LOAD)
|
||||
.orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
|
||||
net.minecraft.world.entity.Entity nmsEntity = deserializeEntity(compound, ((CraftWorld) world).getHandle(), preserveUUID);
|
||||
return nmsEntity.getBukkitEntity();
|
||||
}
|
||||
|
||||
private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) {
|
||||
private net.minecraft.world.entity.Entity deserializeEntity(CompoundTag compound, ServerLevel world, boolean preserveUUID) {
|
||||
if (!preserveUUID) {
|
||||
// Generate a new UUID, so we don't have to worry about deserializing the same entity twice
|
||||
compound.remove("UUID");
|
||||
}
|
||||
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?"));
|
||||
if (compound.contains("Passengers", Tag.TAG_LIST)) {
|
||||
ListTag passengersCompound = compound.getList("Passengers", Tag.TAG_COMPOUND);
|
||||
for (Tag tag : passengersCompound) {
|
||||
if (!(tag instanceof CompoundTag serializedPassenger)) {
|
||||
continue;
|
||||
}
|
||||
net.minecraft.world.entity.Entity passengerEntity = deserializeEntity(serializedPassenger, world, preserveUUID);
|
||||
passengerEntity.startRiding(nmsEntity, true);
|
||||
}
|
||||
}
|
||||
return nmsEntity;
|
||||
}
|
||||
|
||||
private byte[] serializeNbtToBytes(CompoundTag compound) {
|
||||
compound.putInt("DataVersion", getDataVersion());
|
||||
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
|
||||
try {
|
||||
|
@ -597,8 +677,8 @@ public final class CraftMagicNumbers implements UnsafeValues {
|
|||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
private net.minecraft.nbt.CompoundTag deserializeNbtFromBytes(byte[] data) {
|
||||
net.minecraft.nbt.CompoundTag compound;
|
||||
private CompoundTag deserializeNbtFromBytes(byte[] data) {
|
||||
CompoundTag compound;
|
||||
try {
|
||||
compound = net.minecraft.nbt.NbtIo.readCompressed(
|
||||
new java.io.ByteArrayInputStream(data), net.minecraft.nbt.NbtAccounter.unlimitedHeap()
|
||||
|
|
Loading…
Reference in a new issue