Resolve interface name suffixes as chat components (#1545)

Co-authored-by: Martin Robertz <[email protected]>
Co-authored-by: Kogepan229 <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-08-28 16:09:34 +00:00
1 parent be38cc197f
commit 80dec9f563
7 files changed
+61 -17

No files matched your search

@@ -1,5 +1,7 @@
package appeng.api.interfaces;
import net.minecraft.util.IChatComponent;
/**
* Implement this on a TileEntity (or IMetaTileEntity, wrapped via the hosting TileEntity) to provide a dynamic suffix
* for the AE2 interface name shown in the terminal.
@@ -10,12 +12,26 @@ package appeng.api.interfaces;
public interface IInterfaceNameProvider {
/**
* Returns a suffix string to append to the ME Interface's display name, or {@code null} if no suffix should be
* shown.
* Returns a suffix to append to the ME Interface's display name, or {@code null} if no suffix should be shown.
*
* The returned string will be appended as-is (e.g. {@code " [24]"}).
* The suffix is built on the server, where client language files are not loaded, so it is returned as a component
* and only turned into text on the client:
*
* @return suffix string or {@code null}
* <pre>
* IChatComponent suffix = new ChatComponentText(" (");
* suffix.appendSibling(new ChatComponentItemDisplayName(stack));
* suffix.appendText(")");
* return suffix;
* </pre>
*
* Any component works, including the custom ones GTNHLib registers. AE2 serializes it when sending the terminal
* update and appends {@code getUnformattedText()} on the client, so formatting is dropped and only the text is
* used. A component the client cannot deserialize is shown as raw text rather than dropping the whole entry.
*
* The serialized form travels as a single string field in the terminal update packet, which caps it at 16383 bytes
* of UTF-8. Providers are responsible for keeping the suffix reasonably short.
*
* @return suffix component or {@code null}
*/
String getInterfaceNameSuffix();
IChatComponent getInterfaceNameSuffix();
}
@@ -5,6 +5,7 @@ import static appeng.util.item.AEItemStackType.ITEM_STACK_TYPE;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IChatComponent;
import appeng.api.networking.IGridHost;
import appeng.api.storage.data.IAEStackType;
@@ -58,10 +59,11 @@ public interface IInterfaceViewable extends IGridHost {
}
/**
* Returns the optional suffix to append after the name has been translated on the client. May be null if there is
* no suffix.
* Returns the optional suffix to append after the name has been translated on the client, or null if there is no
* suffix. Returned as a component so that it is localized with the client's language rather than the server's, see
* {@link appeng.api.interfaces.IInterfaceNameProvider} for the details.
*/
default String getNameSuffix() {
default IChatComponent getNameSuffix() {
return null;
}
@@ -40,6 +40,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
@@ -1181,11 +1182,24 @@ public class GuiInterfaceTerminal extends AEBaseGui
}
}
if (suffix != null && !suffix.isEmpty()) {
return translatedName + suffix;
return translatedName + resolveSuffix(suffix);
}
return translatedName;
}
/**
* Turns the serialized {@link IChatComponent} suffix back into text, so that it is localized with the client's
* language rather than the server's. A suffix that fails to deserialize is shown as raw text.
*/
private static String resolveSuffix(String suffix) {
try {
final IChatComponent component = IChatComponent.Serializer.func_150699_a(suffix);
return component != null ? component.getUnformattedText() : suffix;
} catch (Exception e) {
return suffix;
}
}
private void parsePacketCmd(PacketInterfaceTerminalUpdate.PacketEntry cmd) {
long id = cmd.entryId;
if (cmd instanceof PacketInterfaceTerminalUpdate.PacketAdd addCmd) {
@@ -27,6 +27,7 @@ import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.IChatComponent;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
@@ -354,7 +355,7 @@ public final class ContainerInterfaceTerminal extends AEBaseContainer implements
/* Name changed? */
String rawName = machine.getRawName();
String suffix = machine.getNameSuffix();
String suffix = serializeSuffix(machine.getNameSuffix());
if (!Objects.equals(known.name, rawName) || !Objects.equals(known.suffix, suffix)) {
if (update == null) update = new PacketInterfaceTerminalUpdate();
@@ -466,6 +467,13 @@ public final class ContainerInterfaceTerminal extends AEBaseContainer implements
return !ItemStack.areItemStacksEqual(a, b);
}
/**
* The suffix travels as a string, so components are serialized here and turned back into text on the client.
*/
private static String serializeSuffix(final IChatComponent suffix) {
return suffix == null ? null : IChatComponent.Serializer.func_150696_a(suffix);
}
private static class InvTracker {
private final long id;
@@ -493,7 +501,7 @@ public final class ContainerInterfaceTerminal extends AEBaseContainer implements
this.id = id;
this.shouldDisplay = getTerminalVisibility(machine);
this.name = machine.getRawName();
this.suffix = machine.getNameSuffix();
this.suffix = serializeSuffix(machine.getNameSuffix());
this.patterns = machine.getPatterns();
this.world = machine.getTileEntity().getWorldObj();
this.rowSize = machine.rowSize();
@@ -40,6 +40,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
@@ -1663,11 +1664,11 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
public String getTermName() {
final String baseName = getRawTermName();
final String suffix = getAdjacentNameSuffix();
final IChatComponent suffix = getAdjacentNameSuffix();
if (suffix == null) {
return baseName;
}
return baseName + suffix;
return baseName + suffix.getUnformattedText();
}
/**
@@ -1685,7 +1686,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
/**
* Returns the suffix to append after translation, or null if none.
*/
public String getAdjacentNameSuffix() {
public IChatComponent getAdjacentNameSuffix() {
if (((ICustomNameObject) this.iHost).hasCustomName()) return null;
final TileEntity hostTile = this.iHost.getTileEntity();
if (hostTile == null || hostTile.getWorldObj() == null) return null;
@@ -1704,7 +1705,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
}
}
if (directedTile instanceof IInterfaceNameProvider provider) {
final String suffix = provider.getInterfaceNameSuffix();
final IChatComponent suffix = provider.getInterfaceNameSuffix();
if (suffix != null) return suffix;
}
}
@@ -15,6 +15,7 @@ import java.util.EnumSet;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IChatComponent;
import net.minecraftforge.common.util.ForgeDirection;
import appeng.api.config.Settings;
@@ -73,7 +74,7 @@ public interface IInterfaceHost extends ICraftingProvider, IUpgradeableHost, ICr
* Optional suffix to append after translation on client.
*/
@Override
default String getNameSuffix() {
default IChatComponent getNameSuffix() {
return getInterfaceDuality().getAdjacentNameSuffix();
}
@@ -2004,7 +2004,9 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
} else {
if (craftingProvider instanceof DualityInterface di) {
rawName = di.getRawTermName();
suffix = di.getAdjacentNameSuffix();
final IChatComponent suffixComponent = di.getAdjacentNameSuffix();
// This name is already translated server-side, so the suffix is flattened here as well.
suffix = suffixComponent == null ? null : suffixComponent.getUnformattedText();
} else if (craftingProvider instanceof IInterfaceViewable iv) {
rawName = iv.getName();
suffix = null;