package net.radsteve.islanders.mixin; import com.google.common.collect.Iterators; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import net.minecraft.item.Item; import net.minecraft.item.Items; import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.registry.tag.ItemTags; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.sync.ItemStackHash; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.radsteve.islanders.Islanders; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; 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.callback.CallbackInfo; /// Warning: This is a huge mess and you should not touch this with a ten-foot pole /// This prevents special items from being put into bundles. /// Time counter: 5h @Mixin(ServerPlayNetworkHandler.class) public class BundleHandlerMixin { @Shadow public ServerPlayerEntity player; /// DIRECTION ONE - item clicked first and dragged onto bundle @Unique private ClickSlotC2SPacket islanders$lastClickPacket = null; @Unique private Item islanders$lastClickedItem = null; @Inject(method = "onClickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandler;disableSyncing()V"), cancellable = true) private void islanders$onClickSlot(ClickSlotC2SPacket packet, CallbackInfo ci) { if (islanders$lastClickPacket == null) { islanders$lastClickPacket = packet; return; } final var modifiedStacks = packet.modifiedStacks(); if (modifiedStacks.size() != 1) { islanders$lastClickPacket = packet; return; } final int slotIdx = Iterators.getOnlyElement(modifiedStacks.keySet().iterator()); final Slot currentSlot = player.currentScreenHandler.slots.get(slotIdx); if (!currentSlot.getStack().itemMatches((entry) -> entry.isIn(ItemTags.BUNDLES))) { islanders$lastClickPacket = packet; islanders$lastClickedItem = currentSlot.getStack().getItem(); return; } if (!(modifiedStacks.get(slotIdx) instanceof ItemStackHash.Impl)) { islanders$lastClickPacket = packet; return; } final var lastModifiedStacks = islanders$lastClickPacket.modifiedStacks(); if (lastModifiedStacks.size() != 1) { islanders$lastClickPacket = packet; return; } final Item specialItem = islanders$lastClickedItem; if (!(specialItem == Items.DRAGON_EGG || specialItem == Items.MACE || specialItem == Items.POPPED_CHORUS_FRUIT /* TODO: extra validation */)) { islanders$lastClickPacket = packet; return; } final ScreenHandler screenHandler = player.currentScreenHandler; for (Int2ObjectMap.Entry entry : Int2ObjectMaps.fastIterable(packet.modifiedStacks())) { screenHandler.setReceivedHash(entry.getIntKey(), entry.getValue()); } screenHandler.setReceivedCursorHash(packet.cursor()); screenHandler.enableSyncing(); screenHandler.updateToClient(); screenHandler.sendContentUpdates(); ci.cancel(); islanders$lastClickPacket = packet; Islanders.getLogger().info("{} tried moving special item into bundle", player.getNameForScoreboard()); } /// DIRECTION TWO - bundle clicked first and dragged onto item /// This is a mess. Do not touch :) @Unique private ClickSlotC2SPacket islanders$lastClickPacket$0 = null; @Unique private RegistryEntry islanders$lastClickedItem$0 = null; @Inject(method = "onClickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandler;disableSyncing()V"), cancellable = true) private void islanders$onClickSlot$0(ClickSlotC2SPacket packet, CallbackInfo ci) { if (islanders$lastClickPacket$0 == null) { islanders$lastClickPacket$0 = packet; } final var modifiedStacks = packet.modifiedStacks(); if (islanders$lastClickedItem$0 == null) { islanders$lastClickPacket$0 = packet; if (!modifiedStacks.isEmpty()) { final int slotIdx = Iterators.get(modifiedStacks.keySet().iterator(), 0); final Slot currentSlot = player.currentScreenHandler.slots.get(slotIdx); islanders$lastClickedItem$0 = currentSlot.getStack().getRegistryEntry(); } return; } final boolean lastIsBundle = islanders$lastClickedItem$0.isIn(ItemTags.BUNDLES); if (modifiedStacks.isEmpty() && !lastIsBundle) { islanders$lastClickPacket$0 = packet; return; } RegistryEntry currentItem; if (modifiedStacks.isEmpty()) { currentItem = islanders$lastClickedItem$0; } else { final int slotIdx = Iterators.get(modifiedStacks.keySet().iterator(), 0); final Slot currentSlot = player.currentScreenHandler.slots.get(slotIdx); currentItem = currentSlot.getStack().getRegistryEntry(); } if (islanders$lastClickPacket$0 == null) { return; } if (!islanders$lastClickedItem$0.isIn(ItemTags.BUNDLES)) { islanders$lastClickPacket$0 = packet; islanders$lastClickedItem$0 = currentItem; return; } final Item specialItem = currentItem.value(); if (!(specialItem == Items.DRAGON_EGG || specialItem == Items.MACE || specialItem == Items.POPPED_CHORUS_FRUIT /* TODO: extra validation */)) { islanders$lastClickPacket$0 = packet; islanders$lastClickedItem$0 = currentItem; return; } final ScreenHandler screenHandler = player.currentScreenHandler; for (Int2ObjectMap.Entry entry : Int2ObjectMaps.fastIterable(packet.modifiedStacks())) { screenHandler.setReceivedHash(entry.getIntKey(), entry.getValue()); } screenHandler.setReceivedCursorHash(packet.cursor()); screenHandler.enableSyncing(); screenHandler.updateToClient(); screenHandler.sendContentUpdates(); ci.cancel(); islanders$lastClickPacket$0 = packet; Islanders.getLogger().info("{} tried moving special item into bundle", player.getNameForScoreboard()); } }