mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #22 - Singleplayer and shared worlds, shader fixes
This commit is contained in:
@ -0,0 +1,35 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
/**
|
||||
* 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 CertificateInvalidException extends RuntimeException {
|
||||
|
||||
public CertificateInvalidException() {
|
||||
}
|
||||
|
||||
public CertificateInvalidException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CertificateInvalidException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CertificateInvalidException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
|
||||
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.classes.net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 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 GuiUpdateCheckerOverlay extends Gui {
|
||||
|
||||
private static final ResourceLocation eaglerIcons = new ResourceLocation("eagler:gui/eagler_gui.png");
|
||||
|
||||
private Minecraft mc;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
private boolean isIngame;
|
||||
private GuiScreen backScreen;
|
||||
|
||||
private GuiButton checkForUpdatesButton;
|
||||
private GuiButton startDownloadButton;
|
||||
private GuiButton viewAllUpdatesButton;
|
||||
private GuiButton dismissUpdatesButton;
|
||||
|
||||
public GuiUpdateCheckerOverlay(boolean isIngame, GuiScreen screen) {
|
||||
this.isIngame = isIngame;
|
||||
this.backScreen = screen;
|
||||
}
|
||||
|
||||
public void setResolution(Minecraft mc, int w, int h) {
|
||||
this.mc = mc;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
checkForUpdatesButton = new GuiButton(0, w - 150, 0, 150, 20, I18n.format("update.button") + " " + I18n.format(mc.gameSettings.enableUpdateSvc ? "gui.yes" : "gui.no"));
|
||||
startDownloadButton = new GuiButton(1, w - 115, 0, 115, 20, I18n.format("update.startDownload"));
|
||||
viewAllUpdatesButton = new GuiButton(2, w - 115, 0, 115, 20, I18n.format("update.viewAll", 0));
|
||||
dismissUpdatesButton = new GuiButton(3, w - 115, 0, 115, 20, I18n.format("update.dismiss"));
|
||||
}
|
||||
|
||||
public void drawScreen(int mx, int my, float partialTicks) {
|
||||
UpdateProgressStruct progressState = UpdateService.getUpdatingStatus();
|
||||
if(progressState.isBusy) {
|
||||
drawScreenBusy(mx, my, partialTicks, progressState);
|
||||
return;
|
||||
}
|
||||
|
||||
checkForUpdatesButton.visible = isIngame;
|
||||
startDownloadButton.visible = false;
|
||||
viewAllUpdatesButton.visible = false;
|
||||
dismissUpdatesButton.visible = false;
|
||||
|
||||
int i = UpdateService.getAvailableUpdates().size();
|
||||
boolean shownSP = i > 0 || !mc.isSingleplayer() || LANServerController.isHostingLAN();
|
||||
checkForUpdatesButton.visible &= shownSP;
|
||||
|
||||
if(mc.gameSettings.enableUpdateSvc) {
|
||||
String str;
|
||||
UpdateCertificate cert = UpdateService.getLatestUpdateFound();
|
||||
if(cert != null) {
|
||||
startDownloadButton.visible = true;
|
||||
viewAllUpdatesButton.visible = true;
|
||||
dismissUpdatesButton.visible = true;
|
||||
viewAllUpdatesButton.displayString = I18n.format("update.viewAll", i);
|
||||
str = I18n.format("update.found");
|
||||
mc.fontRendererObj.drawStringWithShadow(str, width - mc.fontRendererObj.getStringWidth(str) - 3, 22, 0xFFFFAA);
|
||||
|
||||
int embedY = 35;
|
||||
int embedWidth = 115;
|
||||
int embedWidth2 = (int)(embedWidth / 0.75f);
|
||||
|
||||
List<String> lst = cert.bundleVersionComment.length() == 0 ? null : mc.fontRendererObj.listFormattedStringToWidth(cert.bundleVersionComment, embedWidth2 - 14);
|
||||
|
||||
int embedHeight = 44;
|
||||
if(lst != null) {
|
||||
embedHeight += 3 + lst.size() * 6;
|
||||
}
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(width - embedWidth - 1, embedY, 0.0f);
|
||||
GlStateManager.scale(0.75f, 0.75f, 0.75f);
|
||||
|
||||
int embedHeight2 = (int)(embedHeight / 0.75f);
|
||||
|
||||
drawGradientRect(1, 1, embedWidth2 - 1, embedHeight2 - 1, 0xFFFFFFAA, 0xFFFFFFAA);
|
||||
drawGradientRect(0, 1, embedWidth2, 2, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(0, embedHeight2 - 1, embedWidth2, embedHeight2, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(0, 1, 1, embedHeight2 - 1, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(embedWidth2 - 1, 1, embedWidth2, embedHeight2 - 1, 0xFF000000, 0xFF000000);
|
||||
|
||||
mc.getTextureManager().bindTexture(eaglerIcons);
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.scale(0.3f, 0.3f, 0.3f);
|
||||
drawGradientRect(23, 23, 127, 127, 0xFF000000, 0xFF000000);
|
||||
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // rip
|
||||
drawTexturedModalRect(25, 25, 156, 0, 100, 100);
|
||||
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
mc.fontRendererObj.drawString(EnumChatFormatting.UNDERLINE + cert.bundleDisplayName, 45, 11, 0x000000);
|
||||
mc.fontRendererObj.drawString(I18n.format("update.update") + " " + EnumChatFormatting.DARK_RED + cert.bundleDisplayVersion, 45, 25, 0x000000);
|
||||
|
||||
if(lst != null) {
|
||||
for(int j = 0, l = lst.size(); j < l; ++j) {
|
||||
mc.fontRendererObj.drawString(lst.get(j), 5, 42 + j * 8, 0x000000);
|
||||
}
|
||||
}
|
||||
|
||||
mc.fontRendererObj.drawString(I18n.format("update.author") + " " + cert.bundleAuthorName, 5, 44 + (lst == null ? 0 : (3 + lst.size() * 8)), 0x777777);
|
||||
|
||||
startDownloadButton.yPosition = embedHeight + embedY + 5;
|
||||
viewAllUpdatesButton.yPosition = startDownloadButton.yPosition + 22;
|
||||
dismissUpdatesButton.yPosition = viewAllUpdatesButton.yPosition + 22;
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}else if(isIngame) {
|
||||
if(shownSP) {
|
||||
str = I18n.format("update.noneNew");
|
||||
mc.fontRendererObj.drawString(str, width - mc.fontRendererObj.getStringWidth(str) - 3, 22, 0xDDDDDD);
|
||||
if(i > 0) {
|
||||
viewAllUpdatesButton.yPosition = 40;
|
||||
viewAllUpdatesButton.visible = true;
|
||||
viewAllUpdatesButton.displayString = I18n.format("update.viewAll", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkForUpdatesButton.drawButton(mc, mx, my);
|
||||
startDownloadButton.drawButton(mc, mx, my);
|
||||
viewAllUpdatesButton.drawButton(mc, mx, my);
|
||||
dismissUpdatesButton.drawButton(mc, mx, my);
|
||||
}
|
||||
|
||||
public void drawScreenBusy(int mx, int my, float partialTicks, UpdateProgressStruct progressState) {
|
||||
checkForUpdatesButton.visible = false;
|
||||
startDownloadButton.visible = false;
|
||||
viewAllUpdatesButton.visible = false;
|
||||
dismissUpdatesButton.visible = false;
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(width, isIngame ? 0.0f : 10.0f, 0.0f);
|
||||
String str = I18n.format("update.downloading");
|
||||
mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 2, 2, 0xFFFFAA);
|
||||
GlStateManager.translate(0.0f, 14.0f, 0.0f);
|
||||
GlStateManager.scale(0.75f, 0.75f, 0.75f);
|
||||
if(!StringUtils.isAllBlank(progressState.statusString1)) {
|
||||
str = progressState.statusString1;
|
||||
mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 3, 0, 0xFFFFFF);
|
||||
}
|
||||
int cc = isIngame ? 0xBBBBBB : 0xFFFFFF;
|
||||
if(!StringUtils.isAllBlank(progressState.statusString2)) {
|
||||
str = progressState.statusString2;
|
||||
mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 3, 11, cc);
|
||||
}
|
||||
int progX1 = -135;
|
||||
int progY1 = 22;
|
||||
int progX2 = -3;
|
||||
int progY2 = 32;
|
||||
float prog = progressState.progressBar;
|
||||
if(prog >= 0.0f) {
|
||||
int bk = 0xFFBBBBBB;
|
||||
int fg = 0xFFDD0000;
|
||||
drawGradientRect(progX1 + 1, progY1 + 1, progX1 + (int)((progX2 - progX1 - 1) * prog), progY2 - 1, fg, fg);
|
||||
drawGradientRect(progX1 + (int)((progX2 - progX1 - 1) * prog), progY1 + 1, progX2 - 1, progY2 - 1, bk, bk);
|
||||
drawGradientRect(progX1, progY1, progX2, progY1 + 1, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(progX1, progY2 - 1, progX2, progY2, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(progX1, progY1 + 1, progX1 + 1, progY2 - 1, 0xFF000000, 0xFF000000);
|
||||
drawGradientRect(progX2 - 1, progY1 + 1, progX2, progY2 - 1, 0xFF000000, 0xFF000000);
|
||||
}
|
||||
if(!StringUtils.isAllBlank(progressState.statusString3)) {
|
||||
GlStateManager.translate(0.0f, progY2 + 2, 0.0f);
|
||||
GlStateManager.scale(0.66f, 0.66f, 0.66f);
|
||||
str = progressState.statusString3;
|
||||
List<String> wrappedURL = mc.fontRendererObj.listFormattedStringToWidth(str, (int)((progX2 - progX1) * 1.5f));
|
||||
for(int i = 0, l = wrappedURL.size(); i < l; ++i) {
|
||||
str = wrappedURL.get(i);
|
||||
mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 5, i * 11, cc);
|
||||
}
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
public void mouseClicked(int mx, int my, int btn) {
|
||||
if (btn == 0) {
|
||||
if(checkForUpdatesButton.mousePressed(mc, mx, my)) {
|
||||
mc.gameSettings.enableUpdateSvc = !mc.gameSettings.enableUpdateSvc;
|
||||
mc.gameSettings.saveOptions();
|
||||
checkForUpdatesButton.displayString = I18n.format("update.button") + " " + I18n.format(mc.gameSettings.enableUpdateSvc ? "gui.yes" : "gui.no");
|
||||
}
|
||||
if(startDownloadButton.mousePressed(mc, mx, my)) {
|
||||
if(!UpdateService.getUpdatingStatus().isBusy) {
|
||||
UpdateCertificate cert = UpdateService.getLatestUpdateFound();
|
||||
if(cert != null) {
|
||||
UpdateService.startClientUpdateFrom(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(viewAllUpdatesButton.mousePressed(mc, mx, my)) {
|
||||
mc.displayGuiScreen(new GuiUpdateVersionList(backScreen));
|
||||
}
|
||||
if(dismissUpdatesButton.mousePressed(mc, mx, my)) {
|
||||
UpdateCertificate cert = UpdateService.getLatestUpdateFound();
|
||||
if(cert != null) {
|
||||
UpdateService.dismiss(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
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 GuiUpdateVersionList extends GuiScreen {
|
||||
|
||||
final GuiScreen back;
|
||||
GuiUpdateVersionSlot slots;
|
||||
int selected;
|
||||
GuiButton downloadButton;
|
||||
int mx = 0;
|
||||
int my = 0;
|
||||
String tooltip = null;
|
||||
|
||||
public GuiUpdateVersionList(GuiScreen back) {
|
||||
this.back = back;
|
||||
}
|
||||
|
||||
public void initGui() {
|
||||
selected = -1;
|
||||
buttonList.clear();
|
||||
buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 28, 100, 20, I18n.format("gui.done")));
|
||||
buttonList.add(downloadButton = new GuiButton(1, this.width / 2 - 50, this.height - 28, 100, 20, I18n.format("updateList.download")));
|
||||
buttonList.add(new GuiButton(2, this.width / 2 - 154, this.height - 28, 100, 20, I18n.format("updateList.refresh")));
|
||||
slots = new GuiUpdateVersionSlot(this);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void updateButtons() {
|
||||
downloadButton.enabled = selected != -1;
|
||||
}
|
||||
|
||||
static Minecraft getMinecraft(GuiUpdateVersionList screen) {
|
||||
return screen.mc;
|
||||
}
|
||||
|
||||
public void actionPerformed(GuiButton btn) {
|
||||
switch(btn.id) {
|
||||
case 1:
|
||||
if(selected != -1) {
|
||||
UpdateService.startClientUpdateFrom(slots.certList.get(selected));
|
||||
}
|
||||
case 0:
|
||||
default:
|
||||
mc.displayGuiScreen(back);
|
||||
break;
|
||||
case 2:
|
||||
this.initGui();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawScreen(int par1, int par2, float par3) {
|
||||
mx = par1;
|
||||
my = par2;
|
||||
slots.drawScreen(par1, par2, par3);
|
||||
this.drawCenteredString(fontRendererObj, I18n.format("updateList.title"), this.width / 2, 16, 16777215);
|
||||
this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.0"), this.width / 2, this.height - 55, 0x888888);
|
||||
this.drawCenteredString(fontRendererObj, I18n.format("updateList.note.1"), this.width / 2, this.height - 45, 0x888888);
|
||||
super.drawScreen(par1, par2, par3);
|
||||
if(tooltip != null) {
|
||||
drawHoveringText(mc.fontRendererObj.listFormattedStringToWidth(tooltip, 180), par1, par2);
|
||||
tooltip = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMouseInput() throws IOException {
|
||||
super.handleMouseInput();
|
||||
slots.handleMouseInput();
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
|
||||
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.classes.net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.client.gui.GuiSlot;
|
||||
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 GuiUpdateVersionSlot extends GuiSlot {
|
||||
|
||||
private static final ResourceLocation eaglerGuiTex = new ResourceLocation("eagler:gui/eagler_gui.png");
|
||||
|
||||
final List<UpdateCertificate> certList = new ArrayList();
|
||||
|
||||
final GuiUpdateVersionList screen;
|
||||
|
||||
public GuiUpdateVersionSlot(GuiUpdateVersionList screen) {
|
||||
super(GuiUpdateVersionList.getMinecraft(screen), screen.width, screen.height, 32, screen.height - 64, 37);
|
||||
this.screen = screen;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
certList.clear();
|
||||
Collection<UpdateCertificate> certs = UpdateService.getAvailableUpdates();
|
||||
synchronized(certs) {
|
||||
certList.addAll(certs);
|
||||
}
|
||||
certList.sort((c1, c2) -> {
|
||||
if(c1.bundleVersionInteger > c2.bundleVersionInteger) {
|
||||
return -1;
|
||||
}else if(c1.bundleVersionInteger == c2.bundleVersionInteger) {
|
||||
if(c1.sigTimestamp > c2.sigTimestamp) {
|
||||
return -1;
|
||||
}else if(c1.sigTimestamp == c2.sigTimestamp) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize() {
|
||||
return certList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void elementClicked(int var1, boolean var2, int var3, int var4) {
|
||||
screen.selected = var1;
|
||||
screen.updateButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelected(int var1) {
|
||||
return var1 == screen.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBackground() {
|
||||
screen.drawBackground(0);
|
||||
}
|
||||
|
||||
public static final SimpleDateFormat dateFmt = EagRuntime.fixDateFormat(new SimpleDateFormat("M/dd/yyyy"));
|
||||
private static final char[] hexChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
@Override
|
||||
protected void drawSlot(int id, int xx, int yy, int width, int height, int ii) {
|
||||
if(id < certList.size()) {
|
||||
this.mc.getTextureManager().bindTexture(eaglerGuiTex);
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(xx, yy, 0.0f);
|
||||
GlStateManager.pushMatrix();
|
||||
int iconSize = 33;
|
||||
GlStateManager.scale(iconSize * 0.01f, iconSize * 0.01f, iconSize * 0.01f);
|
||||
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // rip
|
||||
screen.drawTexturedModalRect(0, 0, 156, 0, 100, 100);
|
||||
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.translate(iconSize + 1, 0.0f, 0.0f);
|
||||
GlStateManager.scale(0.75f, 0.75f, 0.75f);
|
||||
UpdateCertificate cert = certList.get(id);
|
||||
screen.drawString(mc.fontRendererObj,
|
||||
EnumChatFormatting.WHITE + cert.bundleDisplayName + EnumChatFormatting.GRAY + " - "
|
||||
+ (cert.bundleVersionInteger > EaglercraftVersion.updateBundlePackageVersionInt
|
||||
? EnumChatFormatting.GREEN
|
||||
: (cert.bundleVersionInteger < EaglercraftVersion.updateBundlePackageVersionInt
|
||||
? EnumChatFormatting.RED
|
||||
: EnumChatFormatting.YELLOW))
|
||||
+ cert.bundleDisplayVersion + EnumChatFormatting.DARK_GRAY + " "
|
||||
+ cert.bundleVersionInteger + " " + EnumChatFormatting.GRAY
|
||||
+ dateFmt.format(new Date(cert.sigTimestamp)) + EnumChatFormatting.WHITE + " " + (cert.bundleDataLength / 1024) + " kB",
|
||||
2, 2, 0xFFFFFF);
|
||||
List<String> strs = (List<String>)mc.fontRendererObj.listFormattedStringToWidth(cert.bundleVersionComment, (int)((getListWidth() - iconSize - 6) * 1.25f));
|
||||
if(strs.size() > 0) {
|
||||
screen.drawString(mc.fontRendererObj, strs.get(0), 2, 13, 0x888888);
|
||||
}
|
||||
if(strs.size() > 1) {
|
||||
screen.drawString(mc.fontRendererObj, strs.get(1), 2, 24, 0x888888);
|
||||
}
|
||||
if(strs.size() > 2 && screen.mx > xx + iconSize && screen.my > yy + 8 && screen.mx < xx + getListWidth() - 5 && screen.my < yy + 25) {
|
||||
screen.tooltip = cert.bundleVersionComment;
|
||||
}
|
||||
char[] hexStr1 = new char[] { hexChars[(cert.bundleDataHash[0] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[1] & 0xF], hexChars[(cert.bundleDataHash[1] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[1] & 0xF], hexChars[(cert.bundleDataHash[2] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[2] & 0xF] };
|
||||
char[] hexStr2 = new char[] { hexChars[(cert.bundleDataHash[29] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[29] & 0xF], hexChars[(cert.bundleDataHash[30] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[30] & 0xF], hexChars[(cert.bundleDataHash[31] >> 4) & 0xF],
|
||||
hexChars[cert.bundleDataHash[31] & 0xF] };
|
||||
screen.drawString(mc.fontRendererObj,
|
||||
"Author: " + EnumChatFormatting.GRAY + cert.bundleAuthorName + EnumChatFormatting.WHITE + " Hash: "
|
||||
+ EnumChatFormatting.GRAY + "0x" + (new String(hexStr1)) + "......" + (new String(hexStr2)),
|
||||
2, 35, 0xFFFFFF);
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getListWidth() {
|
||||
return 250;
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.IPacket00Handshake;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* 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 RelayUpdateChecker {
|
||||
|
||||
private static class RelayEntry {
|
||||
|
||||
private final String uri;
|
||||
private boolean queued;
|
||||
private boolean handshake;
|
||||
private RelayServerSocket currentSocket;
|
||||
|
||||
private RelayEntry(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final List<RelayEntry> relaysList = new ArrayList();
|
||||
|
||||
private static long lastUpdateCheck = -1l;
|
||||
private static boolean hasInit = false;
|
||||
|
||||
private static final long updateCheckRate = 3l * 60l * 60l * 1000l;
|
||||
|
||||
private static final String magic = "~!REQUEST_UPDATE_CERT";
|
||||
|
||||
public static void runTick() {
|
||||
if(!EagRuntime.getConfiguration().isCheckRelaysForUpdates()) {
|
||||
return;
|
||||
}
|
||||
if(!hasInit) {
|
||||
hasInit = true;
|
||||
for(net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry etr : EagRuntime.getConfiguration().getRelays()) {
|
||||
relaysList.add(new RelayEntry(etr.address));
|
||||
}
|
||||
byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate");
|
||||
if(b != null) {
|
||||
try {
|
||||
lastUpdateCheck = (new DataInputStream(new ByteArrayInputStream(b))).readLong();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
long millis = System.currentTimeMillis();
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if((mc.theWorld == null || mc.isSingleplayer()) && millis - lastUpdateCheck > updateCheckRate) {
|
||||
lastUpdateCheck = millis;
|
||||
try {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream(8);
|
||||
(new DataOutputStream(bao)).writeLong(lastUpdateCheck);
|
||||
PlatformApplication.setLocalStorage("lastRelayUpdate", bao.toByteArray());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
for (int i = 0, l = relaysList.size(); i < l; ++i) {
|
||||
relaysList.get(i).queued = true;
|
||||
}
|
||||
}
|
||||
for(int i = 0, l = relaysList.size(); i < l; ++i) {
|
||||
RelayEntry etr = relaysList.get(i);
|
||||
if(etr.currentSocket != null) {
|
||||
updateRelay(etr);
|
||||
if(etr.currentSocket != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0, l = relaysList.size(); i < l; ++i) {
|
||||
RelayEntry etr = relaysList.get(i);
|
||||
if(etr.queued) {
|
||||
etr.queued = false;
|
||||
connect(etr);
|
||||
if(etr.currentSocket != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void connect(RelayEntry socket) {
|
||||
try {
|
||||
socket.handshake = false;
|
||||
socket.currentSocket = PlatformWebRTC.openRelayConnection(socket.uri, 10000);
|
||||
if(socket.currentSocket.isClosed()) {
|
||||
socket.currentSocket = null;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateRelay(RelayEntry socket) {
|
||||
try {
|
||||
if(socket.currentSocket.isClosed()) {
|
||||
socket.currentSocket = null;
|
||||
}else if(socket.currentSocket.isOpen()) {
|
||||
if(!socket.handshake) {
|
||||
socket.handshake = true;
|
||||
socket.currentSocket.writePacket(new IPacket00Handshake(0x02, RelayManager.preferredRelayVersion, magic));
|
||||
}else {
|
||||
// close immediately
|
||||
if(socket.currentSocket.nextPacket() != null) {
|
||||
socket.currentSocket.close();
|
||||
socket.currentSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.MultimapBuilder.ListMultimapBuilder;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglerZLIB;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest;
|
||||
|
||||
/**
|
||||
* 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 UpdateCertificate {
|
||||
|
||||
public static class DLSource {
|
||||
|
||||
public final String type;
|
||||
public final String addr;
|
||||
private DLSource(String type, String addr) {
|
||||
this.type = type;
|
||||
this.addr = addr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(addr, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DLSource other = (DLSource) obj;
|
||||
return Objects.equals(addr, other.addr) && Objects.equals(type, other.type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final byte[] rawCertData;
|
||||
|
||||
public final int sigVersion;
|
||||
public final long sigTimestamp;
|
||||
|
||||
public final int bundleDataLength;
|
||||
public final byte[] bundleDataHash;
|
||||
|
||||
public final String bundlePackageName;
|
||||
public final String bundleDisplayName;
|
||||
public final String bundleAuthorName;
|
||||
public final int bundleVersionInteger;
|
||||
public final String bundleDisplayVersion;
|
||||
public final String bundleVersionComment;
|
||||
|
||||
public final DLSource[] bundleDataSources;
|
||||
|
||||
public static UpdateCertificate parseAndVerifyCertificate(byte[] certData) throws IOException, CertificateInvalidException {
|
||||
InputStream is = new ByteArrayInputStream(certData);
|
||||
if(is.read() != 'E' || is.read() != 'A' || is.read() != 'G' || is.read() != 'S' || is.read() != 'I' || is.read() != 'G') {
|
||||
throw new IOException("Data is not a certificate!");
|
||||
}
|
||||
|
||||
int vers = is.read() << 8;
|
||||
vers |= is.read();
|
||||
if(vers != 1) {
|
||||
throw new IOException("Invalid certificate version: " + vers);
|
||||
}
|
||||
|
||||
byte[] rsa2048sum = new byte[256];
|
||||
is.read(rsa2048sum);
|
||||
|
||||
byte[] rsa2048sumDec = (new BigInteger(rsa2048sum)).modPow(new BigInteger("65537"), EaglercraftVersion.updateSignatureModulus).toByteArray();
|
||||
|
||||
if(rsa2048sumDec.length > 256) {
|
||||
throw new IOException("Invalid decrypted hash length: " + rsa2048sum.length);
|
||||
}
|
||||
|
||||
if(rsa2048sumDec.length < 256) {
|
||||
byte[] tmp = rsa2048sumDec;
|
||||
rsa2048sumDec = new byte[256];
|
||||
System.arraycopy(tmp, 0, rsa2048sumDec, 256 - tmp.length, tmp.length);
|
||||
}
|
||||
|
||||
int payloadLen = is.read() << 8;
|
||||
payloadLen |= is.read();
|
||||
|
||||
byte[] signaturePayload = new byte[payloadLen];
|
||||
is.read(signaturePayload);
|
||||
|
||||
SHA256Digest sha256 = new SHA256Digest();
|
||||
sha256.update(new byte[] { (byte) 170, (byte) 191, (byte) 203, (byte) 188, (byte) 47, (byte) 37, (byte) 17,
|
||||
(byte) 187, (byte) 169, (byte) 225, (byte) 247, (byte) 193, (byte) 100, (byte) 101, (byte) 233,
|
||||
(byte) 106, (byte) 80, (byte) 204, (byte) 192, (byte) 140, (byte) 19, (byte) 18, (byte) 165, (byte) 252,
|
||||
(byte) 138, (byte) 187, (byte) 229, (byte) 148, (byte) 118, (byte) 208, (byte) 179, (byte) 233 }, 0, 32);
|
||||
sha256.update(signaturePayload, 0, signaturePayload.length);
|
||||
byte[] hash2048 = new byte[256];
|
||||
sha256.doFinal(hash2048, 0);
|
||||
sha256.reset();
|
||||
sha256.update(new byte[] { (byte) 95, (byte) 222, (byte) 208, (byte) 153, (byte) 171, (byte) 133, (byte) 7,
|
||||
(byte) 88, (byte) 111, (byte) 87, (byte) 37, (byte) 104, (byte) 98, (byte) 115, (byte) 185, (byte) 153,
|
||||
(byte) 206, (byte) 188, (byte) 143, (byte) 18, (byte) 247, (byte) 28, (byte) 130, (byte) 87, (byte) 56,
|
||||
(byte) 223, (byte) 45, (byte) 192, (byte) 108, (byte) 166, (byte) 254, (byte) 19 }, 0, 32);
|
||||
sha256.update(signaturePayload, 0, signaturePayload.length);
|
||||
sha256.doFinal(hash2048, 32);
|
||||
sha256.reset();
|
||||
sha256.update(new byte[] { (byte) 101, (byte) 245, (byte) 91, (byte) 125, (byte) 50, (byte) 79, (byte) 71,
|
||||
(byte) 52, (byte) 244, (byte) 249, (byte) 84, (byte) 5, (byte) 139, (byte) 21, (byte) 13, (byte) 200,
|
||||
(byte) 75, (byte) 0, (byte) 103, (byte) 1, (byte) 14, (byte) 159, (byte) 199, (byte) 194, (byte) 56,
|
||||
(byte) 161, (byte) 63, (byte) 248, (byte) 90, (byte) 134, (byte) 96, (byte) 160 }, 0, 32);
|
||||
sha256.update(signaturePayload, 0, signaturePayload.length);
|
||||
sha256.doFinal(hash2048, 64);
|
||||
sha256.reset();
|
||||
sha256.update(new byte[] { (byte) 84, (byte) 208, (byte) 74, (byte) 114, (byte) 251, (byte) 86, (byte) 195,
|
||||
(byte) 222, (byte) 90, (byte) 18, (byte) 194, (byte) 226, (byte) 20, (byte) 56, (byte) 191, (byte) 235,
|
||||
(byte) 187, (byte) 93, (byte) 18, (byte) 122, (byte) 161, (byte) 40, (byte) 160, (byte) 88, (byte) 151,
|
||||
(byte) 88, (byte) 215, (byte) 216, (byte) 253, (byte) 235, (byte) 7, (byte) 60 }, 0, 32);
|
||||
sha256.update(signaturePayload, 0, signaturePayload.length);
|
||||
sha256.doFinal(hash2048, 96);
|
||||
|
||||
hash2048[0] = (byte)((signaturePayload.length >> 8) & 0xFF);
|
||||
hash2048[1] = (byte)(signaturePayload.length & 0xFF);
|
||||
|
||||
if(!Arrays.equals(hash2048, rsa2048sumDec)) {
|
||||
throw new CertificateInvalidException("SHA256 checksum of signature payload is invalid!");
|
||||
}
|
||||
|
||||
return new UpdateCertificate(certData, EaglerZLIB.newGZIPInputStream(new ByteArrayInputStream(signaturePayload)), vers);
|
||||
}
|
||||
|
||||
private UpdateCertificate(byte[] certData, InputStream is, int sigVers) throws IOException {
|
||||
this.rawCertData = certData;
|
||||
this.sigVersion = sigVers;
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
this.sigTimestamp = dis.readLong();
|
||||
this.bundleDataLength = dis.readInt();
|
||||
this.bundleDataHash = new byte[32];
|
||||
dis.read(bundleDataHash);
|
||||
this.bundlePackageName = dis.readUTF();
|
||||
this.bundleDisplayName = dis.readUTF();
|
||||
this.bundleAuthorName = dis.readUTF();
|
||||
this.bundleVersionInteger = dis.readInt();
|
||||
this.bundleDisplayVersion = dis.readUTF();
|
||||
this.bundleVersionComment = dis.readUTF();
|
||||
dis.skip(dis.read());
|
||||
int sourceCount = dis.readInt();
|
||||
this.bundleDataSources = new DLSource[sourceCount];
|
||||
for(int i = 0; i < sourceCount; ++i) {
|
||||
dis.skip(4);
|
||||
bundleDataSources[i] = new DLSource(dis.readUTF(), dis.readUTF());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBundleDataValid(byte[] bundleData) {
|
||||
if(bundleData.length != bundleDataLength) {
|
||||
return false;
|
||||
}
|
||||
SHA256Digest sha256 = new SHA256Digest();
|
||||
sha256.update(bundleData, 0, bundleData.length);
|
||||
byte[] out = new byte[32];
|
||||
sha256.doFinal(out, 0);
|
||||
return Arrays.equals(out, bundleDataHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(bundleDataHash);
|
||||
result = prime * result + Arrays.hashCode(bundleDataSources);
|
||||
result = prime * result
|
||||
+ Objects.hash(bundleAuthorName, bundleDataLength, bundleDisplayName, bundleDisplayVersion,
|
||||
bundlePackageName, bundleVersionComment, bundleVersionInteger, sigTimestamp, sigVersion);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (!(obj instanceof UpdateCertificate))
|
||||
return false;
|
||||
UpdateCertificate other = (UpdateCertificate) obj;
|
||||
return Objects.equals(bundleAuthorName, other.bundleAuthorName)
|
||||
&& Arrays.equals(bundleDataHash, other.bundleDataHash) && bundleDataLength == other.bundleDataLength
|
||||
&& Arrays.equals(bundleDataSources, other.bundleDataSources)
|
||||
&& Objects.equals(bundleDisplayName, other.bundleDisplayName)
|
||||
&& Objects.equals(bundleDisplayVersion, other.bundleDisplayVersion)
|
||||
&& Objects.equals(bundlePackageName, other.bundlePackageName)
|
||||
&& Objects.equals(bundleVersionComment, other.bundleVersionComment)
|
||||
&& bundleVersionInteger == other.bundleVersionInteger && sigTimestamp == other.sigTimestamp
|
||||
&& sigVersion == other.sigVersion;
|
||||
}
|
||||
|
||||
public ListMultimap<String,String> getSourceMultimap() {
|
||||
ListMultimap<String,String> ret = ListMultimapBuilder.hashKeys().arrayListValues().build();
|
||||
for(int i = 0; i < bundleDataSources.length; ++i) {
|
||||
ret.put(bundleDataSources[i].type, bundleDataSources[i].addr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
/**
|
||||
* 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 UpdateProgressStruct {
|
||||
|
||||
public boolean isBusy = false;
|
||||
public String statusString1 = null;
|
||||
public String statusString2 = null;
|
||||
public String statusString3 = null;
|
||||
public float progressBar = -1.0f;
|
||||
|
||||
public void clear() {
|
||||
isBusy = false;
|
||||
statusString1 = null;
|
||||
statusString2 = null;
|
||||
statusString3 = null;
|
||||
progressBar = -1.0f;
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.update;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformUpdateSvc;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
|
||||
/**
|
||||
* 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 UpdateService {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger("UpdateService");
|
||||
|
||||
private static UpdateCertificate myUpdateCert = null;
|
||||
private static boolean isBundleDataValid = false;
|
||||
|
||||
private static UpdateCertificate latestUpdateFound = null;
|
||||
private static final Set<UpdateCertificate> availableUpdates = new HashSet();
|
||||
private static final Set<UpdateCertificate> dismissedUpdates = new HashSet();
|
||||
|
||||
public static boolean supported() {
|
||||
return EaglercraftVersion.enableUpdateService && EagRuntime.getConfiguration().allowUpdateSvc() && PlatformUpdateSvc.supported();
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if(!supported()) {
|
||||
logger.info("Update service is not supported on this client");
|
||||
return;
|
||||
}
|
||||
PlatformUpdateSvc.initialize();
|
||||
if(PlatformUpdateSvc.getClientSignatureData() != null) {
|
||||
logger.info("Testing client update certificate...");
|
||||
try {
|
||||
myUpdateCert = UpdateCertificate.parseAndVerifyCertificate(PlatformUpdateSvc.getClientSignatureData());
|
||||
if(!EaglercraftVersion.updateBundlePackageName.equalsIgnoreCase(myUpdateCert.bundlePackageName)) {
|
||||
throw new CertificateInvalidException("Certificate package name does not match current client package name!");
|
||||
}
|
||||
if(EaglercraftVersion.updateBundlePackageVersionInt != myUpdateCert.bundleVersionInteger) {
|
||||
throw new CertificateInvalidException("Certificate client version does not match current client version!");
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
myUpdateCert = null;
|
||||
logger.error("Client update certificate is invalid!");
|
||||
logger.error(t);
|
||||
}
|
||||
if(myUpdateCert != null && PlatformUpdateSvc.getClientBundleData() != null) {
|
||||
isBundleDataValid = myUpdateCert.isBundleDataValid(PlatformUpdateSvc.getClientBundleData());
|
||||
if(!isBundleDataValid) {
|
||||
logger.error("Client checksum does not match certificate! \"Download Offline\" button will download a fresh client");
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] latestUpdate = PlatformApplication.getLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey);
|
||||
if(latestUpdate != null) {
|
||||
addCertificateToSet(latestUpdate, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getClientSignatureData() {
|
||||
if(myUpdateCert != null) {
|
||||
return PlatformUpdateSvc.getClientSignatureData();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] getClientBundleData() {
|
||||
if(isBundleDataValid) {
|
||||
return PlatformUpdateSvc.getClientBundleData();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UpdateCertificate getClientCertificate() {
|
||||
return myUpdateCert;
|
||||
}
|
||||
|
||||
public static void addCertificateToSet(byte[] certificateData) {
|
||||
addCertificateToSet(certificateData, true);
|
||||
}
|
||||
|
||||
private static void addCertificateToSet(byte[] certificateData, boolean saveLatest) {
|
||||
if (EagRuntime.getConfiguration().allowUpdateDL()) {
|
||||
synchronized(availableUpdates) {
|
||||
try {
|
||||
UpdateCertificate cert = UpdateCertificate.parseAndVerifyCertificate(certificateData);
|
||||
if (EaglercraftVersion.updateBundlePackageName.equalsIgnoreCase(cert.bundlePackageName)) {
|
||||
if (myUpdateCert == null || !Arrays.equals(cert.bundleDataHash, myUpdateCert.bundleDataHash)) {
|
||||
if(availableUpdates.add(cert)) {
|
||||
logger.info("Found new update: {} - {}", cert.bundleDisplayName, cert.bundleDisplayVersion);
|
||||
if (cert.bundleVersionInteger > EaglercraftVersion.updateBundlePackageVersionInt
|
||||
&& (latestUpdateFound == null
|
||||
|| cert.bundleVersionInteger > latestUpdateFound.bundleVersionInteger
|
||||
|| (cert.bundleVersionInteger == latestUpdateFound.bundleVersionInteger
|
||||
&& cert.sigTimestamp > latestUpdateFound.sigTimestamp))
|
||||
&& !dismissedUpdates.contains(cert)) {
|
||||
latestUpdateFound = cert;
|
||||
if (saveLatest) {
|
||||
PlatformApplication.setLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey,
|
||||
certificateData);
|
||||
}
|
||||
}
|
||||
}else if(EagRuntime.getConfiguration().isLogInvalidCerts()) {
|
||||
logger.info("Ignoring already indexed update: {} - {}", cert.bundleDisplayName, cert.bundleDisplayVersion);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (EagRuntime.getConfiguration().isLogInvalidCerts()) {
|
||||
logger.warn("Ignoring 3rd party update certificate: {} - {} ({})", cert.bundleDisplayName,
|
||||
cert.bundleDisplayVersion, cert.bundlePackageName);
|
||||
logger.warn("Note: the certificate still had a valid signature (leaked private key?!)");
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (EagRuntime.getConfiguration().isLogInvalidCerts()) {
|
||||
logger.error("Invalid update certificate recieved! The certificate may be from a different client");
|
||||
logger.error(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void startClientUpdateFrom(UpdateCertificate clientUpdate) {
|
||||
PlatformUpdateSvc.startClientUpdateFrom(clientUpdate);
|
||||
}
|
||||
|
||||
public static UpdateProgressStruct getUpdatingStatus() {
|
||||
return PlatformUpdateSvc.getUpdatingStatus();
|
||||
}
|
||||
|
||||
public static UpdateCertificate getLatestUpdateFound() {
|
||||
return latestUpdateFound;
|
||||
}
|
||||
|
||||
public static Collection<UpdateCertificate> getAvailableUpdates() {
|
||||
return availableUpdates;
|
||||
}
|
||||
|
||||
public static void dismiss(UpdateCertificate cert) {
|
||||
if(latestUpdateFound == cert) {
|
||||
latestUpdateFound = null;
|
||||
}
|
||||
dismissedUpdates.add(cert);
|
||||
}
|
||||
|
||||
public static void quine() {
|
||||
if(myUpdateCert != null) {
|
||||
byte[] data = getClientBundleData();
|
||||
if(data != null) {
|
||||
logger.info("Generating signed offline download...");
|
||||
PlatformUpdateSvc.quine(myUpdateCert, data);
|
||||
}else {
|
||||
logger.error("Client checksum does not match certificate! Downloading a fresh client...");
|
||||
PlatformUpdateSvc.startClientUpdateFrom(myUpdateCert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldDisableDownloadButton() {
|
||||
return EagRuntime.getConfiguration().getDownloadOfflineButtonLink() == null && (myUpdateCert == null
|
||||
|| (getClientBundleData() == null && PlatformUpdateSvc.getUpdatingStatus().isBusy));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user