From 36e0c3e456a08e8b619e37d0e8f573d2c54b3bda Mon Sep 17 00:00:00 2001 From: chylex <info@chylex.com> Date: Sun, 10 Jan 2016 21:07:34 +0100 Subject: [PATCH] Rewrite and update to 2.0 (fix headless env, add futureproofing, cleanup code) --- build.gradle | 2 +- .../java/chylex/javacheck/Java7Checker.java | 4 +- .../java/chylex/javacheck/Java8Checker.java | 4 +- .../chylex/javacheck/JavaVersionChecker.java | 48 +++++++ .../javacheck/report/JavaCheckerReporter.java | 132 ++++++------------ .../report/OutdatedJavaException.java | 2 +- .../javacheck/util/ForgeCompatibility.java | 100 +++++++++++++ .../javacheck/test/JavaCheckerTestMod.java | 4 +- 8 files changed, 198 insertions(+), 98 deletions(-) create mode 100644 src/main/java/chylex/javacheck/JavaVersionChecker.java create mode 100644 src/main/java/chylex/javacheck/util/ForgeCompatibility.java diff --git a/build.gradle b/build.gradle index c3deaac..ba9dcce 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ group = "chylex.javacheck" ext.buildnumber = 0 project.buildnumber = System.getenv('BUILD_NUMBER') == null ? "CUSTOM" : System.getenv('BUILD_NUMBER') -version = project.hasProperty("mavendir") ? "v1.3-b"+project.buildnumber : "MC-UNIVERSAL v1.3" +version = project.hasProperty("mavendir") ? "v2.0-b"+project.buildnumber : "MC-UNIVERSAL v2.0" String archiveSuffix = (project.hasProperty("mavendir") ? "-" : " ")+version+".jar" minecraft{ diff --git a/src/main/java/chylex/javacheck/Java7Checker.java b/src/main/java/chylex/javacheck/Java7Checker.java index d30ad30..e77e4ea 100644 --- a/src/main/java/chylex/javacheck/Java7Checker.java +++ b/src/main/java/chylex/javacheck/Java7Checker.java @@ -6,10 +6,10 @@ import net.minecraft.launchwrapper.LaunchClassLoader; import org.apache.commons.lang3.JavaVersion; import chylex.javacheck.report.JavaCheckerReporter; -public class Java7Checker implements ITweaker{ +public final class Java7Checker implements ITweaker{ @Override public void injectIntoClassLoader(LaunchClassLoader classLoader){ - JavaCheckerReporter.run(JavaVersion.JAVA_1_7); + JavaVersionChecker.run(JavaVersion.JAVA_1_7); } @Override diff --git a/src/main/java/chylex/javacheck/Java8Checker.java b/src/main/java/chylex/javacheck/Java8Checker.java index 7b14c3a..16808f8 100644 --- a/src/main/java/chylex/javacheck/Java8Checker.java +++ b/src/main/java/chylex/javacheck/Java8Checker.java @@ -6,10 +6,10 @@ import net.minecraft.launchwrapper.LaunchClassLoader; import org.apache.commons.lang3.JavaVersion; import chylex.javacheck.report.JavaCheckerReporter; -public class Java8Checker implements ITweaker{ +public final class Java8Checker implements ITweaker{ @Override public void injectIntoClassLoader(LaunchClassLoader classLoader){ - JavaCheckerReporter.run(JavaVersion.JAVA_1_8); + JavaVersionChecker.run(JavaVersion.JAVA_1_8); } @Override diff --git a/src/main/java/chylex/javacheck/JavaVersionChecker.java b/src/main/java/chylex/javacheck/JavaVersionChecker.java new file mode 100644 index 0000000..db013f7 --- /dev/null +++ b/src/main/java/chylex/javacheck/JavaVersionChecker.java @@ -0,0 +1,48 @@ +package chylex.javacheck; +import org.apache.commons.lang3.JavaVersion; +import org.apache.commons.lang3.SystemUtils; +import chylex.javacheck.report.JavaCheckerReporter; +import chylex.javacheck.report.OutdatedJavaException; +import chylex.javacheck.util.ForgeCompatibility; + +public final class JavaVersionChecker{ + public static final String issueReportSite = "https://github.com/chylex/Java-Checker/issues"; + + public static void run(JavaVersion minVersion){ + try{ + unsafeRun(minVersion); + }catch(OutdatedJavaException me){ + throw me; + }catch(ShadingException up){ + throw up; + }catch(Throwable t){ + t.printStackTrace(); + System.out.println("Detected an unexpected error in Java Version Checker, ignoring since trying to run the game is more important."); + System.out.println("If you crashed and happen to see this, please report the error above to: "+issueReportSite); + } + } + + private static void unsafeRun(JavaVersion minVersion){ + if (minVersion == null || !SystemUtils.isJavaVersionAtLeast(minVersion)){ + if (minVersion == null)minVersion = JavaVersion.JAVA_1_8; // debugging purposes + + JavaCheckerReporter.reportOutdatedJava(minVersion); + throw new OutdatedJavaException(); + } + else{ + if (isShaded() && !ForgeCompatibility.tryResetModState()){ + throw new ShadingException(); + } + } + } + + private static boolean isShaded(){ + return !JavaCheckerReporter.class.getPackage().getName().equals("chylex.javacheck.report"); + } + + public static class ShadingException extends RuntimeException{ + public ShadingException(){ + super("An exception happened when updating the coremod list, the mod you are shading Java Checker in will not run without it. Please, report the stack traces above to "+issueReportSite); + } + } +} diff --git a/src/main/java/chylex/javacheck/report/JavaCheckerReporter.java b/src/main/java/chylex/javacheck/report/JavaCheckerReporter.java index 4aab9ef..abe6aaf 100644 --- a/src/main/java/chylex/javacheck/report/JavaCheckerReporter.java +++ b/src/main/java/chylex/javacheck/report/JavaCheckerReporter.java @@ -1,5 +1,6 @@ package chylex.javacheck.report; import java.awt.Desktop; +import java.awt.GraphicsEnvironment; import java.io.File; import java.util.List; import javax.swing.JEditorPane; @@ -10,106 +11,55 @@ import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.event.HyperlinkListener; import org.apache.commons.lang3.JavaVersion; import org.apache.commons.lang3.SystemUtils; +import chylex.javacheck.util.ForgeCompatibility; public final class JavaCheckerReporter{ - public static void run(JavaVersion minVersion){ - if (minVersion == null || !SystemUtils.isJavaVersionAtLeast(minVersion)){ - if (minVersion == null)minVersion = JavaVersion.JAVA_1_8; - - try{ - Class relaunchLog = findRelaunchLog(); - if (relaunchLog != null)relaunchLog.getMethod("severe",String.class,Object[].class).invoke(null,getConsoleReport(minVersion),new Object[0]); - }catch(Throwable t){} - - String style = "font-family:Dialog;font-size:12;font-weight:bold"; - JEditorPane pane = new JEditorPane("text/html","<html><body style='"+style+"'>"+getWindowReport(minVersion)+"</body></html>"); - pane.setBackground(new JLabel().getBackground()); - pane.setEditable(false); - - pane.addHyperlinkListener(new HyperlinkListener(){ - @Override - public void hyperlinkUpdate(HyperlinkEvent e){ - if (e.getEventType() == EventType.ACTIVATED){ - try{ - if (Desktop.isDesktopSupported())Desktop.getDesktop().browse(e.getURL().toURI()); - }catch(Exception ex){ - ex.printStackTrace(); - } + public static void reportOutdatedJava(JavaVersion minVersion){ + String consoleReport = getConsoleReport(minVersion); + + if (!ForgeCompatibility.tryLog(consoleReport)){ + System.out.println(consoleReport); + } + + if (!GraphicsEnvironment.isHeadless() && ForgeCompatibility.isClientSide()){ + displayErrorPopup("Outdated Java",getHtmlReport(minVersion)); + } + } + + private static void displayErrorPopup(String title, String contents){ + JEditorPane pane = new JEditorPane("text/html","<html><body style='font-family:Dialog;font-size:12;font-weight:bold'>"+contents+"</body></html>"); + pane.setBackground(new JLabel().getBackground()); + pane.setEditable(false); + + pane.addHyperlinkListener(new HyperlinkListener(){ + @Override + public void hyperlinkUpdate(HyperlinkEvent e){ + if (e.getEventType() == EventType.ACTIVATED){ + try{ + if (Desktop.isDesktopSupported())Desktop.getDesktop().browse(e.getURL().toURI()); + }catch(Exception ex){ + ex.printStackTrace(); } } - }); - - JOptionPane.showMessageDialog(null,pane,"Outdated Java",JOptionPane.ERROR_MESSAGE); - throw new OutdatedJavaException(); - } - else{ - try{ - Class cmm = findCoreModManager(); - - List coremods = getListOrNullSafe(cmm,"getLoadedCoremods"); - if (coremods == null)coremods = getListOrNullSafe(cmm,"getIgnoredMods"); - - List reparsed = getListOrNullSafe(cmm,"getReparseableCoremods"); - - String myFile = new File(JavaCheckerReporter.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getName(); - coremods.remove(myFile); - reparsed.add(myFile); - }catch(Throwable t){ - t.printStackTrace(); } - } + }); + + JOptionPane.showMessageDialog(null,pane,title,JOptionPane.ERROR_MESSAGE); } private static String getConsoleReport(JavaVersion minVersion){ - return new StringBuilder(242).append("\n") - .append("\n!! DO NOT REPORT !!\n\n") - .append("One of the mods requires Java "+minVersion.toString()+" or newer, you are using ").append(SystemUtils.JAVA_VERSION).append(".\n") - .append("Visit https://java.com/download/ for the latest version.\n") - .append("Please, uninstall the old version first to prevent further issues.") - .append("\n\n!! DO NOT REPORT !!\n") - .toString(); + return + "\n\n!! DO NOT REPORT !!\n\n"+ + "One of the mods requires Java "+minVersion+" or newer, you are using "+SystemUtils.JAVA_VERSION+".\n"+ + "Visit https://java.com/download/ for the latest version.\n"+ + "Please, uninstall the old version first to prevent further issues."+ + "\n\n!! DO NOT REPORT !!\n"; } - private static String getWindowReport(JavaVersion minVersion){ - return new StringBuilder(230) - .append("One of the mods requires Java "+minVersion.toString()+" or newer, you are using ").append(SystemUtils.JAVA_VERSION).append(".<br>") - .append("Visit <a href=\"https://java.com/download/\"><span style=\"color:blue\">https://java.com/download/</span></a> for the latest version.<br>") - .append("Please, uninstall the old version first to prevent further issues.") - .toString(); - } - - private static Class findRelaunchLog() throws Throwable{ - try{ - return Class.forName("cpw.mods.fml.relauncher.FMLRelaunchLog"); - }catch(ClassNotFoundException e){} - - try{ - return Class.forName("net.minecraftforge.fml.relauncher.FMLRelaunchLog"); - }catch(ClassNotFoundException e){} - - return null; - } - - private static Class findCoreModManager() throws Throwable{ - try{ - return Class.forName("cpw.mods.fml.relauncher.CoreModManager"); - }catch(ClassNotFoundException e){} - - try{ - return Class.forName("net.minecraftforge.fml.relauncher.CoreModManager"); - }catch(ClassNotFoundException e){} - - return null; - } - - private static List getListOrNullSafe(Class cls, String methodName){ - try{ - return (List)cls.getMethod(methodName).invoke(null); - }catch(NoSuchMethodException e){ - }catch(Throwable t){ - t.printStackTrace(); - } - - return null; + private static String getHtmlReport(JavaVersion minVersion){ + return + "One of the mods requires Java "+minVersion+" or newer, you are using "+SystemUtils.JAVA_VERSION+".<br>"+ + "Visit <a href=\"https://java.com/download/\"><span style=\"color:blue\">https://java.com/download/</span></a> for the latest version.<br>"+ + "Please, uninstall the old version first to prevent further issues."; } } diff --git a/src/main/java/chylex/javacheck/report/OutdatedJavaException.java b/src/main/java/chylex/javacheck/report/OutdatedJavaException.java index ddba08e..a31fb5f 100644 --- a/src/main/java/chylex/javacheck/report/OutdatedJavaException.java +++ b/src/main/java/chylex/javacheck/report/OutdatedJavaException.java @@ -2,7 +2,7 @@ package chylex.javacheck.report; import java.io.PrintStream; import java.io.PrintWriter; -public class OutdatedJavaException extends RuntimeException{ +public final class OutdatedJavaException extends RuntimeException{ public OutdatedJavaException(){ setStackTrace(new StackTraceElement[0]); } diff --git a/src/main/java/chylex/javacheck/util/ForgeCompatibility.java b/src/main/java/chylex/javacheck/util/ForgeCompatibility.java new file mode 100644 index 0000000..1b8fa77 --- /dev/null +++ b/src/main/java/chylex/javacheck/util/ForgeCompatibility.java @@ -0,0 +1,100 @@ +package chylex.javacheck.util; +import java.io.File; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.util.List; + +public final class ForgeCompatibility{ + public static boolean tryLog(String data){ + try{ + org.apache.logging.log4j.LogManager.getLogger("JavaChecker").log(org.apache.logging.log4j.Level.ERROR,data); + return true; + }catch(Throwable t){ // apache logging is not available + t.printStackTrace(); + } + + try{ + Class relaunchLog = findFMLClass("relaunch","FMLRelaunchLog"); + Method logSevere = findMethod(relaunchLog,"severe",String.class,Object[].class); + + if (logSevere != null){ + logSevere.invoke(null,data,new Object[0]); + return true; + } + }catch(Throwable t){ // relaunch log not available + t.printStackTrace(); + } + + return false; + } + + public static boolean tryResetModState(){ + try{ + Class cmm = findFMLClass("relauncher","CoreModManager"); + Method getCoremods = findMethodAlt(cmm,new String[]{ "getLoadedCoremods", "getIgnoredMods" }); + Method getReparsed = findMethodAlt(cmm,new String[]{ "getReparseableCoremods" }); + + if (getCoremods == null || getReparsed == null)return false; + + String myFile = getModFileName(); + + ((List)getCoremods.invoke(null)).remove(myFile); + ((List)getReparsed.invoke(null)).add(myFile); + return true; + }catch(Throwable t){ + t.printStackTrace(); + return false; + } + } + + public static boolean isClientSide(){ + try{ + return Class.forName("net.minecraft.client.Minecraft") != null; + }catch(ClassNotFoundException e){ + return false; + }catch(Throwable t){ + t.printStackTrace(); + return false; + } + } + + private static Class findFMLClass(String classPackage, String className){ + String searchTarget = classPackage.isEmpty() ? className : classPackage+"."+className; + + try{ + return Class.forName("cpw.mods.fml."+searchTarget); + }catch(ClassNotFoundException e){} + + try{ + return Class.forName("net.minecraftforge.fml."+searchTarget); + }catch(ClassNotFoundException e){} + + return null; + } + + private static Method findMethod(Class cls, String methodName, Class...params){ + if (cls == null)return null; + + try{ + return cls.getMethod(methodName,params); + }catch(NoSuchMethodException e){} + + return null; + } + + private static Method findMethodAlt(Class cls, String[] methodNames, Class...params){ + if (cls == null)return null; + + for(String methodName:methodNames){ + try{ + return cls.getMethod(methodName,params); + }catch(NoSuchMethodException e){} + } + + return null; + } + + private static String getModFileName() throws URISyntaxException{ + return new File(ForgeCompatibility.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getName(); + } +} diff --git a/src/main/test/chylex/javacheck/test/JavaCheckerTestMod.java b/src/main/test/chylex/javacheck/test/JavaCheckerTestMod.java index b15c7e8..c2d7b31 100644 --- a/src/main/test/chylex/javacheck/test/JavaCheckerTestMod.java +++ b/src/main/test/chylex/javacheck/test/JavaCheckerTestMod.java @@ -2,12 +2,14 @@ package chylex.javacheck.test; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import chylex.javacheck.JavaVersionChecker; import chylex.javacheck.report.JavaCheckerReporter; @Mod(modid = "JavaCheckerTestMod") public class JavaCheckerTestMod{ @EventHandler public void onPreInit(FMLPreInitializationEvent e){ - JavaCheckerReporter.run(null); + System.out.println(JavaCheckerReporter.class.getPackage().getName()); + JavaVersionChecker.run(null); } }