Update #37 - Touch support without userscript, many other feats

This commit is contained in:
lax1dude
2024-09-21 20:17:42 -07:00
parent 173727c8c4
commit ec1ab8ece3
683 changed files with 62074 additions and 8996 deletions

View File

@ -0,0 +1,46 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.List;
import net.minecraft.util.IChatComponent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class CachedNotifBadgeTexture {
public final int glTexture;
public final int scaleFactor;
public final int width;
public final int height;
public final List<ClickEventZone> cursorEvents;
public final IChatComponent rootClickEvent;
public final boolean hasClickEvents;
public final boolean hasHoverEvents;
protected CachedNotifBadgeTexture(int glTexture, int scaleFactor, int width, int height,
List<ClickEventZone> cursorEvents, IChatComponent rootClickEvent, boolean hasClickEvents,
boolean hasHoverEvents) {
this.glTexture = glTexture;
this.scaleFactor = scaleFactor;
this.width = width;
this.height = height;
this.cursorEvents = cursorEvents;
this.rootClickEvent = rootClickEvent;
this.hasClickEvents = hasClickEvents;
this.hasHoverEvents = hasHoverEvents;
}
}

View File

@ -0,0 +1,41 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.minecraft.util.IChatComponent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ClickEventZone {
public final int posX;
public final int posY;
public final int width;
public final int height;
public final IChatComponent chatComponent;
public final boolean hasHoverEvent;
public final boolean hasClickEvent;
public ClickEventZone(int posX, int posY, int width, int height, IChatComponent chatComponent,
boolean hasHoverEvent, boolean hasClickEvent) {
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.chatComponent = chatComponent;
this.hasHoverEvent = hasHoverEvent;
this.hasClickEvent = hasClickEvent;
}
}

View File

@ -0,0 +1,69 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.Mouse;
import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.util.ResourceLocation;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class GuiButtonNotifBell extends GuiButton {
private static final ResourceLocation eaglerTextures = new ResourceLocation("eagler:gui/eagler_gui.png");
private int unread = 0;
public GuiButtonNotifBell(int buttonID, int xPos, int yPos) {
super(buttonID, xPos, yPos, 20, 20, "");
}
public void setUnread(int num) {
unread = num;
}
public void drawButton(Minecraft minecraft, int i, int j) {
if (this.visible) {
minecraft.getTextureManager().bindTexture(eaglerTextures);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
boolean flag = i >= this.xPosition && j >= this.yPosition && i < this.xPosition + this.width
&& j < this.yPosition + this.height;
int k = 0;
int c = 14737632;
if (flag) {
k += this.height;
c = 16777120;
Mouse.showCursor(EnumCursorType.HAND);
}
drawTexturedModalRect(xPosition, yPosition, unread > 0 ? 116 : 136, k, width, height);
if(unread > 0) {
GlStateManager.pushMatrix();
GlStateManager.translate(xPosition + 15.5f, yPosition + 11.0f, 0.0f);
if(unread >= 10) {
GlStateManager.translate(0.0f, 1.0f, 0.0f);
GlStateManager.scale(0.5f, 0.5f, 0.5f);
}else {
GlStateManager.scale(0.75f, 0.75f, 0.75f);
}
drawCenteredString(minecraft.fontRendererObj, Integer.toString(unread), 0, 0, c);
GlStateManager.popMatrix();
}
}
}
}

View File

@ -0,0 +1,172 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.io.IOException;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class GuiScreenNotifications extends GuiScreen {
private static final String[] priorityLangKeys = new String[] {
"notifications.priority.low",
"notifications.priority.normal",
"notifications.priority.higher",
"notifications.priority.highest"
};
private static final int[] priorityOrder = new int[] {
0, 3, 2, 1
};
GuiScreen parent;
int selected;
GuiSlotNotifications slots;
GuiButton clearAllButton;
GuiButton priorityButton;
int showPriority = 0;
EnumBadgePriority selectedMaxPriority = EnumBadgePriority.LOW;
int lastUpdate = -1;
public GuiScreenNotifications(GuiScreen parent) {
this.parent = parent;
}
public void initGui() {
selected = -1;
buttonList.clear();
buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 32, 100, 20, I18n.format("gui.done")));
buttonList.add(clearAllButton = new GuiButton(1, this.width / 2 - 154, this.height - 32, 100, 20,
I18n.format("notifications.clearAll")));
int i = priorityOrder[showPriority];
buttonList.add(priorityButton = new GuiButton(2, this.width / 2 - 50, this.height - 32, 100, 20,
I18n.format("notifications.priority", I18n.format(priorityLangKeys[i]))));
selectedMaxPriority = EnumBadgePriority.getByID(i);
slots = new GuiSlotNotifications(this);
lastUpdate = -69420;
updateList();
updateButtons();
}
void updateButtons() {
clearAllButton.enabled = !slots.currentDisplayNotifs.isEmpty();
}
void updateList() {
if(mc.thePlayer == null) return;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
int verHash = showPriority | (mgr.getNotifListUpdateCount() << 2);
if(verHash != lastUpdate) {
lastUpdate = verHash;
EaglercraftUUID selectedUUID = null;
List<GuiSlotNotifications.NotifBadgeSlot> lst = slots.currentDisplayNotifs;
int oldSelectedId = selected;
if(oldSelectedId >= 0 && oldSelectedId < lst.size()) {
selectedUUID = lst.get(oldSelectedId).badge.badgeUUID;
}
lst.clear();
lst.addAll(Collections2.transform(Collections2.filter(mgr.getNotifLongHistory(), new Predicate<NotificationBadge>() {
@Override
public boolean apply(NotificationBadge input) {
return input.priority.priority >= priorityOrder[showPriority];
}
}), GuiSlotNotifications.NotifBadgeSlot::new));
selected = -1;
if(selectedUUID != null) {
for(int i = 0, l = lst.size(); i < l; ++i) {
if(selectedUUID.equals(lst.get(i).badge.badgeUUID)) {
selected = i;
break;
}
}
}
if(selected != -1) {
if(oldSelectedId != selected) {
slots.scrollBy((selected - oldSelectedId) * slots.getSlotHeight());
}
}
updateButtons();
}
}
public void updateScreen() {
if(mc.thePlayer == null) {
mc.displayGuiScreen(parent);
return;
}
updateList();
}
static Minecraft getMinecraft(GuiScreenNotifications screen) {
return screen.mc;
}
public void actionPerformed(GuiButton btn) {
switch(btn.id) {
case 0:
mc.displayGuiScreen(parent);
break;
case 1:
if(mc.thePlayer != null) {
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
mgr.removeAllNotifFromActiveList(mgr.getNotifLongHistory());
clearAllButton.enabled = false;
}
break;
case 2:
showPriority = (showPriority + 1) & 3;
int i = priorityOrder[showPriority];
priorityButton.displayString = I18n.format("notifications.priority", I18n.format(priorityLangKeys[i]));
selectedMaxPriority = EnumBadgePriority.getByID(i);
updateList();
break;
default:
break;
}
}
public void drawScreen(int par1, int par2, float par3) {
if(mc.thePlayer == null) return;
slots.drawScreen(par1, par2, par3);
this.drawCenteredString(fontRendererObj, I18n.format("notifications.title"), this.width / 2, 16, 16777215);
super.drawScreen(par1, par2, par3);
}
public void handleMouseInput() throws IOException {
super.handleMouseInput();
slots.handleMouseInput();
}
public void handleTouchInput() throws IOException {
super.handleTouchInput();
slots.handleTouchInput();
}
public void onGuiClosed() {
if(mc.thePlayer != null) {
mc.thePlayer.sendQueue.getNotifManager().commitUnreadFlag();
}
}
}

View File

@ -0,0 +1,338 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.gui.GuiUtilRenderComponents;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class GuiSlotNotifications extends GuiSlot {
private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png");
private static final ResourceLocation largeNotifBk = new ResourceLocation("eagler:gui/notif_bk_large.png");
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm a");
final GuiScreenNotifications parent;
final List<NotifBadgeSlot> currentDisplayNotifs;
int mouseX;
int mouseY;
protected static class NotifBadgeSlot {
protected final NotificationBadge badge;
protected final List<ClickEventZone> cursorEvents = new ArrayList<>();
protected int currentScreenX = -69420;
protected int currentScreenY = -69420;
protected NotifBadgeSlot(NotificationBadge badge) {
this.badge = badge;
}
}
public GuiSlotNotifications(GuiScreenNotifications parent) {
super(GuiScreenNotifications.getMinecraft(parent), parent.width, parent.height, 32, parent.height - 44, 68);
this.parent = parent;
this.currentDisplayNotifs = new ArrayList<>();
}
@Override
protected int getSize() {
return currentDisplayNotifs.size();
}
@Override
protected void elementClicked(int id, boolean doubleClk, int xx, int yy) {
if(selectedElement != id) return; //workaround for vanilla bs
if(id < currentDisplayNotifs.size()) {
NotifBadgeSlot slot = currentDisplayNotifs.get(id);
if(slot.currentScreenY != -69420) {
int w = getListWidth();
int localX = xx - slot.currentScreenX;
int localY = yy - slot.currentScreenY;
if(localX >= w - 22 && localX < w - 5 && localY >= 5 && localY < 21) {
slot.badge.removeNotif();
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
IChatComponent cmp = slot.badge.bodyComponent;
if(cmp != null) {
if(doubleClk) {
if (cmp.getChatStyle().getChatClickEvent() != null
&& cmp.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) {
if(parent.handleComponentClick(cmp)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
}
}else {
if(parent.selected != id) {
parent.selected = id;
}else {
List<ClickEventZone> cursorEvents = slot.cursorEvents;
if(cursorEvents != null && !cursorEvents.isEmpty()) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasClickEvent) {
int offsetPosX = slot.currentScreenX + evt.posX;
int offsetPosY = slot.currentScreenY + evt.posY;
if(xx >= offsetPosX && yy >= offsetPosY && xx < offsetPosX + evt.width && yy < offsetPosY + evt.height) {
if(parent.handleComponentClick(evt.chatComponent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
}
}
}
}
}
}
}
}
}
}
@Override
protected boolean isSelected(int var1) {
return var1 == parent.selected;
}
@Override
protected void drawBackground() {
parent.drawBackground(0);
}
@Override
protected void drawSlot(int id, int xx, int yy, int width, int height, int ii) {
if(id < currentDisplayNotifs.size()) {
NotifBadgeSlot slot = currentDisplayNotifs.get(id);
slot.currentScreenX = xx;
slot.currentScreenY = yy;
NotificationBadge bd = slot.badge;
if(yy + 32 > this.top && yy + 32 < this.bottom) {
bd.markRead();
}
GlStateManager.pushMatrix();
GlStateManager.translate(xx, yy, 0.0f);
mc.getTextureManager().bindTexture(largeNotifBk);
int badgeWidth = getListWidth() - 4;
int badgeHeight = getSlotHeight() - 4;
float r = ((bd.backgroundColor >> 16) & 0xFF) * 0.00392156f;
float g = ((bd.backgroundColor >> 8) & 0xFF) * 0.00392156f;
float b = (bd.backgroundColor & 0xFF) * 0.00392156f;
if(parent.selected != id) {
r *= 0.85f;
g *= 0.85f;
b *= 0.85f;
}
GlStateManager.color(r, g, b, 1.0f);
parent.drawTexturedModalRect(0, 0, 0, bd.unreadFlagRender ? 64 : 0, badgeWidth - 32, 64);
parent.drawTexturedModalRect(badgeWidth - 32, 0, 224, bd.unreadFlagRender ? 64 : 0, 32, 64);
mc.getTextureManager().bindTexture(eaglerGui);
if(bd.priority == EnumBadgePriority.LOW) {
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16);
}
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
switch(bd.priority) {
default:
break;
case NORMAL:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16);
break;
case HIGHER:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16);
break;
case HIGHEST:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16);
break;
}
int bodyYOffset = 16;
int leftPadding = 6;
int rightPadding = 26;
int mainIconSW = 32;
boolean mainIconEn = bd.mainIcon != null && bd.mainIcon.isValid();
if(mainIconEn) {
int iw = bd.mainIcon.texture.getWidth();
int ih = bd.mainIcon.texture.getHeight();
float iaspect = (float)iw / (float)ih;
mainIconSW = (int)(32 * iaspect);
leftPadding += Math.min(mainIconSW, 64) + 3;
}
int textZoneWidth = badgeWidth - leftPadding - rightPadding;
if(mainIconEn) {
mc.getTextureManager().bindTexture(bd.mainIcon.resource);
ServerNotificationRenderer.drawTexturedRect(6, bodyYOffset, mainIconSW, 32);
}
boolean titleIconEn = bd.titleIcon != null && bd.titleIcon.isValid();
if(titleIconEn) {
mc.getTextureManager().bindTexture(bd.titleIcon.resource);
ServerNotificationRenderer.drawTexturedRect(6, 5, 8, 8);
}
String titleText = "";
IChatComponent titleComponent = bd.getTitleProfanityFilter();
if(titleComponent != null) {
titleText = titleComponent.getFormattedText();
}
titleText += EnumChatFormatting.GRAY + (titleText.length() > 0 ? " @ " : "@ ")
+ (bd.unreadFlagRender ? EnumChatFormatting.YELLOW : EnumChatFormatting.GRAY)
+ formatAge(bd.serverTimestamp);
GlStateManager.pushMatrix();
GlStateManager.translate(6 + (titleIconEn ? 10 : 0), 6, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, bd.titleTxtColor);
GlStateManager.popMatrix();
String sourceText = null;
IChatComponent sourceComponent = bd.getSourceProfanityFilter();
if(sourceComponent != null) {
sourceText = sourceComponent.getFormattedText();
if(sourceText.length() == 0) {
sourceText = null;
}
}
List<IChatComponent> bodyLines = null;
float bodyFontSize = (sourceText != null || titleIconEn) ? 0.75f : 1.0f;
IChatComponent bodyComponent = bd.getBodyProfanityFilter();
if(bodyComponent != null) {
bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize),
mc.fontRendererObj, true, true);
int maxHeight = badgeHeight - (sourceText != null ? 32 : 22);
int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize));
if(bodyLines.size() > maxLines) {
bodyLines = bodyLines.subList(0, maxLines);
IChatComponent cmp = bodyLines.get(maxLines - 1);
List<IChatComponent> siblings = cmp.getSiblings();
IChatComponent dots = new ChatComponentText("...");
if(siblings != null && siblings.size() > 0) {
dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle());
}
cmp.appendSibling(dots);
}
}
slot.cursorEvents.clear();
if(bodyLines != null && !bodyLines.isEmpty()) {
GlStateManager.pushMatrix();
GlStateManager.translate(leftPadding, bodyYOffset, 0.0f);
int l = bodyLines.size();
GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize);
IChatComponent toolTip = null;
for(int i = 0; i < l; ++i) {
int startXLocal = 0;
int startXReal = leftPadding;
for(IChatComponent comp : bodyLines.get(i)) {
int w = mc.fontRendererObj.drawStringWithShadow(
comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal,
i * 9, bd.bodyTxtColor) - startXLocal;
ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent();
HoverEvent hoverEvent = toolTip == null ? comp.getChatStyle().getChatHoverEvent() : null;
if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) {
clickEvent = null;
}
if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) {
hoverEvent = null;
}
if(clickEvent != null) {
slot.cursorEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize),
bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize),
(int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null));
}
if(hoverEvent != null) {
int px = xx + startXReal + (int) (startXLocal * bodyFontSize);
int py = yy + bodyYOffset + (int) (i * 9 * bodyFontSize);
if (mouseX >= px && mouseX < px + (int) (w * bodyFontSize) && mouseY >= py
&& mouseY < py + (int) (9 * bodyFontSize)) {
toolTip = comp;
}
}
startXLocal += w;
}
}
GlStateManager.popMatrix();
if(toolTip != null) {
parent.handleComponentHover(toolTip, mouseX - xx, mouseY - yy);
}
}
if(sourceText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, bd.sourceTxtColor);
GlStateManager.popMatrix();
}
GlStateManager.popMatrix();
}
}
private String formatAge(long serverTimestamp) {
long cur = System.currentTimeMillis();
long daysAgo = Math.round((cur - serverTimestamp) / 86400000.0);
String ret = dateFormat.format(new Date(serverTimestamp));
if(daysAgo > 0l) {
ret += " (" + daysAgo + (daysAgo == 1l ? " day" : " days") + " ago)";
}else if(daysAgo < 0l) {
ret += " (in " + -daysAgo + (daysAgo == -1l ? " day" : " days") + ")";
}
return ret;
}
@Override
public int getListWidth() {
return 224;
}
@Override
public void drawScreen(int mouseXIn, int mouseYIn, float parFloat1) {
mouseX = mouseXIn;
mouseY = mouseYIn;
for(int i = 0, l = currentDisplayNotifs.size(); i < l; ++i) {
NotifBadgeSlot slot = currentDisplayNotifs.get(i);
slot.currentScreenX = -69420;
slot.currentScreenY = -69420;
}
super.drawScreen(mouseXIn, mouseYIn, parFloat1);
}
}

View File

@ -0,0 +1,171 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.Minecraft;
import net.minecraft.util.IChatComponent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class NotificationBadge {
public final ServerNotificationManager mgr;
public final EaglercraftUUID badgeUUID;
public final IChatComponent bodyComponent;
protected IChatComponent bodyComponentProfanityFilter;
public final IChatComponent titleComponent;
protected IChatComponent titleComponentProfanityFilter;
public final IChatComponent sourceComponent;
protected IChatComponent sourceComponentProfanityFilter;
public final long clientTimestamp;
public final long serverTimestamp;
public final boolean silent;
public final EnumBadgePriority priority;
public final NotificationIcon mainIcon;
public final NotificationIcon titleIcon;
public final int hideAfterSec;
public final int expireAfterSec;
public final int backgroundColor;
public final int bodyTxtColor;
public final int titleTxtColor;
public final int sourceTxtColor;
protected CachedNotifBadgeTexture currentCacheGLTexture = null;
protected int currentCacheScaleFac = -1;
protected boolean currentCacheXButton = false;
protected boolean currentCacheProfanityFilter = false;
protected long hideAtMillis = -1l;
protected boolean unreadFlag = true;
protected boolean unreadFlagRender = true;
protected NotificationBadge(ServerNotificationManager mgr, EaglercraftUUID badgeUUID, IChatComponent bodyComponent,
IChatComponent titleComponent, IChatComponent sourceComponent, long clientTimestamp, long serverTimestamp,
boolean silent, EnumBadgePriority priority, NotificationIcon mainIcon, NotificationIcon titleIcon,
int hideAfterSec, int expireAfterSec, int backgroundColor, int bodyTxtColor, int titleTxtColor,
int sourceTxtColor) {
this.mgr = mgr;
this.badgeUUID = badgeUUID;
this.bodyComponent = bodyComponent;
this.titleComponent = titleComponent;
this.sourceComponent = sourceComponent;
this.clientTimestamp = clientTimestamp;
this.serverTimestamp = serverTimestamp;
this.silent = silent;
this.priority = priority;
this.mainIcon = mainIcon;
this.titleIcon = titleIcon;
this.hideAfterSec = hideAfterSec;
this.expireAfterSec = expireAfterSec;
this.backgroundColor = backgroundColor;
this.bodyTxtColor = bodyTxtColor;
this.titleTxtColor = titleTxtColor;
this.sourceTxtColor = sourceTxtColor;
}
protected void incrIconRefcounts() {
if(mainIcon != null) {
mainIcon.retain();
}
if(titleIcon != null) {
titleIcon.retain();
}
}
protected void decrIconRefcounts() {
deleteGLTexture();
if(mainIcon != null) {
mainIcon.release();
}
if(titleIcon != null) {
titleIcon.release();
}
}
protected CachedNotifBadgeTexture getGLTexture(ServerNotificationRenderer renderer, int scaleFactor, boolean showXButton) {
boolean profanityFilter = Minecraft.getMinecraft().isEnableProfanityFilter();
if(currentCacheGLTexture == null || currentCacheScaleFac != scaleFactor || currentCacheXButton != showXButton || currentCacheProfanityFilter != profanityFilter) {
deleteGLTexture();
currentCacheGLTexture = renderer.renderBadge(this, scaleFactor, showXButton);
currentCacheScaleFac = scaleFactor;
currentCacheXButton = showXButton;
currentCacheProfanityFilter = profanityFilter;
}
return currentCacheGLTexture;
}
protected void deleteGLTexture() {
if(currentCacheGLTexture != null) {
GlStateManager.deleteTexture(currentCacheGLTexture.glTexture);
currentCacheGLTexture = null;
}
}
public void hideNotif() {
if(hideAtMillis == -1l) {
markRead();
unreadFlagRender = false;
hideAtMillis = EagRuntime.steadyTimeMillis();
}
}
public void removeNotif() {
mgr.removeNotifFromActiveList(badgeUUID);
}
public void markRead() {
if(unreadFlag) {
unreadFlag = false;
--mgr.unreadCounter;
}
}
public IChatComponent getBodyProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(bodyComponentProfanityFilter == null && bodyComponent != null) {
bodyComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(bodyComponent);
}
return bodyComponentProfanityFilter;
}else {
return bodyComponent;
}
}
public IChatComponent getTitleProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(titleComponentProfanityFilter == null && titleComponent != null) {
titleComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(titleComponent);
}
return titleComponentProfanityFilter;
}else {
return titleComponent;
}
}
public IChatComponent getSourceProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(sourceComponentProfanityFilter == null && sourceComponent != null) {
sourceComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(sourceComponent);
}
return sourceComponentProfanityFilter;
}else {
return sourceComponent;
}
}
}

View File

@ -0,0 +1,51 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture;
import net.minecraft.util.ResourceLocation;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class NotificationIcon {
private static int notifIconTmpId = 0;
protected int refCount = 0;
protected boolean serverRegistered = true;
public final EaglercraftUUID iconUUID;
public final EaglerSkinTexture texture;
public final ResourceLocation resource;
protected NotificationIcon(EaglercraftUUID iconUUID, EaglerSkinTexture texture) {
this.iconUUID = iconUUID;
this.texture = texture;
this.resource = new ResourceLocation("eagler:gui/server/notifs/tex_" + notifIconTmpId++);
}
public void retain() {
++refCount;
}
public void release() {
--refCount;
}
public boolean isValid() {
return serverRegistered || refCount > 0;
}
}

View File

@ -0,0 +1,277 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeHideV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.IChatComponent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ServerNotificationManager {
private static final Logger logger = LogManager.getLogger("ServerNotificationManager");
private final Map<EaglercraftUUID,NotificationIcon> activeIcons = new HashMap<>();
private final Map<EaglercraftUUID,NotificationBadge> activeNotifications = new HashMap<>();
private List<NotificationBadge> sortedNotifList = new ArrayList<>(0);
private List<NotificationBadge> sortedDisplayNotifList = new ArrayList<>(0);
private int updateCounter = 0;
private long lastCleanup = EagRuntime.steadyTimeMillis();
private final TextureManager textureMgr;
protected int unreadCounter = 0;
public ServerNotificationManager() {
this.textureMgr = Minecraft.getMinecraft().getTextureManager();
}
public void processPacketAddIcons(SPacketNotifIconsRegisterV4EAG packet) {
for(SPacketNotifIconsRegisterV4EAG.CreateIcon icn : packet.iconsToCreate) {
if(icn.uuidMost == 0 && icn.uuidLeast == 0) {
logger.error("Skipping notification icon with UUID 0!");
continue;
}
EaglercraftUUID uuid = new EaglercraftUUID(icn.uuidMost, icn.uuidLeast);
PacketImageData imageData = icn.imageData;
NotificationIcon existing = activeIcons.get(uuid);
if(existing != null) {
if (existing.texture.getWidth() != imageData.width
|| existing.texture.getHeight() != imageData.height) {
logger.error("Error: server tried to change the dimensions of icon {}!", uuid);
}else if(!Arrays.equals(existing.texture.getData(), imageData.rgba)) {
existing.texture.copyPixelsIn(ImageData.swapRB(imageData.rgba));
}
existing.serverRegistered = true;
continue;
}
NotificationIcon newIcon = new NotificationIcon(uuid,
new EaglerSkinTexture(ImageData.swapRB(imageData.rgba), imageData.width, imageData.height));
textureMgr.loadTexture(newIcon.resource, newIcon.texture);
activeIcons.put(uuid, newIcon);
}
}
public void processPacketRemIcons(SPacketNotifIconsReleaseV4EAG packet) {
for(SPacketNotifIconsReleaseV4EAG.DestroyIcon icn : packet.iconsToDestroy) {
NotificationIcon existing = activeIcons.get(new EaglercraftUUID(icn.uuidMost, icn.uuidLeast));
if(existing != null) {
existing.serverRegistered = false;
}
}
}
public void processPacketShowBadge(SPacketNotifBadgeShowV4EAG packet) {
EaglercraftUUID newUuid = new EaglercraftUUID(packet.badgeUUIDMost, packet.badgeUUIDLeast);
NotificationBadge existing = activeNotifications.get(newUuid);
if(existing != null) {
logger.error("Duplicate notification UUID {}, all notifications should have unique UUIDs!", newUuid);
return;
}
NotificationBadge newBadge = new NotificationBadge(this, newUuid,
!StringUtils.isAllBlank(packet.bodyComponent) ? IChatComponent.Serializer.jsonToComponent(packet.bodyComponent) : null,
!StringUtils.isAllBlank(packet.titleComponent) ? IChatComponent.Serializer.jsonToComponent(packet.titleComponent) : null,
!StringUtils.isAllBlank(packet.sourceComponent) ? IChatComponent.Serializer.jsonToComponent(packet.sourceComponent) : null,
EagRuntime.steadyTimeMillis(), packet.originalTimestampSec * 1000l, packet.silent, packet.priority,
getIcon(packet.mainIconUUIDMost, packet.mainIconUUIDLeast),
getIcon(packet.titleIconUUIDMost, packet.titleIconUUIDLeast), packet.hideAfterSec, packet.expireAfterSec,
packet.backgroundColor, packet.bodyTxtColor, packet.titleTxtColor, packet.sourceTxtColor);
++unreadCounter;
addNotifToActiveList(newBadge);
}
private NotificationIcon getIcon(long uuidMost, long uuidLeast) {
if(uuidMost == 0l && uuidLeast == 0l) {
return null;
}
return activeIcons.get(new EaglercraftUUID(uuidMost, uuidLeast));
}
public void processPacketHideBadge(SPacketNotifBadgeHideV4EAG packet) {
removeNotifFromActiveList(new EaglercraftUUID(packet.badgeUUIDLeast, packet.badgeUUIDMost));
}
public int getNotifListUpdateCount() {
return updateCounter;
}
public List<NotificationBadge> getNotifBadgesToDisplay() {
return sortedDisplayNotifList;
}
public List<NotificationBadge> getNotifLongHistory() {
return sortedNotifList;
}
protected void addNotifToActiveList(NotificationBadge badge) {
NotificationBadge exists = activeNotifications.put(badge.badgeUUID, badge);
if(exists != null) {
exists.decrIconRefcounts();
}
badge.incrIconRefcounts();
resortLists();
}
protected void removeNotifFromActiveList(EaglercraftUUID badge) {
NotificationBadge exists = activeNotifications.remove(badge);
if(exists != null) {
exists.decrIconRefcounts();
resortLists();
}
}
protected void removeAllNotifFromActiveList(Collection<NotificationBadge> badges) {
boolean resort = false;
for(NotificationBadge badge : badges) {
NotificationBadge exists = activeNotifications.remove(badge.badgeUUID);
if(exists != null) {
exists.decrIconRefcounts();
resort = true;
}
}
if(resort) {
resortLists();
}
}
protected static final Comparator<NotificationBadge> clientAgeComparator = (a, b) -> {
return (int)(b.clientTimestamp - a.clientTimestamp);
};
private void resortLists() {
updateCounter++;
int ll = activeNotifications.size();
if(!sortedNotifList.isEmpty()) sortedNotifList = new ArrayList<>(ll);
if(!sortedDisplayNotifList.isEmpty()) sortedDisplayNotifList = new ArrayList<>(Math.min(ll, 4));
if(ll > 0) {
sortedNotifList.addAll(activeNotifications.values());
Collections.sort(sortedNotifList, clientAgeComparator);
long millis = EagRuntime.steadyTimeMillis();
for(int i = 0, l = sortedNotifList.size(); i < l; ++i) {
NotificationBadge bd = sortedNotifList.get(i);
if(millis - bd.clientTimestamp < (long)(bd.hideAfterSec * 1000)) {
sortedDisplayNotifList.add(bd);
}else {
bd.deleteGLTexture();
}
}
}
}
public void runTick() {
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastCleanup > 2500l) {
lastCleanup = millis;
int len = sortedNotifList.size();
if(len > 128) {
removeAllNotifFromActiveList(new ArrayList<NotificationBadge>(sortedNotifList.subList(128, len)));
}
Iterator<NotificationIcon> itr = activeIcons.values().iterator();
while(itr.hasNext()) {
NotificationIcon icn = itr.next();
if(!icn.isValid()) {
itr.remove();
textureMgr.deleteTexture(icn.resource);
}
}
if(!sortedDisplayNotifList.isEmpty()) {
Iterator<NotificationBadge> itr2 = sortedDisplayNotifList.iterator();
while(itr2.hasNext()) {
NotificationBadge bd = itr2.next();
if(bd.hideAtMillis != -1l) {
if(millis - bd.hideAtMillis > 500l) {
bd.deleteGLTexture();
itr2.remove();
}
}else {
long age = millis - bd.clientTimestamp;
if(age > (long)(bd.hideAfterSec * 1000) || age > (long)(bd.expireAfterSec * 1000)) {
bd.deleteGLTexture();
itr2.remove();
}
}
}
}
if(!activeNotifications.isEmpty()) {
Iterator<NotificationBadge> itr3 = activeNotifications.values().iterator();
List<NotificationBadge> toDelete = null;
while(itr3.hasNext()) {
NotificationBadge bd = itr3.next();
long age = millis - bd.clientTimestamp;
if(age > (long)(bd.expireAfterSec * 1000)) {
if(toDelete == null) {
toDelete = new ArrayList<>();
}
toDelete.add(bd);
}
}
if(toDelete != null) {
removeAllNotifFromActiveList(toDelete);
}
}
}
}
public int getUnread() {
if(unreadCounter < 0) unreadCounter = 0;
return unreadCounter;
}
public void commitUnreadFlag() {
for(NotificationBadge badge : activeNotifications.values()) {
badge.unreadFlagRender = badge.unreadFlag;
}
}
public void markRead() {
for(NotificationBadge badge : activeNotifications.values()) {
badge.unreadFlag = false;
badge.unreadFlagRender = false;
}
unreadCounter = 0;
}
public void destroy() {
for(NotificationIcon icn : activeIcons.values()) {
textureMgr.deleteTexture(icn.resource);
}
activeIcons.clear();
activeNotifications.clear();
sortedNotifList = null;
sortedDisplayNotifList = null;
}
}

View File

@ -0,0 +1,539 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.VertexFormat;
import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiUtilRenderComponents;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ExtGLEnums.*;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ServerNotificationRenderer {
protected static final Logger logger = LogManager.getLogger("ServerNotificationRenderer");
protected Minecraft mc;
protected int width;
protected int height;
protected int scaleFactor;
protected IFramebufferGL rendererFramebuffer;
protected static final int BADGE_WIDTH = 160;
protected static final int BADGE_HEIGHT = 64;
private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png");
public ServerNotificationRenderer() {
}
public void init() {
destroy();
rendererFramebuffer = _wglCreateFramebuffer();
}
public void setResolution(Minecraft mc, int w, int h, int scaleFactor) {
this.mc = mc;
this.width = w;
this.height = h;
this.scaleFactor = scaleFactor;
}
public boolean handleClicked(GuiScreen currentScreen, int posX, int posY) {
if(mc.thePlayer == null) return false;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
List<NotificationBadge> lst = mgr.getNotifBadgesToDisplay();
if(!lst.isEmpty()) {
int baseOffset = mc.guiAchievement.getHeight();
boolean showX = (currentScreen instanceof GuiChat);
if(showX) {
baseOffset += 25; // exit button in chat screen;
}
long millis = EagRuntime.steadyTimeMillis();
for(int i = 0, l = lst.size(); i < l; ++i) {
NotificationBadge badge = lst.get(i);
CachedNotifBadgeTexture tex = badge.currentCacheGLTexture;
if(tex != null) {
int baseX = width - tex.width;
float texHeight = tex.height;
float timeRemainingSec;
long age = millis - badge.clientTimestamp;
if(badge.hideAtMillis != -1l) {
timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001);
}else {
timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001);
}
timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec);
float f = MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F);
f *= f;
texHeight *= f;
if(badge.hideAtMillis == -1l) {
if(posX >= baseX && posX < width && posY >= baseOffset && posY < baseOffset + texHeight) {
if(showX) {
int xposX = baseX + tex.width - 21;
int xposY = baseOffset + 5;
if(posX >= xposX && posY >= xposY && posX < xposX + 16 && posY < xposY + 16) {
badge.hideNotif();
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
if(tex.rootClickEvent != null) {
if(currentScreen.handleComponentClick(tex.rootClickEvent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
List<ClickEventZone> cursorEvents = tex.cursorEvents;
if(tex.hasClickEvents && cursorEvents != null) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasClickEvent) {
int offsetPosX = baseX + evt.posX;
int offsetPosY = baseOffset + evt.posY;
if(posX >= offsetPosX && posY >= offsetPosY && posX < offsetPosX + evt.width && posY < offsetPosY + evt.height) {
if(currentScreen.handleComponentClick(evt.chatComponent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
}
}
}
}
}
baseOffset += texHeight;
}
}
}
return false;
}
public void renderOverlay(int mouseX, int mouseY) {
if(mc.thePlayer == null) return;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
List<NotificationBadge> lst = mgr.getNotifBadgesToDisplay();
if(!lst.isEmpty()) {
GlStateManager.clear(GL_DEPTH_BUFFER_BIT);
boolean showXButtons = false;
int baseOffset = mc.guiAchievement.getHeight();
if(mc.currentScreen != null) {
if(mc.currentScreen instanceof GuiChat) {
baseOffset += 25; // exit button in chat screen;
showXButtons = true;
}else if(mc.currentScreen instanceof GuiScreenNotifications) {
return;
}
}
long millis = EagRuntime.steadyTimeMillis();
boolean isBlend = false;
for(int i = 0, l = lst.size(); i < l; ++i) {
NotificationBadge badge = lst.get(i);
boolean isHiding = false;
if(badge.hideAtMillis != -1l) {
isHiding = true;
if(millis - badge.hideAtMillis > 500l) {
continue;
}
}
CachedNotifBadgeTexture tex = badge.getGLTexture(this, scaleFactor, showXButtons);
if(tex != null) {
GlStateManager.bindTexture(tex.glTexture);
float alphaTop = 1.0f;
float alphaBottom = 1.0f;
float timeRemainingSec;
long age = millis - badge.clientTimestamp;
if(isHiding) {
timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001);
}else {
timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001);
}
timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec);
alphaTop *= MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F);
alphaTop *= alphaTop;
alphaBottom *= MathHelper.clamp_float(timeRemainingSec * 2.0F, 0.0F, 1.0F);
alphaBottom *= alphaBottom;
if(alphaTop == 0.0F && alphaBottom == 0.0F) {
continue;
}
boolean blend = alphaTop < 1.0f || alphaBottom < 1.0f;
if(blend != isBlend) {
if(blend) {
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0);
}else {
GlStateManager.disableBlend();
}
isBlend = blend;
}
int px = width - tex.width;
drawTexturedGradientFBRect(px, baseOffset, tex.width, tex.height,
((int) (alphaTop * 255.0f) << 24) | 0xFFFFFF,
((int) (alphaBottom * 255.0f) << 24) | 0xFFFFFF, 200.0f);
if(showXButtons && tex.hasHoverEvents) {
if(mouseX >= px && mouseY >= baseOffset && mouseX < px + tex.width && mouseY < baseOffset + tex.height) {
List<ClickEventZone> cursorEvents = tex.cursorEvents;
if(cursorEvents != null) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasHoverEvent) {
int offsetPosX = px + evt.posX;
int offsetPosY = baseOffset + evt.posY;
if(mouseX >= offsetPosX && mouseY >= offsetPosY && mouseX < offsetPosX + evt.width
&& mouseY < offsetPosY + evt.height) {
if(isBlend) {
GlStateManager.disableBlend();
isBlend = false;
}
mc.currentScreen.handleComponentHover(evt.chatComponent, mouseX, mouseY);
}
}
}
}
}
}
baseOffset += tex.height * alphaTop;
}
}
if(isBlend) {
GlStateManager.disableBlend();
}
}
}
protected CachedNotifBadgeTexture renderBadge(NotificationBadge badge, int scaleFactor, boolean showXButton) {
int badgeWidth = BADGE_WIDTH;
int badgeHeight = 10;
int leftPadding = 6;
int rightPadding = 26;
int mainIconSW = 32;
if(badge.mainIcon != null) {
int iw = badge.mainIcon.texture.getWidth();
int ih = badge.mainIcon.texture.getHeight();
float iaspect = (float)iw / (float)ih;
mainIconSW = (int)(32 * iaspect);
leftPadding += Math.min(mainIconSW, 64) + 3;
}
int textZoneWidth = badgeWidth - leftPadding - rightPadding;
int bodyYOffset = 5;
String titleText = null;
IChatComponent titleComponent = badge.getTitleProfanityFilter();
if(titleComponent != null) {
titleText = titleComponent.getFormattedText();
if(titleText.length() > 0) {
badgeHeight += 12;
bodyYOffset += 12;
}else {
titleText = null;
}
}
if(badge.titleIcon != null && titleText == null) {
badgeHeight += 12;
bodyYOffset += 12;
}
float bodyFontSize = 0.75f;
List<IChatComponent> bodyLines = null;
List<ClickEventZone> clickEvents = null;
IChatComponent rootClickEvt = null;
boolean hasClickEvents = false;
boolean hasHoverEvents = false;
int bodyHeight = 0;
IChatComponent bodyComponent = badge.getBodyProfanityFilter();
if(bodyComponent != null) {
if (bodyComponent.getChatStyle().getChatClickEvent() != null
&& bodyComponent.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) {
rootClickEvt = bodyComponent;
}
bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize),
mc.fontRendererObj, true, true);
int maxHeight = BADGE_HEIGHT - 32;
int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize));
if(bodyLines.size() > maxLines) {
bodyLines = bodyLines.subList(0, maxLines);
bodyComponent = bodyLines.get(maxLines - 1);
List<IChatComponent> siblings = bodyComponent.getSiblings();
IChatComponent dots = new ChatComponentText("...");
if(siblings != null && siblings.size() > 0) {
dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle());
}
bodyComponent.appendSibling(dots);
}
bodyHeight = MathHelper.floor_float(bodyLines.size() * (9 * bodyFontSize));
}
String sourceText = null;
IChatComponent sourceComponent = badge.getSourceProfanityFilter();
if(sourceComponent != null) {
sourceText = sourceComponent.getFormattedText();
if(sourceText.length() == 0) {
sourceText = null;
}
}
if(badge.mainIcon != null) {
bodyHeight = Math.max(sourceText != null ? 30 : 32, bodyHeight);
}
if(sourceText != null) {
badgeHeight += 6;
}
badgeHeight += bodyHeight;
badgeHeight = Math.max(badgeHeight, showXButton ? 42 : 26);
if(badgeHeight > BADGE_HEIGHT) {
logger.info("Warning: Badge {} was {} pixels too high!", badge.badgeUUID, BADGE_HEIGHT - badgeHeight);
badgeHeight = BADGE_HEIGHT;
}
int glTex = GlStateManager.generateTexture();
GlStateManager.bindTexture(glTex);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, badgeWidth * scaleFactor, badgeHeight * scaleFactor, 0, GL_RGBA,
GL_UNSIGNED_BYTE, (ByteBuffer) null);
_wglBindFramebuffer(_GL_FRAMEBUFFER, rendererFramebuffer);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(glTex), 0);
_wglDrawBuffers(_GL_COLOR_ATTACHMENT0);
int[] oldViewport = new int[4];
EaglercraftGPU.glGetInteger(GL_VIEWPORT, oldViewport);
GlStateManager.viewport(0, 0, badgeWidth * scaleFactor, badgeHeight * scaleFactor);
GlStateManager.disableDepth();
GlStateManager.depthMask(false);
GlStateManager.enableTexture2D();
GlStateManager.disableLighting();
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
GlStateManager.ortho(0.0D, badgeWidth, badgeHeight, 0.0D, 1000.0D, 3000.0D);
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
GlStateManager.translate(0.0F, 0.0F, -2000.0F);
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR);
mc.getTextureManager().bindTexture(eaglerGui);
drawTexturedColoredRect(worldRenderer, 0, 0, 96, 192, 160, 8, (badge.backgroundColor >>> 16) & 0xFF,
(badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF);
drawTexturedColoredRect(worldRenderer, 0, 8, 96, 192 + (BADGE_HEIGHT - badgeHeight + 8), 160, (badgeHeight - 8),
(badge.backgroundColor >>> 16) & 0xFF, (badge.backgroundColor >>> 8) & 0xFF,
badge.backgroundColor & 0xFF, 0xFF);
switch(badge.priority) {
case LOW:
default:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16, (badge.backgroundColor >>> 16) & 0xFF,
(badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF);
break;
case NORMAL:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
case HIGHER:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
case HIGHEST:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
}
if(showXButton) {
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, 5, 80, 208, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
}
tess.draw();
if(badge.mainIcon != null) {
mc.getTextureManager().bindTexture(badge.mainIcon.resource);
drawTexturedRect(6, bodyYOffset, mainIconSW, 32);
}
if(badge.titleIcon != null) {
mc.getTextureManager().bindTexture(badge.titleIcon.resource);
drawTexturedRect(6, 5, 8, 8);
}
if(titleText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(6 + (badge.titleIcon != null ? 10 : 0), 6, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, badge.titleTxtColor);
GlStateManager.popMatrix();
}
if(bodyLines != null && !bodyLines.isEmpty()) {
GlStateManager.pushMatrix();
if(!showXButton && badge.mainIcon == null && titleText != null) {
bodyYOffset -= 2;
}
GlStateManager.translate(leftPadding, bodyYOffset, 0.0f);
int l = bodyLines.size();
GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize);
for(int i = 0; i < l; ++i) {
int startXLocal = 0;
int startXReal = leftPadding;
for(IChatComponent comp : bodyLines.get(i)) {
int w = mc.fontRendererObj.drawStringWithShadow(
comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal,
i * 9, badge.bodyTxtColor) - startXLocal;
ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent();
HoverEvent hoverEvent = comp.getChatStyle().getChatHoverEvent();
if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) {
clickEvent = null;
}
if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) {
hoverEvent = null;
}
if(clickEvent != null || hoverEvent != null) {
hasClickEvents |= clickEvent != null;
hasHoverEvents |= hoverEvent != null;
if(clickEvents == null) {
clickEvents = new ArrayList<>();
}
clickEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize),
bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize),
(int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null));
}
startXLocal += w;
}
}
GlStateManager.popMatrix();
}
if(sourceText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f);
GlStateManager.scale(0.5f, 0.5f, 0.5f);
mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, badge.sourceTxtColor);
GlStateManager.popMatrix();
}
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.popMatrix();
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.depthMask(true);
GlStateManager.enableDepth();
_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
GlStateManager.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
return new CachedNotifBadgeTexture(glTex, scaleFactor, badgeWidth, badgeHeight, clickEvents, rootClickEvt, hasClickEvents, hasHoverEvents);
}
static void drawTexturedColoredRect(WorldRenderer worldRenderer, float xCoord, float yCoord, int minU,
int minV, int width, int height, int r, int g, int b, int a) {
float f = 0.00390625F;
float f1 = 0.00390625F;
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + height) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + width) * f), (double) ((float) (minV + height) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + width) * f), (double) ((float) (minV + 0) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + 0) * f1)).endVertex();
}
static void drawTexturedGradientFBRect(float xCoord, float yCoord, int width, int height, int rgbaTop, int rgbaBottom, float zIndex) {
int topR = (rgbaTop >>> 16) & 0xFF;
int topG = (rgbaTop >>> 8) & 0xFF;
int topB = rgbaTop & 0xFF;
int topA = (rgbaTop >>> 24) & 0xFF;
int bottomR = (rgbaBottom >>> 16) & 0xFF;
int bottomG = (rgbaBottom >>> 8) & 0xFF;
int bottomB = rgbaBottom & 0xFF;
int bottomA = (rgbaBottom >>> 24) & 0xFF;
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR);
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), zIndex)
.color(bottomR, bottomG, bottomB, bottomA).tex(0.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), zIndex)
.color(bottomR, bottomG, bottomB, bottomA).tex(1.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), zIndex)
.color(topR, topG, topB, topA).tex(1.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), zIndex).color(topR, topG, topB, topA)
.tex(0.0, 1.0).endVertex();
tess.draw();
}
static void drawTexturedRect(float xCoord, float yCoord, int width, int height) {
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX);
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).tex(0.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).tex(1.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).tex(1.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).tex(0.0, 0.0).endVertex();
tess.draw();
}
public void destroy() {
if(rendererFramebuffer != null) {
_wglDeleteFramebuffer(rendererFramebuffer);
rendererFramebuffer = null;
}
}
}