Add a java version detection and nag system for users on Java 7 or below.

Added detection of mods that rely on Java 8 and a graceful error screen.
The nag screen will be shown once a day. It can be disabled by editing the forge.cfg.
However it is HIGHLY recomended that user update to Java 8.
This commit is contained in:
LexManos committed 2016-05-03 18:26:52 -07:00
1 parent 4d27393c91
commit d8249b7886
15 files changed
+416 -18

No files matched your search

@@ -8,6 +8,7 @@ import static org.lwjgl.opengl.GL20.*;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Collections;
import java.util.Map;
import javax.vecmath.Matrix3f;
@@ -89,8 +90,12 @@ import net.minecraftforge.common.model.IModelPart;
import net.minecraftforge.common.model.ITransformation;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.client.GuiJava8Error;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Java8VersionException;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.BufferUtils;
@@ -266,7 +271,8 @@ public class ForgeHooksClient
//RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance);
}
public static void renderMainMenu(GuiMainMenu gui, FontRenderer font, int width, int height)
private static int updatescrollcounter = 0;
public static String renderMainMenu(GuiMainMenu gui, FontRenderer font, int width, int height, String splashText)
{
Status status = ForgeVersion.getStatus();
if (status == BETA || status == BETA_OUTDATED)
@@ -278,6 +284,17 @@ public class ForgeHooksClient
gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (1 * (font.FONT_HEIGHT + 1)), -1);
}
if (!Loader.instance().java8)
{
String line = I18n.format("fml.messages.java8warning.1", TextFormatting.RED, TextFormatting.RESET);
gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (8 * (font.FONT_HEIGHT + 1)), -1);
line = I18n.format("fml.messages.java8warning.2");
gui.drawString(font, line, (width - font.getStringWidth(line)) / 2, 4 + (9 * (font.FONT_HEIGHT + 1)), -1);
splashText = updatescrollcounter < 50 ? "UPDATE!" : "JAVA!";
updatescrollcounter+=1;
updatescrollcounter%=100;
}
String line = null;
switch(status)
{
@@ -294,6 +311,24 @@ public class ForgeHooksClient
// if we have a line, render it in the bottom right, above Mojang's copyright line
gui.drawString(font, line, width - font.getStringWidth(line) - 2, height - (2 * (font.FONT_HEIGHT + 1)), -1);
}
return splashText;
}
public static void mainMenuMouseClick(int mouseX, int mouseY, int mouseButton, FontRenderer font, int width)
{
if (!Loader.instance().java8)
{
if (mouseY >= (4 + (8 * 10)) && mouseY < (4 + (10 * 10)))
{
int w = font.getStringWidth(I18n.format("fml.messages.java8warning.1", TextFormatting.RED, TextFormatting.RESET));
w = Math.max(w, font.getStringWidth(I18n.format("fml.messages.java8warning.2")));
if (mouseX >= ((width - w) / 2) && mouseX <= ((width + w) / 2))
{
FMLClientHandler.instance().showGuiScreen(new GuiJava8Error(new Java8VersionException(Collections.<ModContainer>emptyList())));
}
}
}
}
public static ISound playSound(SoundManager manager, ISound sound)
@@ -14,6 +14,7 @@ import java.net.URL;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -74,6 +75,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
public static boolean defaultHasSpawnFuzz = true;
public static boolean forgeLightPipelineEnabled = true;
public static boolean replaceVanillaBucketModel = true;
public static long java8Reminder = 0;
private static Configuration config;
private static ForgeModContainer INSTANCE;
@@ -259,6 +261,11 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
replaceVanillaBucketModel = prop.getBoolean(Boolean.FALSE);
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_CLIENT, "java8Reminder", java8Reminder,
"The timestamp of the last reminder to update to Java 8 in number of milliseconds since January 1, 1970, 00:00:00 GMT. Nag will show only once every 24 hours. To disable it set this to some really high number.");
java8Reminder = prop.getLong(java8Reminder);
propOrder.add(prop.getName());
config.setCategoryPropertyOrder(CATEGORY_CLIENT, propOrder);
if (config.hasChanged())
@@ -267,6 +274,13 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
}
}
public static void updateNag()
{
Property prop = config.get(Configuration.CATEGORY_CLIENT, "java8Reminder", java8Reminder);
prop.set((new Date()).getTime());
config.save();
}
/**
* By subscribing to the OnConfigChangedEvent we are able to execute code when our config screens are closed.
* This implementation uses the optional configID string to handle multiple Configurations using one event handler.
@@ -680,14 +680,7 @@ public class Property
*/
public int getInt()
{
try
{
return Integer.parseInt(value);
}
catch (NumberFormatException e)
{
return Integer.parseInt(defaultValue);
}
return getInt(Integer.parseInt(defaultValue));
}
/**
@@ -727,6 +720,54 @@ public class Property
}
}
/**
* Returns the value in this property as a long,
* if the value is not a valid long, it will return the initially provided default.
*
* @return The value
*/
public long getLong()
{
return getLong(Long.parseLong(defaultValue));
}
/**
* Returns the value in this property as a long,
* if the value is not a valid long, it will return the
* provided default.
*
* @param _default The default to provide if the current value is not a validlong
* @return The value
*/
public long getLong(long _default)
{
try
{
return Long.parseLong(value);
}
catch (NumberFormatException e)
{
return _default;
}
}
/**
* Checks if the current value stored in this property can be converted to a long.
* @return True if the type of the Property is an Long
*/
public boolean isLongValue()
{
try
{
Long.parseLong(value);
return true;
}
catch (NumberFormatException e)
{
return false;
}
}
/**
* Returns the value in this property as a boolean,
* if the value is not a valid boolean, it will return the
@@ -1148,6 +1189,7 @@ public class Property
this.setValues(values);
}
public void set(int value){ set(Integer.toString(value)); }
public void set(long value){ set(Long.toString(value)); }
public void set(boolean value){ set(Boolean.toString(value)); }
public void set(double value){ set(Double.toString(value)); }
}
@@ -19,6 +19,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -69,6 +70,7 @@ import net.minecraft.util.StringUtils;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.storage.SaveFormatComparator;
import net.minecraft.world.storage.SaveFormatOld;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.DummyModContainer;
import net.minecraftforge.fml.common.DuplicateModsFoundException;
@@ -76,6 +78,7 @@ import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.FMLContainerHolder;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.IFMLSidedHandler;
import net.minecraftforge.fml.common.Java8VersionException;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.LoaderException;
import net.minecraftforge.fml.common.MetadataCollection;
@@ -158,6 +161,8 @@ public class FMLClientHandler implements IFMLSidedHandler
private boolean loading = true;
private Java8VersionException j8onlymods;
private WrongMinecraftVersionException wrongMC;
private CustomModLoadingErrorDisplayException customError;
@@ -212,6 +217,10 @@ public class FMLClientHandler implements IFMLSidedHandler
{
dupesFound = dupes;
}
catch (Java8VersionException j8mods)
{
j8onlymods = j8mods;
}
catch (MissingModsException missing)
{
modsMissing = missing;
@@ -297,7 +306,7 @@ public class FMLClientHandler implements IFMLSidedHandler
*/
public void finishMinecraftLoading()
{
if (modsMissing != null || wrongMC != null || customError!=null || dupesFound!=null || modSorting!=null)
if (modsMissing != null || wrongMC != null || customError!=null || dupesFound!=null || modSorting!=null || j8onlymods!=null)
{
SplashProgress.finish();
return;
@@ -344,6 +353,8 @@ public class FMLClientHandler implements IFMLSidedHandler
}
loading = false;
client.gameSettings.loadOptions(); //Reload options to load any mod added keybindings.
Loader.instance().loadingComplete();
SplashProgress.finish();
}
public void extendModList()
@@ -383,6 +394,10 @@ public class FMLClientHandler implements IFMLSidedHandler
{
showGuiScreen(new GuiWrongMinecraft(wrongMC));
}
else if (j8onlymods != null)
{
showGuiScreen(new GuiJava8Error(j8onlymods));
}
else if (modsMissing != null)
{
showGuiScreen(new GuiModsMissing(modsMissing));
@@ -401,10 +416,16 @@ public class FMLClientHandler implements IFMLSidedHandler
}
else
{
Loader.instance().loadingComplete();
SplashProgress.finish();
logMissingTextureErrors();
if (!Loader.instance().java8)
{
if ((new Date()).getTime() >= ForgeModContainer.java8Reminder + (1000 * 60 * 60 * 24))
{
showGuiScreen(new GuiJava8Error(new Java8VersionException(Collections.<ModContainer>emptyList())));
ForgeModContainer.updateNag();
}
}
}
logMissingTextureErrors();
}
/**
* Get the server instance
@@ -0,0 +1,140 @@
/*
* Forge Mod Loader
* Copyright (c) 2012-2013 cpw.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* cpw - implementation
*/
package net.minecraftforge.fml.client;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Java8VersionException;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import org.apache.logging.log4j.Level;
import com.google.common.collect.Lists;
import java.awt.*;
import java.io.IOException;
import java.net.URI;
import java.util.List;
public class GuiJava8Error extends GuiErrorScreen
{
private Java8VersionException java8VersionException;
public GuiJava8Error(Java8VersionException java8VersionException)
{
super(null,null);
this.java8VersionException = java8VersionException;
}
@Override
public void initGui()
{
this.buttonList.clear();
this.buttonList.add(new GuiButton(1, 50, this.height - 38, this.width/2 -55, 20, I18n.format("fml.button.visitjavadownloads")));
if (java8VersionException.getMods().isEmpty())
{
this.buttonList.add(new GuiButton(3, this.width / 2 + 5, this.height - 38, this.width / 2 - 55, 20, I18n.format("fml.button.continue")));
}
else
{
this.buttonList.add(new GuiButton(2, this.width / 2 + 5, this.height - 38, this.width / 2 - 55, 20, I18n.format("menu.quit")));
}
}
@Override
protected void actionPerformed(GuiButton button) throws IOException
{
if (button.id == 1)
{
try
{
Desktop.getDesktop().browse(new URI("http://www.oracle.com/technetwork/java/javase/downloads/index.html"));
}
catch (Exception e)
{
FMLLog.log(Level.ERROR, e, "Problem launching browser");
}
}
else if (button.id == 2)
{
FMLCommonHandler.instance().exitJava(1,true);
}
else if (button.id == 3)
{
FMLClientHandler.instance().showGuiScreen(null);
}
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
int offset = 25;
if (!java8VersionException.getMods().isEmpty())
{
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.java8problem", TextFormatting.RED, TextFormatting.BOLD, TextFormatting.RESET), this.width / 2, offset, 0xFFFFFF);
}
else
{
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.java8recommended", TextFormatting.RED, TextFormatting.BOLD, TextFormatting.RESET), this.width / 2, offset, 0xFFFFFF);
}
offset+=15;
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.javaversion", System.getProperty("java.version").split("\\.")[1], System.getProperty("java.version")), this.width / 2, offset, 0xFFFFFF);
offset += 10;
if (!java8VersionException.getMods().isEmpty())
{
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.upgradejavaorremove", TextFormatting.RED,TextFormatting.BOLD, TextFormatting.RESET), this.width / 2, offset, 0xFFFFFF);
offset += 15;
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.modslistedbelow", I18n.format("fml.messages.requirejava8")), this.width / 2, offset, 0xFFFFFF);
offset += 10;
this.drawCenteredString(this.fontRendererObj, I18n.format("fml.messages.countbadandgood", java8VersionException.getMods().size(), Loader.instance().getActiveModList().size()), this.width / 2, offset, 0xFFFFFF);
offset += 5;
for (ModContainer mc : java8VersionException.getMods())
{
offset += 10;
this.drawCenteredString(this.fontRendererObj, String.format("%s (%s)", mc.getName(), mc.getModId()), this.width / 2, offset, 0xEEEEEE);
}
}
else
{
String text = I18n.format("fml.messages.upgradejava", TextFormatting.RED,TextFormatting.BOLD, TextFormatting.RESET).replaceAll("\\\\n", "\n");
List<String> lines = Lists.newArrayList();
for (String line : text.split("\n"))
{
lines.addAll(this.fontRendererObj.listFormattedStringToWidth(line, this.width - this.fontRendererObj.FONT_HEIGHT * 4));
}
int maxWidth = 0;
for (String line : lines)
{
maxWidth = Math.max(maxWidth, this.fontRendererObj.getStringWidth(line));
}
for (String line : lines)
{
this.drawString(this.fontRendererObj, line, (this.width - maxWidth) / 2, offset, 0xFFFFFF);
offset += this.fontRendererObj.FONT_HEIGHT + 2;
}
offset += 15;
}
// super.super
for (int i = 0; i < this.buttonList.size(); ++i)
{
((GuiButton)this.buttonList.get(i)).drawButton(this.mc, mouseX, mouseY);
}
}
}
@@ -32,6 +32,7 @@ public class DummyModContainer implements ModContainer
private ModMetadata md;
private ArtifactVersion processedVersion;
private String label;
private int classVersion;
public DummyModContainer(ModMetadata md)
{
@@ -213,4 +214,16 @@ public class DummyModContainer implements ModContainer
{
return null;
}
@Override
public void setClassVersion(int classVersion)
{
this.classVersion = classVersion;
}
@Override
public int getClassVersion()
{
return this.classVersion;
}
}
@@ -90,6 +90,7 @@ public class FMLModContainer implements ModContainer
private Map<String, String> customModProperties;
private ModCandidate candidate;
private URL updateJSONUrl;
private int classVersion;
public FMLModContainer(String className, ModCandidate container, Map<String, Object> modDescriptor)
{
@@ -699,4 +700,16 @@ public class FMLModContainer implements ModContainer
{
return updateJSONUrl;
}
@Override
public void setClassVersion(int classVersion)
{
this.classVersion = classVersion;
}
@Override
public int getClassVersion()
{
return this.classVersion;
}
}
@@ -214,4 +214,16 @@ public class InjectedModContainer implements ModContainer
{
return wrappedContainer.getUpdateUrl();
}
@Override
public void setClassVersion(int classVersion)
{
wrappedContainer.setClassVersion(classVersion);
}
@Override
public int getClassVersion()
{
return wrappedContainer.getClassVersion();
}
}
@@ -0,0 +1,43 @@
/*
* Forge Mod Loader
* Copyright (c) 2012-2013 cpw.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* cpw - implementation
*/
package net.minecraftforge.fml.common;
import java.util.List;
public class Java8VersionException extends EnhancedRuntimeException
{
private static final long serialVersionUID = 1L;
private final List<ModContainer> mods;
public Java8VersionException(List<ModContainer> mods)
{
super("Mods require Java 8");
this.mods = mods;
}
@Override
protected void printStackTrace(WrappedPrintStream stream)
{
stream.println("Mods requiring Java 8:");
for (ModContainer mc : mods)
{
stream.println(String.format("\t%s : %s", mc.getName(), mc.getModId()));
}
stream.println("");
}
public List<ModContainer> getMods()
{
return mods;
}
}
@@ -161,6 +161,7 @@ public class Loader
private File forcedModFile;
private ModDiscoverer discoverer;
private ProgressBar progressBar;
public final boolean java8;
public static Loader instance()
{
@@ -187,6 +188,14 @@ public class Loader
private Loader()
{
String[] ver = System.getProperty("java.version").split("\\.");
int major = Integer.parseInt(ver[1]);
java8 = major > 7;
if (!java8)
{
FMLLog.severe("The game is not running with Java 8. Forge recommends Java 8 for maximum compatibility with mods");
}
modClassLoader = new ModClassLoader(getClass().getClassLoader());
if (!mccversion.equals(MC_VERSION))
{
@@ -489,6 +498,7 @@ public class Loader
ModAPIManager.INSTANCE.manageAPI(modClassLoader, discoverer);
disableRequestedMods();
modController.distributeStateMessage(FMLLoadEvent.class);
checkJavaCompatibility();
sortModList();
ModAPIManager.INSTANCE.cleanupAPIContainers(modController.getActiveModList());
ModAPIManager.INSTANCE.cleanupAPIContainers(mods);
@@ -543,6 +553,24 @@ public class Loader
modController.transition(LoaderState.PREINITIALIZATION, false);
}
private void checkJavaCompatibility()
{
if (java8) return;
List<ModContainer> j8mods = Lists.newArrayList();
for (ModContainer mc : getActiveModList())
{
if (mc.getClassVersion() >= 52)
{
j8mods.add(mc);
}
}
if (!j8mods.isEmpty())
{
throw new Java8VersionException(j8mods);
}
}
public void preinitializeMods()
{
if (!modController.isInState(LoaderState.PREINITIALIZATION))
@@ -152,4 +152,8 @@ public interface ModContainer
boolean shouldLoadInEnvironment();
URL getUpdateUrl();
void setClassVersion(int classVersion);
int getClassVersion();
}
@@ -35,6 +35,7 @@ public class ASMDataTable
private String annotationName;
private String className;
private String objectName;
private int classVersion;
private Map<String,Object> annotationInfo;
public ASMData(ModCandidate candidate, String annotationName, String className, String objectName, Map<String,Object> info)
{
@@ -83,6 +83,7 @@ public class JarDiscoverer implements ITypeDiscoverer
table.addContainer(container);
foundMods.add(container);
container.bindMetadata(mc);
container.setClassVersion(modParser.getClassVersion());
}
}
}