From ac5832fd1a9fd87e00914c4546f9be01e651278b Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:27:34 -0700 Subject: [PATCH 01/22] api/features: Move iface term functionality to API Does two things: * Deprecate old interface terminal support classes. * Refactors the interface terminal API into AEApi. * Changes the API to use only relevant operations. Relevant here means things we will actually use when the packet format is updated in the next few commits. This is a breaking change, but it won't be hard to fix. --- .../api/features/IIfaceTermRegistry.java | 14 +++++ .../api/features/IRegistryContainer.java | 2 + .../appeng/api/util/IIfaceTermViewable.java | 51 +++++++++++++++++++ .../registries/IfaceTermRegistry.java | 45 ++++++++++++++++ .../registries/RegistryContainer.java | 7 +++ .../helpers/IInterfaceTerminalSupport.java | 10 ++++ ...terfaceTerminalSupportedClassProvider.java | 27 ++++------ 7 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 src/main/java/appeng/api/features/IIfaceTermRegistry.java create mode 100644 src/main/java/appeng/api/util/IIfaceTermViewable.java create mode 100644 src/main/java/appeng/core/features/registries/IfaceTermRegistry.java diff --git a/src/main/java/appeng/api/features/IIfaceTermRegistry.java b/src/main/java/appeng/api/features/IIfaceTermRegistry.java new file mode 100644 index 00000000000..7f9baf4c64c --- /dev/null +++ b/src/main/java/appeng/api/features/IIfaceTermRegistry.java @@ -0,0 +1,14 @@ +package appeng.api.features; + +import appeng.api.util.IIfaceTermViewable; + +/** + * Registry for interface terminal support. + */ +public interface IIfaceTermRegistry { + + /** + * Registers a class to be considered supported in interface terminals. + */ + void register(Class clazz); +} diff --git a/src/main/java/appeng/api/features/IRegistryContainer.java b/src/main/java/appeng/api/features/IRegistryContainer.java index d1b922ee9e6..8dc002fd598 100644 --- a/src/main/java/appeng/api/features/IRegistryContainer.java +++ b/src/main/java/appeng/api/features/IRegistryContainer.java @@ -73,6 +73,8 @@ public interface IRegistryContainer { */ IInscriberRegistry inscriber(); + IIfaceTermRegistry ifaceTerm(); + /** * get access to the locatable registry */ diff --git a/src/main/java/appeng/api/util/IIfaceTermViewable.java b/src/main/java/appeng/api/util/IIfaceTermViewable.java new file mode 100644 index 00000000000..0ae440cf72f --- /dev/null +++ b/src/main/java/appeng/api/util/IIfaceTermViewable.java @@ -0,0 +1,51 @@ +package appeng.api.util; + +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; + +import appeng.api.networking.IGridHost; + +/** + * Replacement for {@code IInterfaceTerminalSupport} in API. + */ +public interface IIfaceTermViewable extends IGridHost { + + DimensionalCoord getLocation(); + + /** + * Number of rows to expect. This is used with {@link #rowSize()} to determine how to render the slots. + */ + int rows(); + + /** + * Number of slots per row. + */ + int rowSize(); + + /** + * Get the patterns. If multiple rows are supported, this is assumed to be tightly packed. Use {@link #rowSize()} + * with {@link #rows()} to determine where a row starts. + */ + IInventory getPatterns(); + + String getName(); + + TileEntity getTileEntity(); + + boolean shouldDisplay(); + + /** + * Self representation + */ + default ItemStack getSelfRep() { + return null; + } + + /** + * "Target" Display representation + */ + default ItemStack getDisplayRep() { + return null; + } +} diff --git a/src/main/java/appeng/core/features/registries/IfaceTermRegistry.java b/src/main/java/appeng/core/features/registries/IfaceTermRegistry.java new file mode 100644 index 00000000000..846fdb8127f --- /dev/null +++ b/src/main/java/appeng/core/features/registries/IfaceTermRegistry.java @@ -0,0 +1,45 @@ +package appeng.core.features.registries; + +import java.util.HashSet; +import java.util.Set; + +import appeng.api.features.IIfaceTermRegistry; +import appeng.api.util.IIfaceTermViewable; +import appeng.parts.misc.PartInterface; +import appeng.parts.p2p.PartP2PInterface; +import appeng.tile.misc.TileInterface; + +/** + * Interface Terminal Registry impl for registering viewable instances. + */ +public class IfaceTermRegistry implements IIfaceTermRegistry { + + private final Set> supportedClasses = new HashSet<>(); + private static IfaceTermRegistry INSTANCE; + + /** + * Singleton, do not instantiate more than once. + */ + public IfaceTermRegistry() { + supportedClasses.add(TileInterface.class); + supportedClasses.add(PartInterface.class); + supportedClasses.add(PartP2PInterface.class); + INSTANCE = this; + } + + /** + * Get all supported classes that were registered during startup + */ + public Set> getSupportedClasses() { + return supportedClasses; + } + + @Override + public void register(Class clazz) { + supportedClasses.add(clazz); + } + + public static IfaceTermRegistry instance() { + return INSTANCE; + } +} diff --git a/src/main/java/appeng/core/features/registries/RegistryContainer.java b/src/main/java/appeng/core/features/registries/RegistryContainer.java index 7c1d4086b50..506bb73c0ec 100644 --- a/src/main/java/appeng/core/features/registries/RegistryContainer.java +++ b/src/main/java/appeng/core/features/registries/RegistryContainer.java @@ -11,6 +11,7 @@ package appeng.core.features.registries; import appeng.api.features.IGrinderRegistry; +import appeng.api.features.IIfaceTermRegistry; import appeng.api.features.IInscriberRegistry; import appeng.api.features.ILocatableRegistry; import appeng.api.features.IMatterCannonAmmoRegistry; @@ -42,6 +43,7 @@ public class RegistryContainer implements IRegistryContainer { private final IExternalStorageRegistry storage = new ExternalStorageRegistry(); private final ICellRegistry cell = new CellRegistry(); private final IItemDisplayRegistry itemDisplay = new ItemDisplayRegistry(); + private final IIfaceTermRegistry ifaceTerm = new IfaceTermRegistry(); private final ILocatableRegistry locatable = new LocatableRegistry(); private final ISpecialComparisonRegistry comparison = new SpecialComparisonRegistry(); private final IWirelessTermRegistry wireless = new WirelessRegistry(); @@ -97,6 +99,11 @@ public IInscriberRegistry inscriber() { return this.inscriber; } + @Override + public IIfaceTermRegistry ifaceTerm() { + return this.ifaceTerm; + } + @Override public ILocatableRegistry locatable() { return this.locatable; diff --git a/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java b/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java index ffc4fcd7b0b..9b4c7a9b5df 100644 --- a/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java +++ b/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java @@ -6,10 +6,18 @@ import appeng.api.networking.IGridHost; import appeng.api.util.DimensionalCoord; +/** + * Refactoring this class into API, and renaming. + * + * @see appeng.api.util.IIfaceTermViewable + */ +@Deprecated public interface IInterfaceTerminalSupport extends IGridHost { class PatternsConfiguration { + /** This property should be used in the terminal level and items tightly packed. */ + @Deprecated public int offset; public int size; @@ -23,12 +31,14 @@ public PatternsConfiguration(int offset, int size) { PatternsConfiguration[] getPatternsConfigurations(); + @Deprecated IInventory getPatterns(int index); String getName(); TileEntity getTileEntity(); + @Deprecated default long getSortValue() { var te = getTileEntity(); return ((long) te.zCoord << 24) ^ ((long) te.xCoord << 8) ^ te.yCoord; diff --git a/src/main/java/appeng/helpers/InterfaceTerminalSupportedClassProvider.java b/src/main/java/appeng/helpers/InterfaceTerminalSupportedClassProvider.java index 01746b750ce..6f6e95f5565 100644 --- a/src/main/java/appeng/helpers/InterfaceTerminalSupportedClassProvider.java +++ b/src/main/java/appeng/helpers/InterfaceTerminalSupportedClassProvider.java @@ -1,27 +1,20 @@ package appeng.helpers; -import java.util.HashSet; import java.util.Set; -import appeng.parts.misc.PartInterface; -import appeng.parts.p2p.PartP2PInterface; -import appeng.tile.misc.TileInterface; - +/** + * Interface Terminal Support handler. + * + * @deprecated This is being refactored to the API. + */ +@Deprecated public class InterfaceTerminalSupportedClassProvider { - private static final Set> supportedClasses = new HashSet<>(); - - static { - supportedClasses.add(TileInterface.class); - supportedClasses.add(PartInterface.class); - supportedClasses.add(PartP2PInterface.class); - } - + @Deprecated public static Set> getSupportedClasses() { - return supportedClasses; + return null; } - public static void register(Class clazz) { - supportedClasses.add(clazz); - } + @Deprecated + public static void register(Class clazz) {} } From ec392c1f49ee6ae65ad64b56d68873678b4cb01c Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:28:41 -0700 Subject: [PATCH 02/22] me/cache: Add indication of booting/nonbooting to event The booting/nonbooting event is posted whenever the ME system starts its "boot" phase and when the "boot" phase is finished. This commit distinguishes the two phases. Some subscribers might need this information to determine whether to push an update or not. An example is written in this commit - part interface terminal is only curious about when the terminal boots back up, not when the network enters the booting state. --- .../events/MENetworkBootingStatusChange.java | 9 +++++++++ .../java/appeng/me/cache/PathGridCache.java | 4 ++-- .../reporting/PartInterfaceTerminal.java | 20 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/appeng/api/networking/events/MENetworkBootingStatusChange.java b/src/main/java/appeng/api/networking/events/MENetworkBootingStatusChange.java index da45dbe896e..025ad1ce285 100644 --- a/src/main/java/appeng/api/networking/events/MENetworkBootingStatusChange.java +++ b/src/main/java/appeng/api/networking/events/MENetworkBootingStatusChange.java @@ -22,4 +22,13 @@ * Note: Most machines just need to check {@link IGridNode}.isActive() */ public class MENetworkBootingStatusChange extends MENetworkEvent { + + /** + * Indicates whether the network has changed to this state when the event is posted. + */ + public final boolean isBooting; + + public MENetworkBootingStatusChange(boolean isBooting) { + this.isBooting = isBooting; + } } diff --git a/src/main/java/appeng/me/cache/PathGridCache.java b/src/main/java/appeng/me/cache/PathGridCache.java index a51f347be60..73d7f277909 100644 --- a/src/main/java/appeng/me/cache/PathGridCache.java +++ b/src/main/java/appeng/me/cache/PathGridCache.java @@ -85,7 +85,7 @@ public void onUpdateTick() { if (this.updateNetwork) { if (!this.booting) { - this.myGrid.postEvent(new MENetworkBootingStatusChange()); + this.myGrid.postEvent(new MENetworkBootingStatusChange(true)); } this.booting = true; @@ -186,7 +186,7 @@ public void onUpdateTick() { this.booting = false; this.setChannelPowerUsage(this.getChannelsByBlocks() / 128.0); - this.myGrid.postEvent(new MENetworkBootingStatusChange()); + this.myGrid.postEvent(new MENetworkBootingStatusChange(false)); } } } diff --git a/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java b/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java index f914fa4309f..d3f1744f6ee 100644 --- a/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java +++ b/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java @@ -14,6 +14,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.Vec3; +import appeng.api.networking.events.MENetworkBootingStatusChange; +import appeng.api.networking.events.MENetworkEventSubscribe; import appeng.client.texture.CableBusTextures; import appeng.core.sync.GuiBridge; import appeng.util.Platform; @@ -23,6 +25,7 @@ public class PartInterfaceTerminal extends AbstractPartDisplay { private static final CableBusTextures FRONT_BRIGHT_ICON = CableBusTextures.PartInterfaceTerm_Bright; private static final CableBusTextures FRONT_DARK_ICON = CableBusTextures.PartInterfaceTerm_Dark; private static final CableBusTextures FRONT_COLORED_ICON = CableBusTextures.PartInterfaceTerm_Colored; + private boolean needsUpdate; public PartInterfaceTerminal(final ItemStack is) { super(is); @@ -59,4 +62,21 @@ public CableBusTextures getFrontColored() { public CableBusTextures getFrontDark() { return FRONT_DARK_ICON; } + + /** + * Indicates that the network has changed. Calling this will reset the flag. + */ + public boolean needsUpdate() { + boolean ret = needsUpdate; + + needsUpdate = false; + return ret; + } + + @MENetworkEventSubscribe + public void onNetworkBootingChanged(MENetworkBootingStatusChange event) { + if (!event.isBooting) { + this.needsUpdate = true; + } + } } From 203c64c8745c98870bb62d42dab6c78d2ffee906 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:27:28 -0700 Subject: [PATCH 03/22] appeng/core/sync/packets: Add PacketIfaceTermUpdate.java Adds a new packet specifically for updating the entries on the terminal. Reduces network traffic, potentially significantly, as it allows for incremental updates instead of sending a fresh list every time the entries on the list change (Note it WAS smart enough to not send a fresh list if only patterns changed). Deprecates appeng/core/sync/packets/PacketCompressedNBT Commit does not add functionality to handling on the client side yet. --- .../implementations/GuiInterfaceTerminal.java | 5 + .../core/sync/AppEngPacketHandlerBase.java | 4 +- .../sync/packets/PacketCompressedNBT.java | 7 +- .../sync/packets/PacketIfaceTermUpdate.java | 456 ++++++++++++++++++ 4 files changed, 467 insertions(+), 5 deletions(-) create mode 100644 src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 1ee28f24126..ebb7587d7bf 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -18,6 +18,7 @@ import java.util.Set; import java.util.WeakHashMap; +import appeng.core.sync.packets.PacketIfaceTermUpdate; import net.minecraft.client.gui.GuiButton; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; @@ -425,6 +426,10 @@ private boolean handleTab() { return false; } + public void postUpdate(List updates) { + + } + public void postUpdate(final NBTTagCompound in) { if (in.getBoolean("clear")) { this.byId.clear(); diff --git a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java index 4102a1f6eb1..e3d5c17922c 100644 --- a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java +++ b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java @@ -26,6 +26,7 @@ import appeng.core.sync.packets.PacketCraftingItemInterface; import appeng.core.sync.packets.PacketCraftingRemainingOperations; import appeng.core.sync.packets.PacketCraftingTreeData; +import appeng.core.sync.packets.PacketIfaceTermUpdate; import appeng.core.sync.packets.PacketInventoryAction; import appeng.core.sync.packets.PacketLightning; import appeng.core.sync.packets.PacketMEInventoryUpdate; @@ -110,7 +111,8 @@ public enum PacketTypes { PACKET_CRAFTING_REMAINING_OPERATIONS(PacketCraftingRemainingOperations.class), PACKET_CRAFTING_ITEM_INTERFACE(PacketCraftingItemInterface.class), PACKET_CRAFTING_TREE_DATA(PacketCraftingTreeData.class), - PACKET_NEI_BOOKMARK(PacketNEIBookmark.class); + PACKET_NEI_BOOKMARK(PacketNEIBookmark.class), + PACKET_IFACE_TERMINAL_UPDATE(PacketIfaceTermUpdate.class),; private final Class packetClass; private final Constructor packetConstructor; diff --git a/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java b/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java index 1f34d5f4a8a..7a59fddfd6f 100644 --- a/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java +++ b/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java @@ -24,7 +24,6 @@ import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; -import appeng.client.gui.implementations.GuiInterfaceTerminal; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; import cpw.mods.fml.relauncher.Side; @@ -89,8 +88,8 @@ public void write(final int value) throws IOException { public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) { final GuiScreen gs = Minecraft.getMinecraft().currentScreen; - if (gs instanceof GuiInterfaceTerminal) { - ((GuiInterfaceTerminal) gs).postUpdate(this.in); - } + // if (gs instanceof GuiInterfaceTerminal) { + // ((GuiInterfaceTerminal) gs).postUpdate(this.in); + // } } } diff --git a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java new file mode 100644 index 00000000000..e7c5e6b585c --- /dev/null +++ b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java @@ -0,0 +1,456 @@ +package appeng.core.sync.packets; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants.NBT; + +import appeng.client.gui.implementations.GuiInterfaceTerminal; +import appeng.core.AELog; +import appeng.core.sync.AppEngPacket; +import appeng.core.sync.network.INetworkInfo; +import appeng.helpers.Reflected; +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; + +/** + * Packet used for interface terminal updates. Packet allows the server to send an array of command packets, which are + * then processed in order. This allows for chaining commands to produce the desired update. + */ +public class PacketIfaceTermUpdate extends AppEngPacket { + + public static final int CLEAR_ALL_BIT = 1; + public static final int DISCONNECT_BIT = 2; + + private final List commands = new ArrayList<>(); + private int statusFlags; + + @Reflected + public PacketIfaceTermUpdate(final ByteBuf buf) throws IOException { + decode(buf); + } + + public PacketIfaceTermUpdate() {} + + private void decode(ByteBuf buf) { + this.statusFlags = buf.readByte(); + int numEntries = buf.readInt(); + + for (int i = 0; i < numEntries; ++i) { + int packetType = -1; + try { + packetType = buf.readByte(); + PacketType type = PacketType.valueOf(packetType); + switch (type) { + case ADD -> this.commands.add(new PacketAdd(buf)); + case REMOVE -> this.commands.add(new PacketRemove(buf)); + case OVERWRITE -> this.commands.add(new PacketOverwrite(buf)); + case RENAME -> this.commands.add(new PacketRename(buf)); + } + } catch (ArrayIndexOutOfBoundsException e) { + AELog.error("Unknown packet type of type %d (-1 = uninitialized)", packetType); + break; + } catch (IOException e) { + AELog.error(e); + break; + } + } + } + + public void encode() { + try { + ByteBuf buf = Unpooled.buffer(2048); + buf.writeInt(this.getPacketID()); + buf.writeByte(this.statusFlags); + buf.writeInt(commands.size()); + for (PacketEntry entry : commands) { + entry.write(buf); + } + super.configureWrite(buf); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Remove all entries on the terminal. This is done BEFORE any entries are processed, so you can set this to clear + * old entries, and add new ones after in one packet. + */ + public void setClear() { + this.statusFlags |= CLEAR_ALL_BIT; + } + + /** + * The terminal disconnected. Entries are still processed, but indicates for the GUI to darken/turn off the + * terminal. No further updates will arrive until the terminal reconnects. + */ + public void setDisconnect() { + this.statusFlags |= DISCONNECT_BIT; + } + + /** + * Adds a new entry. Fill out the rest of the command using the {@link PacketAdd#setItems(int, int, NBTTagList)} and + * {@link PacketAdd#setLoc(int, int, int, int)}. + * + * @return the packet, which needs to have information filled out. + */ + public PacketAdd addNewEntry(long id, String name, boolean online) { + PacketAdd packet = new PacketAdd(id, name, online); + + commands.add(packet); + return packet; + } + + /** + * Remove the entry with the id from the terminal. + */ + public void addRemovalEntry(long id) { + commands.add(new PacketRemove(id)); + } + + /** + * Rename the entry + */ + public void addRenamedEntry(long id, String newName) { + commands.add(new PacketRename(id, newName)); + } + + /** + * Overwrite the entry with new items or new status + */ + public PacketOverwrite addOverwriteEntry(long id) { + PacketOverwrite packet = new PacketOverwrite(id); + + commands.add(packet); + return packet; + } + + @Override + @SideOnly(Side.CLIENT) + public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) { + final GuiScreen gs = Minecraft.getMinecraft().currentScreen; + + if (gs instanceof GuiInterfaceTerminal) { + ((GuiInterfaceTerminal) gs).postUpdate(this.commands, this.statusFlags); + } + } + + enum PacketType { + + ADD, + REMOVE, + OVERWRITE, + RENAME; + + public static final PacketType[] TYPES = PacketType.values(); + + /** + * Get the indexed value of packet type, throws on invalid index + */ + public static PacketType valueOf(int idx) throws ArrayIndexOutOfBoundsException { + return TYPES[idx]; + } + } + + /** + * A packet for updating an entry. + */ + public abstract static class PacketEntry { + + public final long entryId; + + protected PacketEntry(long entryId) { + this.entryId = entryId; + } + + protected PacketEntry(ByteBuf buf) throws IOException { + this.entryId = buf.readLong(); + read(buf); + } + + /** + * Needs to write the packet id. + */ + protected abstract void write(ByteBuf buf) throws IOException; + + /** + * Reading the entry id is not needed. + */ + protected abstract void read(ByteBuf buf) throws IOException; + } + + /** + * A command for sending a new entry. + */ + public static class PacketAdd extends PacketEntry { + + public String name; + public int x, y, z, dim; + public int rows, rowSize; + public boolean online; + public ItemStack selfRep, dispRep; + public NBTTagList items; + + PacketAdd(long id, String name, boolean online) { + super(id); + this.name = name; + this.online = online; + } + + PacketAdd(ByteBuf buf) throws IOException { + super(buf); + } + + public PacketAdd setLoc(int x, int y, int z, int dim) { + this.x = x; + this.y = y; + this.z = z; + this.dim = dim; + return this; + } + + public PacketAdd setItems(int rows, int rowSize, NBTTagList items) { + this.rows = rows; + this.rowSize = rowSize; + this.items = items; + + return this; + } + + /** + * Set representatives for the interface + * + * @param selfRep the stack representing me + * @param dispRep the stack representing my target + */ + public PacketAdd setReps(ItemStack selfRep, ItemStack dispRep) { + this.selfRep = selfRep; + this.dispRep = dispRep; + + return this; + } + + @Override + protected void write(ByteBuf buf) throws IOException { + buf.writeByte(PacketType.ADD.ordinal()); + buf.writeLong(entryId); + ByteBufUtils.writeUTF8String(buf, this.name); + buf.writeInt(x); + buf.writeInt(y); + buf.writeInt(z); + buf.writeInt(dim); + buf.writeInt(rows); + buf.writeInt(rowSize); + + ByteBuf tempBuf = Unpooled.buffer(256); + try (ByteBufOutputStream stream = new ByteBufOutputStream(tempBuf)) { + + NBTTagCompound wrapper = new NBTTagCompound(); + + if (selfRep != null) { + wrapper.setTag("self", selfRep.writeToNBT(new NBTTagCompound())); + } + if (dispRep != null) { + wrapper.setTag("disp", dispRep.writeToNBT(new NBTTagCompound())); + } + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); + } + try { + buf.writeInt(tempBuf.readableBytes()); + buf.writeBytes(tempBuf); + } finally { + tempBuf.release(); + } + } + + @Override + protected void read(ByteBuf buf) throws IOException { + this.name = ByteBufUtils.readUTF8String(buf); + this.x = buf.readInt(); + this.y = buf.readInt(); + this.z = buf.readInt(); + this.dim = buf.readInt(); + this.rows = buf.readInt(); + this.rowSize = buf.readInt(); + int payloadSize = buf.readInt(); + try (ByteBufInputStream stream = new ByteBufInputStream(buf, payloadSize)) { + NBTTagCompound payload = CompressedStreamTools.readCompressed(stream); + if (payload.hasKey("self", NBT.TAG_COMPOUND)) { + this.selfRep = ItemStack.loadItemStackFromNBT(payload.getCompoundTag("self")); + } + + if (payload.hasKey("disp", NBT.TAG_COMPOUND)) { + this.dispRep = ItemStack.loadItemStackFromNBT(payload.getCompoundTag("disp")); + } + + this.items = payload.getTagList("data", NBT.TAG_COMPOUND); + } + } + } + + public static class PacketRemove extends PacketEntry { + + PacketRemove(long id) { + super(id); + } + + PacketRemove(ByteBuf buf) throws IOException { + super(buf); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(PacketType.REMOVE.ordinal()); + buf.writeLong(entryId); + } + + @Override + protected void read(ByteBuf buf) {} + } + + /** + * Overwrite online status or inventory of the entry. + */ + public static class PacketOverwrite extends PacketEntry { + + public static final int ONLINE_BIT = 1; + public static final int ONLINE_VALID = 1 << 1; + public static final int ITEMS_VALID = 1 << 2; + public static final int ALL_ITEM_UPDATE_BIT = 1 << 3; + public boolean onlineValid; + public boolean online; + public boolean itemsValid; + public boolean allItemUpdate; + public int[] validIndices; + public NBTTagList items; + + protected PacketOverwrite(long id) { + super(id); + } + + protected PacketOverwrite(ByteBuf buf) throws IOException { + super(buf); + } + + public PacketOverwrite setOnline(boolean online) { + this.onlineValid = true; + this.online = online; + return this; + } + + public PacketOverwrite setItems(int[] validIndices, NBTTagList items) { + this.itemsValid = true; + this.allItemUpdate = validIndices == null || validIndices.length == 0; + this.validIndices = validIndices; + this.items = items; + + return this; + } + + @Override + protected void write(ByteBuf buf) throws IOException { + buf.writeByte(PacketType.OVERWRITE.ordinal()); + buf.writeLong(entryId); + + int flags = 0; + + if (onlineValid) { + flags |= ONLINE_VALID; + flags |= online ? ONLINE_BIT : 0; + } + if (itemsValid) { + flags |= ITEMS_VALID; + if (allItemUpdate) { + flags |= ALL_ITEM_UPDATE_BIT; + buf.writeByte(flags); + } else { + buf.writeByte(flags); + buf.writeInt(validIndices.length); + for (int validIndex : validIndices) { + buf.writeInt(validIndex); + } + } + try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) { + + NBTTagCompound wrapper = new NBTTagCompound(); + + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); + } + } else { + buf.writeByte(flags); + } + } + + @Override + protected void read(ByteBuf buf) throws IOException { + int flags = buf.readByte(); + + /* Decide whether to use online flag or not */ + if ((flags & ONLINE_VALID) == ONLINE_VALID) { + this.onlineValid = true; + this.online = (flags & ONLINE_BIT) == ONLINE_BIT; + } + /* Decide whether to read item list or not */ + if ((flags & ITEMS_VALID) == ITEMS_VALID) { + if ((flags & ALL_ITEM_UPDATE_BIT) == ALL_ITEM_UPDATE_BIT) { + this.allItemUpdate = true; + } else { + int numItems = buf.readInt(); + this.itemsValid = true; + this.validIndices = new int[numItems]; + for (int i = 0; i < numItems; ++i) { + this.validIndices[i] = buf.readInt(); + } + } + + try (ByteBufInputStream stream = new ByteBufInputStream(buf)) { + this.items = CompressedStreamTools.readCompressed(stream).getTagList("data", NBT.TAG_COMPOUND); + } + } + } + } + + /** + * Rename the entry. + */ + public static class PacketRename extends PacketEntry { + + public String newName; + + protected PacketRename(long id, String newName) { + super(id); + this.newName = newName; + } + + protected PacketRename(ByteBuf buf) throws IOException { + super(buf); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(PacketType.RENAME.ordinal()); + buf.writeLong(entryId); + ByteBufUtils.writeUTF8String(buf, newName); + } + + @Override + protected void read(ByteBuf buf) { + newName = ByteBufUtils.readUTF8String(buf); + } + } +} From a89f6346331991b7c813f09ba73ba927a8618773 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:33:16 -0700 Subject: [PATCH 04/22] appeng/helpers: Use new iface terminal API --- .../java/appeng/helpers/DualityInterface.java | 5 --- .../java/appeng/helpers/IInterfaceHost.java | 33 +++++++++---------- .../java/appeng/parts/misc/PartInterface.java | 5 +++ .../appeng/parts/p2p/PartP2PInterface.java | 5 +++ .../java/appeng/tile/misc/TileInterface.java | 5 +++ 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/main/java/appeng/helpers/DualityInterface.java b/src/main/java/appeng/helpers/DualityInterface.java index 31ef4fe3b92..68d4d6c5329 100644 --- a/src/main/java/appeng/helpers/DualityInterface.java +++ b/src/main/java/appeng/helpers/DualityInterface.java @@ -1155,11 +1155,6 @@ public String getTermName() { } } - public long getSortValue() { - final TileEntity te = this.iHost.getTileEntity(); - return (te.zCoord << 24) ^ (te.xCoord << 8) ^ te.yCoord; - } - public BaseActionSource getActionSource() { return interfaceRequestSource; } diff --git a/src/main/java/appeng/helpers/IInterfaceHost.java b/src/main/java/appeng/helpers/IInterfaceHost.java index 2cdcb0ec67f..e5db9ca329b 100644 --- a/src/main/java/appeng/helpers/IInterfaceHost.java +++ b/src/main/java/appeng/helpers/IInterfaceHost.java @@ -10,11 +10,10 @@ package appeng.helpers; -import java.util.ArrayList; import java.util.EnumSet; -import java.util.List; import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; @@ -24,9 +23,9 @@ import appeng.api.implementations.IUpgradeableHost; import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.crafting.ICraftingRequester; +import appeng.api.util.IIfaceTermViewable; -public interface IInterfaceHost - extends ICraftingProvider, IUpgradeableHost, ICraftingRequester, IInterfaceTerminalSupport { +public interface IInterfaceHost extends ICraftingProvider, IUpgradeableHost, ICraftingRequester, IIfaceTermViewable { DualityInterface getInterfaceDuality(); @@ -37,17 +36,7 @@ public interface IInterfaceHost void saveChanges(); @Override - default PatternsConfiguration[] getPatternsConfigurations() { - DualityInterface dual = getInterfaceDuality(); - List patterns = new ArrayList<>(); - for (int i = 0; i <= dual.getInstalledUpgrades(Upgrades.PATTERN_CAPACITY); ++i) { - patterns.add(new PatternsConfiguration(i * 9, 9)); - } - return patterns.toArray(new PatternsConfiguration[0]); - } - - @Override - default IInventory getPatterns(int index) { + default IInventory getPatterns() { return getInterfaceDuality().getPatterns(); } @@ -62,7 +51,17 @@ default String getName() { } @Override - default long getSortValue() { - return getInterfaceDuality().getSortValue(); + default int rows() { + return getInterfaceDuality().getInstalledUpgrades(Upgrades.PATTERN_CAPACITY) + 1; + } + + @Override + default int rowSize() { + return DualityInterface.NUMBER_OF_PATTERN_SLOTS; + } + + @Override + default ItemStack getDisplayRep() { + return getInterfaceDuality().getCrafterIcon(); } } diff --git a/src/main/java/appeng/parts/misc/PartInterface.java b/src/main/java/appeng/parts/misc/PartInterface.java index 23704196044..d8a7f1064f8 100644 --- a/src/main/java/appeng/parts/misc/PartInterface.java +++ b/src/main/java/appeng/parts/misc/PartInterface.java @@ -386,4 +386,9 @@ public int getPriority() { public void setPriority(final int newValue) { this.duality.setPriority(newValue); } + + @Override + public ItemStack getSelfRep() { + return this.getItemStack(); + } } diff --git a/src/main/java/appeng/parts/p2p/PartP2PInterface.java b/src/main/java/appeng/parts/p2p/PartP2PInterface.java index 8ae7facbc7c..f5e9a2d25c7 100644 --- a/src/main/java/appeng/parts/p2p/PartP2PInterface.java +++ b/src/main/java/appeng/parts/p2p/PartP2PInterface.java @@ -390,4 +390,9 @@ public void onTunnelNetworkChange() { public boolean shouldDisplay() { return IInterfaceHost.super.shouldDisplay() && !isOutput(); } + + @Override + public ItemStack getSelfRep() { + return this.getItemStack(); + } } diff --git a/src/main/java/appeng/tile/misc/TileInterface.java b/src/main/java/appeng/tile/misc/TileInterface.java index 8409a7a6fbe..d3628bcf1b8 100644 --- a/src/main/java/appeng/tile/misc/TileInterface.java +++ b/src/main/java/appeng/tile/misc/TileInterface.java @@ -319,4 +319,9 @@ public boolean isActive() { public boolean isBooting() { return (clientFlags & BOOTING_FLAG) == BOOTING_FLAG; } + + @Override + public ItemStack getSelfRep() { + return this.getItemFromTile(this); + } } From 6597d85b3d329a1681a36fadc537c5f89d1043e6 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:36:59 -0700 Subject: [PATCH 05/22] appeng/container/implementations: Optimize iface terminal The interface terminal is ripe for many optimizations. This commit seeks to fix the biggest issues: * Every tick, the interface terminal checks for any network changes. This has been improved to check only when the network has finished booting. * Every tick, the interface terminal iterates through all of its tracked inventories to check for changes. This is bad when the ME system has a nontrivial amount of tracked inventories, because it it tries to check whether items are different, and is expensive for NBT items such as patterns. This has been changed to send changes that have been marked dirty. This commit does not implement handling on the GUI side. --- .../ContainerInterfaceTerminal.java | 517 ++++++++++-------- 1 file changed, 278 insertions(+), 239 deletions(-) diff --git a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java index 7cfb9467501..2b9dbb6c1ca 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java +++ b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java @@ -1,3 +1,4 @@ + /* * This file is part of Applied Energistics 2. Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. Applied * Energistics 2 is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General @@ -10,62 +11,69 @@ package appeng.container.implementations; -import java.io.IOException; -import java.util.Collection; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraftforge.common.util.ForgeDirection; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; +import com.google.common.primitives.Ints; import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; -import appeng.api.networking.security.IActionHost; import appeng.api.util.DimensionalCoord; +import appeng.api.util.IIfaceTermViewable; import appeng.container.AEBaseContainer; +import appeng.core.features.registries.IfaceTermRegistry; import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.PacketCompressedNBT; -import appeng.helpers.IInterfaceTerminalSupport; -import appeng.helpers.IInterfaceTerminalSupport.PatternsConfiguration; -import appeng.helpers.InterfaceTerminalSupportedClassProvider; +import appeng.core.sync.packets.PacketIfaceTermUpdate; import appeng.helpers.InventoryAction; import appeng.items.misc.ItemEncodedPattern; import appeng.parts.reporting.PartInterfaceTerminal; -import appeng.tile.inventory.AppEngInternalInventory; import appeng.util.InventoryAdaptor; import appeng.util.Platform; -import appeng.util.inv.AdaptorIInventory; import appeng.util.inv.AdaptorPlayerHand; import appeng.util.inv.ItemSlot; -import appeng.util.inv.WrapperInvSlot; public final class ContainerInterfaceTerminal extends AEBaseContainer { - /** - * this stuff is all server side.. - */ - private static long autoBase = Long.MIN_VALUE; + private int nextId = 0; - private final Multimap supportedInterfaces = HashMultimap.create(); - private final Map byId = new HashMap<>(); + private final Map tracked = new HashMap<>(); + private final Map trackedById = new HashMap<>(); + private PacketIfaceTermUpdate dirty; + private boolean isDirty; private IGrid grid; - private NBTTagCompound data = new NBTTagCompound(); + private PartInterfaceTerminal partRef; + private boolean wasOff; public ContainerInterfaceTerminal(final InventoryPlayer ip, final PartInterfaceTerminal anchor) { super(ip, anchor); - + assert anchor != null; + this.partRef = anchor; if (Platform.isServer()) { this.grid = anchor.getActionableNode().getGrid(); + dirty = this.updateList(); + if (dirty != null) { + dirty.encode(); + this.isDirty = true; + } else { + dirty = new PacketIfaceTermUpdate(); + } } - - this.bindPlayerInventory(ip, 14, 0); + this.bindPlayerInventory(ip, 14, 3); } @Override @@ -80,154 +88,128 @@ public void detectAndSendChanges() { return; } - int total = 0; - boolean missing = false; - - final IActionHost host = this.getActionHost(); - if (host != null) { - final IGridNode agn = host.getActionableNode(); - if (agn != null && agn.isActive()) { - for (var clz : InterfaceTerminalSupportedClassProvider.getSupportedClasses()) { - for (final IGridNode gn : this.grid.getMachines(clz)) { - final IInterfaceTerminalSupport interfaceTerminalSupport = (IInterfaceTerminalSupport) gn - .getMachine(); - if (!gn.isActive() || !interfaceTerminalSupport.shouldDisplay()) continue; - - final Collection t = supportedInterfaces.get(interfaceTerminalSupport); - final String name = interfaceTerminalSupport.getName(); - missing = t.isEmpty() || t.stream().anyMatch(it -> !it.unlocalizedName.equals(name)); - total += interfaceTerminalSupport.getPatternsConfigurations().length; - if (missing) break; - } - // we can stop if any is missing. The value of `total` is not important if `missing == true` - if (missing) break; - } - } - } + final IGridNode agn = this.partRef.getActionableNode(); - if (total != this.supportedInterfaces.size() || missing) { - this.regenList(this.data); - } else { - for (final InvTracker inv : supportedInterfaces.values()) { - for (int x = 0; x < inv.client.getSizeInventory(); x++) { - if (this.isDifferent(inv.server.getStackInSlot(inv.offset + x), inv.client.getStackInSlot(x))) { - this.addItems(this.data, inv, x, 1); - } - } + if (!agn.isActive()) { + /* + * Should turn off the terminal. However, there's no need to remove all the entries from the client. + * Continue tracking on the server just in case the system comes back online. Just don't send any new + * updates. This prevents DoSing the player if their network is flickering. + */ + if (!this.wasOff) { + PacketIfaceTermUpdate update = new PacketIfaceTermUpdate(); + + update.setDisconnect(); + this.wasOff = true; + NetworkHandler.instance.sendTo(update, (EntityPlayerMP) this.getPlayerInv().player); } + return; } + this.wasOff = false; - if (!this.data.hasNoTags()) { - try { - NetworkHandler.instance - .sendTo(new PacketCompressedNBT(this.data), (EntityPlayerMP) this.getPlayerInv().player); - } catch (final IOException e) { - // :P + if (this.partRef.needsUpdate()) { + PacketIfaceTermUpdate update = this.updateList(); + if (update != null) { + update.encode(); + NetworkHandler.instance.sendTo(update, (EntityPlayerMP) this.getPlayerInv().player); } - - this.data = new NBTTagCompound(); + } else if (isDirty) { + this.dirty.encode(); + NetworkHandler.instance.sendTo(this.dirty, (EntityPlayerMP) this.getPlayerInv().player); + this.dirty = new PacketIfaceTermUpdate(); + this.isDirty = false; } } @Override public void doAction(final EntityPlayerMP player, final InventoryAction action, final int slot, final long id) { - final InvTracker inv = this.byId.get(id); + final InvTracker inv = this.trackedById.get(id); if (inv != null) { - final ItemStack is = inv.server.getStackInSlot(slot + inv.offset); - final boolean hasItemInHand = player.inventory.getItemStack() != null; - - final InventoryAdaptor playerHand = new AdaptorPlayerHand(player); - - final WrapperInvSlot slotInv = new PatternInvSlot(inv.server); + final ItemStack handStack = player.inventory.getItemStack(); - final IInventory theSlot = slotInv.getWrapper(slot + inv.offset); - final InventoryAdaptor interfaceSlot = new AdaptorIInventory(theSlot); + if (handStack != null && !(handStack.getItem() instanceof ItemEncodedPattern)) { + // Why even bother if we're not dealing with an encoded pattern in hand + return; + } - IInventory interfaceHandler = inv.server; - boolean canInsert = true; + final ItemStack slotStack = inv.patterns.getStackInSlot(slot); + final InventoryAdaptor playerHand = new AdaptorPlayerHand(player); switch (action) { + /* Set down/pickup. This is the same as SPLIT_OR_PLACE_SINGLE as our max stack sizes are 1 in slots. */ case PICKUP_OR_SET_DOWN -> { - if (hasItemInHand) { - for (int s = 0; s < interfaceHandler.getSizeInventory(); s++) { - if (Platform.isSameItemPrecise( - interfaceHandler.getStackInSlot(s), - player.inventory.getItemStack())) { - canInsert = false; - break; + if (handStack != null) { + for (int s = 0; s < inv.patterns.getSizeInventory(); s++) { + /* Is there a duplicate pattern here? */ + if (Platform.isSameItemPrecise(inv.patterns.getStackInSlot(s), handStack)) { + /* We're done here - dupe found. */ + return; } } - if (canInsert) { - ItemStack inSlot = theSlot.getStackInSlot(0); - if (inSlot == null) { - player.inventory.setItemStack(interfaceSlot.addItems(player.inventory.getItemStack())); - } else { - inSlot = inSlot.copy(); - final ItemStack inHand = player.inventory.getItemStack().copy(); - - theSlot.setInventorySlotContents(0, null); - player.inventory.setItemStack(null); - - player.inventory.setItemStack(interfaceSlot.addItems(inHand.copy())); - - if (player.inventory.getItemStack() == null) { - player.inventory.setItemStack(inSlot); - } else { - player.inventory.setItemStack(inHand); - theSlot.setInventorySlotContents(0, inSlot); - } - } - } - } else { - final IInventory mySlot = slotInv.getWrapper(slot + inv.offset); - mySlot.setInventorySlotContents(0, playerHand.addItems(mySlot.getStackInSlot(0))); } - } - case SPLIT_OR_PLACE_SINGLE -> { - if (hasItemInHand) { - for (int s = 0; s < interfaceHandler.getSizeInventory(); s++) { - if (Platform.isSameItemPrecise( - interfaceHandler.getStackInSlot(s), - player.inventory.getItemStack())) { - canInsert = false; - break; - } - } - if (canInsert) { - ItemStack extra = playerHand.removeItems(1, null, null); - if (extra != null && !interfaceSlot.containsItems()) { - extra = interfaceSlot.addItems(extra); - } - if (extra != null) { - playerHand.addItems(extra); - } - } - } else if (is != null) { - ItemStack extra = interfaceSlot.removeItems((is.stackSize + 1) / 2, null, null); - if (extra != null) { - extra = playerHand.addItems(extra); + + if (slotStack == null) { + /* Insert to container, if valid */ + if (handStack == null) { + /* Nothing happens */ + return; } - if (extra != null) { - interfaceSlot.addItems(extra); + inv.patterns.setInventorySlotContents(slot, playerHand.removeItems(1, null, null)); + } else { + /* Exchange? */ + if (handStack != null && handStack.stackSize > 1) { + /* Exchange is impossible, abort */ + return; } + inv.patterns.setInventorySlotContents(slot, playerHand.removeItems(1, null, null)); + playerHand.addItems(slotStack.copy()); } + syncIfaceSlot(inv, id, slot, inv.patterns.getStackInSlot(slot)); } + /* Shift click from slot -> player. Player -> slot is not supported. */ case SHIFT_CLICK -> { - final IInventory mySlot = slotInv.getWrapper(slot + inv.offset); - final InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor(player, ForgeDirection.UNKNOWN); - mySlot.setInventorySlotContents(0, mergeToPlayerInventory(playerInv, mySlot.getStackInSlot(0))); + InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor(player.inventory, ForgeDirection.UNKNOWN); + ItemStack leftOver = mergeToPlayerInventory(playerInv, slotStack); + + if (leftOver == null) { + inv.patterns.setInventorySlotContents(slot, null); + syncIfaceSlot(inv, id, slot, null); + } } + /* Move all blank patterns -> player */ case MOVE_REGION -> { - final InventoryAdaptor playerInvAd = InventoryAdaptor.getAdaptor(player, ForgeDirection.UNKNOWN); - for (int x = 0; x < inv.client.getSizeInventory(); x++) { - inv.server.setInventorySlotContents( - x + inv.offset, - mergeToPlayerInventory(playerInvAd, inv.server.getStackInSlot(x + inv.offset))); + final InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor(player, ForgeDirection.UNKNOWN); + List valid = new ArrayList<>(); + + for (int i = 0; i < inv.patterns.getSizeInventory(); i++) { + ItemStack toExtract = inv.patterns.getStackInSlot(i); + + if (toExtract == null) { + continue; + } + + ItemStack leftOver = mergeToPlayerInventory(playerInv, toExtract); + + if (leftOver != null) { + break; + } else { + inv.patterns.setInventorySlotContents(i, null); + } + valid.add(i); + } + if (valid.size() > 0) { + int[] validIndices = Ints.toArray(valid); + NBTTagList tag = new NBTTagList(); + for (int i = 0; i < valid.size(); ++i) { + tag.appendTag(new NBTTagCompound()); + } + dirty.addOverwriteEntry(id).setItems(validIndices, tag); + isDirty = true; } } case CREATIVE_DUPLICATE -> { - if (player.capabilities.isCreativeMode && !hasItemInHand) { - player.inventory.setItemStack(is == null ? null : is.copy()); + if (player.capabilities.isCreativeMode) { + playerHand.addItems(handStack); } } default -> { @@ -239,6 +221,32 @@ public void doAction(final EntityPlayerMP player, final InventoryAction action, } } + /** + * Since we are not using "slots" like MC and instead taking the Thaumic Energistics route, this is used to + * eventually send a packet to the client to indicate that a slot has changed.
+ * Why weren't slots used? Because at first I was more concerned about performance issues, and by the time I + * realized I was perhaps making a huge mistake in not using the notion of a "slot", I had already finished + * implementing my own version of a "slot". Because of this everything in this GUI is basically hand written. Also, + * the previous implementation was seemingly allocating slots.
+ * // rant over + */ + private void syncIfaceSlot(InvTracker inv, long id, int slot, ItemStack stack) { + int[] validIndices = { slot }; + NBTTagList list = new NBTTagList(); + NBTTagCompound item = new NBTTagCompound(); + + if (stack != null) { + stack.writeToNBT(item); + } + list.appendTag(item); + inv.updateNBT(); + this.dirty.addOverwriteEntry(id).setItems(validIndices, list); + this.isDirty = true; + } + + /** + * Merge from slot -> player inv. Returns the items not added. + */ private ItemStack mergeToPlayerInventory(InventoryAdaptor playerInv, ItemStack stack) { if (stack == null) return null; for (ItemSlot slot : playerInv) { @@ -252,36 +260,75 @@ private ItemStack mergeToPlayerInventory(InventoryAdaptor playerInv, ItemStack s return playerInv.addItems(stack); } - private void regenList(final NBTTagCompound data) { - this.byId.clear(); - this.supportedInterfaces.clear(); - - final IActionHost host = this.getActionHost(); - if (host != null) { - final IGridNode agn = host.getActionableNode(); - if (agn != null && agn.isActive()) { - for (var clz : InterfaceTerminalSupportedClassProvider.getSupportedClasses()) { - for (final IGridNode gn : this.grid.getMachines(clz)) { - final IInterfaceTerminalSupport terminalSupport = (IInterfaceTerminalSupport) gn.getMachine(); - if (!gn.isActive() || !terminalSupport.shouldDisplay()) continue; - - final var configurations = terminalSupport.getPatternsConfigurations(); + /** + * Finds out whether any updates are needed, and if so, incrementally updates the list. + */ + private PacketIfaceTermUpdate updateList() { + PacketIfaceTermUpdate update = null; + var supported = IfaceTermRegistry.instance().getSupportedClasses(); + Set visited = new HashSet<>(); + + for (Class c : supported) { + for (IGridNode node : grid.getMachines(c)) { + IIfaceTermViewable machine = (IIfaceTermViewable) node.getMachine(); + /* First check if we are already tracking this node */ + if (tracked.containsKey(machine)) { + /* Check for updates */ + InvTracker known = tracked.get(machine); + + /* Name changed? */ + String name = machine.getName(); + + if (!Objects.equals(known.name, name)) { + if (update == null) update = new PacketIfaceTermUpdate(); + update.addRenamedEntry(known.id, name); + known.name = name; + } - for (int i = 0; i < configurations.length; ++i) { - this.supportedInterfaces - .put(terminalSupport, new InvTracker(terminalSupport, configurations[i], i)); - } + /* Status changed? */ + boolean isActive = node.isActive() || machine.shouldDisplay(); + + if (!known.online && isActive) { + /* Node offline -> online */ + known.online = true; + if (update == null) update = new PacketIfaceTermUpdate(); + known.updateNBT(); + update.addOverwriteEntry(known.id).setOnline(true).setItems(new int[0], known.invNbt); + } else if (known.online && !isActive) { + /* Node online -> offline */ + known.online = false; + if (update == null) update = new PacketIfaceTermUpdate(); + update.addOverwriteEntry(known.id).setOnline(false); } + } else { + /* Add a new entry */ + if (update == null) update = new PacketIfaceTermUpdate(); + InvTracker entry = new InvTracker(nextId++, machine, node.isActive()); + update.addNewEntry(entry.id, entry.name, entry.online).setLoc(entry.x, entry.y, entry.z, entry.dim) + .setItems(entry.rows, entry.rowSize, entry.invNbt) + .setReps(machine.getSelfRep(), machine.getDisplayRep()); + tracked.put(machine, entry); + trackedById.put(entry.id, entry); } + visited.add(machine); } } - data.setBoolean("clear", true); + /* Now find any entries that we need to remove */ + Iterator> it = tracked.entrySet().iterator(); + while (it.hasNext()) { + var entry = it.next(); + if (visited.contains(entry.getKey())) { + continue; + } + + if (update == null) update = new PacketIfaceTermUpdate(); - for (final InvTracker inv : this.supportedInterfaces.values()) { - this.byId.put(inv.which, inv); - this.addItems(data, inv, 0, inv.client.getSizeInventory()); + trackedById.remove(entry.getValue().id); + it.remove(); + update.addRemovalEntry(entry.getValue().id); } + return update; } private boolean isDifferent(final ItemStack a, final ItemStack b) { @@ -296,89 +343,81 @@ private boolean isDifferent(final ItemStack a, final ItemStack b) { return !ItemStack.areItemStacksEqual(a, b); } - private void addItems(final NBTTagCompound data, final InvTracker inv, final int offset, final int length) { - final String name = '=' + Long.toString(inv.which, Character.MAX_RADIX); - final NBTTagCompound tag = data.getCompoundTag(name); - - if (tag.hasNoTags()) { - tag.setLong("sortBy", inv.sortBy); - tag.setString("un", inv.unlocalizedName); - tag.setInteger("x", inv.X); - tag.setInteger("y", inv.Y); - tag.setInteger("z", inv.Z); - tag.setInteger("dim", inv.dim); - tag.setInteger("size", inv.client.getSizeInventory()); - } - - for (int x = 0; x < length; x++) { - final NBTTagCompound itemNBT = new NBTTagCompound(); - - final ItemStack is = inv.server.getStackInSlot(x + offset + inv.offset); - - // "update" client side. - inv.client.setInventorySlotContents(offset + x, is == null ? null : is.copy()); - - if (is != null) { - is.writeToNBT(itemNBT); - } - - tag.setTag(Integer.toString(x + offset), itemNBT); - } - - data.setTag(name, tag); - } - private static class InvTracker { - private final long sortBy; - private final long which = autoBase++; - private final String unlocalizedName; - private final IInventory client; - private final IInventory server; - private final int offset; - private final int X; - private final int Y; - private final int Z; + private final long id; + private String name; + private final IInventory patterns; + private final int rowSize; + private final int rows; + private final int x; + private final int y; + private final int z; private final int dim; - - public InvTracker(final DimensionalCoord coord, long sortValue, final IInventory patterns, - final String unlocalizedName, int offset, int size) { - this(coord.x, coord.y, coord.z, coord.getDimension(), sortValue, patterns, unlocalizedName, offset, size); - } - - public InvTracker(int x, int y, int z, int dim, long sortValue, final IInventory patterns, - final String unlocalizedName, int offset, int size) { - this.server = patterns; - this.client = new AppEngInternalInventory(null, size); - this.unlocalizedName = unlocalizedName; - this.sortBy = sortValue + offset << 16; - this.offset = offset; - this.X = x; - this.Y = y; - this.Z = z; - this.dim = dim; - } - - public InvTracker(IInterfaceTerminalSupport terminalSupport, PatternsConfiguration configuration, int index) { - this( - terminalSupport.getLocation(), - terminalSupport.getSortValue(), - terminalSupport.getPatterns(index), - terminalSupport.getName(), - configuration.offset, - configuration.size); + private boolean online; + private NBTTagList invNbt; + + InvTracker(long id, IIfaceTermViewable machine, boolean online) { + DimensionalCoord location = machine.getLocation(); + + this.id = id; + this.name = machine.getName(); + this.patterns = machine.getPatterns(); + this.rowSize = machine.rowSize(); + this.rows = machine.rows(); + this.x = location.x; + this.y = location.y; + this.z = location.z; + this.dim = location.getDimension(); + this.online = online; + this.invNbt = new NBTTagList(); + updateNBT(); } - } - - private static class PatternInvSlot extends WrapperInvSlot { - public PatternInvSlot(final IInventory inv) { - super(inv); + /** + * Refresh nbt items in the row, item idx. + */ + private void updateNBT(int row, int idx) { + ItemStack stack = this.patterns.getStackInSlot(row * this.rowSize + idx); + + if (stack != null) { + NBTTagCompound itemNbt = this.invNbt.getCompoundTagAt(idx); + stack.writeToNBT(itemNbt); + } else { + // replace + this.invNbt.func_150304_a(idx, new NBTTagCompound()); + } } - @Override - public boolean isItemValid(final ItemStack itemstack) { - return itemstack != null && itemstack.getItem() instanceof ItemEncodedPattern; + /** + * Refreshes all nbt tags. + */ + private void updateNBT() { + this.invNbt = new NBTTagList(); + for (int i = 0; i < this.rows; ++i) { + for (int j = 0; j < this.rowSize; ++j) { + final int offset = this.rowSize * i; + ItemStack stack = this.patterns.getStackInSlot(offset + j); + + if (stack != null) { + this.invNbt.appendTag(stack.writeToNBT(new NBTTagCompound())); + } else { + this.invNbt.appendTag(new NBTTagCompound()); + } + } + } } } + // + // private static class PatternInvSlot extends WrapperInvSlot { + // + // public PatternInvSlot(final IInventory inv) { + // super(inv); + // } + // + // @Override + // public boolean isItemValid(final ItemStack itemstack) { + // return itemstack != null && itemstack.getItem() instanceof ItemEncodedPattern; + // } + // } } From 247c18901b9a54b559dab7fcb7fb31d897575209 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Sun, 17 Sep 2023 23:21:09 -0700 Subject: [PATCH 06/22] appeng/client/render/AppEngRenderItem: Don't disable depth test appeng/client/render: Render items using translations appeng/client/gui/AEBaseGui: Use custom renderSlot Squash of 2 commits: * Depth test should be allowed to be used when rendering slots to make it easier to support z-level translations rather than use a pass-based rendering system. * This commit disables those depth tests which allows tooltips to be drawn before the rest of the screen is drawn, which makes it way less messy to code as everything can be done in 1 pass. MC's native RenderItem disregards z-level when drawing item overlays, since it disables GL_DEPTH_TEST before drawing it. This commit adds a way for AE2 to draw slots with its own z-level methods. --- .../java/appeng/client/gui/AEBaseGui.java | 58 +++++++++++++++---- .../client/render/AppEngRenderItem.java | 17 ------ .../client/render/TranslatedRenderItem.java | 55 ++++++++++++++++++ src/main/resources/META-INF/appeng_at.cfg | 2 + 4 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 src/main/java/appeng/client/render/TranslatedRenderItem.java diff --git a/src/main/java/appeng/client/gui/AEBaseGui.java b/src/main/java/appeng/client/gui/AEBaseGui.java index d64cc3b99f9..5f0594c593d 100644 --- a/src/main/java/appeng/client/gui/AEBaseGui.java +++ b/src/main/java/appeng/client/gui/AEBaseGui.java @@ -50,6 +50,7 @@ import appeng.client.me.SlotDisconnected; import appeng.client.me.SlotME; import appeng.client.render.AppEngRenderItem; +import appeng.client.render.TranslatedRenderItem; import appeng.container.AEBaseContainer; import appeng.container.slot.AppEngCraftingSlot; import appeng.container.slot.AppEngSlot; @@ -85,7 +86,8 @@ public abstract class AEBaseGui extends GuiContainer { private final List meSlots = new LinkedList<>(); // drag y private final Set drag_click = new HashSet<>(); - private final AppEngRenderItem aeRenderItem = new AppEngRenderItem(); + public static final AppEngRenderItem aeRenderItem = new AppEngRenderItem(); + public static final TranslatedRenderItem translatedRenderItem = new TranslatedRenderItem(); private GuiScrollbar scrollBar = null; private boolean disableShiftClick = false; private Stopwatch dbl_clickTimer = Stopwatch.createStarted(); @@ -784,8 +786,10 @@ public void drawItem(final int x, final int y, final ItemStack is) { GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL12.GL_RESCALE_NORMAL); GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glTranslatef(0.0f, 0.0f, 101.0f); RenderHelper.enableGUIStandardItemLighting(); itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.renderEngine, is, x, y); + GL11.glTranslatef(0.0f, 0.0f, -101.0f); GL11.glPopAttrib(); itemRender.zLevel = 0.0F; @@ -817,10 +821,9 @@ private void drawSlot(final Slot s) { RenderItem pIR = this.setItemRender(this.aeRenderItem); try { - this.zLevel = 100.0F; - itemRender.zLevel = 100.0F; - if (!this.isPowered()) { + this.zLevel = 100.0F; + itemRender.zLevel = 100.0F; GL11.glDisable(GL11.GL_LIGHTING); drawRect( s.xDisplayPosition, @@ -829,14 +832,14 @@ private void drawSlot(final Slot s) { 16 + s.yDisplayPosition, GuiColors.ItemSlotOverlayUnpowered.getColor()); GL11.glEnable(GL11.GL_LIGHTING); - } - - this.zLevel = 0.0F; - itemRender.zLevel = 0.0F; + this.zLevel = 0.0F; + itemRender.zLevel = 0.0F; + } else { + this.aeRenderItem.setAeStack(Platform.getAEStackInSlot(s)); - this.aeRenderItem.setAeStack(Platform.getAEStackInSlot(s)); + this.drawAESlot(s); + } - this.safeDrawSlot(s); } catch (final Exception err) { AELog.warn("[AppEng] AE prevented crash while drawing slot: " + err.toString()); } @@ -940,7 +943,7 @@ private void drawSlot(final Slot s) { if (s instanceof AppEngSlot) { ((AppEngSlot) s).setDisplay(true); - this.safeDrawSlot(s); + this.drawMCSlot(s); } else { this.safeDrawSlot(s); } @@ -954,6 +957,39 @@ private void drawSlot(final Slot s) { this.safeDrawSlot(s); } + public void drawMCSlot(Slot slotIn) { + int i = slotIn.xDisplayPosition; + int j = slotIn.yDisplayPosition; + ItemStack itemstack = slotIn.getStack(); + String s = null; + + GL11.glEnable(GL11.GL_DEPTH_TEST); + translatedRenderItem.zLevel = 100.0f; + translatedRenderItem + .renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), itemstack, i, j); + translatedRenderItem.zLevel = 200.0f; + translatedRenderItem + .renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), itemstack, i, j, s); + translatedRenderItem.zLevel = 0.0f; + } + + public void drawAESlot(Slot slotIn) { + int i = slotIn.xDisplayPosition; + int j = slotIn.yDisplayPosition; + ItemStack itemstack = slotIn.getStack(); + String s = null; + + this.zLevel = 100.0F; + itemRender.zLevel = 100.0F; + itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), itemstack, i, j); + itemRender.zLevel = 0.0F; + + this.zLevel = 0.0F; + GL11.glTranslatef(0.0f, 0.0f, 200.0f); + aeRenderItem.renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), itemstack, i, j, s); + GL11.glTranslatef(0.0f, 0.0f, -200.0f); + } + private RenderItem setItemRender(final RenderItem item) { if (IntegrationRegistry.INSTANCE.isEnabled(IntegrationType.NEI)) { return ((INEI) IntegrationRegistry.INSTANCE.getInstance(IntegrationType.NEI)).setItemRender(item); diff --git a/src/main/java/appeng/client/render/AppEngRenderItem.java b/src/main/java/appeng/client/render/AppEngRenderItem.java index 7db9653b0c0..c5623a7cc28 100644 --- a/src/main/java/appeng/client/render/AppEngRenderItem.java +++ b/src/main/java/appeng/client/render/AppEngRenderItem.java @@ -79,7 +79,6 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text final int k = (int) Math.round(255.0D - health * 255.0D); GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_ALPHA_TEST); GL11.glDisable(GL11.GL_BLEND); @@ -95,7 +94,6 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text GL11.glEnable(GL11.GL_ALPHA_TEST); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); } @@ -105,7 +103,6 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text : GuiText.SmallFontCraft.getLocal(); GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPushMatrix(); GL11.glScaled(scaleFactor, scaleFactor, scaleFactor); @@ -117,7 +114,6 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text GL11.glPopMatrix(); GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); } final long amount = this.aeStack != null ? this.aeStack.getStackSize() : is.stackSize; @@ -126,7 +122,6 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text final String stackSize = this.getToBeRenderedStackSize(amount); GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPushMatrix(); GL11.glScaled(scaleFactor, scaleFactor, scaleFactor); @@ -138,24 +133,12 @@ public void renderItemOverlayIntoGUI(final FontRenderer fontRenderer, final Text GL11.glPopMatrix(); GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); } fontRenderer.setUnicodeFlag(unicodeFlag); } } - private void renderQuad(final Tessellator par1Tessellator, final int par2, final int par3, final int par4, - final int par5, final int par6) { - par1Tessellator.startDrawingQuads(); - par1Tessellator.setColorOpaque_I(par6); - par1Tessellator.addVertex(par2, par3, 0.0D); - par1Tessellator.addVertex(par2, par3 + par5, 0.0D); - par1Tessellator.addVertex(par2 + par4, par3 + par5, 0.0D); - par1Tessellator.addVertex(par2 + par4, par3, 0.0D); - par1Tessellator.draw(); - } - private String getToBeRenderedStackSize(final long originalSize) { if (AEConfig.instance.useTerminalUseLargeFont()) { return SLIM_CONVERTER.toSlimReadableForm(originalSize); diff --git a/src/main/java/appeng/client/render/TranslatedRenderItem.java b/src/main/java/appeng/client/render/TranslatedRenderItem.java new file mode 100644 index 00000000000..19784fdceac --- /dev/null +++ b/src/main/java/appeng/client/render/TranslatedRenderItem.java @@ -0,0 +1,55 @@ +package appeng.client.render; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; + +/** + * Uses translations instead of depth test to perform rendering. + */ +public class TranslatedRenderItem extends RenderItem { + + @Override + public void renderItemOverlayIntoGUI(FontRenderer font, TextureManager texManager, ItemStack stack, int x, int y, + String customText) { + if (stack != null) { + GL11.glPushMatrix(); + if (stack.stackSize > 1 || customText != null) { + GL11.glTranslatef(0.0f, 0.0f, this.zLevel); + String s1 = customText == null ? String.valueOf(stack.stackSize) : customText; + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); + font.drawStringWithShadow(s1, x + 19 - 2 - font.getStringWidth(s1), y + 6 + 3, 16777215); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glTranslatef(0.0f, 0.0f, -this.zLevel); + } + + if (stack.getItem().showDurabilityBar(stack)) { + double health = stack.getItem().getDurabilityForDisplay(stack); + int j1 = (int) Math.round(13.0D - health * 13.0D); + int k = (int) Math.round(255.0D - health * 255.0D); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_BLEND); + Tessellator tessellator = Tessellator.instance; + int l = 255 - k << 16 | k << 8; + int i1 = (255 - k) / 4 << 16 | 16128; + this.renderQuad(tessellator, x + 2, y + 13, 13, 2, 0); + this.renderQuad(tessellator, x + 2, y + 13, 12, 1, i1); + this.renderQuad(tessellator, x + 2, y + 13, j1, 1, l); + // GL11.glEnable(GL11.GL_BLEND); // Forge: Disable Bled because it screws with a lot of things down the + // line. + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + } + GL11.glPopMatrix(); + } + } +} diff --git a/src/main/resources/META-INF/appeng_at.cfg b/src/main/resources/META-INF/appeng_at.cfg index 6018bae349e..489b3d1948f 100644 --- a/src/main/resources/META-INF/appeng_at.cfg +++ b/src/main/resources/META-INF/appeng_at.cfg @@ -3,3 +3,5 @@ public net.minecraft.client.gui.inventory.GuiContainer func_146977_a(Lnet/minecr public net.minecraft.client.gui.GuiTextField func_146188_c(IIII)V # drawSelectionBox #Pattern Value Setting public net.minecraft.client.gui.inventory.GuiContainer field_147006_u #theSlot +# RenderItem +public net.minecraft.client.renderer.entity.RenderItem func_77017_a(Lnet/minecraft/client/renderer/Tessellator;IIIII)V #renderQuad From 8bd7c6695e90499478beac8c34bbed59a82fb630 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Thu, 21 Sep 2023 07:48:24 -0700 Subject: [PATCH 07/22] appeng/client/gui/widgets: GuiImgButton.java: Add extra options button Adds generic extra options button, which is a button that is skinned as '...' (ellipses). Can be used for any gui. --- .../java/appeng/api/config/ActionItems.java | 3 ++- .../client/gui/widgets/GuiImgButton.java | 5 +++++ .../core/localization/ButtonToolTips.java | 3 ++- .../appliedenergistics2/lang/en_US.lang | 2 ++ .../textures/guis/states.png | Bin 16666 -> 18891 bytes 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/appeng/api/config/ActionItems.java b/src/main/java/appeng/api/config/ActionItems.java index 4fcc6730af6..bd7347a23e0 100644 --- a/src/main/java/appeng/api/config/ActionItems.java +++ b/src/main/java/appeng/api/config/ActionItems.java @@ -28,5 +28,6 @@ public enum ActionItems { TOGGLE_SHOW_FULL_INTERFACES_OFF, TOGGLE_SHOW_ONLY_INVALID_PATTERN_ON, TOGGLE_SHOW_ONLY_INVALID_PATTERN_OFF, - HIGHLIGHT_INTERFACE + HIGHLIGHT_INTERFACE, + EXTRA_OPTIONS, } diff --git a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java index f254e1bd8f8..bf28fc67d26 100644 --- a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java +++ b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java @@ -635,6 +635,7 @@ public GuiImgButton(final int x, final int y, final Enum idx, final Enum val) { CraftingMode.IGNORE_MISSING, ButtonToolTips.CraftingModeIgnoreMissing, ButtonToolTips.CraftingModeIgnoreMissingDesc); + this.registerApp(16 * 6 + 5, Settings.ACTIONS, ActionItems.EXTRA_OPTIONS, ButtonToolTips.ExtraOptions, ""); } } @@ -798,6 +799,10 @@ public boolean isVisible() { return this.visible; } + public boolean getMouseIn() { + return this.field_146123_n; + } + public void set(final Enum e) { if (this.currentValue != e) { this.currentValue = e; diff --git a/src/main/java/appeng/core/localization/ButtonToolTips.java b/src/main/java/appeng/core/localization/ButtonToolTips.java index 7e385799778..35b644d661f 100644 --- a/src/main/java/appeng/core/localization/ButtonToolTips.java +++ b/src/main/java/appeng/core/localization/ButtonToolTips.java @@ -184,7 +184,8 @@ public enum ButtonToolTips { CraftingModeStandard, CraftingModeStandardDesc, CraftingModeIgnoreMissing, - CraftingModeIgnoreMissingDesc; + CraftingModeIgnoreMissingDesc, + ExtraOptions; private final String root; diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_US.lang b/src/main/resources/assets/appliedenergistics2/lang/en_US.lang index 21620440996..eeba5e10a32 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_US.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/en_US.lang @@ -445,6 +445,8 @@ gui.tooltips.appliedenergistics2.CraftingModeStandardDesc=Only starts craft if a gui.tooltips.appliedenergistics2.CraftingModeIgnoreMissing=Ignore Missing Crafting Mode gui.tooltips.appliedenergistics2.CraftingModeIgnoreMissingDesc=Starts craft even if ingredients are missing from the ME System. +gui.tooltips.appliedenergistics2.ExtraOptions=Extra Options + # Units gui.appliedenergistics2.units.appliedenergstics=AE gui.appliedenergistics2.units.ic2=Energy Units diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png index 434498a7adec658f2e954d43d163fd0cb815a2f2..fef9c0f46df531fa1b019cdab913bab465038309 100644 GIT binary patch literal 18891 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%0Dss7jlcA;IAQ9r}S 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 ayeeOgPgu@z4*X{az$0ZXrE&%HkpB-7W%Skn From 893e7a68da890631eda85e16a1af57fb2a6e3d4a Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Thu, 21 Sep 2023 07:51:17 -0700 Subject: [PATCH 08/22] appeng/client/gui: AEBaseGui.java: bind texture w/ ResourceLocation Before it was allocating a new ResourceLocation PER BIND, which happens multiple times per draw frame in some GUIs. --- src/main/java/appeng/client/gui/AEBaseGui.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/appeng/client/gui/AEBaseGui.java b/src/main/java/appeng/client/gui/AEBaseGui.java index 5f0594c593d..ccdd02a88d6 100644 --- a/src/main/java/appeng/client/gui/AEBaseGui.java +++ b/src/main/java/appeng/client/gui/AEBaseGui.java @@ -1015,6 +1015,10 @@ public void bindTexture(final String file) { this.mc.getTextureManager().bindTexture(loc); } + public void bindTexture(final ResourceLocation loc) { + mc.getTextureManager().bindTexture(loc); + } + public void func_146977_a(final Slot s) { this.drawSlot(s); } From ff006ebe769900dda93a6bb76ec9024b4b7e6aab Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:03:31 -0700 Subject: [PATCH 09/22] appeng/client/gui/widgets: GuiScrollbar.java: Add visibility flag Skip rendering if visible is false --- src/main/java/appeng/client/gui/widgets/GuiScrollbar.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/appeng/client/gui/widgets/GuiScrollbar.java b/src/main/java/appeng/client/gui/widgets/GuiScrollbar.java index 3d85e05841e..a696a58e9ac 100644 --- a/src/main/java/appeng/client/gui/widgets/GuiScrollbar.java +++ b/src/main/java/appeng/client/gui/widgets/GuiScrollbar.java @@ -30,6 +30,7 @@ public class GuiScrollbar implements IScrollSource { private int maxScroll = 0; private int minScroll = 0; private int currentScroll = 0; + private boolean visible = true; public void setTexture(final String base, final String file, final int shiftX, final int shiftY) { txtBase = base; @@ -39,6 +40,9 @@ public void setTexture(final String base, final String file, final int shiftX, f } public void draw(final AEBaseGui g) { + if (!visible) { + return; + } g.bindTexture(txtBase, txtFile); GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -50,6 +54,10 @@ public void draw(final AEBaseGui g) { } } + public void setVisible(boolean visible) { + this.visible = visible; + } + private int getRange() { return this.maxScroll - this.minScroll; } From 6815d91dfe59dd35bbed0116f85ba5b7072ee9c5 Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Sat, 23 Sep 2023 18:17:50 -0700 Subject: [PATCH 10/22] appeng/client/gui: Add IGuiTooltipHandler This interface is used to abstract NEI tooltip handling and getting the hovered slot from GUIs. --- .../java/appeng/client/gui/AEBaseMEGui.java | 3 ++- .../appeng/client/gui/IGuiTooltipHandler.java | 20 +++++++++++++++++++ .../gui/implementations/GuiCraftConfirm.java | 3 ++- .../gui/implementations/GuiCraftingCPU.java | 4 +++- .../java/appeng/integration/modules/NEI.java | 11 +++++----- 5 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 src/main/java/appeng/client/gui/IGuiTooltipHandler.java diff --git a/src/main/java/appeng/client/gui/AEBaseMEGui.java b/src/main/java/appeng/client/gui/AEBaseMEGui.java index 67ee5d698c0..ad0dff44a7a 100644 --- a/src/main/java/appeng/client/gui/AEBaseMEGui.java +++ b/src/main/java/appeng/client/gui/AEBaseMEGui.java @@ -25,12 +25,13 @@ import appeng.core.localization.ButtonToolTips; import appeng.util.Platform; -public abstract class AEBaseMEGui extends AEBaseGui { +public abstract class AEBaseMEGui extends AEBaseGui implements IGuiTooltipHandler { public AEBaseMEGui(final Container container) { super(container); } + @Override public List handleItemTooltip(final ItemStack stack, final int mouseX, final int mouseY, final List currentToolTip) { if (stack != null) { diff --git a/src/main/java/appeng/client/gui/IGuiTooltipHandler.java b/src/main/java/appeng/client/gui/IGuiTooltipHandler.java new file mode 100644 index 00000000000..8101a867c41 --- /dev/null +++ b/src/main/java/appeng/client/gui/IGuiTooltipHandler.java @@ -0,0 +1,20 @@ +package appeng.client.gui; + +import java.util.List; + +import net.minecraft.item.ItemStack; + +/** + * Interface for handling tooltips from NEIGuiContainerManager. + */ +public interface IGuiTooltipHandler { + + default List handleItemTooltip(final ItemStack stack, final int mouseX, final int mouseY, + final List currentToolTip) { + return currentToolTip; + } + + default ItemStack getHoveredStack() { + return null; + } +} diff --git a/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java b/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java index f8875d610df..0da04e53d83 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java +++ b/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java @@ -35,6 +35,7 @@ import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.client.gui.AEBaseGui; +import appeng.client.gui.IGuiTooltipHandler; import appeng.client.gui.widgets.GuiCraftingCPUTable; import appeng.client.gui.widgets.GuiCraftingTree; import appeng.client.gui.widgets.GuiImgButton; @@ -63,7 +64,7 @@ import appeng.util.Platform; import appeng.util.ReadableNumberConverter; -public class GuiCraftConfirm extends AEBaseGui implements ICraftingCPUTableHolder { +public class GuiCraftConfirm extends AEBaseGui implements ICraftingCPUTableHolder, IGuiTooltipHandler { public static final int TREE_VIEW_TEXTURE_WIDTH = 238; public static final int TREE_VIEW_TEXTURE_HEIGHT = 238; diff --git a/src/main/java/appeng/client/gui/implementations/GuiCraftingCPU.java b/src/main/java/appeng/client/gui/implementations/GuiCraftingCPU.java index 4cc9c74db00..cc9677d27b2 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiCraftingCPU.java +++ b/src/main/java/appeng/client/gui/implementations/GuiCraftingCPU.java @@ -38,6 +38,7 @@ import appeng.api.util.DimensionalCoord; import appeng.api.util.WorldCoord; import appeng.client.gui.AEBaseGui; +import appeng.client.gui.IGuiTooltipHandler; import appeng.client.gui.widgets.GuiScrollbar; import appeng.client.gui.widgets.ISortSource; import appeng.client.gui.widgets.ITooltip; @@ -55,7 +56,7 @@ import appeng.util.Platform; import appeng.util.ReadableNumberConverter; -public class GuiCraftingCPU extends AEBaseGui implements ISortSource { +public class GuiCraftingCPU extends AEBaseGui implements ISortSource, IGuiTooltipHandler { private static final int GUI_HEIGHT = 184; private static final int GUI_WIDTH = 238; @@ -616,6 +617,7 @@ public Enum getSortDisplay() { return ViewItems.ALL; } + @Override public ItemStack getHoveredStack() { return hoveredStack; } diff --git a/src/main/java/appeng/integration/modules/NEI.java b/src/main/java/appeng/integration/modules/NEI.java index 7e5264a531a..cc415b886eb 100644 --- a/src/main/java/appeng/integration/modules/NEI.java +++ b/src/main/java/appeng/integration/modules/NEI.java @@ -22,7 +22,7 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; -import appeng.client.gui.AEBaseMEGui; +import appeng.client.gui.IGuiTooltipHandler; import appeng.client.gui.implementations.GuiCraftConfirm; import appeng.client.gui.implementations.GuiCraftingCPU; import appeng.client.gui.implementations.GuiCraftingTerm; @@ -210,8 +210,8 @@ public List handleItemDisplayName(final GuiContainer arg0, final ItemSta @Override public List handleItemTooltip(final GuiContainer guiScreen, final ItemStack stack, final int mouseX, final int mouseY, final List currentToolTip) { - if (guiScreen instanceof AEBaseMEGui) { - return ((AEBaseMEGui) guiScreen).handleItemTooltip(stack, mouseX, mouseY, currentToolTip); + if (guiScreen instanceof IGuiTooltipHandler) { + return ((IGuiTooltipHandler) guiScreen).handleItemTooltip(stack, mouseX, mouseY, currentToolTip); } return currentToolTip; @@ -228,8 +228,9 @@ public void load(GuiContainer gui) {} @Override public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { - if (gui instanceof GuiCraftConfirm) return ((GuiCraftConfirm) gui).getHoveredStack(); - else if (gui instanceof GuiCraftingCPU) return ((GuiCraftingCPU) gui).getHoveredStack(); + if (gui instanceof IGuiTooltipHandler) { + return ((IGuiTooltipHandler) gui).getHoveredStack(); + } return null; } From 1157a4f0749f58509dec71de0ea327f1d357cf2f Mon Sep 17 00:00:00 2001 From: Firenoo <49818773+firenoo@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:34:34 -0700 Subject: [PATCH 11/22] appeng/client/gui/implementations: New Interface Terminal GUI Adds the following to interface terminal: * Interfaces are grouped by their name. Each group's name is pinned to the top until the next name comes up. * Viewport now supports smooth scrolling. * SHIFT + scroll jumps from section to section * CTRL + scroll jumps up/down the whole page. * Search now filters out irrelevant patterns like NEI, graying them out. --- .../implementations/GuiInterfaceTerminal.java | 1311 ++++++++++++----- .../textures/guis/newinterfaceterminal.png | Bin 4082 -> 3350 bytes 2 files changed, 977 insertions(+), 334 deletions(-) diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index ebb7587d7bf..f62667b2f52 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -11,26 +11,34 @@ package appeng.client.gui.implementations; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; -import java.util.WeakHashMap; +import java.util.TreeMap; +import java.util.TreeSet; -import appeng.core.sync.packets.PacketIfaceTermUpdate; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants.NBT; +import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; - -import com.google.common.collect.HashMultimap; +import org.lwjgl.opengl.GL12; import appeng.api.AEApi; import appeng.api.config.ActionItems; @@ -39,42 +47,59 @@ import appeng.api.util.DimensionalCoord; import appeng.api.util.WorldCoord; import appeng.client.gui.AEBaseGui; +import appeng.client.gui.IGuiTooltipHandler; import appeng.client.gui.widgets.GuiImgButton; import appeng.client.gui.widgets.GuiScrollbar; import appeng.client.gui.widgets.IDropToFillTextField; import appeng.client.gui.widgets.MEGuiTextField; -import appeng.client.me.ClientDCInternalInv; -import appeng.client.me.SlotDisconnected; import appeng.client.render.BlockPosHighlighter; import appeng.container.implementations.ContainerInterfaceTerminal; import appeng.container.slot.AppEngSlot; import appeng.core.AEConfig; +import appeng.core.AppEng; import appeng.core.CommonHelper; import appeng.core.localization.ButtonToolTips; import appeng.core.localization.GuiColors; import appeng.core.localization.GuiText; import appeng.core.localization.PlayerMessages; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketIfaceTermUpdate; +import appeng.core.sync.packets.PacketIfaceTermUpdate.PacketEntry; +import appeng.core.sync.packets.PacketInventoryAction; +import appeng.helpers.InventoryAction; import appeng.helpers.PatternHelper; import appeng.integration.IntegrationRegistry; import appeng.integration.IntegrationType; +import appeng.items.misc.ItemEncodedPattern; import appeng.parts.reporting.PartInterfaceTerminal; +import appeng.tile.inventory.AppEngInternalInventory; import appeng.util.Platform; +import cpw.mods.fml.common.Loader; + +/** + * Interface Terminal GUI
+ * Most of the interface terminal has been rewritten. You may ask, why are you not using SLOTS? And I asked myself that + * too. I was about to go and rewrite it again until I realized that the interface terminal must support adding/removing + * entries dynamically. And finding the slots, removing them from the slot list - not exactly a huge fan, especially + * considering we are doing this first, and foremost, for performance gains. Also, the whole Z-level thing is very + * frustrating for me. Basically, I've gone rogue and taken the Thaumic Energistics route.
+ * The previous implementation did not use static slots either - instead, in generated new slots dynamically every + * render tick or when slots were clicked. Why do that, when you can write 500 extra lines of code to reimplement a + * version of slots specific to the interface terminal? + * + * @author firenoo + */ +public class GuiInterfaceTerminal extends AEBaseGui implements IDropToFillTextField, IGuiTooltipHandler { -public class GuiInterfaceTerminal extends AEBaseGui implements IDropToFillTextField { - - private static final int MAGIC_HEIGHT_NUMBER = 52 + 99; - private static final int offsetX = 21; - - private final HashMap byId = new HashMap<>(); - private final HashMultimap byName = HashMultimap.create(); - private final HashMap blockPosHashMap = new HashMap<>(); - private final HashMap guiButtonHashMap = new HashMap<>(); - private final ArrayList names = new ArrayList<>(); - private final ArrayList lines = new ArrayList<>(); - private final Set matchedStacks = new HashSet<>(); - - private final Map> cachedSearches = new WeakHashMap<>(); + public static final int HEADER_HEIGHT = 52; + public static final int INV_HEIGHT = 98; + public static final int VIEW_WIDTH = 174; + public static final int VIEW_LEFT = 10; + protected static final ResourceLocation BACKGROUND = new ResourceLocation( + AppEng.MOD_ID, + "textures/guis/newinterfaceterminal.png"); + private final IfaceList masterList = new IfaceList(); private final MEGuiTextField searchFieldOutputs; private final MEGuiTextField searchFieldInputs; private final MEGuiTextField searchFieldNames; @@ -82,14 +107,23 @@ public class GuiInterfaceTerminal extends AEBaseGui implements IDropToFillTextFi private final GuiImgButton guiButtonAssemblersOnly; private final GuiImgButton guiButtonBrokenRecipes; private final GuiImgButton terminalStyleBox; - private boolean refreshList = false; private boolean onlyMolecularAssemblers = false; private boolean onlyBrokenRecipes = false; - - // private final IConfigManager configSrc; - private int rows = 3; - - private static final String MOLECULAR_ASSEMBLER = "tile.appliedenergistics2.BlockMolecularAssembler"; + private boolean online; + /** The height of the viewport. */ + private int viewHeight; + private final List extraOptionsText; + private ItemStack tooltipStack; + private final boolean neiPresent; + /* + * Z-level Map (FLOATS) 0.0 - BACKGROUND 1.0 - ItemStacks 2.0 - Slot color overlays 20.0 - ItemStack overlays 21.0 - + * Slot mouse hover overlay 200.0 - Tooltips + */ + private static final float ITEMSTACK_Z = 1.0f; + private static final float SLOT_Z = 0.5f; + private static final float ITEMSTACK_OVERLAY_Z = 20.0f; + private static final float SLOT_HOVER_Z = 31.0f; + private static final float TOOLTIP_Z = 200.0f; public GuiInterfaceTerminal(final InventoryPlayer inventoryPlayer, final PartInterfaceTerminal te) { super(new ContainerInterfaceTerminal(inventoryPlayer, te)); @@ -97,12 +131,13 @@ public GuiInterfaceTerminal(final InventoryPlayer inventoryPlayer, final PartInt this.setScrollBar(new GuiScrollbar()); this.xSize = 208; this.ySize = 255; + this.neiPresent = Loader.isModLoaded("NotEnoughItems"); searchFieldInputs = new MEGuiTextField(86, 12, ButtonToolTips.SearchFieldInputs.getLocal()) { @Override public void onTextChange(final String oldText) { - refreshList(); + masterList.markDirty(); } }; @@ -110,7 +145,7 @@ public void onTextChange(final String oldText) { @Override public void onTextChange(final String oldText) { - refreshList(); + masterList.markDirty(); } }; @@ -118,7 +153,7 @@ public void onTextChange(final String oldText) { @Override public void onTextChange(final String oldText) { - refreshList(); + masterList.markDirty(); } }; searchFieldNames.setFocused(true); @@ -128,46 +163,59 @@ public void onTextChange(final String oldText) { guiButtonBrokenRecipes = new GuiImgButton(0, 0, Settings.ACTIONS, null); terminalStyleBox = new GuiImgButton(0, 0, Settings.TERMINAL_STYLE, null); + + this.extraOptionsText = new ArrayList<>(2); + extraOptionsText.add(ButtonToolTips.HighlightInterface.getLocal()); } private void setScrollBar() { - this.getScrollBar().setTop(52).setLeft(189).setHeight(this.rows * 18 - 2); - this.getScrollBar().setRange(0, this.lines.size() - this.rows, 2); + int maxScroll = this.masterList.getHeight() - this.viewHeight - 1; + if (maxScroll <= 0) { + this.getScrollBar().setTop(52).setLeft(189).setHeight(this.viewHeight).setRange(0, 0, 1); + } else { + this.getScrollBar().setTop(52).setLeft(189).setHeight(this.viewHeight).setRange(0, maxScroll, 12); + } } @Override public void initGui() { - this.rows = calculateRowsCount(); - super.initGui(); - this.ySize = MAGIC_HEIGHT_NUMBER + this.rows * 18; + this.buttonList.clear(); + this.viewHeight = calculateViewHeight(); + this.ySize = HEADER_HEIGHT + INV_HEIGHT + this.viewHeight; + final int unusedSpace = this.height - this.ySize; this.guiTop = (int) Math.floor(unusedSpace / (unusedSpace < 0 ? 3.8f : 2.0f)); - searchFieldInputs.x = guiLeft + Math.max(32, offsetX); + searchFieldInputs.x = guiLeft + Math.max(32, VIEW_LEFT); searchFieldInputs.y = guiTop + 25; - searchFieldOutputs.x = guiLeft + Math.max(32, offsetX); + searchFieldOutputs.x = guiLeft + Math.max(32, VIEW_LEFT); searchFieldOutputs.y = guiTop + 38; - searchFieldNames.x = guiLeft + Math.max(32, offsetX) + 99; + searchFieldNames.x = guiLeft + Math.max(32, VIEW_LEFT) + 99; searchFieldNames.y = guiTop + 38; - guiButtonAssemblersOnly.xPosition = guiLeft + Math.max(32, offsetX) + 99; - guiButtonAssemblersOnly.yPosition = guiTop + 20; + terminalStyleBox.xPosition = guiLeft - 18; + terminalStyleBox.yPosition = guiTop + 8; - guiButtonHideFull.xPosition = guiButtonAssemblersOnly.xPosition + 18; - guiButtonHideFull.yPosition = guiTop + 20; + guiButtonBrokenRecipes.xPosition = guiLeft - 18; + guiButtonBrokenRecipes.yPosition = terminalStyleBox.yPosition + 18; - guiButtonBrokenRecipes.xPosition = guiButtonHideFull.xPosition + 18; - guiButtonBrokenRecipes.yPosition = guiTop + 20; + guiButtonHideFull.xPosition = guiLeft - 18; + guiButtonHideFull.yPosition = guiButtonBrokenRecipes.yPosition + 18; - terminalStyleBox.xPosition = guiLeft - 18; - terminalStyleBox.yPosition = guiTop + 8; + guiButtonAssemblersOnly.xPosition = guiLeft - 18; + guiButtonAssemblersOnly.yPosition = guiButtonHideFull.yPosition + 18; this.setScrollBar(); this.repositionSlots(); + + buttonList.add(guiButtonAssemblersOnly); + buttonList.add(guiButtonHideFull); + buttonList.add(guiButtonBrokenRecipes); + buttonList.add(terminalStyleBox); } protected void repositionSlots() { @@ -178,13 +226,14 @@ protected void repositionSlots() { } } - protected int calculateRowsCount() { - final int maxRows = this.getMaxRows(); + protected int calculateViewHeight() { + final int maxViewHeight = this.getMaxViewHeight(); final boolean hasNEI = IntegrationRegistry.INSTANCE.isEnabled(IntegrationType.NEI); final int NEIPadding = hasNEI ? 22 /* input */ + 18 /* top panel */ : 0; - final int extraSpace = this.height - MAGIC_HEIGHT_NUMBER - NEIPadding; + final int availableSpace = this.height - HEADER_HEIGHT - INV_HEIGHT - NEIPadding; - return Math.max(3, Math.min(maxRows, extraSpace / 18)); + // screen should use 95% of the space it can, 5% margins + return Math.min((int) (availableSpace * 0.95), maxViewHeight); } @Override @@ -196,52 +245,16 @@ public void drawFG(final int offsetX, final int offsetY, final int mouseX, final GuiColors.InterfaceTerminalTitle.getColor()); fontRendererObj.drawString( GuiText.inventory.getLocal(), - GuiInterfaceTerminal.offsetX + 2, + GuiInterfaceTerminal.VIEW_LEFT + 2, this.ySize - 96, GuiColors.InterfaceTerminalInventory.getColor()); - - int offset = 51; - final int ex = getScrollBar().getCurrentScroll(); - for (int x = 0; x < this.rows && ex + x < this.lines.size(); x++) { - final Object lineObj = this.lines.get(ex + x); - if (lineObj instanceof ClientDCInternalInv inv) { - for (int z = 0; z < inv.getInventory().getSizeInventory(); z++) { - if (this.matchedStacks.contains(inv.getInventory().getStackInSlot(z))) drawRect( - z * 18 + 22, - 1 + offset, - z * 18 + 22 + 16, - 1 + offset + 16, - GuiColors.InterfaceTerminalMatch.getColor()); - } - } else if (lineObj instanceof String name) { - final int rows = this.byName.get(name).size(); - String postfix = ""; - - if (rows > 1) { - postfix = " (" + rows + ')'; - } - - while (name.length() > 2 && this.fontRendererObj.getStringWidth(name + postfix) > 158) { - name = name.substring(0, name.length() - 1); - } - - this.fontRendererObj.drawString( - name + postfix, - GuiInterfaceTerminal.offsetX + 3, - 6 + offset, - GuiColors.InterfaceTerminalName.getColor()); - } - - offset += 18; + if (!neiPresent && tooltipStack != null) { + renderToolTip(tooltipStack, mouseX, mouseY); } } @Override public void drawScreen(final int mouseX, final int mouseY, final float btn) { - - buttonList.clear(); - inventorySlots.inventorySlots.removeIf(slot -> slot instanceof SlotDisconnected); - guiButtonAssemblersOnly.set( onlyMolecularAssemblers ? ActionItems.MOLECULAR_ASSEMBLEERS_ON : ActionItems.MOLECULAR_ASSEMBLEERS_OFF); guiButtonHideFull.set( @@ -254,34 +267,6 @@ public void drawScreen(final int mouseX, final int mouseY, final float btn) { terminalStyleBox.set(AEConfig.instance.settings.getSetting(Settings.TERMINAL_STYLE)); - buttonList.add(guiButtonAssemblersOnly); - buttonList.add(guiButtonHideFull); - buttonList.add(guiButtonBrokenRecipes); - - buttonList.add(terminalStyleBox); - guiButtonHashMap.clear(); - - int offset = 51; - final int ex = this.getScrollBar().getCurrentScroll(); - for (int x = 0; x < this.rows && ex + x < this.lines.size(); x++) { - final Object lineObj = this.lines.get(ex + x); - if (lineObj instanceof ClientDCInternalInv inv) { - for (int z = 0; z < inv.getInventory().getSizeInventory(); z++) { - inventorySlots.inventorySlots.add(new SlotDisconnected(inv, z, z * 18 + 22, 1 + offset)); - } - - GuiButton guiButton = new GuiImgButton( - guiLeft + 4, - guiTop + offset + 1, - Settings.ACTIONS, - ActionItems.HIGHLIGHT_INTERFACE); - guiButtonHashMap.put(guiButton, inv); - buttonList.add(guiButton); - } - - offset += 18; - } - super.drawScreen(mouseX, mouseY, btn); handleTooltip(mouseX, mouseY, searchFieldInputs); @@ -295,43 +280,23 @@ protected void mouseClicked(final int xCoord, final int yCoord, final int btn) { searchFieldOutputs.mouseClicked(xCoord, yCoord, btn); searchFieldNames.mouseClicked(xCoord, yCoord, btn); + if (masterList.mouseClicked(xCoord - guiLeft - VIEW_LEFT, yCoord - guiTop - HEADER_HEIGHT, btn)) { + return; + } super.mouseClicked(xCoord, yCoord, btn); } @Override protected void actionPerformed(final GuiButton btn) { - if (guiButtonHashMap.containsKey(btn)) { - DimensionalCoord blockPos = blockPosHashMap.get(guiButtonHashMap.get(btn)); - WorldCoord blockPos2 = new WorldCoord( - (int) mc.thePlayer.posX, - (int) mc.thePlayer.posY, - (int) mc.thePlayer.posZ); - if (mc.theWorld.provider.dimensionId != blockPos.getDimension()) { - mc.thePlayer.addChatMessage( - new ChatComponentTranslation( - PlayerMessages.InterfaceInOtherDim.getName(), - blockPos.getDimension())); - } else { - BlockPosHighlighter.highlightBlock( - blockPos, - System.currentTimeMillis() + 500 * WorldCoord.getTaxicabDistance(blockPos, blockPos2)); - mc.thePlayer.addChatMessage( - new ChatComponentTranslation( - PlayerMessages.InterfaceHighlighted.getName(), - blockPos.x, - blockPos.y, - blockPos.z)); - } - mc.thePlayer.closeScreen(); + if (btn == guiButtonAssemblersOnly) { + onlyMolecularAssemblers = !onlyMolecularAssemblers; + masterList.markDirty(); } else if (btn == guiButtonHideFull) { AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal = !AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal; - this.refreshList(); - } else if (btn == guiButtonAssemblersOnly) { - onlyMolecularAssemblers = !onlyMolecularAssemblers; - this.refreshList(); + masterList.markDirty(); } else if (btn == guiButtonBrokenRecipes) { onlyBrokenRecipes = !onlyBrokenRecipes; - this.refreshList(); + masterList.markDirty(); } else if (btn instanceof GuiImgButton iBtn) { if (iBtn.getSetting() != Settings.ACTIONS) { final Enum cv = iBtn.getCurrentValue(); @@ -340,8 +305,7 @@ protected void actionPerformed(final GuiButton btn) { if (btn == this.terminalStyleBox) { AEConfig.instance.settings.putSetting(iBtn.getSetting(), next); - - this.reinitialize(); + initGui(); } iBtn.set(next); @@ -349,41 +313,374 @@ protected void actionPerformed(final GuiButton btn) { } } - private void reinitialize() { - this.buttonList.clear(); - this.initGui(); - } - @Override public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) { - this.bindTexture("guis/newinterfaceterminal.png"); - this.drawTexturedModalRect(offsetX, offsetY, 0, 0, this.xSize, 53); + this.bindTexture(BACKGROUND); + /* Draws the top part. */ + this.drawTexturedModalRect(offsetX, offsetY, 0, 0, xSize, HEADER_HEIGHT); + /* Draws the middle part. */ + Tessellator.instance.startDrawingQuads(); + addTexturedRectToTesselator( + offsetX, + offsetY + HEADER_HEIGHT, + offsetX + xSize, + offsetY + HEADER_HEIGHT + viewHeight + 1, + 0.0f, + 0.0f, + (HEADER_HEIGHT + IfaceSection.TITLE_HEIGHT + 1.0f) / 256.0f, + this.xSize / 256.0f, + (HEADER_HEIGHT + 106.0f) / 256.0f); + Tessellator.instance.draw(); + /* Draw the bottom part */ + this.drawTexturedModalRect(offsetX, offsetY + HEADER_HEIGHT + viewHeight, 0, 158, xSize, INV_HEIGHT); + if (online) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + /* (0,0) => viewPort's (0,0) */ + GL11.glPushMatrix(); + GL11.glTranslatef(offsetX + VIEW_LEFT, offsetY + HEADER_HEIGHT, 0); + tooltipStack = null; + masterList.hoveredEntry = null; + drawViewport(mouseX - offsetX - VIEW_LEFT, mouseY - offsetY - HEADER_HEIGHT - 1); + GL11.glPopMatrix(); + GL11.glPopAttrib(); + } + searchFieldInputs.drawTextBox(); + searchFieldOutputs.drawTextBox(); + searchFieldNames.drawTextBox(); + } + + /** + * Draws the viewport area + */ + private void drawViewport(int relMouseX, int relMouseY) { + /* Viewport Magic */ + final int scroll = this.getScrollBar().getCurrentScroll(); + int viewY = -scroll; // current y in viewport coordinates + int entryIdx = 0; + List visibleSections = this.masterList.getVisibleSections(); + + final float guiScaleX = (float) mc.displayWidth / width; + final float guiScaleY = (float) mc.displayHeight / height; + GL11.glScissor( + (int) ((guiLeft + VIEW_LEFT) * guiScaleX), + (int) ((height - (guiTop + HEADER_HEIGHT + viewHeight)) * guiScaleY), + (int) (VIEW_WIDTH * guiScaleX), + (int) (this.viewHeight * guiScaleY)); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + + /* + * Render each section + */ + while (viewY < this.viewHeight && entryIdx < visibleSections.size()) { + IfaceSection section = visibleSections.get(entryIdx); + int sectionHeight = section.getHeight(); + + /* Is it viewable/in the viewport at all? */ + if (viewY + sectionHeight < 0) { + entryIdx++; + viewY += sectionHeight; + section.visible = false; + continue; + } - int offset = 51; - final int ex = this.getScrollBar().getCurrentScroll(); + section.visible = true; + int advanceY = drawSection(section, viewY, relMouseX, relMouseY); + viewY += advanceY; + entryIdx++; + } + } + + /** + * Render the section (if it is visible) + * + * @param section the section to render + * @param viewY current y coordinate relative to gui + * @param relMouseX transformed mouse coords relative to viewport + * @param relMouseY transformed mouse coords relative to viewport + * @return the height of the section rendered in viewport coordinates, max of viewHeight. + */ + private int drawSection(IfaceSection section, int viewY, int relMouseX, int relMouseY) { + int title; + int renderY = 0; + final int sectionBottom = viewY + section.getHeight() - 1; + final int fontColor = GuiColors.InterfaceTerminalInventory.getColor(); + /* + * Render title + */ + GL11.glTranslatef(0.0f, 0.0f, 50f); + bindTexture(BACKGROUND); + if (sectionBottom > 0 && sectionBottom < IfaceSection.TITLE_HEIGHT) { + /* Transition draw */ + drawTexturedModalRect( + 0, + 0, + VIEW_LEFT, + HEADER_HEIGHT + IfaceSection.TITLE_HEIGHT - sectionBottom, + VIEW_WIDTH, + sectionBottom); + fontRendererObj.drawString(section.name, 2, sectionBottom - IfaceSection.TITLE_HEIGHT + 2, fontColor); + title = sectionBottom; + } else if (viewY < 0) { + /* Hidden title draw */ + drawTexturedModalRect(0, 0, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, IfaceSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, 2, fontColor); + title = 0; + } else { + /* Normal title draw */ + drawTexturedModalRect(0, viewY, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, IfaceSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, viewY + 2, fontColor); + title = 0; + } + GL11.glTranslatef(0.0f, 0.0f, -50f); + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + Iterator visible = section.getVisible(); + while (visible.hasNext()) { + if (viewY < viewHeight) { + renderY += drawEntry( + visible.next(), + viewY + IfaceSection.TITLE_HEIGHT + renderY, + title, + relMouseX, + relMouseY); + } else { + IfaceEntry entry = visible.next(); + entry.dispY = -9999; + entry.optionsButton.yPosition = -1; + } + } + return IfaceSection.TITLE_HEIGHT + renderY; + } - for (int x = 0; x < this.rows; x++) { - this.drawTexturedModalRect(offsetX, offsetY + 53 + x * 18, 0, 52, this.xSize, 18); + /** + * Draws the entry. In practice it just draws the slots + items. + * + * @param viewY the gui coordinate z + */ + private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouseX, int relMouseY) { + bindTexture(BACKGROUND); + Tessellator.instance.startDrawingQuads(); + int relY = 0; + final int slotLeftMargin = (VIEW_WIDTH - entry.rowSize * 18); + + entry.dispY = viewY; + /* PASS 1: BG */ + for (int row = 0; row < entry.rows; ++row) { + final int rowYTop = row * 18; + final int rowYBot = rowYTop + 18; + + relY += 18; + /* Is the slot row in view? */ + if (viewY + rowYBot <= titleBottom) { + continue; + } + for (int col = 0; col < entry.rowSize; ++col) { + addTexturedRectToTesselator( + col * 18 + slotLeftMargin, + viewY + rowYTop, + 18 * col + 18 + slotLeftMargin, + viewY + rowYBot, + 0, + 21 / 256f, + 173 / 256f, + (21 + 18) / 256f, + (173 + 18) / 256f); + } + } + Tessellator.instance.draw(); + /* Draw button */ + if (viewY + entry.optionsButton.height > 0 && viewY < viewHeight) { + entry.optionsButton.yPosition = viewY + 5; + entry.optionsButton.drawButton(mc, relMouseX, relMouseY); + if (entry.optionsButton.getMouseIn() + && relMouseY >= Math.max(IfaceSection.TITLE_HEIGHT, entry.optionsButton.yPosition)) { + // draw a tooltip + GL11.glTranslatef(0f, 0f, TOOLTIP_Z); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + drawHoveringText(extraOptionsText, relMouseX, relMouseY); + GL11.glTranslatef(0f, 0f, -TOOLTIP_Z); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + } + } else { + entry.optionsButton.yPosition = -1; } + /* PASS 2: Items */ + for (int row = 0; row < entry.rows; ++row) { + final int rowYTop = row * 18; + final int rowYBot = rowYTop + 18; + /* Is the slot row in view? */ + if (viewY + rowYBot <= titleBottom) { + continue; + } + AppEngInternalInventory inv = entry.getInventory(); + + for (int col = 0; col < entry.rowSize; ++col) { + final int colLeft = col * 18 + slotLeftMargin + 1; + final int colRight = colLeft + 18 + 1; + final int slotIdx = row * entry.rowSize + col; + ItemStack stack = inv.getStackInSlot(slotIdx); + + boolean tooltip = relMouseX > colLeft - 1 && relMouseX < colRight - 1 + && relMouseY >= Math.max(viewY + rowYTop, IfaceSection.TITLE_HEIGHT) + && relMouseY < Math.min(viewY + rowYBot, viewHeight); + if (stack != null) { + final ItemEncodedPattern iep = (ItemEncodedPattern) stack.getItem(); + final ItemStack toRender = iep.getOutput(stack); + + GL11.glPushMatrix(); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEMSTACK_Z); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableGUIStandardItemLighting(); + translatedRenderItem.zLevel = 3.0f - 50.0f; + translatedRenderItem + .renderItemAndEffectIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); + GL11.glTranslatef(0.0f, 0.0f, ITEMSTACK_OVERLAY_Z - ITEMSTACK_Z); + aeRenderItem.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); + aeRenderItem.zLevel = 0.0f; + RenderHelper.disableStandardItemLighting(); + if (!tooltip) { + if (entry.brokenRecipes[slotIdx]) { + GL11.glTranslatef(0.0f, 0.0f, SLOT_Z - ITEMSTACK_OVERLAY_Z); + drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayInvalid.getColor()); + } else if (entry.filteredRecipes[slotIdx]) { + GL11.glTranslatef(0.0f, 0.0f, ITEMSTACK_OVERLAY_Z); + drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); + } + } else { + tooltipStack = stack; + } + GL11.glPopMatrix(); + } else if (entry.filteredRecipes[slotIdx]) { + GL11.glPushMatrix(); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEMSTACK_OVERLAY_Z); + drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); + GL11.glPopMatrix(); + } + if (tooltip) { + // overlay highlight + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glTranslatef(0.0f, 0.0f, SLOT_HOVER_Z); + drawRect(colLeft, viewY + 1 + rowYTop, -2 + colRight, viewY - 1 + rowYBot, 0x77FFFFFF); + GL11.glTranslatef(0.0f, 0.0f, -SLOT_HOVER_Z); + masterList.hoveredEntry = entry; + entry.hoveredSlotIdx = slotIdx; + } + GL11.glDisable(GL11.GL_LIGHTING); + } + } + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + return relY + 1; + } - for (int x = 0; x < this.rows && ex + x < this.lines.size(); x++) { + @Override + public List handleItemTooltip(ItemStack stack, int mouseX, int mouseY, List currentToolTip) { + return currentToolTip; + } - final Object lineObj = this.lines.get(ex + x); - if (lineObj instanceof ClientDCInternalInv inv) { + @Override + public ItemStack getHoveredStack() { + return tooltipStack; + } - GL11.glColor4f(1, 1, 1, 1); - final int width = inv.getInventory().getSizeInventory() * 18; - this.drawTexturedModalRect(offsetX + 20, offsetY + offset, 20, 173, width, 18); + /** + * A copy of super method, but modified to allow for depth testing. + */ + @Override + public void drawHoveringText(List textLines, int x, int y, FontRenderer font) { + if (!textLines.isEmpty()) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + int maxStrWidth = 0; + + // is this more efficient than doing 1 pass, then doing a translate before drawing the text? + for (String s : textLines) { + int width = font.getStringWidth(s); + + if (width > maxStrWidth) { + maxStrWidth = width; + } } - offset += 18; - } + // top left corner + int curX = x + 12; + int curY = y - 12; + int totalHeight = 8; - this.drawTexturedModalRect(offsetX, offsetY + 50 + this.rows * 18, 0, 158, this.xSize, 99); + if (textLines.size() > 1) { + totalHeight += 2 + (textLines.size() - 1) * 10; + } - searchFieldInputs.drawTextBox(); - searchFieldOutputs.drawTextBox(); - searchFieldNames.drawTextBox(); + /* String is too long? Display on the left side */ + if (curX + maxStrWidth > this.width) { + curX -= 28 + maxStrWidth; + } + + /* String is too tall? move it up */ + if (curY + totalHeight + 6 > this.height) { + curY = this.height - totalHeight - 6; + } + + int borderColor = -267386864; + // drawing the border... + this.drawGradientRect(curX - 3, curY - 4, curX + maxStrWidth + 3, curY - 3, borderColor, borderColor); + this.drawGradientRect( + curX - 3, + curY + totalHeight + 3, + curX + maxStrWidth + 3, + curY + totalHeight + 4, + borderColor, + borderColor); + this.drawGradientRect( + curX - 3, + curY - 3, + curX + maxStrWidth + 3, + curY + totalHeight + 3, + borderColor, + borderColor); + this.drawGradientRect(curX - 4, curY - 3, curX - 3, curY + totalHeight + 3, borderColor, borderColor); + this.drawGradientRect( + curX + maxStrWidth + 3, + curY - 3, + curX + maxStrWidth + 4, + curY + totalHeight + 3, + borderColor, + borderColor); + int color1 = 1347420415; + int color2 = (color1 & 16711422) >> 1 | color1 & -16777216; + this.drawGradientRect(curX - 3, curY - 3 + 1, curX - 3 + 1, curY + totalHeight + 3 - 1, color1, color2); + this.drawGradientRect( + curX + maxStrWidth + 2, + curY - 3 + 1, + curX + maxStrWidth + 3, + curY + totalHeight + 3 - 1, + color1, + color2); + this.drawGradientRect(curX - 3, curY - 3, curX + maxStrWidth + 3, curY - 3 + 1, color1, color1); + this.drawGradientRect( + curX - 3, + curY + totalHeight + 2, + curX + maxStrWidth + 3, + curY + totalHeight + 3, + color2, + color2); + + for (int i = 0; i < textLines.size(); ++i) { + String line = textLines.get(i); + font.drawStringWithShadow(line, curX, curY, -1); + + if (i == 0) { + // gap between name and lore text + curY += 2; + } + + curY += 10; + } + + RenderHelper.enableGUIStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } } @Override @@ -394,18 +691,47 @@ protected void keyTyped(final char character, final int key) { || (searchFieldOutputs.getText().isEmpty() && searchFieldOutputs.isFocused()) || (searchFieldNames.getText().isEmpty() && searchFieldNames.isFocused())) return; - } else if (character == '\t') { - if (handleTab()) return; + } else if (character == '\t' && handleTab()) { + return; } if (searchFieldInputs.textboxKeyTyped(character, key) || searchFieldOutputs.textboxKeyTyped(character, key) || searchFieldNames.textboxKeyTyped(character, key)) { - refreshList(); + return; + } + // if (Character.isDigit(character) && character != '0') { + // // move hotbar to slot + // if (masterList.hoveredEntry != null && masterList.hoveredEntry.hoveredSlotIdx != -1) { + // } + // } + super.keyTyped(character, key); + } + } + + @Override + protected boolean mouseWheelEvent(int mouseX, int mouseY, int wheel) { + boolean isMouseInViewport = isMouseInViewport(mouseX, mouseY); + GuiScrollbar scrollbar = getScrollBar(); + if (isMouseInViewport && isCtrlKeyDown()) { + if (wheel < 0) { + scrollbar.setCurrentScroll(masterList.getHeight()); } else { - super.keyTyped(character, key); + getScrollBar().setCurrentScroll(0); } + return true; + } else if (isMouseInViewport && isShiftKeyDown()) { + // advance to the next section + return masterList.scrollNextSection(wheel > 0); + } else { + return super.mouseWheelEvent(mouseX, mouseY, wheel); } } + private boolean isMouseInViewport(int mouseX, int mouseY) { + return mouseX > guiLeft + VIEW_LEFT && mouseX < guiLeft + VIEW_LEFT + VIEW_WIDTH + && mouseY > guiTop + HEADER_HEIGHT + && mouseY < guiTop + HEADER_HEIGHT + viewHeight; + } + private boolean handleTab() { if (searchFieldInputs.isFocused()) { searchFieldInputs.setFocused(false); @@ -426,139 +752,63 @@ private boolean handleTab() { return false; } - public void postUpdate(List updates) { - - } - - public void postUpdate(final NBTTagCompound in) { - if (in.getBoolean("clear")) { - this.byId.clear(); - this.refreshList = true; - } - - for (final Object oKey : in.func_150296_c()) { - final String key = (String) oKey; - if (key.startsWith("=")) { - try { - final long id = Long.parseLong(key.substring(1), Character.MAX_RADIX); - final NBTTagCompound invData = in.getCompoundTag(key); - int size = invData.getInteger("size"); - final ClientDCInternalInv current = this - .getById(id, invData.getLong("sortBy"), invData.getString("un"), size); - int X = invData.getInteger("x"); - int Y = invData.getInteger("y"); - int Z = invData.getInteger("z"); - int dim = invData.getInteger("dim"); - blockPosHashMap.put(current, new DimensionalCoord(X, Y, Z, dim)); - - for (int x = 0; x < current.getInventory().getSizeInventory(); x++) { - final String which = Integer.toString(x); - if (invData.hasKey(which)) { - current.getInventory().setInventorySlotContents( - x, - ItemStack.loadItemStackFromNBT(invData.getCompoundTag(which))); - } - } - } catch (final NumberFormatException ignored) {} - } + public void postUpdate(List updates, int statusFlags) { + if ((statusFlags & PacketIfaceTermUpdate.CLEAR_ALL_BIT) == PacketIfaceTermUpdate.CLEAR_ALL_BIT) { + /* Should clear all client entries. */ + this.masterList.list.clear(); } + /* Should indicate disconnected, so the terminal turns dark. */ + this.online = (statusFlags & PacketIfaceTermUpdate.DISCONNECT_BIT) != PacketIfaceTermUpdate.DISCONNECT_BIT; - if (this.refreshList) { - this.refreshList = false; - // invalid caches on refresh - this.cachedSearches.clear(); - this.refreshList(); + for (PacketIfaceTermUpdate.PacketEntry cmd : updates) { + parsePacketCmd(cmd); } + this.masterList.markDirty(); } - /** - * Rebuilds the list of interfaces. - *

- * Respects a search term if present (ignores case) and adding only matching patterns. - */ - private void refreshList() { - this.byName.clear(); - this.buttonList.clear(); - this.matchedStacks.clear(); - - final String searchFieldInputs = this.searchFieldInputs.getText().toLowerCase(); - final String searchFieldOutputs = this.searchFieldOutputs.getText().toLowerCase(); - final String searchFieldNames = this.searchFieldNames.getText().toLowerCase(); - - final Set cachedSearch = this.getCacheForSearchTerm( - "IN:" + searchFieldInputs - + "OUT:" - + searchFieldOutputs - + "NAME:" - + searchFieldNames - + AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal - + onlyMolecularAssemblers - + onlyBrokenRecipes); - final boolean rebuild = cachedSearch.isEmpty(); - - for (final ClientDCInternalInv entry : this.byId.values()) { - // ignore inventory if not doing a full rebuild and cache already marks it as miss. - if (!rebuild && !cachedSearch.contains(entry)) { - continue; + private void parsePacketCmd(PacketIfaceTermUpdate.PacketEntry cmd) { + long id = cmd.entryId; + if (cmd instanceof PacketIfaceTermUpdate.PacketAdd addCmd) { + IfaceEntry entry = new IfaceEntry(id, addCmd.name, addCmd.rows, addCmd.rowSize, addCmd.online) + .setLocation(addCmd.x, addCmd.y, addCmd.z, addCmd.dim).setIcons(addCmd.selfRep, addCmd.dispRep) + .setItems(addCmd.items); + masterList.addEntry(entry); + } else if (cmd instanceof PacketIfaceTermUpdate.PacketRemove) { + masterList.removeEntry(id); + } else if (cmd instanceof PacketIfaceTermUpdate.PacketOverwrite owCmd) { + IfaceEntry entry = masterList.list.get(id); + + if (entry == null) { + return; } - // Shortcut to skip any filter if search term is ""/empty - boolean found = searchFieldInputs.isEmpty() && searchFieldOutputs.isEmpty(); - boolean interfaceHasFreeSlots = false; - boolean interfaceHasBrokenRecipes = false; - - // Search if the current inventory holds a pattern containing the search term. - if (!found || AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal || onlyBrokenRecipes) { - for (final ItemStack itemStack : entry.getInventory()) { - // If only Interfaces with empty slots should be shown, check that here - if (itemStack == null) { - interfaceHasFreeSlots = true; - continue; - } - - if (onlyBrokenRecipes && recipeIsBroken(itemStack)) { - interfaceHasBrokenRecipes = true; - } + if (owCmd.onlineValid) { + entry.online = owCmd.online; + } - if ((!searchFieldInputs.isEmpty() && itemStackMatchesSearchTerm(itemStack, searchFieldInputs, 0)) - || (!searchFieldOutputs.isEmpty() - && itemStackMatchesSearchTerm(itemStack, searchFieldOutputs, 1))) { - found = true; - matchedStacks.add(itemStack); - } + if (owCmd.itemsValid) { + if (owCmd.allItemUpdate) { + entry.fullItemUpdate(owCmd.items, owCmd.validIndices.length); + } else { + entry.partialItemUpdate(owCmd.items, owCmd.validIndices); } } - - if ((found && entry.getName().toLowerCase().contains(searchFieldNames)) - && (!onlyMolecularAssemblers || entry.getUnlocalizedName().contains(MOLECULAR_ASSEMBLER)) - && (!AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal || interfaceHasFreeSlots) - && (!onlyBrokenRecipes || interfaceHasBrokenRecipes)) { - this.byName.put(entry.getName(), entry); - cachedSearch.add(entry); - } else { - cachedSearch.remove(entry); + masterList.isDirty = true; + } else if (cmd instanceof PacketIfaceTermUpdate.PacketRename renameCmd) { + IfaceEntry entry = masterList.list.get(id); + + if (entry != null) { + if (StatCollector.canTranslate(renameCmd.newName)) { + entry.dispName = StatCollector.translateToLocal(renameCmd.newName); + } else { + entry.dispName = StatCollector.translateToFallback(renameCmd.newName); + } } + masterList.isDirty = true; } - - this.names.clear(); - this.names.addAll(this.byName.keySet()); - - Collections.sort(this.names); - - this.lines.clear(); - this.lines.ensureCapacity(this.names.size() + this.byId.size()); - - for (final String n : this.names) { - this.lines.add(n); - final ArrayList clientInventories = new ArrayList<>(this.byName.get(n)); - Collections.sort(clientInventories); - this.lines.addAll(clientInventories); - } - - this.setScrollBar(); } - private boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final String searchTerm, int pass) { + private static boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final String searchTerm, boolean in) { if (itemStack == null) { return false; } @@ -569,7 +819,7 @@ private boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final Stri return false; } - final NBTTagList tags = encodedValue.getTagList(pass == 0 ? "in" : "out", 10); + final NBTTagList tags = encodedValue.getTagList(in ? "in" : "out", NBT.TAG_COMPOUND); final boolean containsInvalidDisplayName = GuiText.UnknownItem.getLocal().toLowerCase().contains(searchTerm); for (int i = 0; i < tags.tagCount(); i++) { @@ -591,7 +841,6 @@ private boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final Stri } private boolean recipeIsBroken(final ItemStack itemStack) { - if (itemStack == null) { return false; } @@ -614,61 +863,455 @@ private boolean recipeIsBroken(final ItemStack itemStack) { } } + private int getMaxViewHeight() { + return AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) == TerminalStyle.SMALL + ? AEConfig.instance.InterfaceTerminalSmallSize * 18 + : Integer.MAX_VALUE; + } + + public boolean isOverTextField(final int mousex, final int mousey) { + return searchFieldInputs.isMouseIn(mousex, mousey) || searchFieldOutputs.isMouseIn(mousex, mousey) + || searchFieldNames.isMouseIn(mousex, mousey); + } + + public void setTextFieldValue(final String displayName, final int mousex, final int mousey, final ItemStack stack) { + if (searchFieldInputs.isMouseIn(mousex, mousey)) { + searchFieldInputs.setText(displayName); + } else if (searchFieldOutputs.isMouseIn(mousex, mousey)) { + searchFieldOutputs.setText(displayName); + } else if (searchFieldNames.isMouseIn(mousex, mousey)) { + searchFieldNames.setText(displayName); + } + } + /** - * Tries to retrieve a cache for a with search term as keyword. - *

- * If this cache should be empty, it will populate it with an earlier cache if available or at least the cache for - * the empty string. - * - * @param searchTerm the corresponding search - * @return a Set matching a superset of the search term + * Tracks the list of entries. */ - private Set getCacheForSearchTerm(final String searchTerm) { - if (!this.cachedSearches.containsKey(searchTerm)) { - this.cachedSearches.put(searchTerm, new HashSet<>()); + private class IfaceList { + + private final Map list = new HashMap<>(); + private final Map sections = new TreeMap<>(); + private final List visibleSections = new ArrayList<>(); + private boolean isDirty; + private int height; + private IfaceEntry hoveredEntry; + + IfaceList() { + this.isDirty = true; } - final Set cache = this.cachedSearches.get(searchTerm); + /** + * Performs a full update. + */ + private void update() { + height = 0; + visibleSections.clear(); + + for (IfaceSection section : sections.values()) { + String query = GuiInterfaceTerminal.this.searchFieldNames.getText(); + if (!query.isEmpty() && !section.name.toLowerCase().contains(query.toLowerCase())) { + continue; + } - if (cache.isEmpty() && searchTerm.length() > 1) { - cache.addAll(this.getCacheForSearchTerm(searchTerm.substring(0, searchTerm.length() - 1))); - return cache; + section.isDirty = true; + if (section.getVisible().hasNext()) { + height += section.getHeight(); + visibleSections.add(section); + } + } + isDirty = false; } - return cache; - } + public void markDirty() { + this.isDirty = true; + setScrollBar(); + } - private int getMaxRows() { - return AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) == TerminalStyle.SMALL - ? AEConfig.instance.InterfaceTerminalSmallSize - : Integer.MAX_VALUE; - } + public int getHeight() { + if (isDirty) { + update(); + } + return height; + } - private ClientDCInternalInv getById(final long id, final long sortBy, final String unlocalizedName, - final int sizeInit) { - ClientDCInternalInv o = this.byId.get(id); + /** + * Jump between sections. + */ + private boolean scrollNextSection(boolean up) { + GuiScrollbar scrollbar = getScrollBar(); + int viewY = scrollbar.getCurrentScroll(); + var sections = getVisibleSections(); + boolean result = false; + + if (up) { + int y = masterList.getHeight(); + int i = sections.size() - 1; + + while (y > 0 && i >= 0) { + y -= sections.get(i).getHeight(); + i -= 1; + if (y < viewY) { + result = true; + scrollbar.setCurrentScroll(y); + break; + } + } + } else { + int y = 0; + + for (IfaceSection section : sections) { + if (y > viewY) { + result = true; + scrollbar.setCurrentScroll(y); + break; + } + y += section.getHeight(); + } + } + return result; + } + + public void addEntry(IfaceEntry entry) { + IfaceSection section = sections.get(entry.dispName); + + if (section == null) { + section = new IfaceSection(entry.dispName); + sections.put(entry.dispName, section); + } + section.addEntry(entry); + list.put(entry.id, entry); + isDirty = true; + } + + public void removeEntry(long id) { + IfaceEntry entry = list.remove(id); - if (o == null) { - this.byId.put(id, o = new ClientDCInternalInv(sizeInit, id, sortBy, unlocalizedName)); - this.refreshList = true; + if (entry != null) { + entry.section.removeEntry(entry); + } } - return o; + public List getVisibleSections() { + if (isDirty) { + update(); + } + return visibleSections; + } + + /** + * Mouse button click. + * + * @param relMouseX viewport coords mouse X + * @param relMouseY viewport coords mouse Y + * @param btn button code + */ + public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { + if (relMouseX < 0 || relMouseX >= VIEW_WIDTH || relMouseY < 0 || relMouseY >= viewHeight) { + return false; + } + for (IfaceSection section : getVisibleSections()) { + if (section.mouseClicked(relMouseX, relMouseY, btn)) { + return true; + } + } + return false; + } } - public boolean isOverTextField(final int mousex, final int mousey) { - return searchFieldInputs.isMouseIn(mousex, mousey) || searchFieldOutputs.isMouseIn(mousex, mousey) - || searchFieldNames.isMouseIn(mousex, mousey); + /** + * A section holds all the interface entries with the same name. + */ + private class IfaceSection { + + public static final int TITLE_HEIGHT = 12; + + String name; + List entries = new ArrayList<>(); + Set visibleEntries = new TreeSet<>(Comparator.comparing(e -> { + if (e.dispRep != null) { + return e.dispRep.getDisplayName() + e.id; + } else { + return String.valueOf(e.id); + } + })); + int height; + private boolean isDirty = true; + boolean visible = false; + + IfaceSection(String name) { + this.name = name; + } + + /** + * Gets the height. Includes title. + */ + public int getHeight() { + if (isDirty) { + update(); + } + return height; + } + + private void update() { + refreshVisible(); + if (visibleEntries.isEmpty()) { + height = 0; + } else { + height = TITLE_HEIGHT; + for (IfaceEntry entry : visibleEntries) { + height += entry.guiHeight; + } + } + isDirty = false; + } + + public void refreshVisible() { + visibleEntries.clear(); + String input = GuiInterfaceTerminal.this.searchFieldInputs.getText().toLowerCase(); + String output = GuiInterfaceTerminal.this.searchFieldOutputs.getText().toLowerCase(); + + for (IfaceEntry entry : entries) { + var moleAss = AEApi.instance().definitions().blocks().molecularAssembler().maybeStack(1); + entry.dispY = -9999; + if (onlyMolecularAssemblers + && (!moleAss.isPresent() || !Platform.isSameItem(moleAss.get(), entry.dispRep))) { + continue; + } + if (AEConfig.instance.showOnlyInterfacesWithFreeSlotsInInterfaceTerminal + && entry.numItems == entry.rows * entry.rowSize) { + continue; + } + if (onlyBrokenRecipes && entry.numBrokenRecipes == 0) { + continue; + } + // Find search terms + if (!input.isEmpty() || !output.isEmpty()) { + AppEngInternalInventory inv = entry.inv; + boolean shouldAdd = false; + + for (int i = 0; i < inv.getSizeInventory(); ++i) { + ItemStack stack = inv.getStackInSlot(i); + if (itemStackMatchesSearchTerm(stack, input, true) + && itemStackMatchesSearchTerm(stack, output, false)) { + shouldAdd = true; + entry.filteredRecipes[i] = false; + } else { + entry.filteredRecipes[i] = true; + } + } + if (!shouldAdd) { + continue; + } + } else { + Arrays.fill(entry.filteredRecipes, false); + } + visibleEntries.add(entry); + } + } + + public void addEntry(IfaceEntry entry) { + this.entries.add(entry); + entry.section = this; + this.isDirty = true; + } + + public void removeEntry(IfaceEntry entry) { + this.entries.remove(entry); + entry.section = null; + this.isDirty = true; + } + + public Iterator getVisible() { + if (isDirty) { + update(); + } + return visibleEntries.iterator(); + } + + public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { + Iterator it = getVisible(); + boolean ret = false; + + while (it.hasNext() && !ret) { + ret = it.next().mouseClicked(relMouseX, relMouseY, btn); + } + + return ret; + } } - public void setTextFieldValue(final String displayName, final int mousex, final int mousey, final ItemStack stack) { + /** + * This class keeps track of an entry and its widgets. + */ + private class IfaceEntry { + + String dispName; + AppEngInternalInventory inv; + GuiImgButton optionsButton; + /** Nullable - icon that represents the interface */ + ItemStack selfRep; + /** Nullable - icon that represents the interface's "target" */ + ItemStack dispRep; + IfaceSection section; + long id; + int x, y, z, dim; + int rows, rowSize; + int guiHeight; + int dispY = -9999; + boolean online; + int numBrokenRecipes; + boolean[] brokenRecipes; + int numItems = 0; + /** Should recipe be filtered out/grayed out? */ + boolean[] filteredRecipes; + private int hoveredSlotIdx = -1; + + IfaceEntry(long id, String name, int rows, int rowSize, boolean online) { + this.id = id; + if (StatCollector.canTranslate(name)) { + this.dispName = StatCollector.translateToLocal(name); + } else { + String fallback = name + ".name"; // its whatever. save some bytes on network but looks ugly + if (StatCollector.canTranslate(fallback)) { + this.dispName = StatCollector.translateToLocal(fallback); + } else { + this.dispName = StatCollector.translateToFallback(name); + } + } + this.inv = new AppEngInternalInventory(null, rows * rowSize, 1); + this.rows = rows; + this.rowSize = rowSize; + this.online = online; + this.optionsButton = new GuiImgButton(2, 0, Settings.ACTIONS, ActionItems.HIGHLIGHT_INTERFACE); + this.optionsButton.setHalfSize(true); + this.guiHeight = 18 * rows + 1; + this.brokenRecipes = new boolean[rows * rowSize]; + this.filteredRecipes = new boolean[rows * rowSize]; + } - if (searchFieldInputs.isMouseIn(mousex, mousey)) { - searchFieldInputs.setText(displayName); - } else if (searchFieldOutputs.isMouseIn(mousex, mousey)) { - searchFieldOutputs.setText(displayName); - } else if (searchFieldNames.isMouseIn(mousex, mousey)) { - searchFieldNames.setText(displayName); + IfaceEntry setLocation(int x, int y, int z, int dim) { + this.x = x; + this.y = y; + this.z = z; + this.dim = dim; + + return this; + } + + IfaceEntry setIcons(ItemStack selfRep, ItemStack dispRep) { + // Kotlin would make this pretty easy :( + this.selfRep = selfRep; + this.dispRep = dispRep; + + return this; + } + + public void fullItemUpdate(NBTTagList items, int newSize) { + inv = new AppEngInternalInventory(null, newSize); + rows = newSize / rowSize; + brokenRecipes = new boolean[newSize]; + numItems = 0; + + for (int i = 0; i < inv.getSizeInventory(); ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), i); + } + this.guiHeight = 18 * rows + 4; + } + + IfaceEntry setItems(NBTTagList items) { + assert items.tagCount() == inv.getSizeInventory(); + + for (int i = 0; i < items.tagCount(); ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), i); + } + return this; + } + + public void partialItemUpdate(NBTTagList items, int[] validIndices) { + for (int i = 0; i < validIndices.length; ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), validIndices[i]); + } + } + + private void setItemInSlot(ItemStack stack, int idx) { + final int oldBroke = brokenRecipes[idx] ? 1 : 0; + final int newBroke = recipeIsBroken(stack) ? 1 : 0; + final int oldHasItem = inv.getStackInSlot(idx) != null ? 1 : 0; + final int newHasItem = stack != null ? 1 : 0; + + // Update broken recipe count + numBrokenRecipes += newBroke - oldBroke; + brokenRecipes[idx] = newBroke == 1; + inv.setInventorySlotContents(idx, stack); + assert numBrokenRecipes >= 0; + // Update item count + numItems += newHasItem - oldHasItem; + assert numItems >= 0; + } + + public AppEngInternalInventory getInventory() { + return inv; + } + + public boolean mouseClicked(int mouseX, int mouseY, int btn) { + if (!section.visible || btn < 0 || btn > 2) { + return false; + } + if (mouseX >= optionsButton.xPosition && mouseX < 2 + optionsButton.width + && mouseY > Math.max(optionsButton.yPosition, IfaceSection.TITLE_HEIGHT) + && mouseY <= Math.min(optionsButton.yPosition + optionsButton.height, viewHeight)) { + optionsButton.func_146113_a(mc.getSoundHandler()); + DimensionalCoord blockPos = new DimensionalCoord(x, y, z, dim); + /* View in world */ + WorldCoord blockPos2 = new WorldCoord( + (int) mc.thePlayer.posX, + (int) mc.thePlayer.posY, + (int) mc.thePlayer.posZ); + if (mc.theWorld.provider.dimensionId != dim) { + mc.thePlayer.addChatMessage( + new ChatComponentTranslation(PlayerMessages.InterfaceInOtherDim.getName(), dim)); + } else { + BlockPosHighlighter.highlightBlock( + blockPos, + System.currentTimeMillis() + 500 * WorldCoord.getTaxicabDistance(blockPos, blockPos2)); + mc.thePlayer.addChatMessage( + new ChatComponentTranslation( + PlayerMessages.InterfaceHighlighted.getName(), + blockPos.x, + blockPos.y, + blockPos.z)); + } + mc.thePlayer.closeScreen(); + return true; + } + + int offsetY = mouseY - dispY; + int offsetX = mouseX - (VIEW_WIDTH - rowSize * 18) - 1; + if (offsetX >= 0 && offsetX < (rowSize * 18) + && mouseY > Math.max(dispY, IfaceSection.TITLE_HEIGHT) + && offsetY < Math.min(viewHeight - dispY, guiHeight)) { + final int col = offsetX / 18; + final int row = offsetY / 18; + final int slotIdx = row * rowSize + col; + + // send packet to server, request an update + // TODO: Client prediction. + PacketInventoryAction packet; + + if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) { + packet = new PacketInventoryAction(InventoryAction.MOVE_REGION, 0, id); + } else if (isShiftKeyDown() && (btn == 0 || btn == 1)) { + packet = new PacketInventoryAction(InventoryAction.SHIFT_CLICK, slotIdx, id); + } else if (btn == 0 || btn == 1) { + packet = new PacketInventoryAction(InventoryAction.PICKUP_OR_SET_DOWN, slotIdx, id); + } else { + packet = new PacketInventoryAction(InventoryAction.CREATIVE_DUPLICATE, slotIdx, id); + } + NetworkHandler.instance.sendToServer(packet); + return true; + } + + return false; } } } diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/newinterfaceterminal.png b/src/main/resources/assets/appliedenergistics2/textures/guis/newinterfaceterminal.png index 187e3d02a6d0f39d46f086652260dd80ee24bab9..6dd010c5ae7bbdae6190a34eb7c1b1860f471a67 100644 GIT binary patch literal 3350 zcmeHJYdloz8h>Za!njT2UJApulTz62ls#d}okFoGWJW?Mj4p12nXxM+N;zdtVWuLA zG&Y9hvZ7KcqtFbKTgpUDQ(NRZ&gy(Tzw_ao59jh>e^|fuuJ!z%-}5~Gcm4nCJ?-P= zs;;810sv5VcXQea00v!R08SCT1s^VrKp$cbI6C?q2nz#X)%j!RZ9TC&v`;jfdIX!8 z`w=SHCYjFg4uWU4^JSv3i&a!^*6!mLt=rP!p!<(ZsK$MDI&y5ZLXg51A6SD#uXWki zAb>SZlUlFL#SM`1NRi00r{lI|(Tay>t}RnmtS%P)KAKjXn--iH_vlzVTRp?EI+*fp zlV9U<-^RefgZ1^o3zzU`@x$i*R%cp%Qoovg?P{WHowwkD@c6mo3pdnOrQH7NbdjQ6 zQZ|28l;tvt+)R!dQ}p_oS7?2t<$6m~FuzDRx`Xn@eN<1c2>;Zu#bj#^&{y^-&nh(ssn{I&0B=U2lh< z%0LI#OtG=Hvu5Y>Tk#!ZSf`Ee^!gDwChKvkY}}G-8rg3!&Cy3UK$+&Y`v?H|`tKJ; zr`hHu0IFW@PFwsEL!|vLqP3ssDpKd7utex_y3)Lxipzu0(6OlNG~&t=J*h31aKAa# z{TwLUYgJ+E(^D}kxsn(D;%a?ILOmlnt0Xn~57~!_jm=Y@5NxQoX*;Mt_+}z6V5>ti zk#`0e8xwj)&Jd;rc`JkG@8$o5zhFu`a-^!JYJTu?P

AQ!Ex2*^Zq+2&}TQvO6C_ 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 NS`w1uw~UxHUaNz&| From 1e51d194fe646bce301e280ce6a3636d124e2844 Mon Sep 17 00:00:00 2001 From: Andrei Antropov Date: Sat, 14 Oct 2023 12:55:10 +0300 Subject: [PATCH 12/22] Add interface compatibility to use in `ae2fc` (#404) --- .../implementations/ContainerInterfaceTerminal.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java index 2b9dbb6c1ca..600c6edf538 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java +++ b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java @@ -33,6 +33,7 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; +import appeng.api.networking.security.IActionHost; import appeng.api.util.DimensionalCoord; import appeng.api.util.IIfaceTermViewable; import appeng.container.AEBaseContainer; @@ -56,13 +57,13 @@ public final class ContainerInterfaceTerminal extends AEBaseContainer { private PacketIfaceTermUpdate dirty; private boolean isDirty; private IGrid grid; - private PartInterfaceTerminal partRef; + private IActionHost anchor; private boolean wasOff; - public ContainerInterfaceTerminal(final InventoryPlayer ip, final PartInterfaceTerminal anchor) { + public ContainerInterfaceTerminal(final InventoryPlayer ip, final IActionHost anchor) { super(ip, anchor); assert anchor != null; - this.partRef = anchor; + this.anchor = anchor; if (Platform.isServer()) { this.grid = anchor.getActionableNode().getGrid(); dirty = this.updateList(); @@ -88,7 +89,7 @@ public void detectAndSendChanges() { return; } - final IGridNode agn = this.partRef.getActionableNode(); + final IGridNode agn = this.anchor.getActionableNode(); if (!agn.isActive()) { /* @@ -107,7 +108,7 @@ public void detectAndSendChanges() { } this.wasOff = false; - if (this.partRef.needsUpdate()) { + if (anchor instanceof PartInterfaceTerminal terminal && terminal.needsUpdate()) { PacketIfaceTermUpdate update = this.updateList(); if (update != null) { update.encode(); From 0f3a7eb6067ff47fe533a6009c3f4a0466495bce Mon Sep 17 00:00:00 2001 From: Andrei Antropov Date: Sat, 14 Oct 2023 15:36:39 +0300 Subject: [PATCH 13/22] Add interface compatibility to use in `ae2fc` and direction tracking (#405) --- .../gui/IInterfaceTerminalPostUpdate.java | 16 ++++++++++++++++ .../implementations/GuiInterfaceTerminal.java | 5 ++++- .../ContainerInterfaceTerminal.java | 18 +++++------------- .../sync/packets/PacketIfaceTermUpdate.java | 15 +++++++++------ 4 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java diff --git a/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java b/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java new file mode 100644 index 00000000000..8b35d136a42 --- /dev/null +++ b/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java @@ -0,0 +1,16 @@ +package appeng.client.gui; + +import java.util.List; + +import appeng.core.sync.packets.PacketIfaceTermUpdate.PacketEntry; + +public interface IInterfaceTerminalPostUpdate { + + /** + * Interface to handle updates inside interface terminal + * + * @param updates List of updates + * @param statusFlags status bitflags + */ + void postUpdate(List updates, int statusFlags); +} diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index f62667b2f52..27d3e46a742 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -48,6 +48,7 @@ import appeng.api.util.WorldCoord; import appeng.client.gui.AEBaseGui; import appeng.client.gui.IGuiTooltipHandler; +import appeng.client.gui.IInterfaceTerminalPostUpdate; import appeng.client.gui.widgets.GuiImgButton; import appeng.client.gui.widgets.GuiScrollbar; import appeng.client.gui.widgets.IDropToFillTextField; @@ -89,7 +90,8 @@ * * @author firenoo */ -public class GuiInterfaceTerminal extends AEBaseGui implements IDropToFillTextField, IGuiTooltipHandler { +public class GuiInterfaceTerminal extends AEBaseGui + implements IDropToFillTextField, IGuiTooltipHandler, IInterfaceTerminalPostUpdate { public static final int HEADER_HEIGHT = 52; public static final int INV_HEIGHT = 98; @@ -752,6 +754,7 @@ private boolean handleTab() { return false; } + @Override public void postUpdate(List updates, int statusFlags) { if ((statusFlags & PacketIfaceTermUpdate.CLEAR_ALL_BIT) == PacketIfaceTermUpdate.CLEAR_ALL_BIT) { /* Should clear all client entries. */ diff --git a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java index 600c6edf538..8a0dac36113 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java +++ b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java @@ -42,6 +42,7 @@ import appeng.core.sync.packets.PacketIfaceTermUpdate; import appeng.helpers.InventoryAction; import appeng.items.misc.ItemEncodedPattern; +import appeng.parts.AEBasePart; import appeng.parts.reporting.PartInterfaceTerminal; import appeng.util.InventoryAdaptor; import appeng.util.Platform; @@ -305,7 +306,8 @@ private PacketIfaceTermUpdate updateList() { /* Add a new entry */ if (update == null) update = new PacketIfaceTermUpdate(); InvTracker entry = new InvTracker(nextId++, machine, node.isActive()); - update.addNewEntry(entry.id, entry.name, entry.online).setLoc(entry.x, entry.y, entry.z, entry.dim) + update.addNewEntry(entry.id, entry.name, entry.online) + .setLoc(entry.x, entry.y, entry.z, entry.dim, entry.side.ordinal()) .setItems(entry.rows, entry.rowSize, entry.invNbt) .setReps(machine.getSelfRep(), machine.getDisplayRep()); tracked.put(machine, entry); @@ -355,6 +357,7 @@ private static class InvTracker { private final int y; private final int z; private final int dim; + private final ForgeDirection side; private boolean online; private NBTTagList invNbt; @@ -370,6 +373,7 @@ private static class InvTracker { this.y = location.y; this.z = location.z; this.dim = location.getDimension(); + this.side = machine instanceof AEBasePart hasSide ? hasSide.getSide() : ForgeDirection.UNKNOWN; this.online = online; this.invNbt = new NBTTagList(); updateNBT(); @@ -409,16 +413,4 @@ private void updateNBT() { } } } - // - // private static class PatternInvSlot extends WrapperInvSlot { - // - // public PatternInvSlot(final IInventory inv) { - // super(inv); - // } - // - // @Override - // public boolean isItemValid(final ItemStack itemstack) { - // return itemstack != null && itemstack.getItem() instanceof ItemEncodedPattern; - // } - // } } diff --git a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java index e7c5e6b585c..627005dbcbd 100644 --- a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java +++ b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java @@ -13,7 +13,7 @@ import net.minecraft.nbt.NBTTagList; import net.minecraftforge.common.util.Constants.NBT; -import appeng.client.gui.implementations.GuiInterfaceTerminal; +import appeng.client.gui.IInterfaceTerminalPostUpdate; import appeng.core.AELog; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; @@ -103,7 +103,7 @@ public void setDisconnect() { /** * Adds a new entry. Fill out the rest of the command using the {@link PacketAdd#setItems(int, int, NBTTagList)} and - * {@link PacketAdd#setLoc(int, int, int, int)}. + * {@link PacketAdd#setLoc(int, int, int, int, int)}. * * @return the packet, which needs to have information filled out. */ @@ -143,8 +143,8 @@ public PacketOverwrite addOverwriteEntry(long id) { public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) { final GuiScreen gs = Minecraft.getMinecraft().currentScreen; - if (gs instanceof GuiInterfaceTerminal) { - ((GuiInterfaceTerminal) gs).postUpdate(this.commands, this.statusFlags); + if (gs instanceof IInterfaceTerminalPostUpdate hasPostUpdate) { + hasPostUpdate.postUpdate(this.commands, this.statusFlags); } } @@ -198,7 +198,7 @@ protected PacketEntry(ByteBuf buf) throws IOException { public static class PacketAdd extends PacketEntry { public String name; - public int x, y, z, dim; + public int x, y, z, dim, side; public int rows, rowSize; public boolean online; public ItemStack selfRep, dispRep; @@ -214,11 +214,12 @@ public static class PacketAdd extends PacketEntry { super(buf); } - public PacketAdd setLoc(int x, int y, int z, int dim) { + public PacketAdd setLoc(int x, int y, int z, int dim, int side) { this.x = x; this.y = y; this.z = z; this.dim = dim; + this.side = side; return this; } @@ -252,6 +253,7 @@ protected void write(ByteBuf buf) throws IOException { buf.writeInt(y); buf.writeInt(z); buf.writeInt(dim); + buf.writeInt(side); buf.writeInt(rows); buf.writeInt(rowSize); @@ -284,6 +286,7 @@ protected void read(ByteBuf buf) throws IOException { this.y = buf.readInt(); this.z = buf.readInt(); this.dim = buf.readInt(); + this.side = buf.readInt(); this.rows = buf.readInt(); this.rowSize = buf.readInt(); int payloadSize = buf.readInt(); From 3d3af96509fd01b0e20a6d78c0571ec898c058ac Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Sun, 15 Oct 2023 13:14:44 +0300 Subject: [PATCH 14/22] Fix rendering inside `InterfaceTerminal` AEStack should be correctly set --- .../appeng/client/gui/implementations/GuiInterfaceTerminal.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 27d3e46a742..66a370d852a 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -75,6 +75,7 @@ import appeng.parts.reporting.PartInterfaceTerminal; import appeng.tile.inventory.AppEngInternalInventory; import appeng.util.Platform; +import appeng.util.item.AEItemStack; import cpw.mods.fml.common.Loader; /** @@ -539,6 +540,7 @@ private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouse translatedRenderItem .renderItemAndEffectIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); GL11.glTranslatef(0.0f, 0.0f, ITEMSTACK_OVERLAY_Z - ITEMSTACK_Z); + aeRenderItem.setAeStack(AEItemStack.create(toRender)); aeRenderItem.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); aeRenderItem.zLevel = 0.0f; RenderHelper.disableStandardItemLighting(); From 38ceda920071bc38d85cf98a40c88fda0fe89c89 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Sun, 15 Oct 2023 14:36:00 +0300 Subject: [PATCH 15/22] Fix `zLevel` in `TranslatedRenderItem` for durability bar --- src/main/java/appeng/client/render/TranslatedRenderItem.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/appeng/client/render/TranslatedRenderItem.java b/src/main/java/appeng/client/render/TranslatedRenderItem.java index 19784fdceac..028b513cb8d 100644 --- a/src/main/java/appeng/client/render/TranslatedRenderItem.java +++ b/src/main/java/appeng/client/render/TranslatedRenderItem.java @@ -29,6 +29,7 @@ public void renderItemOverlayIntoGUI(FontRenderer font, TextureManager texManage } if (stack.getItem().showDurabilityBar(stack)) { + GL11.glTranslatef(0.0f, 0.0f, this.zLevel - 1f); double health = stack.getItem().getDurabilityForDisplay(stack); int j1 = (int) Math.round(13.0D - health * 13.0D); int k = (int) Math.round(255.0D - health * 255.0D); @@ -48,6 +49,7 @@ public void renderItemOverlayIntoGUI(FontRenderer font, TextureManager texManage GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_LIGHTING); GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glTranslatef(0.0f, 0.0f, -(this.zLevel - 1f)); } GL11.glPopMatrix(); } From c493a4f081485d07d9de89d97ba1ef1be2dfc192 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Sun, 15 Oct 2023 14:47:10 +0300 Subject: [PATCH 16/22] Fix `TranslatedRenderItem` integrate `POST_HOOKS` to support DuraDisplay --- .../client/render/TranslatedRenderItem.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/appeng/client/render/TranslatedRenderItem.java b/src/main/java/appeng/client/render/TranslatedRenderItem.java index 028b513cb8d..122e3c9747f 100644 --- a/src/main/java/appeng/client/render/TranslatedRenderItem.java +++ b/src/main/java/appeng/client/render/TranslatedRenderItem.java @@ -1,5 +1,7 @@ package appeng.client.render; +import static appeng.client.render.AppEngRenderItem.POST_HOOKS; + import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.RenderItem; @@ -8,6 +10,8 @@ import org.lwjgl.opengl.GL11; +import appeng.api.storage.IItemDisplayRegistry.ItemRenderHook; + /** * Uses translations instead of depth test to perform rendering. */ @@ -17,8 +21,21 @@ public class TranslatedRenderItem extends RenderItem { public void renderItemOverlayIntoGUI(FontRenderer font, TextureManager texManager, ItemStack stack, int x, int y, String customText) { if (stack != null) { + boolean skip = false; + boolean showDurabilitybar = true; + boolean showStackSize = true; + boolean showCraftLabelText = true; + for (ItemRenderHook hook : POST_HOOKS) { + skip |= hook.renderOverlay(font, texManager, stack, x, y); + showDurabilitybar &= hook.showDurability(stack); + showStackSize &= hook.showStackSize(stack); + showCraftLabelText &= hook.showCraftLabelText(stack); + } + if (skip) { + return; + } GL11.glPushMatrix(); - if (stack.stackSize > 1 || customText != null) { + if ((showStackSize && stack.stackSize > 1) || (showCraftLabelText && customText != null)) { GL11.glTranslatef(0.0f, 0.0f, this.zLevel); String s1 = customText == null ? String.valueOf(stack.stackSize) : customText; GL11.glDisable(GL11.GL_LIGHTING); @@ -28,7 +45,7 @@ public void renderItemOverlayIntoGUI(FontRenderer font, TextureManager texManage GL11.glTranslatef(0.0f, 0.0f, -this.zLevel); } - if (stack.getItem().showDurabilityBar(stack)) { + if (showDurabilitybar && stack.getItem().showDurabilityBar(stack)) { GL11.glTranslatef(0.0f, 0.0f, this.zLevel - 1f); double health = stack.getItem().getDurabilityForDisplay(stack); int j1 = (int) Math.round(13.0D - health * 13.0D); From 0a366d69d47d09e0b7666e364c685a934b231ee4 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Sat, 21 Oct 2023 00:46:06 +0300 Subject: [PATCH 17/22] Fix buffer read write inconsistency --- .../ContainerInterfaceTerminal.java | 1 - .../sync/packets/PacketIfaceTermUpdate.java | 177 +++++++++++++++--- 2 files changed, 156 insertions(+), 22 deletions(-) diff --git a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java index 8a0dac36113..85f7ab71637 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java +++ b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java @@ -69,7 +69,6 @@ public ContainerInterfaceTerminal(final InventoryPlayer ip, final IActionHost an this.grid = anchor.getActionableNode().getGrid(); dirty = this.updateList(); if (dirty != null) { - dirty.encode(); this.isDirty = true; } else { dirty = new PacketIfaceTermUpdate(); diff --git a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java index 627005dbcbd..f150ad318c9 100644 --- a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java +++ b/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java @@ -2,7 +2,9 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -12,9 +14,12 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.common.util.ForgeDirection; import appeng.client.gui.IInterfaceTerminalPostUpdate; +import appeng.core.AEConfig; import appeng.core.AELog; +import appeng.core.features.AEFeature; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; import appeng.helpers.Reflected; @@ -50,9 +55,8 @@ private void decode(ByteBuf buf) { int numEntries = buf.readInt(); for (int i = 0; i < numEntries; ++i) { - int packetType = -1; try { - packetType = buf.readByte(); + int packetType = buf.readByte(); PacketType type = PacketType.valueOf(packetType); switch (type) { case ADD -> this.commands.add(new PacketAdd(buf)); @@ -61,17 +65,49 @@ private void decode(ByteBuf buf) { case RENAME -> this.commands.add(new PacketRename(buf)); } } catch (ArrayIndexOutOfBoundsException e) { - AELog.error("Unknown packet type of type %d (-1 = uninitialized)", packetType); - break; + if (AEConfig.instance.isFeatureEnabled(AEFeature.PacketLogging)) { + AELog.info( + "Corrupted packet commands: (" + i + + ") of (" + + numEntries + + ") -> " + + this.commands.size() + + " : " + + this.commands.stream().map(packetEntry -> packetEntry.getClass().getSimpleName()) + .collect(Collectors.groupingBy(String::new, Collectors.counting()))); + if (AEConfig.instance.isFeatureEnabled(AEFeature.DebugLogging)) { + AELog.info(" <- Parsed content: " + this.commands); + } + } + AELog.debug(e); + return; } catch (IOException e) { AELog.error(e); break; } } + if (AEConfig.instance.isFeatureEnabled(AEFeature.PacketLogging)) { + AELog.info( + " <- Received commands " + this.commands.size() + + " : " + + this.commands.stream().map(packetEntry -> packetEntry.getClass().getSimpleName()) + .collect(Collectors.groupingBy(String::new, Collectors.counting()))); + } } public void encode() { try { + if (AEConfig.instance.isFeatureEnabled(AEFeature.PacketLogging)) { + AELog.info( + " -> Sent commands " + this.commands.size() + + " : " + + this.commands.stream().map(packetEntry -> packetEntry.getClass().getSimpleName()) + .collect(Collectors.groupingBy(String::new, Collectors.counting()))); + if (AEConfig.instance.isFeatureEnabled(AEFeature.DebugLogging)) { + AELog.info(" -> Sent commands: " + this.commands); + } + } + ByteBuf buf = Unpooled.buffer(2048); buf.writeInt(this.getPacketID()); buf.writeByte(this.statusFlags); @@ -257,21 +293,21 @@ protected void write(ByteBuf buf) throws IOException { buf.writeInt(rows); buf.writeInt(rowSize); - ByteBuf tempBuf = Unpooled.buffer(256); - try (ByteBufOutputStream stream = new ByteBufOutputStream(tempBuf)) { + ByteBuf tempBuf = Unpooled.directBuffer(256); + try { + try (ByteBufOutputStream stream = new ByteBufOutputStream(tempBuf)) { - NBTTagCompound wrapper = new NBTTagCompound(); + NBTTagCompound wrapper = new NBTTagCompound(); - if (selfRep != null) { - wrapper.setTag("self", selfRep.writeToNBT(new NBTTagCompound())); - } - if (dispRep != null) { - wrapper.setTag("disp", dispRep.writeToNBT(new NBTTagCompound())); + if (selfRep != null) { + wrapper.setTag("self", selfRep.writeToNBT(new NBTTagCompound())); + } + if (dispRep != null) { + wrapper.setTag("disp", dispRep.writeToNBT(new NBTTagCompound())); + } + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); } - wrapper.setTag("data", items); - CompressedStreamTools.writeCompressed(wrapper, stream); - } - try { buf.writeInt(tempBuf.readableBytes()); buf.writeBytes(tempBuf); } finally { @@ -292,6 +328,27 @@ protected void read(ByteBuf buf) throws IOException { int payloadSize = buf.readInt(); try (ByteBufInputStream stream = new ByteBufInputStream(buf, payloadSize)) { NBTTagCompound payload = CompressedStreamTools.readCompressed(stream); + int available = stream.available(); + if (available > 0) { + byte[] left = new byte[available]; + int read = stream.read(left); + if (AEConfig.instance.isFeatureEnabled(AEFeature.PacketLogging)) { + AELog.info( + "Unread bytes detected (" + read + + "): " + + Arrays.toString(left) + + " at " + + dim + + "#(" + + x + + ":" + + y + + ":" + + z + + ")@" + + ForgeDirection.getOrientation(side)); + } + } if (payload.hasKey("self", NBT.TAG_COMPOUND)) { this.selfRep = ItemStack.loadItemStackFromNBT(payload.getCompoundTag("self")); } @@ -303,6 +360,38 @@ protected void read(ByteBuf buf) throws IOException { this.items = payload.getTagList("data", NBT.TAG_COMPOUND); } } + + @Override + public String toString() { + return "PacketAdd{" + "name='" + + name + + '\'' + + ", x=" + + x + + ", y=" + + y + + ", z=" + + z + + ", dim=" + + dim + + ", side=" + + side + + ", rows=" + + rows + + ", rowSize=" + + rowSize + + ", online=" + + online + + ", selfRep=" + + selfRep + + ", dispRep=" + + dispRep + + ", items=" + + items + + ", entryId=" + + entryId + + '}'; + } } public static class PacketRemove extends PacketEntry { @@ -323,6 +412,11 @@ protected void write(ByteBuf buf) { @Override protected void read(ByteBuf buf) {} + + @Override + public String toString() { + return "PacketRemove{" + "entryId=" + entryId + '}'; + } } /** @@ -387,13 +481,21 @@ protected void write(ByteBuf buf) throws IOException { buf.writeInt(validIndex); } } - try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) { + ByteBuf tempBuf = Unpooled.directBuffer(256); + try { + try (ByteBufOutputStream stream = new ByteBufOutputStream(tempBuf)) { - NBTTagCompound wrapper = new NBTTagCompound(); + NBTTagCompound wrapper = new NBTTagCompound(); - wrapper.setTag("data", items); - CompressedStreamTools.writeCompressed(wrapper, stream); + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); + } + buf.writeInt(tempBuf.readableBytes()); + buf.writeBytes(tempBuf); + } finally { + tempBuf.release(); } + } else { buf.writeByte(flags); } @@ -421,11 +523,39 @@ protected void read(ByteBuf buf) throws IOException { } } - try (ByteBufInputStream stream = new ByteBufInputStream(buf)) { + int payloadSize = buf.readInt(); + try (ByteBufInputStream stream = new ByteBufInputStream(buf, payloadSize)) { this.items = CompressedStreamTools.readCompressed(stream).getTagList("data", NBT.TAG_COMPOUND); + int available = stream.available(); + if (available > 0) { + byte[] left = new byte[available]; + int read = stream.read(left); + if (AEConfig.instance.isFeatureEnabled(AEFeature.PacketLogging)) { + AELog.info("Unread bytes detected (" + read + "): " + Arrays.toString(left)); + } + } } } } + + @Override + public String toString() { + return "PacketOverwrite{" + "onlineValid=" + + onlineValid + + ", online=" + + online + + ", itemsValid=" + + itemsValid + + ", allItemUpdate=" + + allItemUpdate + + ", validIndices=" + + Arrays.toString(validIndices) + + ", items=" + + items + + ", entryId=" + + entryId + + '}'; + } } /** @@ -455,5 +585,10 @@ protected void write(ByteBuf buf) { protected void read(ByteBuf buf) { newName = ByteBufUtils.readUTF8String(buf); } + + @Override + public String toString() { + return "PacketRename{" + "newName='" + newName + '\'' + ", entryId=" + entryId + '}'; + } } } From ef031955fc05a72be5141846529cf76e5b7f8622 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Sun, 22 Oct 2023 22:13:29 +0300 Subject: [PATCH 18/22] Fix `zIndexes` for compatability with `DuraDisplay` --- dependencies.gradle | 2 + ...y.java => IInterfaceTerminalRegistry.java} | 6 +- .../api/features/IRegistryContainer.java | 2 +- ...mViewable.java => IInterfaceViewable.java} | 2 +- .../gui/IInterfaceTerminalPostUpdate.java | 2 +- .../implementations/GuiInterfaceTerminal.java | 195 ++++++++++-------- .../ContainerInterfaceTerminal.java | 44 ++-- ...ry.java => InterfaceTerminalRegistry.java} | 18 +- .../registries/RegistryContainer.java | 8 +- .../core/sync/AppEngPacketHandlerBase.java | 4 +- ...ava => PacketInterfaceTerminalUpdate.java} | 6 +- .../java/appeng/helpers/IInterfaceHost.java | 4 +- .../helpers/IInterfaceTerminalSupport.java | 3 +- 13 files changed, 162 insertions(+), 134 deletions(-) rename src/main/java/appeng/api/features/{IIfaceTermRegistry.java => IInterfaceTerminalRegistry.java} (54%) rename src/main/java/appeng/api/util/{IIfaceTermViewable.java => IInterfaceViewable.java} (94%) rename src/main/java/appeng/core/features/registries/{IfaceTermRegistry.java => InterfaceTerminalRegistry.java} (61%) rename src/main/java/appeng/core/sync/packets/{PacketIfaceTermUpdate.java => PacketInterfaceTerminalUpdate.java} (99%) diff --git a/dependencies.gradle b/dependencies.gradle index 41bd5d8d032..ccbd52f6fd6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -27,4 +27,6 @@ dependencies { functionalTestImplementation('com.github.GTNewHorizons:GT5-Unofficial:5.09.44.26:dev') { exclude module: "Applied-Energistics-2-Unofficial" } + + runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.1.7:dev") } diff --git a/src/main/java/appeng/api/features/IIfaceTermRegistry.java b/src/main/java/appeng/api/features/IInterfaceTerminalRegistry.java similarity index 54% rename from src/main/java/appeng/api/features/IIfaceTermRegistry.java rename to src/main/java/appeng/api/features/IInterfaceTerminalRegistry.java index 7f9baf4c64c..f950c51faf4 100644 --- a/src/main/java/appeng/api/features/IIfaceTermRegistry.java +++ b/src/main/java/appeng/api/features/IInterfaceTerminalRegistry.java @@ -1,14 +1,14 @@ package appeng.api.features; -import appeng.api.util.IIfaceTermViewable; +import appeng.api.util.IInterfaceViewable; /** * Registry for interface terminal support. */ -public interface IIfaceTermRegistry { +public interface IInterfaceTerminalRegistry { /** * Registers a class to be considered supported in interface terminals. */ - void register(Class clazz); + void register(Class clazz); } diff --git a/src/main/java/appeng/api/features/IRegistryContainer.java b/src/main/java/appeng/api/features/IRegistryContainer.java index 8dc002fd598..9e47f66808f 100644 --- a/src/main/java/appeng/api/features/IRegistryContainer.java +++ b/src/main/java/appeng/api/features/IRegistryContainer.java @@ -73,7 +73,7 @@ public interface IRegistryContainer { */ IInscriberRegistry inscriber(); - IIfaceTermRegistry ifaceTerm(); + IInterfaceTerminalRegistry interfaceTerminal(); /** * get access to the locatable registry diff --git a/src/main/java/appeng/api/util/IIfaceTermViewable.java b/src/main/java/appeng/api/util/IInterfaceViewable.java similarity index 94% rename from src/main/java/appeng/api/util/IIfaceTermViewable.java rename to src/main/java/appeng/api/util/IInterfaceViewable.java index 0ae440cf72f..1eb536b99d9 100644 --- a/src/main/java/appeng/api/util/IIfaceTermViewable.java +++ b/src/main/java/appeng/api/util/IInterfaceViewable.java @@ -9,7 +9,7 @@ /** * Replacement for {@code IInterfaceTerminalSupport} in API. */ -public interface IIfaceTermViewable extends IGridHost { +public interface IInterfaceViewable extends IGridHost { DimensionalCoord getLocation(); diff --git a/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java b/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java index 8b35d136a42..753b2ef337e 100644 --- a/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java +++ b/src/main/java/appeng/client/gui/IInterfaceTerminalPostUpdate.java @@ -2,7 +2,7 @@ import java.util.List; -import appeng.core.sync.packets.PacketIfaceTermUpdate.PacketEntry; +import appeng.core.sync.packets.PacketInterfaceTerminalUpdate.PacketEntry; public interface IInterfaceTerminalPostUpdate { diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 66a370d852a..493bb8798e9 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -64,8 +64,8 @@ import appeng.core.localization.GuiText; import appeng.core.localization.PlayerMessages; import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.PacketIfaceTermUpdate; -import appeng.core.sync.packets.PacketIfaceTermUpdate.PacketEntry; +import appeng.core.sync.packets.PacketInterfaceTerminalUpdate; +import appeng.core.sync.packets.PacketInterfaceTerminalUpdate.PacketEntry; import appeng.core.sync.packets.PacketInventoryAction; import appeng.helpers.InventoryAction; import appeng.helpers.PatternHelper; @@ -102,7 +102,7 @@ public class GuiInterfaceTerminal extends AEBaseGui AppEng.MOD_ID, "textures/guis/newinterfaceterminal.png"); - private final IfaceList masterList = new IfaceList(); + private final InterfaceTerminalList masterList = new InterfaceTerminalList(); private final MEGuiTextField searchFieldOutputs; private final MEGuiTextField searchFieldInputs; private final MEGuiTextField searchFieldNames; @@ -122,11 +122,13 @@ public class GuiInterfaceTerminal extends AEBaseGui * Z-level Map (FLOATS) 0.0 - BACKGROUND 1.0 - ItemStacks 2.0 - Slot color overlays 20.0 - ItemStack overlays 21.0 - * Slot mouse hover overlay 200.0 - Tooltips */ - private static final float ITEMSTACK_Z = 1.0f; + private static final float ITEM_STACK_Z = 100.0f; private static final float SLOT_Z = 0.5f; - private static final float ITEMSTACK_OVERLAY_Z = 20.0f; - private static final float SLOT_HOVER_Z = 31.0f; - private static final float TOOLTIP_Z = 200.0f; + private static final float ITEM_STACK_OVERLAY_Z = 200.0f; + private static final float SLOT_HOVER_Z = 310.0f; + private static final float TOOLTIP_Z = 210.0f; + private static final float STEP_Z = 10.0f; + private static final float MAGIC_RENDER_ITEM_Z = 50.0f; public GuiInterfaceTerminal(final InventoryPlayer inventoryPlayer, final PartInterfaceTerminal te) { super(new ContainerInterfaceTerminal(inventoryPlayer, te)); @@ -330,7 +332,7 @@ public void drawBG(final int offsetX, final int offsetY, final int mouseX, final offsetY + HEADER_HEIGHT + viewHeight + 1, 0.0f, 0.0f, - (HEADER_HEIGHT + IfaceSection.TITLE_HEIGHT + 1.0f) / 256.0f, + (HEADER_HEIGHT + InterfaceSection.TITLE_HEIGHT + 1.0f) / 256.0f, this.xSize / 256.0f, (HEADER_HEIGHT + 106.0f) / 256.0f); Tessellator.instance.draw(); @@ -362,7 +364,7 @@ private void drawViewport(int relMouseX, int relMouseY) { final int scroll = this.getScrollBar().getCurrentScroll(); int viewY = -scroll; // current y in viewport coordinates int entryIdx = 0; - List visibleSections = this.masterList.getVisibleSections(); + List visibleSections = this.masterList.getVisibleSections(); final float guiScaleX = (float) mc.displayWidth / width; final float guiScaleY = (float) mc.displayHeight / height; @@ -377,7 +379,7 @@ private void drawViewport(int relMouseX, int relMouseY) { * Render each section */ while (viewY < this.viewHeight && entryIdx < visibleSections.size()) { - IfaceSection section = visibleSections.get(entryIdx); + InterfaceSection section = visibleSections.get(entryIdx); int sectionHeight = section.getHeight(); /* Is it viewable/in the viewport at all? */ @@ -404,7 +406,7 @@ private void drawViewport(int relMouseX, int relMouseY) { * @param relMouseY transformed mouse coords relative to viewport * @return the height of the section rendered in viewport coordinates, max of viewHeight. */ - private int drawSection(IfaceSection section, int viewY, int relMouseX, int relMouseY) { + private int drawSection(InterfaceSection section, int viewY, int relMouseX, int relMouseY) { int title; int renderY = 0; final int sectionBottom = viewY + section.getHeight() - 1; @@ -412,57 +414,74 @@ private int drawSection(IfaceSection section, int viewY, int relMouseX, int relM /* * Render title */ - GL11.glTranslatef(0.0f, 0.0f, 50f); bindTexture(BACKGROUND); - if (sectionBottom > 0 && sectionBottom < IfaceSection.TITLE_HEIGHT) { + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z); + if (sectionBottom > 0 && sectionBottom < InterfaceSection.TITLE_HEIGHT) { /* Transition draw */ - drawTexturedModalRect( - 0, - 0, - VIEW_LEFT, - HEADER_HEIGHT + IfaceSection.TITLE_HEIGHT - sectionBottom, - VIEW_WIDTH, - sectionBottom); - fontRendererObj.drawString(section.name, 2, sectionBottom - IfaceSection.TITLE_HEIGHT + 2, fontColor); title = sectionBottom; } else if (viewY < 0) { /* Hidden title draw */ - drawTexturedModalRect(0, 0, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, IfaceSection.TITLE_HEIGHT); - fontRendererObj.drawString(section.name, 2, 2, fontColor); title = 0; } else { /* Normal title draw */ - drawTexturedModalRect(0, viewY, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, IfaceSection.TITLE_HEIGHT); - fontRendererObj.drawString(section.name, 2, viewY + 2, fontColor); title = 0; } - GL11.glTranslatef(0.0f, 0.0f, -50f); + GL11.glTranslatef(0.0f, 0.0f, -(ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z)); GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - Iterator visible = section.getVisible(); + Iterator visible = section.getVisible(); while (visible.hasNext()) { if (viewY < viewHeight) { renderY += drawEntry( visible.next(), - viewY + IfaceSection.TITLE_HEIGHT + renderY, + viewY + InterfaceSection.TITLE_HEIGHT + renderY, title, relMouseX, relMouseY); } else { - IfaceEntry entry = visible.next(); + InterfaceTerminalEntry entry = visible.next(); entry.dispY = -9999; entry.optionsButton.yPosition = -1; } } - return IfaceSection.TITLE_HEIGHT + renderY; + /* + * Render title + */ + bindTexture(BACKGROUND); + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z); + if (sectionBottom > 0 && sectionBottom < InterfaceSection.TITLE_HEIGHT) { + /* Transition draw */ + drawTexturedModalRect( + 0, + 0, + VIEW_LEFT, + HEADER_HEIGHT + InterfaceSection.TITLE_HEIGHT - sectionBottom, + VIEW_WIDTH, + sectionBottom); + fontRendererObj.drawString(section.name, 2, sectionBottom - InterfaceSection.TITLE_HEIGHT + 2, fontColor); + } else if (viewY < 0) { + /* Hidden title draw */ + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glTranslatef(0.0f, 0.0f, 100f); + drawTexturedModalRect(0, 0, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, InterfaceSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, 2, fontColor); + GL11.glEnable(GL11.GL_DEPTH_TEST); + } else { + /* Normal title draw */ + drawTexturedModalRect(0, viewY, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, InterfaceSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, viewY + 2, fontColor); + } + GL11.glTranslatef(0.0f, 0.0f, -(ITEM_STACK_OVERLAY_Z + ITEM_STACK_Z + STEP_Z)); + + return InterfaceSection.TITLE_HEIGHT + renderY; } /** - * Draws the entry. In practice it just draws the slots + items. + * Draws the entry. In practice, it just draws the slots + items. * * @param viewY the gui coordinate z */ - private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouseX, int relMouseY) { + private int drawEntry(InterfaceTerminalEntry entry, int viewY, int titleBottom, int relMouseX, int relMouseY) { bindTexture(BACKGROUND); Tessellator.instance.startDrawingQuads(); int relY = 0; @@ -498,7 +517,7 @@ private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouse entry.optionsButton.yPosition = viewY + 5; entry.optionsButton.drawButton(mc, relMouseX, relMouseY); if (entry.optionsButton.getMouseIn() - && relMouseY >= Math.max(IfaceSection.TITLE_HEIGHT, entry.optionsButton.yPosition)) { + && relMouseY >= Math.max(InterfaceSection.TITLE_HEIGHT, entry.optionsButton.yPosition)) { // draw a tooltip GL11.glTranslatef(0f, 0f, TOOLTIP_Z); GL11.glDisable(GL11.GL_SCISSOR_TEST); @@ -526,30 +545,30 @@ private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouse ItemStack stack = inv.getStackInSlot(slotIdx); boolean tooltip = relMouseX > colLeft - 1 && relMouseX < colRight - 1 - && relMouseY >= Math.max(viewY + rowYTop, IfaceSection.TITLE_HEIGHT) + && relMouseY >= Math.max(viewY + rowYTop, InterfaceSection.TITLE_HEIGHT) && relMouseY < Math.min(viewY + rowYBot, viewHeight); if (stack != null) { final ItemEncodedPattern iep = (ItemEncodedPattern) stack.getItem(); final ItemStack toRender = iep.getOutput(stack); GL11.glPushMatrix(); - GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEMSTACK_Z); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEM_STACK_Z); GL11.glEnable(GL12.GL_RESCALE_NORMAL); RenderHelper.enableGUIStandardItemLighting(); - translatedRenderItem.zLevel = 3.0f - 50.0f; + translatedRenderItem.zLevel = ITEM_STACK_Z - MAGIC_RENDER_ITEM_Z; translatedRenderItem .renderItemAndEffectIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); - GL11.glTranslatef(0.0f, 0.0f, ITEMSTACK_OVERLAY_Z - ITEMSTACK_Z); + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z - ITEM_STACK_Z); aeRenderItem.setAeStack(AEItemStack.create(toRender)); aeRenderItem.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), toRender, 0, 0); aeRenderItem.zLevel = 0.0f; RenderHelper.disableStandardItemLighting(); if (!tooltip) { if (entry.brokenRecipes[slotIdx]) { - GL11.glTranslatef(0.0f, 0.0f, SLOT_Z - ITEMSTACK_OVERLAY_Z); + GL11.glTranslatef(0.0f, 0.0f, SLOT_Z - ITEM_STACK_OVERLAY_Z); drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayInvalid.getColor()); } else if (entry.filteredRecipes[slotIdx]) { - GL11.glTranslatef(0.0f, 0.0f, ITEMSTACK_OVERLAY_Z); + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z); drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); } } else { @@ -558,7 +577,7 @@ private int drawEntry(IfaceEntry entry, int viewY, int titleBottom, int relMouse GL11.glPopMatrix(); } else if (entry.filteredRecipes[slotIdx]) { GL11.glPushMatrix(); - GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEMSTACK_OVERLAY_Z); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEM_STACK_OVERLAY_Z); drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); GL11.glPopMatrix(); } @@ -758,30 +777,36 @@ private boolean handleTab() { @Override public void postUpdate(List updates, int statusFlags) { - if ((statusFlags & PacketIfaceTermUpdate.CLEAR_ALL_BIT) == PacketIfaceTermUpdate.CLEAR_ALL_BIT) { + if ((statusFlags & PacketInterfaceTerminalUpdate.CLEAR_ALL_BIT) + == PacketInterfaceTerminalUpdate.CLEAR_ALL_BIT) { /* Should clear all client entries. */ this.masterList.list.clear(); } /* Should indicate disconnected, so the terminal turns dark. */ - this.online = (statusFlags & PacketIfaceTermUpdate.DISCONNECT_BIT) != PacketIfaceTermUpdate.DISCONNECT_BIT; + this.online = (statusFlags & PacketInterfaceTerminalUpdate.DISCONNECT_BIT) + != PacketInterfaceTerminalUpdate.DISCONNECT_BIT; - for (PacketIfaceTermUpdate.PacketEntry cmd : updates) { + for (PacketInterfaceTerminalUpdate.PacketEntry cmd : updates) { parsePacketCmd(cmd); } this.masterList.markDirty(); } - private void parsePacketCmd(PacketIfaceTermUpdate.PacketEntry cmd) { + private void parsePacketCmd(PacketInterfaceTerminalUpdate.PacketEntry cmd) { long id = cmd.entryId; - if (cmd instanceof PacketIfaceTermUpdate.PacketAdd addCmd) { - IfaceEntry entry = new IfaceEntry(id, addCmd.name, addCmd.rows, addCmd.rowSize, addCmd.online) - .setLocation(addCmd.x, addCmd.y, addCmd.z, addCmd.dim).setIcons(addCmd.selfRep, addCmd.dispRep) - .setItems(addCmd.items); + if (cmd instanceof PacketInterfaceTerminalUpdate.PacketAdd addCmd) { + InterfaceTerminalEntry entry = new InterfaceTerminalEntry( + id, + addCmd.name, + addCmd.rows, + addCmd.rowSize, + addCmd.online).setLocation(addCmd.x, addCmd.y, addCmd.z, addCmd.dim) + .setIcons(addCmd.selfRep, addCmd.dispRep).setItems(addCmd.items); masterList.addEntry(entry); - } else if (cmd instanceof PacketIfaceTermUpdate.PacketRemove) { + } else if (cmd instanceof PacketInterfaceTerminalUpdate.PacketRemove) { masterList.removeEntry(id); - } else if (cmd instanceof PacketIfaceTermUpdate.PacketOverwrite owCmd) { - IfaceEntry entry = masterList.list.get(id); + } else if (cmd instanceof PacketInterfaceTerminalUpdate.PacketOverwrite owCmd) { + InterfaceTerminalEntry entry = masterList.list.get(id); if (entry == null) { return; @@ -799,8 +824,8 @@ private void parsePacketCmd(PacketIfaceTermUpdate.PacketEntry cmd) { } } masterList.isDirty = true; - } else if (cmd instanceof PacketIfaceTermUpdate.PacketRename renameCmd) { - IfaceEntry entry = masterList.list.get(id); + } else if (cmd instanceof PacketInterfaceTerminalUpdate.PacketRename renameCmd) { + InterfaceTerminalEntry entry = masterList.list.get(id); if (entry != null) { if (StatCollector.canTranslate(renameCmd.newName)) { @@ -892,16 +917,16 @@ public void setTextFieldValue(final String displayName, final int mousex, final /** * Tracks the list of entries. */ - private class IfaceList { + private class InterfaceTerminalList { - private final Map list = new HashMap<>(); - private final Map sections = new TreeMap<>(); - private final List visibleSections = new ArrayList<>(); + private final Map list = new HashMap<>(); + private final Map sections = new TreeMap<>(); + private final List visibleSections = new ArrayList<>(); private boolean isDirty; private int height; - private IfaceEntry hoveredEntry; + private InterfaceTerminalEntry hoveredEntry; - IfaceList() { + InterfaceTerminalList() { this.isDirty = true; } @@ -912,7 +937,7 @@ private void update() { height = 0; visibleSections.clear(); - for (IfaceSection section : sections.values()) { + for (InterfaceSection section : sections.values()) { String query = GuiInterfaceTerminal.this.searchFieldNames.getText(); if (!query.isEmpty() && !section.name.toLowerCase().contains(query.toLowerCase())) { continue; @@ -964,7 +989,7 @@ private boolean scrollNextSection(boolean up) { } else { int y = 0; - for (IfaceSection section : sections) { + for (InterfaceSection section : sections) { if (y > viewY) { result = true; scrollbar.setCurrentScroll(y); @@ -976,11 +1001,11 @@ private boolean scrollNextSection(boolean up) { return result; } - public void addEntry(IfaceEntry entry) { - IfaceSection section = sections.get(entry.dispName); + public void addEntry(InterfaceTerminalEntry entry) { + InterfaceSection section = sections.get(entry.dispName); if (section == null) { - section = new IfaceSection(entry.dispName); + section = new InterfaceSection(entry.dispName); sections.put(entry.dispName, section); } section.addEntry(entry); @@ -989,14 +1014,14 @@ public void addEntry(IfaceEntry entry) { } public void removeEntry(long id) { - IfaceEntry entry = list.remove(id); + InterfaceTerminalEntry entry = list.remove(id); if (entry != null) { entry.section.removeEntry(entry); } } - public List getVisibleSections() { + public List getVisibleSections() { if (isDirty) { update(); } @@ -1014,7 +1039,7 @@ public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { if (relMouseX < 0 || relMouseX >= VIEW_WIDTH || relMouseY < 0 || relMouseY >= viewHeight) { return false; } - for (IfaceSection section : getVisibleSections()) { + for (InterfaceSection section : getVisibleSections()) { if (section.mouseClicked(relMouseX, relMouseY, btn)) { return true; } @@ -1026,13 +1051,13 @@ public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { /** * A section holds all the interface entries with the same name. */ - private class IfaceSection { + private class InterfaceSection { public static final int TITLE_HEIGHT = 12; String name; - List entries = new ArrayList<>(); - Set visibleEntries = new TreeSet<>(Comparator.comparing(e -> { + List entries = new ArrayList<>(); + Set visibleEntries = new TreeSet<>(Comparator.comparing(e -> { if (e.dispRep != null) { return e.dispRep.getDisplayName() + e.id; } else { @@ -1043,7 +1068,7 @@ private class IfaceSection { private boolean isDirty = true; boolean visible = false; - IfaceSection(String name) { + InterfaceSection(String name) { this.name = name; } @@ -1063,7 +1088,7 @@ private void update() { height = 0; } else { height = TITLE_HEIGHT; - for (IfaceEntry entry : visibleEntries) { + for (InterfaceTerminalEntry entry : visibleEntries) { height += entry.guiHeight; } } @@ -1075,7 +1100,7 @@ public void refreshVisible() { String input = GuiInterfaceTerminal.this.searchFieldInputs.getText().toLowerCase(); String output = GuiInterfaceTerminal.this.searchFieldOutputs.getText().toLowerCase(); - for (IfaceEntry entry : entries) { + for (InterfaceTerminalEntry entry : entries) { var moleAss = AEApi.instance().definitions().blocks().molecularAssembler().maybeStack(1); entry.dispY = -9999; if (onlyMolecularAssemblers @@ -1114,19 +1139,19 @@ && itemStackMatchesSearchTerm(stack, output, false)) { } } - public void addEntry(IfaceEntry entry) { + public void addEntry(InterfaceTerminalEntry entry) { this.entries.add(entry); entry.section = this; this.isDirty = true; } - public void removeEntry(IfaceEntry entry) { + public void removeEntry(InterfaceTerminalEntry entry) { this.entries.remove(entry); entry.section = null; this.isDirty = true; } - public Iterator getVisible() { + public Iterator getVisible() { if (isDirty) { update(); } @@ -1134,7 +1159,7 @@ public Iterator getVisible() { } public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { - Iterator it = getVisible(); + Iterator it = getVisible(); boolean ret = false; while (it.hasNext() && !ret) { @@ -1148,7 +1173,7 @@ public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { /** * This class keeps track of an entry and its widgets. */ - private class IfaceEntry { + private class InterfaceTerminalEntry { String dispName; AppEngInternalInventory inv; @@ -1157,7 +1182,7 @@ private class IfaceEntry { ItemStack selfRep; /** Nullable - icon that represents the interface's "target" */ ItemStack dispRep; - IfaceSection section; + InterfaceSection section; long id; int x, y, z, dim; int rows, rowSize; @@ -1171,7 +1196,7 @@ private class IfaceEntry { boolean[] filteredRecipes; private int hoveredSlotIdx = -1; - IfaceEntry(long id, String name, int rows, int rowSize, boolean online) { + InterfaceTerminalEntry(long id, String name, int rows, int rowSize, boolean online) { this.id = id; if (StatCollector.canTranslate(name)) { this.dispName = StatCollector.translateToLocal(name); @@ -1194,7 +1219,7 @@ private class IfaceEntry { this.filteredRecipes = new boolean[rows * rowSize]; } - IfaceEntry setLocation(int x, int y, int z, int dim) { + InterfaceTerminalEntry setLocation(int x, int y, int z, int dim) { this.x = x; this.y = y; this.z = z; @@ -1203,7 +1228,7 @@ IfaceEntry setLocation(int x, int y, int z, int dim) { return this; } - IfaceEntry setIcons(ItemStack selfRep, ItemStack dispRep) { + InterfaceTerminalEntry setIcons(ItemStack selfRep, ItemStack dispRep) { // Kotlin would make this pretty easy :( this.selfRep = selfRep; this.dispRep = dispRep; @@ -1223,7 +1248,7 @@ public void fullItemUpdate(NBTTagList items, int newSize) { this.guiHeight = 18 * rows + 4; } - IfaceEntry setItems(NBTTagList items) { + InterfaceTerminalEntry setItems(NBTTagList items) { assert items.tagCount() == inv.getSizeInventory(); for (int i = 0; i < items.tagCount(); ++i) { @@ -1263,7 +1288,7 @@ public boolean mouseClicked(int mouseX, int mouseY, int btn) { return false; } if (mouseX >= optionsButton.xPosition && mouseX < 2 + optionsButton.width - && mouseY > Math.max(optionsButton.yPosition, IfaceSection.TITLE_HEIGHT) + && mouseY > Math.max(optionsButton.yPosition, InterfaceSection.TITLE_HEIGHT) && mouseY <= Math.min(optionsButton.yPosition + optionsButton.height, viewHeight)) { optionsButton.func_146113_a(mc.getSoundHandler()); DimensionalCoord blockPos = new DimensionalCoord(x, y, z, dim); @@ -1293,7 +1318,7 @@ public boolean mouseClicked(int mouseX, int mouseY, int btn) { int offsetY = mouseY - dispY; int offsetX = mouseX - (VIEW_WIDTH - rowSize * 18) - 1; if (offsetX >= 0 && offsetX < (rowSize * 18) - && mouseY > Math.max(dispY, IfaceSection.TITLE_HEIGHT) + && mouseY > Math.max(dispY, InterfaceSection.TITLE_HEIGHT) && offsetY < Math.min(viewHeight - dispY, guiHeight)) { final int col = offsetX / 18; final int row = offsetY / 18; diff --git a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java index 85f7ab71637..339b372f229 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java +++ b/src/main/java/appeng/container/implementations/ContainerInterfaceTerminal.java @@ -35,11 +35,11 @@ import appeng.api.networking.IGridNode; import appeng.api.networking.security.IActionHost; import appeng.api.util.DimensionalCoord; -import appeng.api.util.IIfaceTermViewable; +import appeng.api.util.IInterfaceViewable; import appeng.container.AEBaseContainer; -import appeng.core.features.registries.IfaceTermRegistry; +import appeng.core.features.registries.InterfaceTerminalRegistry; import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.PacketIfaceTermUpdate; +import appeng.core.sync.packets.PacketInterfaceTerminalUpdate; import appeng.helpers.InventoryAction; import appeng.items.misc.ItemEncodedPattern; import appeng.parts.AEBasePart; @@ -53,9 +53,9 @@ public final class ContainerInterfaceTerminal extends AEBaseContainer { private int nextId = 0; - private final Map tracked = new HashMap<>(); + private final Map tracked = new HashMap<>(); private final Map trackedById = new HashMap<>(); - private PacketIfaceTermUpdate dirty; + private PacketInterfaceTerminalUpdate dirty; private boolean isDirty; private IGrid grid; private IActionHost anchor; @@ -71,7 +71,7 @@ public ContainerInterfaceTerminal(final InventoryPlayer ip, final IActionHost an if (dirty != null) { this.isDirty = true; } else { - dirty = new PacketIfaceTermUpdate(); + dirty = new PacketInterfaceTerminalUpdate(); } } this.bindPlayerInventory(ip, 14, 3); @@ -98,7 +98,7 @@ public void detectAndSendChanges() { * updates. This prevents DoSing the player if their network is flickering. */ if (!this.wasOff) { - PacketIfaceTermUpdate update = new PacketIfaceTermUpdate(); + PacketInterfaceTerminalUpdate update = new PacketInterfaceTerminalUpdate(); update.setDisconnect(); this.wasOff = true; @@ -109,7 +109,7 @@ public void detectAndSendChanges() { this.wasOff = false; if (anchor instanceof PartInterfaceTerminal terminal && terminal.needsUpdate()) { - PacketIfaceTermUpdate update = this.updateList(); + PacketInterfaceTerminalUpdate update = this.updateList(); if (update != null) { update.encode(); NetworkHandler.instance.sendTo(update, (EntityPlayerMP) this.getPlayerInv().player); @@ -117,7 +117,7 @@ public void detectAndSendChanges() { } else if (isDirty) { this.dirty.encode(); NetworkHandler.instance.sendTo(this.dirty, (EntityPlayerMP) this.getPlayerInv().player); - this.dirty = new PacketIfaceTermUpdate(); + this.dirty = new PacketInterfaceTerminalUpdate(); this.isDirty = false; } } @@ -264,14 +264,14 @@ private ItemStack mergeToPlayerInventory(InventoryAdaptor playerInv, ItemStack s /** * Finds out whether any updates are needed, and if so, incrementally updates the list. */ - private PacketIfaceTermUpdate updateList() { - PacketIfaceTermUpdate update = null; - var supported = IfaceTermRegistry.instance().getSupportedClasses(); - Set visited = new HashSet<>(); + private PacketInterfaceTerminalUpdate updateList() { + PacketInterfaceTerminalUpdate update = null; + var supported = InterfaceTerminalRegistry.instance().getSupportedClasses(); + Set visited = new HashSet<>(); - for (Class c : supported) { + for (Class c : supported) { for (IGridNode node : grid.getMachines(c)) { - IIfaceTermViewable machine = (IIfaceTermViewable) node.getMachine(); + IInterfaceViewable machine = (IInterfaceViewable) node.getMachine(); /* First check if we are already tracking this node */ if (tracked.containsKey(machine)) { /* Check for updates */ @@ -281,7 +281,7 @@ private PacketIfaceTermUpdate updateList() { String name = machine.getName(); if (!Objects.equals(known.name, name)) { - if (update == null) update = new PacketIfaceTermUpdate(); + if (update == null) update = new PacketInterfaceTerminalUpdate(); update.addRenamedEntry(known.id, name); known.name = name; } @@ -292,18 +292,18 @@ private PacketIfaceTermUpdate updateList() { if (!known.online && isActive) { /* Node offline -> online */ known.online = true; - if (update == null) update = new PacketIfaceTermUpdate(); + if (update == null) update = new PacketInterfaceTerminalUpdate(); known.updateNBT(); update.addOverwriteEntry(known.id).setOnline(true).setItems(new int[0], known.invNbt); } else if (known.online && !isActive) { /* Node online -> offline */ known.online = false; - if (update == null) update = new PacketIfaceTermUpdate(); + if (update == null) update = new PacketInterfaceTerminalUpdate(); update.addOverwriteEntry(known.id).setOnline(false); } } else { /* Add a new entry */ - if (update == null) update = new PacketIfaceTermUpdate(); + if (update == null) update = new PacketInterfaceTerminalUpdate(); InvTracker entry = new InvTracker(nextId++, machine, node.isActive()); update.addNewEntry(entry.id, entry.name, entry.online) .setLoc(entry.x, entry.y, entry.z, entry.dim, entry.side.ordinal()) @@ -317,14 +317,14 @@ private PacketIfaceTermUpdate updateList() { } /* Now find any entries that we need to remove */ - Iterator> it = tracked.entrySet().iterator(); + Iterator> it = tracked.entrySet().iterator(); while (it.hasNext()) { var entry = it.next(); if (visited.contains(entry.getKey())) { continue; } - if (update == null) update = new PacketIfaceTermUpdate(); + if (update == null) update = new PacketInterfaceTerminalUpdate(); trackedById.remove(entry.getValue().id); it.remove(); @@ -360,7 +360,7 @@ private static class InvTracker { private boolean online; private NBTTagList invNbt; - InvTracker(long id, IIfaceTermViewable machine, boolean online) { + InvTracker(long id, IInterfaceViewable machine, boolean online) { DimensionalCoord location = machine.getLocation(); this.id = id; diff --git a/src/main/java/appeng/core/features/registries/IfaceTermRegistry.java b/src/main/java/appeng/core/features/registries/InterfaceTerminalRegistry.java similarity index 61% rename from src/main/java/appeng/core/features/registries/IfaceTermRegistry.java rename to src/main/java/appeng/core/features/registries/InterfaceTerminalRegistry.java index 846fdb8127f..4bd4f280044 100644 --- a/src/main/java/appeng/core/features/registries/IfaceTermRegistry.java +++ b/src/main/java/appeng/core/features/registries/InterfaceTerminalRegistry.java @@ -3,8 +3,8 @@ import java.util.HashSet; import java.util.Set; -import appeng.api.features.IIfaceTermRegistry; -import appeng.api.util.IIfaceTermViewable; +import appeng.api.features.IInterfaceTerminalRegistry; +import appeng.api.util.IInterfaceViewable; import appeng.parts.misc.PartInterface; import appeng.parts.p2p.PartP2PInterface; import appeng.tile.misc.TileInterface; @@ -12,15 +12,15 @@ /** * Interface Terminal Registry impl for registering viewable instances. */ -public class IfaceTermRegistry implements IIfaceTermRegistry { +public class InterfaceTerminalRegistry implements IInterfaceTerminalRegistry { - private final Set> supportedClasses = new HashSet<>(); - private static IfaceTermRegistry INSTANCE; + private final Set> supportedClasses = new HashSet<>(); + private static InterfaceTerminalRegistry INSTANCE; /** * Singleton, do not instantiate more than once. */ - public IfaceTermRegistry() { + public InterfaceTerminalRegistry() { supportedClasses.add(TileInterface.class); supportedClasses.add(PartInterface.class); supportedClasses.add(PartP2PInterface.class); @@ -30,16 +30,16 @@ public IfaceTermRegistry() { /** * Get all supported classes that were registered during startup */ - public Set> getSupportedClasses() { + public Set> getSupportedClasses() { return supportedClasses; } @Override - public void register(Class clazz) { + public void register(Class clazz) { supportedClasses.add(clazz); } - public static IfaceTermRegistry instance() { + public static InterfaceTerminalRegistry instance() { return INSTANCE; } } diff --git a/src/main/java/appeng/core/features/registries/RegistryContainer.java b/src/main/java/appeng/core/features/registries/RegistryContainer.java index 506bb73c0ec..6561d49b176 100644 --- a/src/main/java/appeng/core/features/registries/RegistryContainer.java +++ b/src/main/java/appeng/core/features/registries/RegistryContainer.java @@ -11,8 +11,8 @@ package appeng.core.features.registries; import appeng.api.features.IGrinderRegistry; -import appeng.api.features.IIfaceTermRegistry; import appeng.api.features.IInscriberRegistry; +import appeng.api.features.IInterfaceTerminalRegistry; import appeng.api.features.ILocatableRegistry; import appeng.api.features.IMatterCannonAmmoRegistry; import appeng.api.features.IP2PTunnelRegistry; @@ -43,7 +43,7 @@ public class RegistryContainer implements IRegistryContainer { private final IExternalStorageRegistry storage = new ExternalStorageRegistry(); private final ICellRegistry cell = new CellRegistry(); private final IItemDisplayRegistry itemDisplay = new ItemDisplayRegistry(); - private final IIfaceTermRegistry ifaceTerm = new IfaceTermRegistry(); + private final IInterfaceTerminalRegistry interfaceTerminalRegistry = new InterfaceTerminalRegistry(); private final ILocatableRegistry locatable = new LocatableRegistry(); private final ISpecialComparisonRegistry comparison = new SpecialComparisonRegistry(); private final IWirelessTermRegistry wireless = new WirelessRegistry(); @@ -100,8 +100,8 @@ public IInscriberRegistry inscriber() { } @Override - public IIfaceTermRegistry ifaceTerm() { - return this.ifaceTerm; + public IInterfaceTerminalRegistry interfaceTerminal() { + return this.interfaceTerminalRegistry; } @Override diff --git a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java index e3d5c17922c..b5af54ce0ca 100644 --- a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java +++ b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java @@ -26,7 +26,7 @@ import appeng.core.sync.packets.PacketCraftingItemInterface; import appeng.core.sync.packets.PacketCraftingRemainingOperations; import appeng.core.sync.packets.PacketCraftingTreeData; -import appeng.core.sync.packets.PacketIfaceTermUpdate; +import appeng.core.sync.packets.PacketInterfaceTerminalUpdate; import appeng.core.sync.packets.PacketInventoryAction; import appeng.core.sync.packets.PacketLightning; import appeng.core.sync.packets.PacketMEInventoryUpdate; @@ -112,7 +112,7 @@ public enum PacketTypes { PACKET_CRAFTING_ITEM_INTERFACE(PacketCraftingItemInterface.class), PACKET_CRAFTING_TREE_DATA(PacketCraftingTreeData.class), PACKET_NEI_BOOKMARK(PacketNEIBookmark.class), - PACKET_IFACE_TERMINAL_UPDATE(PacketIfaceTermUpdate.class),; + PACKET_INTERFACE_TERMINAL_UPDATE(PacketInterfaceTerminalUpdate.class),; private final Class packetClass; private final Constructor packetConstructor; diff --git a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java b/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java similarity index 99% rename from src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java rename to src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java index f150ad318c9..08fbe1ee81c 100644 --- a/src/main/java/appeng/core/sync/packets/PacketIfaceTermUpdate.java +++ b/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java @@ -35,7 +35,7 @@ * Packet used for interface terminal updates. Packet allows the server to send an array of command packets, which are * then processed in order. This allows for chaining commands to produce the desired update. */ -public class PacketIfaceTermUpdate extends AppEngPacket { +public class PacketInterfaceTerminalUpdate extends AppEngPacket { public static final int CLEAR_ALL_BIT = 1; public static final int DISCONNECT_BIT = 2; @@ -44,11 +44,11 @@ public class PacketIfaceTermUpdate extends AppEngPacket { private int statusFlags; @Reflected - public PacketIfaceTermUpdate(final ByteBuf buf) throws IOException { + public PacketInterfaceTerminalUpdate(final ByteBuf buf) throws IOException { decode(buf); } - public PacketIfaceTermUpdate() {} + public PacketInterfaceTerminalUpdate() {} private void decode(ByteBuf buf) { this.statusFlags = buf.readByte(); diff --git a/src/main/java/appeng/helpers/IInterfaceHost.java b/src/main/java/appeng/helpers/IInterfaceHost.java index e5db9ca329b..acf0525656c 100644 --- a/src/main/java/appeng/helpers/IInterfaceHost.java +++ b/src/main/java/appeng/helpers/IInterfaceHost.java @@ -23,9 +23,9 @@ import appeng.api.implementations.IUpgradeableHost; import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.crafting.ICraftingRequester; -import appeng.api.util.IIfaceTermViewable; +import appeng.api.util.IInterfaceViewable; -public interface IInterfaceHost extends ICraftingProvider, IUpgradeableHost, ICraftingRequester, IIfaceTermViewable { +public interface IInterfaceHost extends ICraftingProvider, IUpgradeableHost, ICraftingRequester, IInterfaceViewable { DualityInterface getInterfaceDuality(); diff --git a/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java b/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java index 9b4c7a9b5df..40d3aa78cff 100644 --- a/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java +++ b/src/main/java/appeng/helpers/IInterfaceTerminalSupport.java @@ -5,11 +5,12 @@ import appeng.api.networking.IGridHost; import appeng.api.util.DimensionalCoord; +import appeng.api.util.IInterfaceViewable; /** * Refactoring this class into API, and renaming. * - * @see appeng.api.util.IIfaceTermViewable + * @see IInterfaceViewable */ @Deprecated public interface IInterfaceTerminalSupport extends IGridHost { From eb785326b84d5f78985a36394c01801382b00709 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Mon, 23 Oct 2023 00:35:58 +0300 Subject: [PATCH 19/22] Use `IInterfaceViewable` to decide crafting location --- .../me/cluster/implementations/CraftingCPUCluster.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java index 9c5ec725d07..317ceba6707 100644 --- a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java +++ b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java @@ -66,6 +66,7 @@ import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; import appeng.api.util.DimensionalCoord; +import appeng.api.util.IInterfaceViewable; import appeng.api.util.WorldCoord; import appeng.container.ContainerNull; import appeng.core.AELog; @@ -75,7 +76,6 @@ import appeng.crafting.CraftingWatcher; import appeng.crafting.MECraftingInventory; import appeng.helpers.DualityInterface; -import appeng.helpers.IInterfaceTerminalSupport; import appeng.me.cache.CraftingGridCache; import appeng.me.cluster.IAECluster; import appeng.tile.AEBaseTile; @@ -1243,8 +1243,8 @@ private TileEntity getTile(ICraftingMedium craftingProvider) { return ((DualityInterface) craftingProvider).getHost().getTile(); } else if (craftingProvider instanceof AEBaseTile) { return ((AEBaseTile) craftingProvider).getTile(); - } else if (craftingProvider instanceof IInterfaceTerminalSupport interfaceTerminalSupport) { - return interfaceTerminalSupport.getTileEntity(); + } else if (craftingProvider instanceof IInterfaceViewable interfaceViewable) { + return interfaceViewable.getTileEntity(); } try { Method method = craftingProvider.getClass().getMethod("getTile"); From 7fe498149ca88aef4bc91d2fc9663489608b441b Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Mon, 23 Oct 2023 16:29:42 +0300 Subject: [PATCH 20/22] Fix tooltip zIndex --- .../appeng/client/gui/implementations/GuiInterfaceTerminal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 493bb8798e9..5b979432c49 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -126,7 +126,7 @@ public class GuiInterfaceTerminal extends AEBaseGui private static final float SLOT_Z = 0.5f; private static final float ITEM_STACK_OVERLAY_Z = 200.0f; private static final float SLOT_HOVER_Z = 310.0f; - private static final float TOOLTIP_Z = 210.0f; + private static final float TOOLTIP_Z = 410.0f; private static final float STEP_Z = 10.0f; private static final float MAGIC_RENDER_ITEM_Z = 50.0f; From 56629f42d308431b91e47d7b966934bd395ba456 Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Mon, 23 Oct 2023 16:35:06 +0300 Subject: [PATCH 21/22] Replace `ForgeDirection` serialization with `byte` --- .../core/sync/packets/PacketInterfaceTerminalUpdate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java b/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java index 08fbe1ee81c..b884d836b5c 100644 --- a/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java +++ b/src/main/java/appeng/core/sync/packets/PacketInterfaceTerminalUpdate.java @@ -289,7 +289,7 @@ protected void write(ByteBuf buf) throws IOException { buf.writeInt(y); buf.writeInt(z); buf.writeInt(dim); - buf.writeInt(side); + buf.writeByte(side); buf.writeInt(rows); buf.writeInt(rowSize); @@ -323,7 +323,7 @@ protected void read(ByteBuf buf) throws IOException { this.z = buf.readInt(); this.dim = buf.readInt(); this.side = buf.readInt(); - this.rows = buf.readInt(); + this.rows = buf.readByte(); this.rowSize = buf.readInt(); int payloadSize = buf.readInt(); try (ByteBufInputStream stream = new ByteBufInputStream(buf, payloadSize)) { From f35c5af9a440203c7d13f038c393a2d99f61784d Mon Sep 17 00:00:00 2001 From: Andrei Laiff Date: Thu, 26 Oct 2023 16:43:24 +0300 Subject: [PATCH 22/22] Code cleanup after review --- .../gui/implementations/GuiInterfaceTerminal.java | 5 ----- .../appeng/core/sync/packets/PacketCompressedNBT.java | 10 +--------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 5b979432c49..56eec5fe361 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -721,11 +721,6 @@ protected void keyTyped(final char character, final int key) { || searchFieldNames.textboxKeyTyped(character, key)) { return; } - // if (Character.isDigit(character) && character != '0') { - // // move hotbar to slot - // if (masterList.hoveredEntry != null && masterList.hoveredEntry.hoveredSlotIdx != -1) { - // } - // } super.keyTyped(character, key); } } diff --git a/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java b/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java index 7a59fddfd6f..b551814008b 100644 --- a/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java +++ b/src/main/java/appeng/core/sync/packets/PacketCompressedNBT.java @@ -18,8 +18,6 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiScreen; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; @@ -85,11 +83,5 @@ public void write(final int value) throws IOException { @Override @SideOnly(Side.CLIENT) - public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) { - final GuiScreen gs = Minecraft.getMinecraft().currentScreen; - - // if (gs instanceof GuiInterfaceTerminal) { - // ((GuiInterfaceTerminal) gs).postUpdate(this.in); - // } - } + public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player) {} }