diff --git a/gradle.properties b/gradle.properties index 5cfc6c8a..33f07bc6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ fabric_version=0.83.0+1.20.1 maven_group = eu.pb4 -mod_version = 0.5.7 +mod_version = 0.5.8 minecraft_version_supported = ">=1.20-" diff --git a/polymer-common/build.gradle b/polymer-common/build.gradle index a87e7ea8..1ccab332 100644 --- a/polymer-common/build.gradle +++ b/polymer-common/build.gradle @@ -11,6 +11,7 @@ dependencies { modApi include("xyz.nucleoid:packet-tweaker:${packet_tweaker_version}") { transitive(false) } + modApi(include(annotationProcessor("com.llamalad7.mixinextras:mixinextras-fabric:0.2.0-beta.9"))) modCompileOnly "xyz.nucleoid:disguiselib-fabric:1.2.2" modCompileOnly "org.geysermc.floodgate:api:2.2.0-SNAPSHOT" } diff --git a/polymer-common/src/main/java/eu/pb4/polymer/common/api/PolymerCommonUtils.java b/polymer-common/src/main/java/eu/pb4/polymer/common/api/PolymerCommonUtils.java index f22411e5..14218d88 100644 --- a/polymer-common/src/main/java/eu/pb4/polymer/common/api/PolymerCommonUtils.java +++ b/polymer-common/src/main/java/eu/pb4/polymer/common/api/PolymerCommonUtils.java @@ -18,6 +18,8 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Set; +import java.util.WeakHashMap; public final class PolymerCommonUtils { private PolymerCommonUtils(){} @@ -134,19 +136,29 @@ public static World getFakeWorld() { */ @Nullable public static ServerPlayerEntity getPlayerContext() { + ServerPlayerEntity player = getPlayerContextNoClient(); + if (player == null && CommonImpl.IS_CLIENT) { + player = ClientUtils.getPlayer(); + } + + return player; + } + + @Nullable + public static ServerPlayerEntity getPlayerContextNoClient() { ServerPlayerEntity player = PacketContext.get().getTarget(); if (player == null) { player = CommonImplUtils.getPlayer(); - - if (player == null && CommonImpl.IS_CLIENT) { - player = ClientUtils.getPlayer(); - } } return player; } + public static boolean isNetworkingThread() { + return Thread.currentThread().getName().startsWith("Netty"); + } + public static boolean isBedrockPlayer(ServerPlayerEntity player) { if (CompatStatus.FLOODGATE) { return FloodGateUtils.isPlayerBroken(player); diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemGroupUtils.java b/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemGroupUtils.java index 75afd9a2..0b4c3bfe 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemGroupUtils.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemGroupUtils.java @@ -17,18 +17,21 @@ import java.util.*; + /** * An server side item group that can be synchronized with polymer clients * It also has it's own server side functionality */ public final class PolymerItemGroupUtils { public static final PolymerRegistry REGISTRY = InternalServerRegistry.ITEM_GROUPS; - private PolymerItemGroupUtils() {} /** * Even called on synchronization of ItemGroups */ public static final SimpleEvent LIST_EVENT = new SimpleEvent<>(); + private PolymerItemGroupUtils() { + } + public static Contents getContentsFor(ServerPlayerEntity player, ItemGroup group) { return getContentsFor(group, player.getServer().getRegistryManager(), player.getServerWorld().getEnabledFeatures(), CommonImplUtils.permissionCheck(player, "op_items", 2)); } @@ -51,14 +54,22 @@ public static List getItemGroups(ServerPlayerEntity player) { var list = new LinkedHashSet(); for (var g : ItemGroups.getGroups()) { - if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) { - list.add(g); + try { + if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) { + list.add(g); + } + } catch (Throwable e) { + e.printStackTrace(); } } for (var g : InternalServerRegistry.ITEM_GROUPS) { - if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) { - list.add(g); + try { + if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) { + list.add(g); + } + } catch (Throwable e) { + e.printStackTrace(); } } @@ -106,8 +117,10 @@ public interface ItemGroupEventListener { public interface ItemGroupListBuilder { void add(ItemGroup group); + void remove(ItemGroup group); } - public record Contents(Collection main, Collection search) {} + public record Contents(Collection main, Collection search) { + } } diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemUtils.java b/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemUtils.java index cafb54b2..4ae948e3 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemUtils.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/api/item/PolymerItemUtils.java @@ -19,7 +19,6 @@ import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.item.*; import net.minecraft.item.trim.ArmorTrim; -import net.minecraft.item.trim.ArmorTrimMaterial; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; @@ -87,7 +86,7 @@ public static ItemStack getPolymerItemStack(ItemStack itemStack, TooltipContext return itemStack; } else if (itemStack.getItem() instanceof PolymerItem item) { return item.getPolymerItemStack(itemStack, tooltipContext, player); - } else if (shouldPolymerConvert(itemStack, player)) { + } else if (isPolymerServerItem(itemStack, player)) { return createItemStack(itemStack, tooltipContext, player); } @@ -180,10 +179,10 @@ public static NbtCompound getPolymerNbt(ItemStack itemStack) { } public static boolean isPolymerServerItem(ItemStack itemStack) { - return shouldPolymerConvert(itemStack, null); + return isPolymerServerItem(itemStack, PolymerUtils.getPlayerContext()); } - private static boolean shouldPolymerConvert(ItemStack itemStack, @Nullable ServerPlayerEntity player) { + public static boolean isPolymerServerItem(ItemStack itemStack, @Nullable ServerPlayerEntity player) { if (getPolymerIdentifier(itemStack) != null) { return false; } @@ -232,7 +231,7 @@ private static boolean shouldPolymerConvert(ItemStack itemStack, @Nullable Serve if (itemStack.getNbt().contains("ChargedProjectiles", NbtElement.LIST_TYPE)) { for (var itemNbt : itemStack.getNbt().getList("ChargedProjectiles", NbtElement.COMPOUND_TYPE)) { - if (shouldPolymerConvert(ItemStack.fromNbt((NbtCompound) itemNbt), player)) { + if (isPolymerServerItem(ItemStack.fromNbt((NbtCompound) itemNbt), player)) { return true; } } @@ -240,7 +239,7 @@ private static boolean shouldPolymerConvert(ItemStack itemStack, @Nullable Serve if (itemStack.getNbt().contains("Items", NbtElement.LIST_TYPE)) { for (var itemNbt : itemStack.getNbt().getList("Items", NbtElement.COMPOUND_TYPE)) { - if (shouldPolymerConvert(ItemStack.fromNbt((NbtCompound) itemNbt), player)) { + if (isPolymerServerItem(ItemStack.fromNbt((NbtCompound) itemNbt), player)) { return true; } } diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/impl/PolymerImpl.java b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/PolymerImpl.java index 2ef34428..c5dbe777 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/impl/PolymerImpl.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/PolymerImpl.java @@ -27,6 +27,7 @@ public final class PolymerImpl { public static final boolean LOG_MORE_ERRORS; public static final int LIGHT_UPDATE_TICK_DELAY; public static final boolean FORCE_STRICT_UPDATES; + public static final boolean ITEMSTACK_NBT_HACK; static { var serverConfig = CommonImpl.loadConfig("server", ServerConfig.class); @@ -39,6 +40,7 @@ public final class PolymerImpl { SYNC_MODDED_ENTRIES_POLYMC = serverConfig.polyMcSyncModdedEntries && CompatStatus.POLYMC; LIGHT_UPDATE_TICK_DELAY = serverConfig.lightUpdateTickDelay; FORCE_STRICT_UPDATES = serverConfig.forceStrictUpdates; + ITEMSTACK_NBT_HACK = serverConfig.itemStackNbtHack; if (PolymerImpl.IS_CLIENT) { diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/impl/ServerConfig.java b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/ServerConfig.java index 828d0915..f4e2e1f2 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/impl/ServerConfig.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/ServerConfig.java @@ -18,4 +18,7 @@ public class ServerConfig { public String _c3 = "Forcefully enables strict block updates, making client desyncs less likely to happen"; @SerializedName("force_strict_block_updates") public boolean forceStrictUpdates = false; + public String _c4 = "Enables experimental passing of ItemStack context through nbt, allowing for better mod compat"; + @SerializedName("item_stack_nbt_hack") + public boolean itemStackNbtHack = true; } diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/impl/interfaces/ItemStackAwareNbtCompound.java b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/interfaces/ItemStackAwareNbtCompound.java new file mode 100644 index 00000000..318401a1 --- /dev/null +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/impl/interfaces/ItemStackAwareNbtCompound.java @@ -0,0 +1,7 @@ +package eu.pb4.polymer.core.impl.interfaces; + +import net.minecraft.item.ItemStack; + +public interface ItemStackAwareNbtCompound { + default void polymerCore$setItemStack(ItemStack stack) {}; +} diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/block/packet/PalettedContainerDataMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/block/packet/PalettedContainerDataMixin.java index c6f8aa18..525ec5a2 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/block/packet/PalettedContainerDataMixin.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/block/packet/PalettedContainerDataMixin.java @@ -16,19 +16,22 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(targets = "net/minecraft/world/chunk/PalettedContainer$Data") public class PalettedContainerDataMixin { @Shadow @Final private Palette palette; - @Redirect(method = "writePacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/PaletteStorage;getData()[J")) - private long[] polymer$replaceData(PaletteStorage instance) { - if (this.palette instanceof IdListPalette && this.palette.get(0) instanceof BlockState) { + @Shadow @Final private PaletteStorage storage; + + @ModifyArg(method = "writePacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketByteBuf;writeLongArray([J)Lnet/minecraft/network/PacketByteBuf;"), require = 0) + private long[] polymer$replaceData(long[] initialReturn) { + if (this.palette instanceof IdListPalette && this.palette.get(0) instanceof BlockState) { var palette = (IdListPalette) this.palette; var player = PolymerUtils.getPlayerContext(); if (player == null) { - return instance.getData(); + return initialReturn; } int bits; @@ -38,7 +41,7 @@ public class PalettedContainerDataMixin { } else { bits = playerBitCount.intValue(); } - + final var instance = new PackedIntegerArray(this.storage.getElementBits(), this.storage.getSize(), initialReturn); final int size = instance.getSize(); var data = new PackedIntegerArray(bits, size); @@ -49,6 +52,6 @@ public class PalettedContainerDataMixin { return data.getData(); } - return instance.getData(); + return initialReturn; } } diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/client/item/ItemGroupMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/client/item/ItemGroupMixin.java index 745faa8c..0a3c8712 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/client/item/ItemGroupMixin.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/client/item/ItemGroupMixin.java @@ -41,8 +41,8 @@ public abstract class ItemGroupMixin implements ClientItemGroupExtension { @Shadow public abstract void reloadSearchProvider(); - @Unique private List polymer$itemsGroup = new ArrayList<>(); - @Unique private List polymer$itemsSearch = new ArrayList<>(); + @Unique private final List polymer$itemsGroup = new ArrayList<>(); + @Unique private final List polymer$itemsSearch = new ArrayList<>(); @Unique private int polymerCore$page; diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/command/CommandManagerMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/command/CommandManagerMixin.java index 6f7514d8..ac7b86be 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/command/CommandManagerMixin.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/command/CommandManagerMixin.java @@ -14,7 +14,7 @@ @Mixin(CommandManager.class) public abstract class CommandManagerMixin { - @Inject(method = "argument", at = @At("TAIL"), cancellable = true) + @Inject(method = "argument", at = @At("TAIL")) private static void polymer$handleSuggestions(String name, ArgumentType type, CallbackInfoReturnable> cir) { if (type instanceof ItemStackArgumentType || type instanceof BlockStateArgumentType || type instanceof RegistryEntryArgumentType) { cir.getReturnValue().suggests(type::listSuggestions); diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/ItemStackMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/ItemStackMixin.java index 90802cff..90dca546 100644 --- a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/ItemStackMixin.java +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/ItemStackMixin.java @@ -1,14 +1,23 @@ package eu.pb4.polymer.core.mixin.item; +import eu.pb4.polymer.core.api.item.PolymerItemUtils; +import eu.pb4.polymer.core.api.utils.PolymerUtils; +import eu.pb4.polymer.core.impl.PolymerImpl; +import eu.pb4.polymer.core.impl.PolymerImplUtils; +import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound; import eu.pb4.polymer.core.impl.other.PolymerTooltipContext; import net.minecraft.client.item.TooltipContext; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @@ -16,6 +25,18 @@ @Mixin(ItemStack.class) public class ItemStackMixin { + @Inject(method = "writeNbt", at = @At("TAIL")) + private void polymerCore$magicPerPlayerNbt(NbtCompound nbt, CallbackInfoReturnable cir) { + if (PolymerImpl.ITEMSTACK_NBT_HACK) { + var self = (ItemStack) (Object) this; + var player = PolymerUtils.getPlayerContext(); + if (PolymerItemUtils.isPolymerServerItem(self, player)) { + ((ItemStackAwareNbtCompound) nbt).polymerCore$setItemStack(self.copy()); + } + } + } + + @Inject(method = "getTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/item/TooltipContext;isAdvanced()Z", ordinal = 2), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) private void polymer$quitEarly(@Nullable PlayerEntity player, TooltipContext context, CallbackInfoReturnable> cir, List list) { if (context instanceof PolymerTooltipContext) { diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundFallbackMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundFallbackMixin.java new file mode 100644 index 00000000..6a249497 --- /dev/null +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundFallbackMixin.java @@ -0,0 +1,12 @@ +package eu.pb4.polymer.core.mixin.item; + +import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Inject; + +@Mixin(NbtCompound.class) +public class NbtCompoundFallbackMixin implements ItemStackAwareNbtCompound { + +} diff --git a/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundMixin.java b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundMixin.java new file mode 100644 index 00000000..ad8c968f --- /dev/null +++ b/polymer-core/src/main/java/eu/pb4/polymer/core/mixin/item/NbtCompoundMixin.java @@ -0,0 +1,66 @@ +package eu.pb4.polymer.core.mixin.item; + +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import eu.pb4.polymer.common.api.PolymerCommonUtils; +import eu.pb4.polymer.core.api.item.PolymerItemUtils; +import eu.pb4.polymer.core.impl.PolymerImpl; +import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; +import net.minecraft.registry.Registries; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.DataOutput; + +@SuppressWarnings("InvalidInjectorMethodSignature") +@Mixin(NbtCompound.class) +public class NbtCompoundMixin implements ItemStackAwareNbtCompound { + @Unique + private ItemStack polymerCore$stack; + + @Override + public void polymerCore$setItemStack(ItemStack stack) { + this.polymerCore$stack = stack; + } + + + @Inject(method = "write(Ljava/io/DataOutput;)V", at = @At("HEAD")) + private void polymerCore$storePlayerContextedItemStack(DataOutput output, CallbackInfo ci, @Share("polymerCore:stack") LocalRef polymerStack) { + if (this.polymerCore$stack != null && PolymerCommonUtils.isNetworkingThread()) { + var player = PolymerCommonUtils.getPlayerContextNoClient(); + if (player != null) { + polymerStack.set(PolymerItemUtils.getPolymerItemStack(this.polymerCore$stack, player)); + } + } + } + + @ModifyArg(method = "write(Ljava/io/DataOutput;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;write(Ljava/lang/String;Lnet/minecraft/nbt/NbtElement;Ljava/io/DataOutput;)V")) + private NbtElement polymerCore$swapNbt(NbtElement nbtElement, @Local(ordinal = 0) String key, @Share("polymerCore:stack") LocalRef polymerStack) { + if (PolymerImpl.ITEMSTACK_NBT_HACK && this.polymerCore$stack != null && PolymerCommonUtils.isNetworkingThread()) { + if (key.equals("id") && nbtElement.getType() == NbtElement.STRING_TYPE) { + var stack = polymerStack.get(); + return stack != null ? NbtString.of(Registries.ITEM.getId(stack.getItem()).toString()) : nbtElement; + } else if (key.equals("tag") && nbtElement.getType() == NbtElement.COMPOUND_TYPE) { + var stack = polymerStack.get(); + return stack != null ? stack.getOrCreateNbt() : nbtElement; + } + } + + return nbtElement; + } + + @Inject(method = "copy()Lnet/minecraft/nbt/NbtCompound;", at = @At("RETURN")) + private void polymerCore$copyStack(CallbackInfoReturnable cir) { + ((ItemStackAwareNbtCompound) cir.getReturnValue()).polymerCore$setItemStack(this.polymerCore$stack); + } +} diff --git a/polymer-core/src/main/resources/polymer-core.mixins.json b/polymer-core/src/main/resources/polymer-core.mixins.json index 559f223a..a133be4b 100644 --- a/polymer-core/src/main/resources/polymer-core.mixins.json +++ b/polymer-core/src/main/resources/polymer-core.mixins.json @@ -55,6 +55,8 @@ "item.ItemStackContentMixin", "item.ItemStackMixin", "item.MiningToolItemAccessor", + "item.NbtCompoundFallbackMixin", + "item.NbtCompoundMixin", "item.ServerPlayNetworkHandlerMixin", "item.StonecutterScreenHandlerMixin", "item.packet.PacketByteBufMixin", diff --git a/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java b/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java index 9c614212..06df600e 100644 --- a/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java +++ b/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java @@ -492,6 +492,11 @@ public void onInitialize() { }).start(); }); + + var local = new ThreadLocal(); + local.set(Boolean.TRUE); + long localTime = System.currentTimeMillis(); + if (PolymerImpl.IS_CLIENT) { InternalClientRegistry.decodeState(-1); } diff --git a/polymer-networking/src/main/resources/fabric.mod.json b/polymer-networking/src/main/resources/fabric.mod.json index 0c418b51..e2365173 100644 --- a/polymer-networking/src/main/resources/fabric.mod.json +++ b/polymer-networking/src/main/resources/fabric.mod.json @@ -19,7 +19,7 @@ "icon": "assets/icon.png", "environment": "*", "depends": { - "minecraft": ">=1.20", + "polymer-common": ">=${version}", "fabricloader": ">=0.14.0" }, "custom": { diff --git a/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/api/elements/EntityElement.java b/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/api/elements/EntityElement.java index 50f20db1..a26bc511 100644 --- a/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/api/elements/EntityElement.java +++ b/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/api/elements/EntityElement.java @@ -1,20 +1,29 @@ package eu.pb4.polymer.virtualentity.api.elements; +import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Pair; import eu.pb4.polymer.common.api.PolymerCommonUtils; import eu.pb4.polymer.virtualentity.api.ElementHolder; +import eu.pb4.polymer.virtualentity.mixin.LivingEntityAccessor; import eu.pb4.polymer.virtualentity.mixin.accessors.EntityTrackerEntryAccessor; import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.s2c.play.EntitiesDestroyS2CPacket; +import net.minecraft.network.packet.s2c.play.EntityEquipmentUpdateS2CPacket; import net.minecraft.server.network.EntityTrackerEntry; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; public class EntityElement extends AbstractElement { @@ -100,5 +109,29 @@ public void setInitialPosition(Vec3d pos) { @Override public void tick() { this.entry.tick(); + if (this.entity instanceof LivingEntity livingEntity) { + this.sendEquipmentChanges(livingEntity); + } + } + + private void sendEquipmentChanges(LivingEntity livingEntity) { + var ac = ((LivingEntityAccessor) livingEntity); + var equipmentChanges = ac.callGetEquipmentChanges(); + if (equipmentChanges != null && !equipmentChanges.isEmpty()) { + List> list = new ArrayList<>(equipmentChanges.size()); + equipmentChanges.forEach((slot, stack) -> { + ItemStack itemStack = stack.copy(); + list.add(Pair.of(slot, itemStack)); + switch (slot.getType()) { + case HAND: + ac.callSetSyncedHandStack(slot, itemStack); + break; + case ARMOR: + ac.callSetSyncedArmorStack(slot, itemStack); + } + }); + + this.sendPacket(new EntityEquipmentUpdateS2CPacket(livingEntity.getId(), list)); + } } } diff --git a/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/mixin/LivingEntityAccessor.java b/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/mixin/LivingEntityAccessor.java new file mode 100644 index 00000000..3df0e15c --- /dev/null +++ b/polymer-virtual-entity/src/main/java/eu/pb4/polymer/virtualentity/mixin/LivingEntityAccessor.java @@ -0,0 +1,21 @@ +package eu.pb4.polymer.virtualentity.mixin; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.Map; + +@Mixin(LivingEntity.class) +public interface LivingEntityAccessor { + @Invoker + Map callGetEquipmentChanges(); + + @Invoker + void callSetSyncedHandStack(EquipmentSlot slot, ItemStack stack); + + @Invoker + void callSetSyncedArmorStack(EquipmentSlot slot, ItemStack armor); +} diff --git a/polymer-virtual-entity/src/main/resources/polymer-virtual-entity.mixins.json b/polymer-virtual-entity/src/main/resources/polymer-virtual-entity.mixins.json index 3f7d3eaa..8051c725 100644 --- a/polymer-virtual-entity/src/main/resources/polymer-virtual-entity.mixins.json +++ b/polymer-virtual-entity/src/main/resources/polymer-virtual-entity.mixins.json @@ -8,8 +8,8 @@ "EntityMixin", "EntityPassengersSetS2CPacketAccessor", "EntityPassengersSetS2CPacketMixin", - "accessors.EntityTrackerEntryAccessor", "EntityTrackerEntryMixin", + "LivingEntityAccessor", "PlayerInteractEntityC2SPacketAccessor", "ServerPlayerEntityMixin", "ServerPlayNetworkHandlerMixin", @@ -19,6 +19,7 @@ "accessors.EntityAccessor", "accessors.EntityPositionS2CPacketAccessor", "accessors.EntityTrackerAccessor", + "accessors.EntityTrackerEntryAccessor", "accessors.InteractionEntityAccessor", "accessors.ItemDisplayEntityAccessor", "accessors.TextDisplayEntityAccessor",