diff --git a/src/main/java/appeng/client/gui/toasts/CraftingStatusToast.java b/src/main/java/appeng/client/gui/toasts/CraftingStatusToast.java new file mode 100644 index 000000000..9e0c1e91f --- /dev/null +++ b/src/main/java/appeng/client/gui/toasts/CraftingStatusToast.java @@ -0,0 +1,52 @@ +package appeng.client.gui.toasts; + +import appeng.core.localization.GuiText; +import net.minecraft.client.gui.toasts.GuiToast; +import net.minecraft.client.gui.toasts.IToast; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.jetbrains.annotations.NotNull; + +@SideOnly(Side.CLIENT) +public class CraftingStatusToast implements IToast { + private final ItemStack itemStack; + private final boolean cancelled; + private long firstDrawTime; + private boolean newDisplay; + + public CraftingStatusToast(@NotNull ItemStack itemStack, boolean cancelled) { + this.itemStack = itemStack; + this.cancelled = cancelled; + } + + @NotNull + public Visibility draw(@NotNull GuiToast toastGui, long delta) + { + if (this.newDisplay) + { + this.firstDrawTime = delta; + this.newDisplay = false; + } + var minecraft = toastGui.getMinecraft(); + var fontRenderer = minecraft.fontRenderer; + + // Texture + minecraft.getTextureManager().bindTexture(TEXTURE_TOASTS); + GlStateManager.color(1.0F, 1.0F, 1.0F); + toastGui.drawTexturedModalRect(0, 0, 0, 32, 160, 32); + + // Text + var statusText = cancelled ? GuiText.CraftingToastCancelled : GuiText.CraftingToastDone; + fontRenderer.drawString(statusText.getLocal(), 30, 7, -11534256); + fontRenderer.drawString(itemStack.getDisplayName(), 30, 18, -16777216); + + // Item + RenderHelper.enableGUIStandardItemLighting(); + minecraft.getRenderItem().renderItemAndEffectIntoGUI(null, itemStack, 8, 8); + + return delta - this.firstDrawTime < 5000L ? Visibility.SHOW : Visibility.HIDE; + } +} diff --git a/src/main/java/appeng/core/features/AEFeature.java b/src/main/java/appeng/core/features/AEFeature.java index ca23e7be9..6ae86a52c 100644 --- a/src/main/java/appeng/core/features/AEFeature.java +++ b/src/main/java/appeng/core/features/AEFeature.java @@ -166,6 +166,7 @@ public enum AEFeature { MOLECULAR_ASSEMBLER("MolecularAssembler", Constants.CATEGORY_CRAFTING_FEATURES), PATTERNS("Patterns", Constants.CATEGORY_CRAFTING_FEATURES), CRAFTING_CPU("CraftingCPU", Constants.CATEGORY_CRAFTING_FEATURES), + CRAFTING_TOASTS("CraftingToasts", Constants.CATEGORY_CRAFTING_FEATURES), BASIC_CARDS("BasicCards", Constants.CATEGORY_UPGRADES), ADVANCED_CARDS("AdvancedCards", Constants.CATEGORY_UPGRADES), diff --git a/src/main/java/appeng/core/localization/GuiText.java b/src/main/java/appeng/core/localization/GuiText.java index 56cc91c8a..88b97ce9a 100644 --- a/src/main/java/appeng/core/localization/GuiText.java +++ b/src/main/java/appeng/core/localization/GuiText.java @@ -201,7 +201,11 @@ public enum GuiText { LargeFontCraft, // Used in a ME Interface when no appropriate TileEntity was detected near it - Nothing; + Nothing, + + // Used in Crafting Toasts + CraftingToastDone, + CraftingToastCancelled; private final String root; diff --git a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java index 6e7065161..78f6f0574 100644 --- a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java +++ b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java @@ -86,7 +86,9 @@ public class AppEngPacketHandlerBase { PACKET_CRAFTING_CPUS_UPDATE(PacketCraftingCPUsUpdate.class), - PACKET_TERMINAL_KEYBIND(PacketTerminalUse.class); + PACKET_TERMINAL_KEYBIND(PacketTerminalUse.class), + + PACKET_CRAFTING_TOAST(PacketCraftingToast.class); private final Class packetClass; diff --git a/src/main/java/appeng/core/sync/packets/PacketCraftingToast.java b/src/main/java/appeng/core/sync/packets/PacketCraftingToast.java new file mode 100644 index 000000000..0d9c83c5a --- /dev/null +++ b/src/main/java/appeng/core/sync/packets/PacketCraftingToast.java @@ -0,0 +1,49 @@ +package appeng.core.sync.packets; + +import appeng.api.storage.data.IAEItemStack; +import appeng.client.gui.toasts.CraftingStatusToast; +import appeng.core.AEConfig; +import appeng.core.features.AEFeature; +import appeng.core.sync.AppEngPacket; +import appeng.core.sync.network.INetworkInfo; +import appeng.util.item.AEItemStack; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; + +import java.io.IOException; + +public class PacketCraftingToast extends AppEngPacket { + private final IAEItemStack stack; + private final boolean cancelled; + + public PacketCraftingToast(final ByteBuf stream) { + this.stack = AEItemStack.fromPacket(stream); + this.cancelled = stream.readBoolean(); + } + + public PacketCraftingToast(IAEItemStack stack, boolean cancelled) throws IOException { + this.stack = stack; + this.cancelled = cancelled; + + final ByteBuf data = Unpooled.buffer(); + + data.writeInt(this.getPacketID()); + stack.writeToPacket(data); + data.writeBoolean(cancelled); + + this.configureWrite(data); + } + + @Override + public void clientPacketData(INetworkInfo network, AppEngPacket packet, EntityPlayer player) { + if (AEConfig.instance().isFeatureEnabled(AEFeature.CRAFTING_TOASTS)) { + Minecraft.getMinecraft() + .getToastGui().add(new CraftingStatusToast(stack.asItemStackRepresentation(), cancelled)); + } + } + + @Override + public void serverPacketData(INetworkInfo manager, AppEngPacket packet, EntityPlayer player) {} +} diff --git a/src/main/java/appeng/helpers/PlayerHelper.java b/src/main/java/appeng/helpers/PlayerHelper.java new file mode 100644 index 000000000..910df0f99 --- /dev/null +++ b/src/main/java/appeng/helpers/PlayerHelper.java @@ -0,0 +1,28 @@ +package appeng.helpers; + +import appeng.util.Platform; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.server.FMLServerHandler; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public class PlayerHelper { + @Nullable + public static EntityPlayerMP getPlayerByUUID(UUID uuid) { + final MinecraftServer server; + if (Platform.isClientInstall()) { + server = Minecraft.getMinecraft().getIntegratedServer(); + } else { + server = FMLServerHandler.instance().getServer(); + } + + if (server == null) { + return null; + } + + return server.getPlayerList().getPlayerByUUID(uuid); + } +} diff --git a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java index 2a1accbfc..8ca7336b0 100644 --- a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java +++ b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java @@ -39,12 +39,18 @@ import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.api.util.WorldCoord; import appeng.container.ContainerNull; +import appeng.core.AEConfig; import appeng.core.AELog; +import appeng.core.features.AEFeature; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketCraftingToast; import appeng.crafting.*; import appeng.helpers.PatternHelper; +import appeng.helpers.PlayerHelper; import appeng.me.cache.CraftingGridCache; import appeng.me.cluster.IAECluster; import appeng.me.helpers.MachineSource; +import appeng.me.helpers.PlayerSource; import appeng.tile.crafting.TileCraftingMonitorTile; import appeng.tile.crafting.TileCraftingTile; import appeng.util.Platform; @@ -57,6 +63,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; +import java.io.IOException; import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; @@ -97,6 +104,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { private long elapsedTime; private long startItemCount; private long remainingItemCount; + private UUID requestingPlayerUUID; public CraftingCPUCluster(final WorldCoord min, final WorldCoord max) { this.min = min; @@ -376,6 +384,23 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { this.lastTime = 0; this.elapsedTime = 0; this.isComplete = true; + + notifyRequester(false); + this.requestingPlayerUUID = null; + } + + private void notifyRequester(boolean cancelled) { + if (!Platform.isServer()) return; + if (this.requestingPlayerUUID == null) return; + if (this.finalOutput == null) return; + if (!AEConfig.instance().isFeatureEnabled(AEFeature.CRAFTING_TOASTS)) return; + + var player = PlayerHelper.getPlayerByUUID(this.requestingPlayerUUID); + if (player != null) { + try { + NetworkHandler.instance().sendTo(new PacketCraftingToast(this.finalOutput, cancelled), player); + } catch (IOException ignored) {} + } } private void updateCPU() { @@ -519,6 +544,8 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { this.postCraftingStatusChange(is); } + notifyRequester(true); + this.requestingPlayerUUID = null; this.finalOutput = null; this.updateCPU(); @@ -800,6 +827,14 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { this.finalOutput = job.getOutput(); this.waiting = false; this.isComplete = false; + + // Store the requesting player if present. + if (src instanceof PlayerSource playerSource && playerSource.player().isPresent()) { + this.requestingPlayerUUID = playerSource.player().get().getUniqueID(); + } else { + this.requestingPlayerUUID = null; + } + this.markDirty(); this.updateCPU(); @@ -1032,6 +1067,10 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { data.setLong("elapsedTime", this.getElapsedTime()); data.setLong("startItemCount", this.getStartItemCount()); data.setLong("remainingItemCount", this.getRemainingItemCount()); + + if (Platform.isServer() && this.requestingPlayerUUID != null) { + data.setUniqueId("requestingPlayerUUID", this.requestingPlayerUUID); + } } private NBTTagCompound writeItem(final IAEItemStack finalOutput2) { @@ -1107,6 +1146,10 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { this.elapsedTime = data.getLong("elapsedTime"); this.startItemCount = data.getLong("startItemCount"); this.remainingItemCount = data.getLong("remainingItemCount"); + + if (Platform.isServer() && data.hasUniqueId("requestingPlayerUUID")) { + this.requestingPlayerUUID = data.getUniqueId("requestingPlayerUUID"); + } } public void updateName() { diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang index 3ee92b91b..49c545c3f 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang @@ -251,6 +251,8 @@ gui.appliedenergistics2.SmallFontCraft=Craft gui.appliedenergistics2.LargeFontCraft=+ gui.appliedenergistics2.Renamer=Custom Name: (Enter to set) gui.appliedenergistics2.Nothing=Nothing +gui.appliedenergistics2.CraftingToastDone=Crafting Done! +gui.appliedenergistics2.CraftingToastCancelled=Crafting Cancelled! // GUI Tooltips gui.tooltips.appliedenergistics2.Stash=Store Items