From 3d17eb42809b793e3684d089a3d2bbfd748422cf Mon Sep 17 00:00:00 2001 From: Anthony Hilyard Date: Mon, 20 Sep 2021 14:47:21 -0700 Subject: Updated tooltip handling, added remote pre-item pickup event and network protocol. --- gradle.properties | 2 +- .../java/com/anthonyhilyard/iceberg/Loader.java | 6 + .../iceberg/events/NewItemPickupEvent.java | 41 ++++ .../iceberg/mixin/ForgeEventFactoryMixin.java | 31 +++ .../iceberg/network/IcebergNetworkProtocol.java | 30 +++ .../iceberg/network/NewItemPickupEventPacket.java | 45 +++++ .../com/anthonyhilyard/iceberg/util/Tooltips.java | 207 +++++++++++++++++++++ src/main/resources/iceberg.mixins.json | 7 +- 8 files changed, 366 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/anthonyhilyard/iceberg/events/NewItemPickupEvent.java create mode 100644 src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeEventFactoryMixin.java create mode 100644 src/main/java/com/anthonyhilyard/iceberg/network/IcebergNetworkProtocol.java create mode 100644 src/main/java/com/anthonyhilyard/iceberg/network/NewItemPickupEventPacket.java diff --git a/gradle.properties b/gradle.properties index 193b1c2..eb6a816 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,6 @@ org.gradle.daemon=false name=Iceberg group=com.anthonyhilyard.iceberg author=anthonyhilyard -version=1.0.3 +version=1.0.5 mcVersion=1.16.5 forgeVersion=36.2.2 \ No newline at end of file diff --git a/src/main/java/com/anthonyhilyard/iceberg/Loader.java b/src/main/java/com/anthonyhilyard/iceberg/Loader.java index b1ff65a..38aa9c1 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/Loader.java +++ b/src/main/java/com/anthonyhilyard/iceberg/Loader.java @@ -1,5 +1,7 @@ package com.anthonyhilyard.iceberg; +import com.anthonyhilyard.iceberg.network.IcebergNetworkProtocol; + import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -31,6 +33,10 @@ public class Loader { new IcebergServer(); } + + // Register the network protocol. + IcebergNetworkProtocol.register(); + ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true)); } diff --git a/src/main/java/com/anthonyhilyard/iceberg/events/NewItemPickupEvent.java b/src/main/java/com/anthonyhilyard/iceberg/events/NewItemPickupEvent.java new file mode 100644 index 0000000..a758769 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/events/NewItemPickupEvent.java @@ -0,0 +1,41 @@ +package com.anthonyhilyard.iceberg.events; + +import net.minecraftforge.event.entity.player.PlayerEvent; + +import java.util.UUID; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; + +/** + * This event is fired right before a player picks up a new item. Unlike EntityItemPickupEvent, this event fires on the logical client. + *
+ * This event is not {@link net.minecraftforge.eventbus.api.Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}. + */ +public class NewItemPickupEvent extends PlayerEvent +{ + private final ItemStack itemStack; + + public NewItemPickupEvent(PlayerEntity player, ItemStack itemStack) + { + super(player); + this.itemStack = itemStack; + } + + @SuppressWarnings("resource") + public NewItemPickupEvent(UUID playerUUID, ItemStack itemStack) + { + this(Minecraft.getInstance().level.getPlayerByUUID(playerUUID), itemStack); + } + + public ItemStack getItemStack() + { + return itemStack; + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeEventFactoryMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeEventFactoryMixin.java new file mode 100644 index 0000000..a530465 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeEventFactoryMixin.java @@ -0,0 +1,31 @@ +package com.anthonyhilyard.iceberg.mixin; + +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.fml.network.PacketDistributor; + +import com.anthonyhilyard.iceberg.network.IcebergNetworkProtocol; +import com.anthonyhilyard.iceberg.network.NewItemPickupEventPacket; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ForgeEventFactory.class) +public class ForgeEventFactoryMixin +{ + @Inject(method = { "onItemPickup(Lnet/minecraft/entity/item/ItemEntity;Lnet/minecraft/entity/player/PlayerEntity;)I" }, + at = { @At("HEAD") }, remap = false) + private static void onItemPickup(ItemEntity entityItem, PlayerEntity player, CallbackInfoReturnable info) + { + if (player instanceof ServerPlayerEntity && FMLEnvironment.dist.isDedicatedServer()) + { + IcebergNetworkProtocol.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity)player), new NewItemPickupEventPacket(player.getUUID(), entityItem.getItem())); + } + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/network/IcebergNetworkProtocol.java b/src/main/java/com/anthonyhilyard/iceberg/network/IcebergNetworkProtocol.java new file mode 100644 index 0000000..d0dd1c8 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/network/IcebergNetworkProtocol.java @@ -0,0 +1,30 @@ +package com.anthonyhilyard.iceberg.network; + +import com.anthonyhilyard.iceberg.Loader; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.network.NetworkRegistry; +import net.minecraftforge.fml.network.simple.SimpleChannel; + +public class IcebergNetworkProtocol +{ + private static final String NETWORK_PROTOCOL_VERSION = "1"; + + public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( + new ResourceLocation(Loader.MODID, "main"), () -> NETWORK_PROTOCOL_VERSION, + NETWORK_PROTOCOL_VERSION::equals, NETWORK_PROTOCOL_VERSION::equals + ); + + public static final void register() + { + int messageID = 0; + + CHANNEL.registerMessage( + messageID++, + NewItemPickupEventPacket.class, + NewItemPickupEventPacket::encode, + NewItemPickupEventPacket::decode, + NewItemPickupEventPacket::handle + ); + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/network/NewItemPickupEventPacket.java b/src/main/java/com/anthonyhilyard/iceberg/network/NewItemPickupEventPacket.java new file mode 100644 index 0000000..23df08b --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/network/NewItemPickupEventPacket.java @@ -0,0 +1,45 @@ +package com.anthonyhilyard.iceberg.network; + +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.UUID; +import java.util.function.Supplier; + +import com.anthonyhilyard.iceberg.events.NewItemPickupEvent; + + +public final class NewItemPickupEventPacket +{ + private final UUID playerUUID; + private final ItemStack item; + + public NewItemPickupEventPacket(final UUID playerUUID, final ItemStack item) + { + this.playerUUID = playerUUID; + this.item = item; + } + + public static void encode(final NewItemPickupEventPacket msg, final PacketBuffer packetBuffer) + { + packetBuffer.writeUUID(msg.playerUUID); + packetBuffer.writeItem(msg.item); + } + + public static NewItemPickupEventPacket decode(final PacketBuffer packetBuffer) + { + return new NewItemPickupEventPacket(packetBuffer.readUUID(), packetBuffer.readItem()); + } + + public static void handle(final NewItemPickupEventPacket msg, final Supplier contextSupplier) + { + final NetworkEvent.Context context = contextSupplier.get(); + context.enqueueWork(() -> { + MinecraftForge.EVENT_BUS.post(new NewItemPickupEvent(msg.playerUUID, msg.item)); + }); + context.setPacketHandled(true); + } + +} \ No newline at end of file diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java index 3761fc5..69039db 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java +++ b/src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java @@ -3,18 +3,225 @@ package com.anthonyhilyard.iceberg.util; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nonnull; + import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.Rectangle2d; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.item.ItemStack; +import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.text.ITextProperties; +import net.minecraft.util.text.LanguageMap; import net.minecraft.util.text.Style; import net.minecraftforge.client.event.RenderTooltipEvent; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.gui.GuiUtils; public class Tooltips { + public static class TooltipInfo + { + private int tooltipWidth = 0; + private int titleLines = 1; + private FontRenderer font; + private List lines = new ArrayList<>(); + + public TooltipInfo(List lines, FontRenderer font) + { + this.lines = lines; + this.font = font; + } + + public int getTooltipWidth() { return tooltipWidth; } + public int getTitleLines() { return titleLines; } + public FontRenderer getFont() { return font; } + public List getLines() { return lines; } + + public void setFont(FontRenderer font) { this.font = font; } + + public int getMaxLineWidth() + { + int textWidth = 0; + for (ITextProperties textLine : lines) + { + int textLineWidth = font.width(textLine); + if (textLineWidth > textWidth) + { + textWidth = textLineWidth; + } + } + return textWidth; + } + + public void wrap(int maxWidth) + { + tooltipWidth = 0; + List wrappedLines = new ArrayList<>(); + for (int i = 0; i < lines.size(); i++) + { + ITextProperties textLine = lines.get(i); + List wrappedLine = font.getSplitter().splitLines(textLine, maxWidth, Style.EMPTY); + if (i == 0) + { + titleLines = wrappedLine.size(); + } + + for (ITextProperties line : wrappedLine) + { + int lineWidth = font.width(line); + if (lineWidth > tooltipWidth) + { + tooltipWidth = lineWidth; + } + wrappedLines.add(line); + } + } + + lines = wrappedLines; + } + } + + @SuppressWarnings("deprecation") + public static void renderItemTooltip(@Nonnull final ItemStack stack, MatrixStack mStack, TooltipInfo info, + Rectangle2d rect, int screenWidth, int screenHeight, + int backgroundColor, int borderColorStart, int borderColorEnd) + { + if (info.getLines().isEmpty()) + { + return; + } + + int rectX = rect.getX() - 8; + int rectY = rect.getY() + 18; + int maxTextWidth = rect.getWidth() - 8; + + RenderTooltipEvent.Pre event = new RenderTooltipEvent.Pre(stack, info.getLines(), mStack, rectX, rectY, screenWidth, screenHeight, maxTextWidth, info.getFont()); + if (MinecraftForge.EVENT_BUS.post(event)) + { + return; + } + + rectX = event.getX(); + rectY = event.getY(); + screenWidth = event.getScreenWidth(); + screenHeight = event.getScreenHeight(); + maxTextWidth = event.getMaxWidth(); + info.setFont(event.getFontRenderer()); + + RenderSystem.disableRescaleNormal(); + RenderSystem.disableDepthTest(); + int tooltipTextWidth = info.getMaxLineWidth(); + + boolean needsWrap = false; + + int tooltipX = rectX + 12; + if (tooltipX + tooltipTextWidth + 4 > screenWidth) + { + tooltipX = rectX - 16 - tooltipTextWidth; + if (tooltipX < 4) // if the tooltip doesn't fit on the screen + { + if (rectX > screenWidth / 2) + { + tooltipTextWidth = rectX - 12 - 8; + } + else + { + tooltipTextWidth = screenWidth - 16 - rectX; + } + needsWrap = true; + } + } + + if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) + { + tooltipTextWidth = maxTextWidth; + needsWrap = true; + } + + if (needsWrap) + { + info.wrap(tooltipTextWidth); + tooltipTextWidth = info.getTooltipWidth(); + tooltipX = rectX + 12; + } + + int tooltipY = rectY - 12; + int tooltipHeight = 8; + + if (info.getLines().size() > 1) + { + tooltipHeight += (info.getLines().size() - 1) * 10; + if (info.getLines().size() > info.getTitleLines()) + { + tooltipHeight += 2; // gap between title lines and next lines + } + } + + if (tooltipY < 4) + { + tooltipY = 4; + } + else if (tooltipY + tooltipHeight + 4 > screenHeight) + { + tooltipY = screenHeight - tooltipHeight - 4; + } + + final int zLevel = 400; + RenderTooltipEvent.Color colorEvent = new RenderTooltipEvent.Color(stack, info.getLines(), mStack, tooltipX, tooltipY, info.getFont(), backgroundColor, borderColorStart, borderColorEnd); + MinecraftForge.EVENT_BUS.post(colorEvent); + backgroundColor = colorEvent.getBackground(); + borderColorStart = colorEvent.getBorderStart(); + borderColorEnd = colorEvent.getBorderEnd(); + + mStack.pushPose(); + Matrix4f mat = mStack.last().pose(); + + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3, tooltipY - 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY + tooltipHeight + 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX + tooltipTextWidth + 3, tooltipY - 3, tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColorStart, borderColorStart); + GuiUtils.drawGradientRect(mat, zLevel, tooltipX - 3, tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, borderColorEnd, borderColorEnd); + + MinecraftForge.EVENT_BUS.post(new RenderTooltipEvent.PostBackground(stack, info.getLines(), mStack, tooltipX, tooltipY, info.getFont(), tooltipTextWidth, tooltipHeight)); + + IRenderTypeBuffer.Impl renderType = IRenderTypeBuffer.immediate(Tessellator.getInstance().getBuilder()); + mStack.translate(0.0D, 0.0D, zLevel); + + int tooltipTop = tooltipY; + + for (int lineNumber = 0; lineNumber < info.getLines().size(); ++lineNumber) + { + ITextProperties line = info.getLines().get(lineNumber); + if (line != null) + { + info.getFont().drawInBatch(LanguageMap.getInstance().getVisualOrder(line), (float)tooltipX, (float)tooltipY, -1, true, mat, renderType, false, 0, 15728880); + } + + if (lineNumber + 1 == info.getTitleLines()) + { + tooltipY += 2; + } + + tooltipY += 10; + } + + renderType.endBatch(); + mStack.popPose(); + + MinecraftForge.EVENT_BUS.post(new RenderTooltipEvent.PostText(stack, info.getLines(), mStack, tooltipX, tooltipTop, info.getFont(), tooltipTextWidth, tooltipHeight)); + + RenderSystem.enableDepthTest(); + RenderSystem.enableRescaleNormal(); + } + public static Rectangle2d calculateRect(final ItemStack stack, MatrixStack mStack, List textLines, int mouseX, int mouseY, int screenWidth, int screenHeight, int maxTextWidth, FontRenderer font) { diff --git a/src/main/resources/iceberg.mixins.json b/src/main/resources/iceberg.mixins.json index 41b5589..9dfa917 100644 --- a/src/main/resources/iceberg.mixins.json +++ b/src/main/resources/iceberg.mixins.json @@ -4,8 +4,11 @@ "compatibilityLevel": "JAVA_8", "refmap": "iceberg.refmap.json", "mixins": [ - "PlayerAdvancementsMixin", - "EntityMixin" + "EntityMixin", + "PlayerAdvancementsMixin" + ], + "server": [ + "ForgeEventFactoryMixin" ], "injectors": { "defaultRequire": 1 -- cgit