aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1050-Properly-resend-entities.patch
diff options
context:
space:
mode:
authorNassim Jahnke <[email protected]>2024-12-05 11:18:29 +0100
committerNassim Jahnke <[email protected]>2024-12-05 12:20:56 +0100
commite4e24f3335609b38f460ced71d18babcf11bf9cb (patch)
tree51880d664b3444ce26d6f8cdeb3b8219e5616fca /patches/server/1050-Properly-resend-entities.patch
parentc54c062e6ff742445bf7749c84106ca67090172d (diff)
downloadPaper-e4e24f3335609b38f460ced71d18babcf11bf9cb.tar.gz
Paper-e4e24f3335609b38f460ced71d18babcf11bf9cb.zip
Move around patches again
Diffstat (limited to 'patches/server/1050-Properly-resend-entities.patch')
-rw-r--r--patches/server/1050-Properly-resend-entities.patch318
1 files changed, 318 insertions, 0 deletions
diff --git a/patches/server/1050-Properly-resend-entities.patch b/patches/server/1050-Properly-resend-entities.patch
new file mode 100644
index 0000000000..d9186c61a6
--- /dev/null
+++ b/patches/server/1050-Properly-resend-entities.patch
@@ -0,0 +1,318 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Owen1212055 <[email protected]>
+Date: Wed, 7 Dec 2022 17:25:19 -0500
+Subject: [PATCH] Properly resend entities
+
+This resolves some issues which caused entities to not be resent correctly.
+Entities that are interacted with need to be resent to the client, so we resend all the entity
+data to the player whilst making sure not to clear dirty entries from the tracker. This makes
+sure that values will be correctly updated to other players.
+
+This also adds utilities to aid in further preventing entity desyncs.
+
+This also also fixes the bug causing cancelling PlayerInteractEvent to cause items to continue
+to be used despite being cancelled on the server.
+
+For example, items being consumed but never finishing, shields being put up, etc.
+The underlying issue of this is that the client modifies their synced data values,
+and so we have to (forcibly) resend them in order for the client to reset their using item state.
+
+See: https://github.com/PaperMC/Paper/pull/1896
+
+== AT ==
+public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity
+
+diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
+index 02bf2705ca1c99023a83a22d92e1962181102297..0f99733660f91280e4c6262cf75b3c9cae86f65a 100644
+--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
++++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
+@@ -50,7 +50,7 @@ public class SynchedEntityData {
+ }
+ }
+
+- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
++ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
+ return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
+ }
+
+@@ -151,6 +151,20 @@ public class SynchedEntityData {
+ }
+ }
+
++ // Paper start
++ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
++ // Because these values can possibly be desynced on the client.
++ @Nullable
++ public List<SynchedEntityData.DataValue<?>> packAll() {
++ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
++ for (final DataItem<?> dataItem : this.itemsById) {
++ list.add(dataItem.value());
++ }
++
++ return list;
++ }
++ // Paper end
++
+ public static class DataItem<T> {
+
+ final EntityDataAccessor<T> accessor;
+diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+index fdba8a7344760c5903f838d0e0d65edef23262bb..504c996220b278c194c93e001a3b326d549868ec 100644
+--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
++++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+@@ -567,6 +567,7 @@ public class ServerPlayerGameMode {
+ }
+ // Paper end - extend Player Interact cancellation
+ player.getBukkitEntity().updateInventory(); // SPIGOT-2867
++ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
+ return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
+ } else if (this.gameModeForPlayer == GameType.SPECTATOR) {
+ MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
+@@ -617,6 +618,11 @@ public class ServerPlayerGameMode {
+
+ return enuminteractionresult;
+ } else {
++ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
++ if (this.interactResult && this.interactResult != cancelledItem) {
++ this.player.resyncUsingItem(this.player);
++ }
++ // Paper end - Properly cancel usable items
+ return InteractionResult.PASS;
+ }
+ }
+diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+index cfad5925f7d6b9970e8abef981b67e805bf1866a..8cae479d7b299ff4215c80fa2bbebf41ca967df6 100644
+--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+@@ -2026,6 +2026,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
+ }
+
+ if (cancelled) {
++ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
+ this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
+ return;
+ }
+@@ -2816,7 +2817,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
+
+ // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
+ if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
+- entity.getBukkitEntity().update(ServerGamePacketListenerImpl.this.player);
++ entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it
+ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
+ }
+
+diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
+index 4efdd8f0ca73b59eb90abfe1fadbcb34d845fcd4..c68040a59fa8aa9b8b9f1e0b4fdded565ea592d9 100644
+--- a/src/main/java/net/minecraft/server/players/PlayerList.java
++++ b/src/main/java/net/minecraft/server/players/PlayerList.java
+@@ -396,7 +396,7 @@ public abstract class PlayerList {
+ ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
+ // CraftBukkit end
+
+- player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
++ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
+
+ this.sendLevelInfo(player, worldserver1);
+
+@@ -913,12 +913,17 @@ public abstract class PlayerList {
+ }
+
+ public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl networkHandler) {
++ // Paper start - collect packets
++ this.sendActiveEffects(entity, networkHandler::send);
++ }
++ public void sendActiveEffects(LivingEntity entity, java.util.function.Consumer<Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packetConsumer) {
++ // Paper end - collect packets
+ Iterator iterator = entity.getActiveEffects().iterator();
+
+ while (iterator.hasNext()) {
+ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
+
+- networkHandler.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false));
++ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false)); // Paper - collect packets
+ }
+
+ }
+diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
+index 0deb12c9b5397695a363b9bb598843ed2b6e396c..7496260370adc6f627288637c53a652d27a6cd84 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -601,13 +601,45 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+
+ // CraftBukkit start
+ public void refreshEntityData(ServerPlayer to) {
+- List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
++ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
+
+- if (list != null) {
++ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
+ to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
+ }
+ }
+ // CraftBukkit end
++ // Paper start
++ // This method should only be used if the data of an entity could have become desynced
++ // due to interactions on the client.
++ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
++ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
++ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
++ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
++ if (tracker == null) {
++ return;
++ }
++ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
++ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
++ serverEntity.sendPairingData(player, list::add);
++ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
++ }
++ }
++
++ // This method allows you to specifically resend certain data accessor keys to the client
++ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
++ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
++ return;
++ }
++
++ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
++ for (final EntityDataAccessor<?> key : keys) {
++ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
++ values.add(synchedValue.value());
++ }
++
++ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values));
++ }
++ // Paper end
+
+ public boolean equals(Object object) {
+ return object instanceof Entity ? ((Entity) object).id == this.id : false;
+diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+index 44b63480923c068889972278adfffd0552d691c2..d54f6f1f8ef4df5233478d6341f3181e8c1baace 100644
+--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+@@ -4075,6 +4075,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ return ((Byte) this.entityData.get(LivingEntity.DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
+ }
+
++ // Paper start - Properly cancel usable items
++ public void resyncUsingItem(ServerPlayer serverPlayer) {
++ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
++ }
++ // Paper end - Properly cancel usable items
+ private void updatingUsingItem() {
+ if (this.isUsingItem()) {
+ if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
+diff --git a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
+index 5a12f4c1de2d020e84af933d491397b38d227824..4eca5996a867086be22d22d99db81ab001467516 100644
+--- a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
++++ b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
+@@ -108,8 +108,7 @@ public interface Bucketable {
+ itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket());
+ if (playerBucketFishEvent.isCancelled()) {
+ ((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
+- entity.getBukkitEntity().update((ServerPlayer) player); // We need to play out these packets as the client assumes the fish is gone
+- entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client
++ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper
+ return Optional.of(InteractionResult.FAIL);
+ }
+ entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
+diff --git a/src/main/java/net/minecraft/world/item/component/Consumable.java b/src/main/java/net/minecraft/world/item/component/Consumable.java
+index fe8618451e3a3f5185704f791723f7897870b6f2..6be5e7b0ce975702ae7c337a06faa59ff3414d64 100644
+--- a/src/main/java/net/minecraft/world/item/component/Consumable.java
++++ b/src/main/java/net/minecraft/world/item/component/Consumable.java
+@@ -97,10 +97,12 @@ public record Consumable(float consumeSeconds, ItemUseAnimation animation, Holde
+
+ // CraftBukkit start
+ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {
++ final java.util.List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packets = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); // Paper - properly resend entities - collect packets for bundle
+ itemstack.getAllOfType(ConsumableListener.class).forEach((consumablelistener) -> {
+- consumablelistener.cancelUsingItem(entityplayer, itemstack);
++ consumablelistener.cancelUsingItem(entityplayer, itemstack, packets); // Paper - properly resend entities - collect packets for bundle
+ });
+- entityplayer.server.getPlayerList().sendActivePlayerEffects(entityplayer);
++ entityplayer.server.getPlayerList().sendActiveEffects(entityplayer, packets::add); // Paper - properly resend entities - collect packets for bundle
++ entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(packets));
+ }
+ // CraftBukkit end
+
+diff --git a/src/main/java/net/minecraft/world/item/component/ConsumableListener.java b/src/main/java/net/minecraft/world/item/component/ConsumableListener.java
+index 03ff8a5aa5083d87d77b68371557cf4e97003114..c66475c7e20b5752dcc9263e44f541f9f2b8e6de 100644
+--- a/src/main/java/net/minecraft/world/item/component/ConsumableListener.java
++++ b/src/main/java/net/minecraft/world/item/component/ConsumableListener.java
+@@ -8,5 +8,5 @@ public interface ConsumableListener {
+
+ void onConsume(Level world, LivingEntity user, ItemStack stack, Consumable consumable);
+
+- default void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {} // CraftBukkit
++ default void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> collectedPackets) {} // CraftBukkit // Paper - properly resend entities - collect packets for bundle
+ }
+diff --git a/src/main/java/net/minecraft/world/item/component/OminousBottleAmplifier.java b/src/main/java/net/minecraft/world/item/component/OminousBottleAmplifier.java
+index 202d3c4741f3f6468a09bfd8e661b9823a332ea7..6b5723b817e48a4c231014f28e45b20754c2c090 100644
+--- a/src/main/java/net/minecraft/world/item/component/OminousBottleAmplifier.java
++++ b/src/main/java/net/minecraft/world/item/component/OminousBottleAmplifier.java
+@@ -28,8 +28,14 @@ public record OminousBottleAmplifier(int value) implements ConsumableListener, T
+
+ @Override
+ public void onConsume(Level world, LivingEntity user, ItemStack stack, Consumable consumable) {
+- user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true));
++ user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below
+ }
++ // Paper start - properly resend entities - collect packets for bundle
++ @Override
++ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> collectedPackets) {
++ collectedPackets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), MobEffects.BAD_OMEN));
++ }
++ // Paper end - properly resend entities - collect packets for bundle
+
+ @Override
+ public void addToTooltip(Item.TooltipContext context, Consumer<Component> tooltip, TooltipFlag type) {
+diff --git a/src/main/java/net/minecraft/world/item/component/SuspiciousStewEffects.java b/src/main/java/net/minecraft/world/item/component/SuspiciousStewEffects.java
+index 04760d8ba7c560bd9d11191c666715ae8c3e4bff..021169d709964b1bb65e49bf3fcf3119f0749448 100644
+--- a/src/main/java/net/minecraft/world/item/component/SuspiciousStewEffects.java
++++ b/src/main/java/net/minecraft/world/item/component/SuspiciousStewEffects.java
+@@ -46,9 +46,9 @@ public record SuspiciousStewEffects(List<SuspiciousStewEffects.Entry> effects) i
+
+ // CraftBukkit start
+ @Override
+- public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {
++ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> collectedPackets) { // Paper - properly resend entities - collect packets for bundle
+ for (SuspiciousStewEffects.Entry suspicioussteweffects_a : this.effects) {
+- entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect()));
++ collectedPackets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect())); // Paper - bundlize packets
+ }
+ }
+ // CraftBukkit end
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+index 7142319f6779078e65369bc406d898473abee85e..b25b10c24a379097233e61bcc10add841b6a7115 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+@@ -1008,7 +1008,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
+ return;
+ }
+
+- entityTracker.broadcast(this.getHandle().getAddEntityPacket(entityTracker.serverEntity));
++ // Paper start - resend possibly desynced entity instead of add entity packet
++ for (final ServerPlayerConnection connection : entityTracker.seenBy) {
++ this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer());
++ }
++ // Paper end - resend possibly desynced entity instead of add entity packet
+ }
+
+ public void update(ServerPlayer player) {
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
+index f3a9b3380246fb2dd4b60a8d1a94c5dfed98d316..350ad61ab3fe66abd528e353b431a4a6dac17506 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
+@@ -39,9 +39,11 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
+ protected void update() {
+ super.update();
+
++ // Paper start, don't mark as dirty as this is handled in super.update()
+ // mark dirty, so that the client gets updated with item and rotation
+- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
+- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
++ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
++ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
++ // Paper end
+
+ // update redstone
+ if (!this.getHandle().generation) {