mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #37 - Touch support without userscript, many other feats
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user