158 lines
8.0 KiB
Diff
158 lines
8.0 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Cryptite <cryptite@gmail.com>
|
|
Date: Wed, 13 Nov 2024 08:07:59 -0600
|
|
Subject: [PATCH] Equipment Packet Caching
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
|
|
index 830bd76916e26a3a54954d3cf7b7520af52a2258..dc9c7a844c2aee1dae80006eafe085c6fa126ae1 100644
|
|
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
|
|
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
|
|
@@ -30,6 +30,16 @@ public class ClientboundSetEquipmentPacket implements Packet<ClientGamePacketLis
|
|
this.slots = equipmentList;
|
|
}
|
|
|
|
+ public ClientboundSetEquipmentPacket(int id, List<Pair<EquipmentSlot, ItemStack>> equipmentList, net.minecraft.world.entity.LivingEntity entity, boolean sanitize, String tag) {
|
|
+ this.entity = id;
|
|
+ this.sanitize = sanitize;
|
|
+ slots = new java.util.ArrayList<>(equipmentList.size());
|
|
+ for (Pair<EquipmentSlot, ItemStack> pair : equipmentList) {
|
|
+ EquipmentSlot slot = pair.getFirst();
|
|
+ slots.add(Pair.of(slot, entity.getOrCreateCachedEquipmentItem(tag, slot, pair.getSecond())));
|
|
+ }
|
|
+ }
|
|
+
|
|
private ClientboundSetEquipmentPacket(RegistryFriendlyByteBuf buf) {
|
|
this.entity = buf.readVarInt();
|
|
this.slots = Lists.newArrayList();
|
|
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
index 6a3a8f0466998409a01223bc0c16d92b96e50118..096fc42fddbbbe1f7bb5cd260cd2f0d580c6b77a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -298,6 +298,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event
|
|
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
|
|
|
|
+ // Slice start
|
|
+ private static final java.util.Map<String, com.google.common.cache.Cache<Integer, ItemStack>> equipmentPacketCache = new java.util.HashMap<>();
|
|
+ protected final java.util.Map<String, net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket> cachedEquipmentPacket = new java.util.HashMap<>();
|
|
+ // Slice end
|
|
+
|
|
@Override
|
|
public float getBukkitYaw() {
|
|
return this.getYHeadRot();
|
|
@@ -3361,6 +3366,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
if (map != null) {
|
|
this.handleHandSwap(map);
|
|
if (!map.isEmpty()) {
|
|
+ cachedEquipmentPacket.clear(); // Slice - Must invalidate cached equipment map if we have changes
|
|
this.handleEquipmentChanges(map);
|
|
}
|
|
}
|
|
@@ -4781,4 +4787,84 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
public static record Fallsounds(SoundEvent small, SoundEvent big) {
|
|
|
|
}
|
|
+
|
|
+ // Slice start
|
|
+ public static void invalidateCachedEquipment(String tag) {
|
|
+ com.google.common.cache.Cache<Integer, ItemStack> removedCache = equipmentPacketCache.remove(tag);
|
|
+ if (removedCache != null) {
|
|
+ removedCache.asMap().clear();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public ItemStack getOrCreateCachedEquipmentItem(String tag, EquipmentSlot slot, ItemStack itemStack) {
|
|
+ return equipmentPacketCache.computeIfAbsent(tag, s -> com.google.common.cache.CacheBuilder.newBuilder()
|
|
+ .expireAfterAccess(10, java.util.concurrent.TimeUnit.MINUTES)
|
|
+ .build())
|
|
+ .asMap()
|
|
+ .computeIfAbsent(itemStack.hashCode(), i -> {
|
|
+ String name = slot.name();
|
|
+
|
|
+ //How neat is this.
|
|
+ if (name.equals("MAINHAND")) {
|
|
+ name = "HAND";
|
|
+ } else if (name.equals("OFFHAND")) {
|
|
+ name = "OFF_HAND";
|
|
+ }
|
|
+
|
|
+ org.bukkit.event.entity.EntityEquipmentItemLookup event = new org.bukkit.event.entity.EntityEquipmentItemLookup(getBukkitEntity(), tag, org.bukkit.inventory.EquipmentSlot.valueOf(name), CraftItemStack.asBukkitCopy(itemStack));
|
|
+ this.level().getCraftServer().getPluginManager().callEvent(event);
|
|
+ org.bukkit.inventory.ItemStack eventItem = event.getItemStack();
|
|
+ return CraftItemStack.asNMSCopy(eventItem);
|
|
+ });
|
|
+ }
|
|
+ // Slice end
|
|
+
|
|
+ // Slice start
|
|
+ public void sendEquipment(java.util.function.Consumer<net.minecraft.network.protocol.Packet<net.minecraft.network.protocol.game.ClientGamePacketListener>> consumer, ServerPlayer p) {
|
|
+ org.bukkit.event.player.PlayerReceiveEquipmentEvent event = new org.bukkit.event.player.PlayerReceiveEquipmentEvent(p.getBukkitEntity(), getBukkitEntity());
|
|
+ level().getCraftServer().getPluginManager().callEvent(event);
|
|
+
|
|
+ boolean sendEquipment = !event.isCancelled();
|
|
+ String tag = event.getTag();
|
|
+ if (sendEquipment && this instanceof ServerPlayer player && tag != null) {
|
|
+ ClientboundSetEquipmentPacket equipmentPacket = player.cachedEquipmentPacket.get(tag);
|
|
+ if (equipmentPacket != null) {
|
|
+ //Event says use a tag, and our tag exists; so we simply used our entire cached packet
|
|
+ consumer.accept(equipmentPacket);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sendEquipment) {
|
|
+ List<Pair<EquipmentSlot, ItemStack>> list = Lists.newArrayList();
|
|
+ Iterator iterator = EquipmentSlot.VALUES.iterator();
|
|
+
|
|
+ while (iterator.hasNext()) {
|
|
+ EquipmentSlot enumitemslot = (EquipmentSlot) iterator.next();
|
|
+ ItemStack itemstack = getItemBySlot(enumitemslot);
|
|
+
|
|
+ if (!itemstack.isEmpty()) {
|
|
+ ItemStack finalItemStack;
|
|
+ if (tag != null) {
|
|
+ finalItemStack = getOrCreateCachedEquipmentItem(tag, enumitemslot, itemstack);
|
|
+ } else {
|
|
+ finalItemStack = itemstack.copy();
|
|
+ }
|
|
+
|
|
+ list.add(Pair.of(enumitemslot, finalItemStack));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!list.isEmpty()) {
|
|
+ ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(getId(), list, true);
|
|
+ if (tag != null) {
|
|
+ cachedEquipmentPacket.put(tag, equipmentPacket);
|
|
+ }
|
|
+ consumer.accept(equipmentPacket);
|
|
+ }
|
|
+
|
|
+ detectEquipmentUpdatesPublic(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
|
|
+ }
|
|
+ }
|
|
+ // Slice end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
index d0c409f4efad289e3e325f44b500fc72589d89d4..baecf1c7bb459786bdbe6f7381ef148e0dad0ef5 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
|
@@ -1205,6 +1205,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
}
|
|
// Paper end - body yaw API
|
|
|
|
+ // Slice start
|
|
+ @Override
|
|
+ public void sendEquipment(Player p) {
|
|
+ if (entity instanceof net.minecraft.world.entity.LivingEntity livingEntity) {
|
|
+ net.minecraft.server.level.ServerPlayer serverPlayer = ((CraftPlayer) p).getHandle();
|
|
+ livingEntity.sendEquipment(packet -> serverPlayer.connection.send(packet), serverPlayer);
|
|
+ }
|
|
+ }
|
|
+ // Slice end
|
|
+
|
|
// Paper start - Expose canUseSlot
|
|
@Override
|
|
public boolean canUseEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) {
|