From bb327adb35061fa07ef2af4747b4294f9a402bb6 Mon Sep 17 00:00:00 2001 From: Kogepan229 <73163994+Kogepan229@users.noreply.github.com> Date: Thu, 7 May 2026 21:35:20 +0900 Subject: [PATCH] Fix Cell Microscope drive scan (#127) Co-authored-by: lc-1337 <62835225+lc-1337@users.noreply.github.com> --- .../common/items/ItemCellMicroscope.java | 339 ++++++++++++------ .../common/network/NetworkHandler.java | 4 + .../Packet_C_CellMicroscopeScanFeedback.java | 48 +++ 3 files changed, 287 insertions(+), 104 deletions(-) create mode 100644 src/main/java/thaumicenergistics/common/network/packet/client/Packet_C_CellMicroscopeScanFeedback.java diff --git a/src/main/java/thaumicenergistics/common/items/ItemCellMicroscope.java b/src/main/java/thaumicenergistics/common/items/ItemCellMicroscope.java index 4dbc4053..f7a1bcac 100644 --- a/src/main/java/thaumicenergistics/common/items/ItemCellMicroscope.java +++ b/src/main/java/thaumicenergistics/common/items/ItemCellMicroscope.java @@ -1,50 +1,54 @@ package thaumicenergistics.common.items; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import static appeng.util.item.AEItemStackType.ITEM_STACK_TYPE; + +import java.util.List; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; import appeng.api.AEApi; import appeng.api.storage.ICellContainer; import appeng.api.storage.IMEInventory; -import appeng.api.storage.ISaveProvider; -import appeng.api.storage.StorageChannel; +import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; -import appeng.items.storage.ItemBasicStorageCell; import appeng.util.IterationCounter; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import thaumcraft.api.research.ScanResult; import thaumcraft.common.Thaumcraft; import thaumcraft.common.items.relics.ItemThaumometer; -import thaumcraft.common.lib.network.PacketHandler; -import thaumcraft.common.lib.network.playerdata.PacketScannedToServer; import thaumcraft.common.lib.research.ScanManager; import thaumcraft.common.lib.utils.EntityUtils; +import thaumicenergistics.common.network.packet.client.Packet_C_CellMicroscopeScanFeedback; import thaumicenergistics.common.registries.ThEStrings; public class ItemCellMicroscope extends ItemThaumometer { + private static final String TAG_SCAN_TYPE = "scanType"; + private static final String TAG_SCAN_ENTITY = "scanEntity"; + private static final String TAG_SCAN_X = "scanX"; + private static final String TAG_SCAN_Y = "scanY"; + private static final String TAG_SCAN_Z = "scanZ"; + + private static final byte TARGET_NONE = 0; + private static final byte TARGET_ENTITY_ITEM = 1; + private static final byte TARGET_CELL_CONTAINER = 2; + public ItemCellMicroscope() { super(); } - List startCells = new ArrayList(); - TileEntity cellSaveManager = null; - @SideOnly(Side.CLIENT) @Override public void registerIcons(IIconRegister ir) { @@ -68,11 +72,14 @@ public class ItemCellMicroscope extends ItemThaumometer { @Override public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer p) { + ScanTarget target = this.findScanTarget(world, p); + if (world.isRemote) { - ArrayList cells = this.doScan(stack, world, p, 0); - if (cells.size() != 0) { - this.startCells = cells; - } + this.playScanEffects(world, target, 0); + } else if (target.isValid()) { + this.writeScanTarget(stack, target); + } else { + this.clearScanTarget(stack); } p.setItemInUse(stack, this.getMaxItemUseDuration(stack)); @@ -81,46 +88,49 @@ public class ItemCellMicroscope extends ItemThaumometer { @Override public void onUsingTick(ItemStack stack, EntityPlayer p, int count) { - if (p.worldObj.isRemote && Objects - .equals(p.getCommandSenderName(), Minecraft.getMinecraft().thePlayer.getCommandSenderName())) { + ScanTarget target = this.findScanTarget(p.worldObj, p); - ArrayList cells = this.doScan(stack, p.worldObj, p, count); - - if (cells.size() != 0 && cells.equals(this.startCells)) { - - if (count <= 5) { - p.stopUsingItem(); - this.startCells.clear(); - for (ItemStack cell : cells) { - doCellScan(p, cell); - } - } - - if (count % 2 == 0) { - p.worldObj.playSound( - p.posX, - p.posY, - p.posZ, - "thaumcraft:cameraticks", - 0.2F, - 0.45F + ((float) count / 50) + p.worldObj.rand.nextFloat() * 0.1F, - false); - } - } else { - this.startCells.clear(); - } - } - - } - - private void doCellScan(EntityPlayer p, ItemStack cell) { - IMEInventory inv = AEApi.instance().registries().cell() - .getCellInventory(cell, (ISaveProvider) cellSaveManager, StorageChannel.ITEMS); - - if (inv == null) { + if (p.worldObj.isRemote) { + this.playScanEffects(p.worldObj, target, count); return; } + if (!this.matchesStoredScanTarget(stack, target)) { + this.clearScanTarget(stack); + p.stopUsingItem(); + return; + } + + if (count <= 5) { + p.stopUsingItem(); + this.clearScanTarget(stack); + this.doScan(p, target); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void doScan(EntityPlayer p, ScanTarget target) { + if (target.type == TARGET_ENTITY_ITEM && target.entityItem != null) { + this.doCellScan(p, target.entityItem.getEntityItem()); + } else if (target.type == TARGET_CELL_CONTAINER && target.tile instanceof ICellContainer) { + List cellArray = ((ICellContainer) target.tile).getCellArray(ITEM_STACK_TYPE); + for (IMEInventoryHandler cell : cellArray) { + this.doInventoryScan(p, cell); + } + } + } + + @SuppressWarnings("unchecked") + private void doCellScan(EntityPlayer p, ItemStack cell) { + IMEInventory inv = AEApi.instance().registries().cell() + .getCellInventory(cell, null, ITEM_STACK_TYPE); + + this.doInventoryScan(p, inv); + } + + private void doInventoryScan(EntityPlayer p, IMEInventory inv) { + if (inv == null) return; + IItemList itemList = inv .getAvailableItems(AEApi.instance().storage().createItemList(), IterationCounter.fetchNewId()); @@ -128,72 +138,193 @@ public class ItemCellMicroscope extends ItemThaumometer { ScanResult sr = new ScanResult((byte) 1, Item.getIdFromItem(i.getItem()), i.getItemDamage(), null, ""); if (ScanManager.isValidScanTarget(p, sr, "@")) { - ScanManager.completeScan(p, sr, "@"); - PacketHandler.INSTANCE.sendToServer(new PacketScannedToServer(sr, p, "@")); + if (!ScanManager.completeScan(p, sr, "@")) { + // Thaumcraft syncs successful scans to the client, but not failed ones. + // Run the client-side scan path manually so failure hints are shown. + Packet_C_CellMicroscopeScanFeedback.sendScanFeedback(p, sr); + } } } } - private ArrayList doScan(ItemStack stack, World world, EntityPlayer p, int count) { - - ArrayList cellsOutput = new ArrayList<>(); + private ScanTarget findScanTarget(World world, EntityPlayer p) { Entity pointedEntity = EntityUtils.getPointedEntity(p.worldObj, p, 0.5D, 10.0D, 0.0F, true); - if (pointedEntity != null) { - if (pointedEntity instanceof EntityItem) { - if (((EntityItem) pointedEntity).getEntityItem().getItem() instanceof ItemBasicStorageCell) { - MovingObjectPosition mop = this.getMovingObjectPositionFromPlayer(p.worldObj, p, true); - try { - Thaumcraft.proxy.blockRunes( - world, - mop.blockX, - (double) mop.blockY + 0.50D, - mop.blockZ, - 0.3F, - 0.3F, - 0.7F + world.rand.nextFloat() * 0.7F, - 15, - -0.03F); - } catch (Exception ignored) {} - cellSaveManager = null; - cellsOutput.add(((EntityItem) pointedEntity).getEntityItem()); - return cellsOutput; - } + if (pointedEntity instanceof EntityItem entityItem) { + if (this.isItemStorageCell(entityItem.getEntityItem())) { + return ScanTarget.forEntityItem(entityItem); } } - MovingObjectPosition lookingAtBlock = Minecraft.getMinecraft().objectMouseOver; - TileEntity blockAtPos = world - .getTileEntity(lookingAtBlock.blockX, lookingAtBlock.blockY, lookingAtBlock.blockZ); - if (blockAtPos instanceof ICellContainer) { - ((ICellContainer) blockAtPos).getCellArray(StorageChannel.ITEMS); - IInventory inv = (IInventory) blockAtPos; - cellSaveManager = blockAtPos; - try { - Thaumcraft.proxy.blockRunes( - world, - lookingAtBlock.blockX, - lookingAtBlock.blockY, - lookingAtBlock.blockZ, - 0.3F, - 0.3F, - 0.7F + world.rand.nextFloat() * 0.7F, - 15, - -0.03F); - } catch (Exception ignored) {} - for (int i = 0; i < inv.getSizeInventory(); i++) { - ItemStack itemStack = inv.getStackInSlot(i); - if (itemStack != null) { - cellsOutput.add(itemStack); - } + MovingObjectPosition lookingAtBlock = this.getMovingObjectPositionFromPlayer(world, p, true); + if ((lookingAtBlock != null) && (lookingAtBlock.typeOfHit == MovingObjectType.BLOCK)) { + TileEntity blockAtPos = world + .getTileEntity(lookingAtBlock.blockX, lookingAtBlock.blockY, lookingAtBlock.blockZ); + + if (blockAtPos instanceof ICellContainer) { + return ScanTarget.forCellContainer(lookingAtBlock, blockAtPos); } } - return cellsOutput; + + return ScanTarget.none(); + } + + private boolean isItemStorageCell(ItemStack stack) { + if (stack == null) return false; + + return AEApi.instance().registries().cell().getCellInventory(stack, null, ITEM_STACK_TYPE) != null; + } + + private void playScanEffects(World world, ScanTarget target, int count) { + if (!target.isValid()) return; + + if (count % 2 == 0) { + world.playSound( + target.effectX, + target.effectY, + target.effectZ, + "thaumcraft:cameraticks", + 0.2F, + 0.45F + ((float) count / 50) + world.rand.nextFloat() * 0.1F, + false); + } + + try { + Thaumcraft.proxy.blockRunes( + world, + target.effectX, + target.effectY, + target.effectZ, + 0.3F, + 0.3F, + 0.7F + world.rand.nextFloat() * 0.7F, + 15, + -0.03F); + } catch (Exception ignored) {} + } + + /** + * Stores the scan target selected at right-click time so the server can verify that the player is still looking at + * the same cell when the use action completes. These tags are temporary and are cleared when scanning stops. + */ + private void writeScanTarget(ItemStack stack, ScanTarget target) { + NBTTagCompound tag = stack.getTagCompound(); + if (tag == null) { + tag = new NBTTagCompound(); + stack.setTagCompound(tag); + } + + tag.setByte(TAG_SCAN_TYPE, target.type); + tag.setInteger(TAG_SCAN_ENTITY, target.entityId); + tag.setInteger(TAG_SCAN_X, target.x); + tag.setInteger(TAG_SCAN_Y, target.y); + tag.setInteger(TAG_SCAN_Z, target.z); + } + + /** + * Rejects the scan if the player moved to a different entity or block during the charge-up period. + */ + private boolean matchesStoredScanTarget(ItemStack stack, ScanTarget target) { + if (!target.isValid() || !stack.hasTagCompound()) return false; + + NBTTagCompound tag = stack.getTagCompound(); + byte type = tag.getByte(TAG_SCAN_TYPE); + if (type != target.type) return false; + + if (type == TARGET_ENTITY_ITEM) { + return tag.getInteger(TAG_SCAN_ENTITY) == target.entityId; + } + + return (tag.getInteger(TAG_SCAN_X) == target.x) && (tag.getInteger(TAG_SCAN_Y) == target.y) + && (tag.getInteger(TAG_SCAN_Z) == target.z); + } + + /** + * Removes the temporary scan target after completion, cancellation, or target mismatch so the item does not keep + * stale target data. + */ + private void clearScanTarget(ItemStack stack) { + if (!stack.hasTagCompound()) return; + + NBTTagCompound tag = stack.getTagCompound(); + tag.removeTag(TAG_SCAN_TYPE); + tag.removeTag(TAG_SCAN_ENTITY); + tag.removeTag(TAG_SCAN_X); + tag.removeTag(TAG_SCAN_Y); + tag.removeTag(TAG_SCAN_Z); + + if (tag.hasNoTags()) { + stack.setTagCompound(null); + } } public void onPlayerStoppedUsing(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer, int par4) { super.onPlayerStoppedUsing(par1ItemStack, par2World, par3EntityPlayer, par4); - this.startCells.clear(); + this.clearScanTarget(par1ItemStack); + } + + private static class ScanTarget { + + private final byte type; + private final int entityId; + private final int x; + private final int y; + private final int z; + private final double effectX; + private final double effectY; + private final double effectZ; + private final EntityItem entityItem; + private final TileEntity tile; + + private ScanTarget(byte type, int entityId, int x, int y, int z, double effectX, double effectY, double effectZ, + EntityItem entityItem, TileEntity tile) { + this.type = type; + this.entityId = entityId; + this.x = x; + this.y = y; + this.z = z; + this.effectX = effectX; + this.effectY = effectY; + this.effectZ = effectZ; + this.entityItem = entityItem; + this.tile = tile; + } + + private static ScanTarget none() { + return new ScanTarget(TARGET_NONE, 0, 0, 0, 0, 0, 0, 0, null, null); + } + + private static ScanTarget forEntityItem(EntityItem entityItem) { + return new ScanTarget( + TARGET_ENTITY_ITEM, + entityItem.getEntityId(), + 0, + 0, + 0, + entityItem.posX, + entityItem.posY + 0.5D, + entityItem.posZ, + entityItem, + null); + } + + private static ScanTarget forCellContainer(MovingObjectPosition position, TileEntity tile) { + return new ScanTarget( + TARGET_CELL_CONTAINER, + 0, + position.blockX, + position.blockY, + position.blockZ, + position.blockX, + position.blockY, + position.blockZ, + null, + tile); + } + + private boolean isValid() { + return this.type != TARGET_NONE; + } } } diff --git a/src/main/java/thaumicenergistics/common/network/NetworkHandler.java b/src/main/java/thaumicenergistics/common/network/NetworkHandler.java index f7591f0b..38133b29 100644 --- a/src/main/java/thaumicenergistics/common/network/NetworkHandler.java +++ b/src/main/java/thaumicenergistics/common/network/NetworkHandler.java @@ -10,6 +10,7 @@ import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; import cpw.mods.fml.relauncher.Side; import thaumicenergistics.common.ThaumicEnergistics; import thaumicenergistics.common.network.packet.client.Packet_C_ArcaneCraftingTerminal; +import thaumicenergistics.common.network.packet.client.Packet_C_CellMicroscopeScanFeedback; import thaumicenergistics.common.network.packet.client.Packet_C_DistillationEncoder; import thaumicenergistics.common.network.packet.client.Packet_C_EssentiaVibrationChamber; import thaumicenergistics.common.network.packet.client.Packet_C_ExportBusVoidButtonUpdate; @@ -127,6 +128,9 @@ public class NetworkHandler { // Sync packet registerPacket(Packet_C_Sync.class); + // Cell microscope + registerPacket(Packet_C_CellMicroscopeScanFeedback.class); + registerPacket(Packet_S_NEIRecipe.class); } diff --git a/src/main/java/thaumicenergistics/common/network/packet/client/Packet_C_CellMicroscopeScanFeedback.java b/src/main/java/thaumicenergistics/common/network/packet/client/Packet_C_CellMicroscopeScanFeedback.java new file mode 100644 index 00000000..04034847 --- /dev/null +++ b/src/main/java/thaumicenergistics/common/network/packet/client/Packet_C_CellMicroscopeScanFeedback.java @@ -0,0 +1,48 @@ +package thaumicenergistics.common.network.packet.client; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import thaumcraft.api.research.ScanResult; +import thaumcraft.common.lib.research.ScanManager; +import thaumicenergistics.common.network.NetworkHandler; + +public class Packet_C_CellMicroscopeScanFeedback extends ThEClientPacket { + + private int itemId; + private int itemDamage; + + public static void sendScanFeedback(final EntityPlayer player, final ScanResult scanResult) { + Packet_C_CellMicroscopeScanFeedback packet = new Packet_C_CellMicroscopeScanFeedback(); + + packet.player = player; + packet.itemId = scanResult.id; + packet.itemDamage = scanResult.meta; + + NetworkHandler.sendPacketToClient(packet); + } + + @Override + protected void readData(final ByteBuf stream) { + this.itemId = stream.readInt(); + this.itemDamage = stream.readInt(); + } + + @SideOnly(Side.CLIENT) + @Override + protected void wrappedExecute() { + ScanResult scanResult = new ScanResult((byte) 1, this.itemId, this.itemDamage, null, ""); + if (Item.getItemById(this.itemId) != null) { + ScanManager.completeScan(this.player, scanResult, "@"); + } + } + + @Override + protected void writeData(final ByteBuf stream) { + stream.writeInt(this.itemId); + stream.writeInt(this.itemDamage); + } +}