diff --git a/src/main/java/com/shanebeestudios/mcdeob/McDeob.java b/src/main/java/com/shanebeestudios/mcdeob/McDeob.java index b788b78..0f34028 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/McDeob.java +++ b/src/main/java/com/shanebeestudios/mcdeob/McDeob.java @@ -46,6 +46,10 @@ public static void main(String[] args) { .withRequiredArg() .ofType(String.class); parser.accepts("decompile", "Marks that we should decompile the deobfuscated source"); + parser.accepts("debug", "When enabled, will print more verbose errors"); + parser.accepts("cachedjar", "The name of a jar file to use instead of downloading") + .withOptionalArg() + .ofType(String.class); OptionSet options = null; try { @@ -120,9 +124,11 @@ public static void main(String[] args) { version.setType(type); boolean decompile = options.has("decompile"); + boolean debug = options.has("debug"); + String cachedjar = (String) options.valueOf("cachedjar"); Thread processorThread = new Thread(() -> { - Processor processor = new Processor(version, decompile, null); + Processor processor = new Processor(version, cachedjar, null, decompile, debug); processor.init(); }, "Processor"); processorThread.start(); diff --git a/src/main/java/com/shanebeestudios/mcdeob/Processor.java b/src/main/java/com/shanebeestudios/mcdeob/Processor.java index 86943b4..1383f6e 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/Processor.java +++ b/src/main/java/com/shanebeestudios/mcdeob/Processor.java @@ -3,7 +3,7 @@ import com.shanebeestudios.mcdeob.app.App; import com.shanebeestudios.mcdeob.util.AppLogger; import com.shanebeestudios.mcdeob.util.Logger; -import com.shanebeestudios.mcdeob.util.TimeStamp; +import com.shanebeestudios.mcdeob.util.TimeSpan; import com.shanebeestudios.mcdeob.util.Util; import com.shanebeestudios.mcdeob.version.Version; import net.md_5.specialsource.Jar; @@ -12,6 +12,8 @@ import net.md_5.specialsource.SpecialSource; import net.md_5.specialsource.provider.JarProvider; import net.md_5.specialsource.provider.JointProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; import java.awt.*; @@ -23,14 +25,18 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") public class Processor { - private final Version version; + private final @NotNull Version version; + private final @Nullable String cachedJarName; + private final Optional appOption; private final boolean decompile; - private final App app; + private final boolean debug; private final Path dataFolderPath; private Path jarPath; @@ -41,10 +47,20 @@ public class Processor { private String mappingsName; private String mappedJarName; - public Processor(Version version, boolean decompile, App app) { + public Processor(@NotNull Version version, @Nullable String cachedJarName, @Nullable App app, boolean decompile, boolean debug) { this.version = version; + if (cachedJarName != null) { + if (cachedJarName.endsWith(".jar")) { + this.cachedJarName = cachedJarName; + } else { + this.cachedJarName = cachedJarName + ".jar"; + } + } else { + this.cachedJarName = null; + } + this.appOption = Optional.ofNullable(app); this.decompile = decompile; - this.app = app; + this.debug = debug; if (Util.isRunningMacOS()) { // If running on macOS, put the output directory in the user home directory. // This is due to how macOS APPs work — their '.' directory resolves to one inside the APP itself. @@ -62,7 +78,7 @@ public Path getDataFolderPath() { return this.dataFolderPath; } - public Version getVersion() { + public @NotNull Version getVersion() { return this.version; } @@ -70,99 +86,164 @@ public Path getJarPath() { return this.jarPath; } - @SuppressWarnings("CallToPrintStackTrace") public void init() { - try { - long start = System.currentTimeMillis(); - - // Prepare version and check if valid - String versionName = this.version.getVersion(); - if (!this.version.prepareVersion()) { - if (this.app != null) this.app.fail(); - System.out.println("Invalid version: " + versionName); - return; - } - if (this.app != null) { - this.app.toggleControls(); - } + TimeSpan timeSpan = TimeSpan.start(); - String versionTypeName = this.version.getType().getName(); - this.minecraftJarName = String.format("minecraft_%s_%s.jar", versionTypeName, versionName); - this.mappingsName = String.format("mappings_%s_%s.txt", versionTypeName, versionName); - this.mappedJarName = String.format("remapped_%s_%s.jar", versionTypeName, versionName); + // Prepare version and check if valid + String versionName = this.version.getVersion(); - downloadJar(); - downloadMappings(); - remapJar(); - if (this.decompile) { - decompileJar(); - } - cleanup(); - - TimeStamp timeStamp = TimeStamp.fromNow(start); - Logger.info("Completed in %s!", timeStamp); - if (this.app != null) { - this.app.updateStatusBox(String.format("Completed in %s!", timeStamp)); - this.app.updateButton("Start!"); - this.app.toggleControls(); - } - } catch (IOException e) { - e.printStackTrace(); + // If the version is invalid, fail + if (!this.version.prepareVersion()) { + fail("Invalid version: " + versionName); + return; } - } - public void downloadJar() throws IOException { - long start = System.currentTimeMillis(); - Logger.info("Downloading JAR file from Mojang."); - if (this.app != null) { - this.app.updateStatusBox("Downloading JAR..."); - this.app.updateButton("Downloading JAR...", Color.BLUE); + // If the app is available, turn off controls + this.appOption.ifPresent(App::toggleControls); + + String versionTypeName = this.version.getType().getName(); + this.minecraftJarName = String.format("minecraft_%s_%s.jar", versionTypeName, versionName); + this.mappingsName = String.format("mappings_%s_%s.txt", versionTypeName, versionName); + this.mappedJarName = String.format("remapped_%s_%s.jar", versionTypeName, versionName); + + // Attempt to download jar + if (!downloadJar()) { + fail("Failed to retrieve jar."); + return; } - this.jarPath = this.dataFolderPath.resolve(this.minecraftJarName); - final URL jarURL = new URL(this.version.getJarURL()); - final HttpURLConnection connection = (HttpURLConnection) jarURL.openConnection(); - final long length = connection.getContentLengthLong(); - if (Files.exists(jarPath) && Files.size(jarPath) == length) { - Logger.info("Already have JAR, skipping download."); - } else try (final InputStream inputStream = connection.getInputStream()) { - Files.copy(inputStream, jarPath, REPLACE_EXISTING); + // Attempt to download mappings + if (!downloadMappings()) { + fail("Failed to download mappings."); + return; + } + + // Attempt to remap jar + if (!remapJar()) { + fail("Failed to remap jar."); + return; } - TimeStamp timeStamp = TimeStamp.fromNow(start); - Logger.info("Successfully downloaded JAR file in %s!", timeStamp); + // Attempt to decompile + if (this.decompile && !decompileJar()) { + fail("Failed to decompile JAR."); + return; + } + cleanup(); + + timeSpan.finish(); + Logger.info("Completed in %s!", timeSpan); + this.appOption.ifPresent(app -> app.finish(timeSpan)); + } + + public boolean downloadJar() { + // Attempt to use cached jar first + if (this.cachedJarName != null) { + this.jarPath = this.dataFolderPath.resolve(this.cachedJarName); + if (Files.exists(this.jarPath)) { + Logger.info("Using cached jar!"); + return true; + } else { + Logger.error("Cached jar as not found."); + return false; + } + } else { // If no cached jar, continue with donload + TimeSpan timeSpan = TimeSpan.start(); + Logger.info("Downloading JAR file from Mojang."); + this.appOption.ifPresent(a -> { + a.updateStatusBox("Downloading JAR..."); + a.updateButton("Downloading JAR...", Color.BLUE); + }); + this.jarPath = this.dataFolderPath.resolve(this.minecraftJarName); + + // Try open a connection with Mojang servers for downloads + final HttpURLConnection connection; + long connectionContentLength; + try { + final URL jarURL = new URL(this.version.getJarURL()); + connection = (HttpURLConnection) jarURL.openConnection(); + connectionContentLength = connection.getContentLengthLong(); + } catch (IOException e) { + logException("Failed to open connection to Mojang servers", e); + return false; + } + + // Check if we already have the jar on the computer + boolean jarExists; + try { + jarExists = Files.exists(jarPath) && Files.size(jarPath) == connectionContentLength; + } catch (IOException e) { + logException("Failed to check if jar file exists", e); + return false; + } + + // If the jar exists skip download, else attempt to download + if (jarExists) { + Logger.warn("Already have JAR, skipping download."); + return true; + } else try (final InputStream inputStream = connection.getInputStream()) { + Files.copy(inputStream, jarPath, REPLACE_EXISTING); + } catch (IOException e) { + logException("Failed to download jar", e); + return false; + } + timeSpan.finish(); + Logger.info("Successfully downloaded JAR file in %s!", timeSpan); + return true; + } } - public void downloadMappings() throws IOException { - long start = System.currentTimeMillis(); + public boolean downloadMappings() { + TimeSpan timeSpan = TimeSpan.start(); Logger.info("Downloading mappings file from Mojang..."); - if (this.app != null) { - this.app.updateStatusBox("Downloading mappings..."); - this.app.updateButton("Downloading mappings...", Color.BLUE); + this.appOption.ifPresent(app -> { + app.updateStatusBox("Downloading mappings..."); + app.updateButton("Downloading mappings...", Color.BLUE); + }); + + final HttpURLConnection connection; + try { + final URL mappingURL = new URL(this.version.getMapURL()); + connection = (HttpURLConnection) mappingURL.openConnection(); + } catch (IOException e) { + logException("Failed to open connection to Mojang servers", e); + return false; } - final URL mappingURL = new URL(this.version.getMapURL()); - final HttpURLConnection connection = (HttpURLConnection) mappingURL.openConnection(); final long length = connection.getContentLengthLong(); this.mappingsPath = this.dataFolderPath.resolve(this.mappingsName); - if (Files.exists(this.mappingsPath) && Files.size(this.mappingsPath) == length) { + boolean mappingExists; + try { + mappingExists = Files.exists(this.mappingsPath) && Files.size(this.mappingsPath) == length; + } catch (IOException e) { + logException("Failed to check if mapping file exists", e); + return false; + } + if (mappingExists) { Logger.info("Already have mappings, skipping download."); + return true; } else try (final InputStream inputStream = connection.getInputStream()) { Files.copy(inputStream, this.mappingsPath, REPLACE_EXISTING); + } catch (IOException e) { + logException("Failed to download mappings", e); + return false; } - TimeStamp timeStamp = TimeStamp.fromNow(start); - Logger.info("Successfully downloaded mappings file in %s!", timeStamp); + timeSpan.finish(); + Logger.info("Successfully downloaded mappings file in %s!", timeSpan); + return true; } - public void remapJar() { - long start = System.currentTimeMillis(); - if (this.app != null) { - this.app.updateStatusBox("Remapping..."); - this.app.updateButton("Remapping...", Color.BLUE); - } + public boolean remapJar() { + TimeSpan timeSpan = TimeSpan.start(); + this.appOption.ifPresent(app -> { + app.updateStatusBox("Remapping..."); + app.updateButton("Remapping...", Color.BLUE); + }); this.remappedJar = this.dataFolderPath.resolve(this.mappedJarName); - if (!Files.exists(remappedJar)) { + if (Files.exists(this.remappedJar)) { + Logger.info("%s already remapped... skipping mapping.", this.mappedJarName); + } else { Logger.info("Remapping %s file...", this.minecraftJarName); try { @@ -187,28 +268,34 @@ public void remapJar() { Util.stripFileFromJar(this.remappedJar, "it/*"); } } catch (IOException e) { - throw new RuntimeException(e); + logException("Failed to run remapper", e); + return false; } - TimeStamp timeStamp = TimeStamp.fromNow(start); - Logger.info("Remapping completed in %s!", timeStamp); - } else { - Logger.info("%s already remapped... skipping mapping.", this.mappedJarName); + timeSpan.finish(); + Logger.info("Remapping completed in %s!", timeSpan); } + return true; } @SuppressWarnings("resource") - public void decompileJar() throws IOException { - long start = System.currentTimeMillis(); + public boolean decompileJar() { + TimeSpan timeSpan = TimeSpan.start(); Logger.info("Decompiling final JAR file."); - if (this.app != null) { - this.app.updateStatusBox("Decompiler starting..."); - this.app.updateButton("Decompiling...", Color.BLUE); + this.appOption.ifPresent(app -> { + app.updateStatusBox("Decompiler starting..."); + app.updateButton("Decompiling...", Color.BLUE); + }); + final Path decompileDir; + try { + decompileDir = Files.createDirectories(this.dataFolderPath.resolve("final-decompile")); + } catch (IOException e) { + logException("Failed to create final-decompile path", e); + return false; } - final Path decompileDir = Files.createDirectories(this.dataFolderPath.resolve("final-decompile")); // Setup and run FernFlower - AppLogger appLogger = new AppLogger(this.app); + AppLogger appLogger = new AppLogger(this.appOption.orElse(null)); ConsoleDecompiler decompiler = new ConsoleDecompiler(new File(decompileDir.toUri()), Util.getDecompilerParams(), appLogger); decompiler.addSource(new File(this.remappedJar.toUri())); decompiler.decompileContext(); @@ -217,8 +304,21 @@ public void decompileJar() throws IOException { // Rename jar file to zip Util.renameJarsToZips(decompileDir); - TimeStamp timeStamp = TimeStamp.fromNow(start); - Logger.info("Decompiling completed in %s!", timeStamp); + timeSpan.finish(); + Logger.info("Decompiling completed in %s!", timeSpan); + return true; + } + + @SuppressWarnings("CallToPrintStackTrace") + private void logException(String message, Exception exception) { + Logger.error(message + ": " + exception.getMessage()); + if (this.debug) exception.printStackTrace(); + } + + private void fail(String failMessage) { + Logger.error(failMessage); + this.appOption.ifPresent(app -> app.fail(failMessage)); + cleanup(); } private void cleanup() { diff --git a/src/main/java/com/shanebeestudios/mcdeob/app/App.java b/src/main/java/com/shanebeestudios/mcdeob/app/App.java index ee8091c..ebcf943 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/app/App.java +++ b/src/main/java/com/shanebeestudios/mcdeob/app/App.java @@ -3,6 +3,7 @@ import com.shanebeestudios.mcdeob.Processor; import com.shanebeestudios.mcdeob.app.listener.SnapshotButtonListener; import com.shanebeestudios.mcdeob.app.listener.StartButtonListener; +import com.shanebeestudios.mcdeob.util.TimeSpan; import com.shanebeestudios.mcdeob.util.Util; import com.shanebeestudios.mcdeob.version.Version; import com.shanebeestudios.mcdeob.version.Versions; @@ -16,7 +17,7 @@ public class App extends JFrame { - private JLabel titleLabel; + private JLabel infoLineLabel; private JButton startButton; private JRadioButton serverRadioButton; private JRadioButton clientRadioButton; @@ -30,7 +31,10 @@ public App() { startSetup(); // Initialize versions - Versions.initVersions(); + if (!Versions.initVersions()) { + updateInfoLine("Failed to load versions. Are you connected to the internet?", Util.TITLE_FAIL_COLOR); + return; + } // Update window after versions initialized finishSetup(); @@ -46,7 +50,7 @@ private void startSetup() { } setupWindow(); - createTitle(); + createInfoLine(); createTypeButton(); createVersionPopup(); createDecompileButton(); @@ -80,12 +84,17 @@ private void setupWindow() { setLayout(null); } - private void createTitle() { - this.titleLabel = new JLabel("Initializing versions, please wait...", SwingConstants.CENTER); - this.titleLabel.setForeground(Util.TITLE_LOADING_COLOR); - this.titleLabel.setHorizontalTextPosition(SwingConstants.CENTER); - hookSize(() -> this.titleLabel.setBounds(0, 10, getSize().width, 50)); - add(this.titleLabel); + private void createInfoLine() { + this.infoLineLabel = new JLabel("Initializing versions, please wait...", SwingConstants.CENTER); + this.infoLineLabel.setForeground(Util.TITLE_LOADING_COLOR); + this.infoLineLabel.setHorizontalTextPosition(SwingConstants.CENTER); + hookSize(() -> this.infoLineLabel.setBounds(0, 10, getSize().width, 50)); + add(this.infoLineLabel); + } + + public void updateInfoLine(String text, Color color) { + this.infoLineLabel.setText(text); + this.infoLineLabel.setForeground(color); } private void createTypeButton() { @@ -181,16 +190,28 @@ public void start(Version version, boolean shouldDecompile) { Thread thread = new Thread("Processor") { @Override public void run() { - Processor processor = new Processor(version, shouldDecompile, App.this); + Processor processor = new Processor(version, null, App.this, shouldDecompile, false); processor.init(); } }; thread.start(); } - public void fail() { - toggleControls(); - updateButton("INVALID VERSION!", Color.RED); + public void fail(String failMessage) { + if (this.startButton.isEnabled()) toggleControls(); + updateButton(failMessage, Color.RED); + getToolkit().beep(); + Timer timer = new Timer(1000, e1 -> { + updateButton("Start!"); + toggleControls(); + }); + timer.setRepeats(false); + timer.start(); + } + + public void finish(TimeSpan timeSpan) { + this.updateStatusBox(String.format("Completed in %s!", timeSpan)); + updateButton("Completed!"); getToolkit().beep(); Timer timer = new Timer(1000, e1 -> { updateButton("Start!"); @@ -202,9 +223,7 @@ public void fail() { private void finishSetup() { setupVersions(false); - assert this.titleLabel != null; - this.titleLabel.setText("Let's start de-obfuscating some Minecraft"); - this.titleLabel.setForeground(Util.TITLE_READY_COLOR); + updateInfoLine("Let's start de-obfuscating some Minecraft", Util.TITLE_READY_COLOR); toggleControls(); } diff --git a/src/main/java/com/shanebeestudios/mcdeob/app/listener/StartButtonListener.java b/src/main/java/com/shanebeestudios/mcdeob/app/listener/StartButtonListener.java index eb357fb..42f7c36 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/app/listener/StartButtonListener.java +++ b/src/main/java/com/shanebeestudios/mcdeob/app/listener/StartButtonListener.java @@ -21,7 +21,7 @@ public void actionPerformed(ActionEvent event) { Version version = (Version) this.app.getVersionBox().getSelectedItem(); if (!this.app.getStartButton().getText().equalsIgnoreCase("Start!")) return; if (version == null) { - this.app.fail(); + this.app.fail("Invalid version!"); } else { version.setType(this.app.getServerRadioButton().isSelected() ? Version.Type.SERVER : Version.Type.CLIENT); this.app.updateButton("Starting...", Color.BLUE); diff --git a/src/main/java/com/shanebeestudios/mcdeob/util/Format.java b/src/main/java/com/shanebeestudios/mcdeob/util/Format.java deleted file mode 100644 index a075572..0000000 --- a/src/main/java/com/shanebeestudios/mcdeob/util/Format.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.shanebeestudios.mcdeob.util; - -@SuppressWarnings("unused") -public class Format extends java.awt.Color { - - // COLORS - public static final Format WHITE = new Format(255, 255, 255); - public static final Format LIGHT_GRAY = new Format(192, 192, 192); - public static final Format GRAY = new Format(128, 128, 128); - public static final Format DARK_GRAY = new Format(64, 64, 64); - public static final Format BLACK = new Format(0, 0, 0); - public static final Format RED = new Format(255, 0, 0); - public static final Format PINK = new Format(255, 175, 175); - public static final Format ORANGE = new Format(255, 200, 0); - public static final Format YELLOW = new Format(255, 255, 0); - public static final Format GREEN = new Format(0, 255, 0); - public static final Format MAGENTA = new Format(255, 0, 255); - public static final Format CYAN = new Format(0, 255, 255); - public static final Format BLUE = new Format(0, 0, 255); - - // FORMAT - public static final String RESET = "\u001B[0m"; - public static final String BOLD = "\u001b[1m"; - public static final String UNDERLINE = "\u001b[4m"; - public static final String INVERSE = "\u001b[7m"; - - private Format(java.awt.Color c) { - super(c.getRGB()); - } - - public Format(int r, int g, int b) { - super(r, g, b); - } - - public Format(int rgb) { - super(rgb); - } - - public Format(String hex) { - super(java.awt.Color.decode(hex).getRGB()); - } - - @Override - public String toString() { - return "\u001B[38;2;" + getRed() + ";" + getGreen() + ";" + getBlue() + "m"; - } - -} diff --git a/src/main/java/com/shanebeestudios/mcdeob/util/TimeSpan.java b/src/main/java/com/shanebeestudios/mcdeob/util/TimeSpan.java new file mode 100644 index 0000000..fc4f649 --- /dev/null +++ b/src/main/java/com/shanebeestudios/mcdeob/util/TimeSpan.java @@ -0,0 +1,57 @@ +package com.shanebeestudios.mcdeob.util; + +import java.time.Duration; + +/** + * Represents a span in time + */ +public class TimeSpan { + + /** + * Start and create a new TimeSpan + * + * @return New TimeSpan + */ + public static TimeSpan start() { + return new TimeSpan(); + } + + private final long start; + private Duration duration; + + private TimeSpan() { + this.start = System.currentTimeMillis(); + this.duration = Duration.ZERO; + } + + /** + * Finish timing + */ + public void finish() { + this.duration = Duration.ofMillis(System.currentTimeMillis() - this.start); + } + + @Override + public String toString() { + long minutes = this.duration.toMinutesPart(); + long seconds = this.duration.toSecondsPart(); + final StringBuilder sb = new StringBuilder(); + if (minutes > 0) { + sb.append(minutes); + sb.append(minutes == 1 ? " minute" : " minutes"); + if (seconds > 0) sb.append(", "); + } + + if (seconds > 0) { + sb.append(seconds); + sb.append(seconds == 1 ? " second" : " seconds"); + } + + if (minutes == 0 && seconds == 0) { + sb.append("less than a second"); + } + + return sb.toString(); + } + +} diff --git a/src/main/java/com/shanebeestudios/mcdeob/util/TimeStamp.java b/src/main/java/com/shanebeestudios/mcdeob/util/TimeStamp.java deleted file mode 100644 index eb145d8..0000000 --- a/src/main/java/com/shanebeestudios/mcdeob/util/TimeStamp.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.shanebeestudios.mcdeob.util; - -import java.time.Duration; - -public class TimeStamp { - - public static TimeStamp fromNow(long start) { - return new TimeStamp(System.currentTimeMillis() - start); - } - - private final Duration duration; - - public TimeStamp(long millis) { - this.duration = Duration.ofMillis(millis); - } - - @Override - public String toString() { - long minutes = duration.toMinutesPart(); - long seconds = duration.toSecondsPart(); - final StringBuilder sb = new StringBuilder(); - if (minutes > 0) { - sb.append(minutes); - sb.append(minutes == 1 ? " minute" : " minutes"); - if (seconds > 0) sb.append(", "); - } - - if (seconds > 0) { - sb.append(seconds); - sb.append(seconds == 1 ? " second" : " seconds"); - } - - if (minutes == 0 && seconds == 0) { - sb.append("less than a second"); - } - - return sb.toString(); - } - -} diff --git a/src/main/java/com/shanebeestudios/mcdeob/util/Util.java b/src/main/java/com/shanebeestudios/mcdeob/util/Util.java index 18d29d7..feeee98 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/util/Util.java +++ b/src/main/java/com/shanebeestudios/mcdeob/util/Util.java @@ -3,6 +3,7 @@ import com.shanebeestudios.mcdeob.Processor; import net.md_5.specialsource.Jar; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONObject; import org.json.JSONTokener; @@ -13,7 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.URL; +import java.net.URI; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; @@ -27,6 +28,7 @@ public class Util { public static final Color TITLE_LOADING_COLOR = new Color(227, 184, 43); public static final Color TITLE_READY_COLOR = new Color(63, 199, 82); + public static final Color TITLE_FAIL_COLOR = new Color(204, 25, 25); public static boolean isRunningMacOS() { return System.getProperty("os.name").contains("Mac OS"); @@ -38,16 +40,19 @@ public static boolean isRunningMacOS() { * @param url URL to grab data from * @return JsonObject with retrieved data */ + @Nullable public static JSONObject getJsonFromURL(String url) { try { - URLConnection connection = new URL(url).openConnection(); + URLConnection connection = URI.create(url).toURL().openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); final JSONObject jsonObject = new JSONObject(new JSONTokener(in)); in.close(); return jsonObject; - } catch (IOException e) { - throw new RuntimeException(e); + } catch (IOException ex) { + //noinspection CallToPrintStackTrace + ex.printStackTrace(); } + return null; } public static File copyInputStreamToFile(InputStream inputStream, String pathName) throws IOException { diff --git a/src/main/java/com/shanebeestudios/mcdeob/version/Versions.java b/src/main/java/com/shanebeestudios/mcdeob/version/Versions.java index c94d085..5e6cd4f 100644 --- a/src/main/java/com/shanebeestudios/mcdeob/version/Versions.java +++ b/src/main/java/com/shanebeestudios/mcdeob/version/Versions.java @@ -1,5 +1,6 @@ package com.shanebeestudios.mcdeob.version; +import com.shanebeestudios.mcdeob.util.Logger; import com.shanebeestudios.mcdeob.util.Util; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; @@ -16,14 +17,23 @@ public class Versions { private static final Map RELEASE_MAP = new LinkedHashMap<>(); private static final Map SNAPSHOT_MAP = new LinkedHashMap<>(); - public static void initVersions() { + public static boolean initVersions() { JSONObject mojangManifest = Util.getJsonFromURL("https://launchermeta.mojang.com/mc/game/version_manifest.json"); JSONObject seargeManifest = Util.getJsonFromURL("https://raw.githubusercontent.com/ShaneBeeStudios/Mappings/refs/heads/main/mappings/versions.json"); + if (mojangManifest == null) { + Logger.error("Failed to download version manifests."); + return false; + } else if (seargeManifest == null) { + Logger.error("Failed to download legacy version manifests."); + } + Version.MappingType mappingType = Version.MappingType.MOJANG; List seargeVersions = new ArrayList<>(); - for (Object o : seargeManifest.getJSONArray("versions")) { - seargeVersions.add(o.toString()); + if (seargeManifest != null) { + for (Object o : seargeManifest.getJSONArray("versions")) { + seargeVersions.add(o.toString()); + } } for (Object o : mojangManifest.getJSONArray("versions")) { @@ -51,6 +61,7 @@ public static void initVersions() { mappingType = Version.MappingType.SEARGE; } } + return true; } public static Collection getAllVersions() {