diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/PackManagerController.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/PackManagerController.java index 8281528..f7a1054 100644 --- a/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/PackManagerController.java +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/PackManagerController.java @@ -562,6 +562,18 @@ SwingHelper.addErrorDialogCallback(frame, deferred); }); + frame.getVersionCheckMenuItem().addActionListener(e -> { + Optional optional = getSelectedPack(true); + + if (optional.isPresent()) { + Pack pack = optional.get(); + + VersionCheckDialog dialog = new VersionCheckDialog(frame); + VersionCheckController controller = new VersionCheckController(dialog, executor); + controller.showUpdates(pack.getModsDir(), pack.getCachedConfig().getGameVersion(), frame); + } + }); + frame.getOpenOutputFolderMenuItem().addActionListener(e -> SwingHelper.browseDir(distDir, frame)); frame.getOpenWorkspaceFolderMenuItem().addActionListener(e1 -> SwingHelper.browseDir(workspaceDir, frame)); diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/VersionCheckController.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/VersionCheckController.java new file mode 100644 index 0000000..cfcaddb --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/VersionCheckController.java @@ -0,0 +1,171 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.controller; + +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.skcraft.concurrency.Deferred; +import com.skcraft.concurrency.Deferreds; +import com.skcraft.concurrency.SettableProgress; +import com.skcraft.launcher.creator.controller.task.DirectoryWalker; +import com.skcraft.launcher.creator.dialog.VersionCheckDialog; +import com.skcraft.launcher.creator.model.creator.ModFile; +import com.skcraft.launcher.creator.model.swing.ModFileTableModel; +import com.skcraft.launcher.creator.util.ModInfoReader; +import com.skcraft.launcher.creator.util.ModInfoReader.ModInfo; +import com.skcraft.launcher.creator.util.NemModList; +import com.skcraft.launcher.creator.util.NemModList.ModEntry; +import com.skcraft.launcher.dialog.ProgressDialog; +import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.util.SwingExecutor; + +import javax.annotation.Nullable; +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +public class VersionCheckController { + + private final VersionCheckDialog dialog; + private final ListeningExecutorService executor; + + public VersionCheckController(VersionCheckDialog dialog, ListeningExecutorService executor) { + this.dialog = dialog; + this.executor = executor; + } + + public void showUpdates(File dir, String gameVersion, Window parentWindow) { + initListeners(); + + DirectoryWalker walker = new DirectoryWalker(dir); + walker.setRecursive(false); + walker.setFileFilter(pathname -> pathname.getName().endsWith(".jar")); + + ModInfoReader binaryInspector = new ModInfoReader(); + NemModList nemModList = new NemModList(); + + SettableProgress progress = new SettableProgress("Retrieving mod information...", -1); + + Deferred deferred = Deferreds.makeDeferred(executor.submit(walker), executor) + .thenTap(() -> progress.set("Querying NotEnoughMods for version data...", -1)) + .thenTap(() -> { + try { + nemModList.load(gameVersion); + } catch (IOException | InterruptedException e) { + throw new RuntimeException("Failed to retrieve mod information from NotEnoughMods. Perhaps NEM doesn't support your pack's MC version.", e); + } + }) + .thenTap(() -> progress.set("Scanning mod files for manifests...", -1)) + .thenApply(files -> { + List mods = Lists.newArrayList(); + + for (File file : files) { + ModFile mod = new ModFile(); + mod.setGameVersion(gameVersion); + mod.setFile(file); + + List infoList = binaryInspector.detectMods(file); + if (!infoList.isEmpty()) { + ModInfo info = infoList.get(0); + mod.setModId(info.getModId()); + mod.setName(info.getName()); + mod.setVersion(info.getVersion()); + + if (info.getUrl() != null) { + mod.setUrl(getFirstUrl(info.getUrl(), "http://" + info.getUrl())); + } + } + + if (mod.getModId() != null) { + ModEntry entry = nemModList.get(mod.getModId()); + + if (entry != null) { + mod.setLatestVersion(entry.getLatestVersion()); + mod.setLatestDevVersion(entry.getLatestDevVersion()); + if (entry.getUrl() != null) { + mod.setUrl(entry.getUrl()); + } + } + } + + mods.add(mod); + } + + return mods; + }) + .handleAsync(mods -> { + List known = Lists.newArrayList(); + List unknown = Lists.newArrayList(); + + for (ModFile mod : mods) { + if (mod.getVersion() != null && mod.getLatestVersion() != null) { + if (!mod.getCleanVersion().equals(mod.getLatestVersion()) && !mod.getCleanVersion().equals(mod.getLatestDevVersion())) { + known.add(mod); + } + } else { + unknown.add(mod); + } + } + + dialog.getKnownModsTable().setModel(new ModFileTableModel(known)); + dialog.getUnknownModsTable().setModel(new ModFileTableModel(unknown)); + dialog.getKnownModsTable().getRowSorter().toggleSortOrder(1); + dialog.getUnknownModsTable().getRowSorter().toggleSortOrder(1); + dialog.setVisible(true); + }, ex -> { + }, SwingExecutor.INSTANCE); + + ProgressDialog.showProgress(parentWindow, deferred, progress, "Checking for mod updates...", "Checking for mod updates..."); + SwingHelper.addErrorDialogCallback(parentWindow, deferred); + } + + private void initListeners() { + dialog.getCloseButton().addActionListener(e -> dialog.dispose()); + + ModTableMouseListener mouseListener = new ModTableMouseListener(); + dialog.getKnownModsTable().addMouseListener(mouseListener); + dialog.getUnknownModsTable().addMouseListener(mouseListener); + } + + @Nullable + private static URL getFirstUrl(String... options) { + for (String option : options) { + try { + return new URL(option); + } catch (MalformedURLException ignored) { + } + } + + return null; + } + + private class ModTableMouseListener extends MouseAdapter { + + public void mousePressed(MouseEvent e) { + if (e.getClickCount() == 2) { + JTable table = (JTable) e.getSource(); + Point point = e.getPoint(); + int selectedIndex = table.rowAtPoint(point); + if (selectedIndex >= 0) { + selectedIndex = table.convertRowIndexToModel(selectedIndex); + ModFile mod = ((ModFileTableModel) table.getModel()).getMod(selectedIndex); + if (mod != null && mod.getUrl() != null) { + SwingHelper.openURL(mod.getUrl(), dialog); + } + } + } + } + + } + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/task/DirectoryWalker.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/task/DirectoryWalker.java new file mode 100644 index 0000000..89eaeae --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/controller/task/DirectoryWalker.java @@ -0,0 +1,73 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.controller.task; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.skcraft.launcher.util.MorePaths; +import lombok.Getter; +import lombok.Setter; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class DirectoryWalker implements Callable> { + + @Getter private final File dir; + @Getter @Setter private FileFilter fileFilter = pathname -> true; + @Getter @Setter private boolean recursive; + + public DirectoryWalker(File dir) { + checkNotNull(dir, "dir"); + this.dir = dir; + } + + @Override + public List call() throws IOException { + if (!dir.isDirectory()) { + throw new IllegalArgumentException(dir.getAbsolutePath() + " is not a directory"); + } + + List matched = Lists.newArrayList(); + Set seen = Sets.newHashSet(); + + Queue queue = new LinkedList<>(); + queue.add(dir); + + File cur; + while ((cur = queue.poll()) != null) { + String canonical = cur.getCanonicalPath(); + if (!seen.contains(canonical) && MorePaths.isSubDirectory(dir, cur)) { + File[] files = cur.listFiles(); + + if (files != null) { + for (File file : files) { + if (recursive && file.isDirectory()) { + queue.add(file); + } + + if (fileFilter.accept(file)) { + matched.add(file); + } + } + } + } + + } + + return matched; + } + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/PackManagerFrame.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/PackManagerFrame.java index b64dc0d..6b0a30c 100644 --- a/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/PackManagerFrame.java +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/PackManagerFrame.java @@ -49,6 +49,7 @@ @Getter private final JMenuItem deployServerMenuItem = new JMenuItem("Deploy Server..."); @Getter private final JMenuItem generatePackagesMenuItem = new JMenuItem("Generate packages.json..."); @Getter private final JMenuItem openOutputFolderMenuItem = new JMenuItem("Open Upload Folder"); + @Getter private final JMenuItem versionCheckMenuItem = new JMenuItem("Check for Mod Updates"); @Getter private final JMenuItem openWorkspaceFolderMenuItem = new JMenuItem("Open Workspace Folder"); @Getter private final JMenuItem openLauncherFolderMenuItem = new JMenuItem("Open Test Launcher Folder"); @Getter private final JMenuItem openWebRootMenuItem = new JMenuItem("Open Test Web Server Folder"); @@ -181,6 +182,8 @@ menu.setMargin(menuInset); menu.setMnemonic('t'); menuBar.add(menu); + menu.add(versionCheckMenuItem); + menu.addSeparator(); menu.add(openWorkspaceFolderMenuItem); menu.add(openLauncherFolderMenuItem); menu.add(openWebRootMenuItem); diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/VersionCheckDialog.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/VersionCheckDialog.java new file mode 100644 index 0000000..b632fce --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/dialog/VersionCheckDialog.java @@ -0,0 +1,76 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.dialog; + +import com.jidesoft.swing.SearchableUtils; +import com.jidesoft.swing.TableSearchable; +import com.skcraft.launcher.swing.DefaultTable; +import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.swing.TableColumnAdjuster; +import lombok.Getter; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; + +public class VersionCheckDialog extends JDialog { + + @Getter private final JTable knownModsTable = new DefaultTable(); + @Getter private final JTable unknownModsTable = new DefaultTable(); + @Getter private final JButton closeButton = new JButton("Close"); + private final TableColumnAdjuster updateTableAdjuster = new TableColumnAdjuster(knownModsTable); + private final TableColumnAdjuster unknownTableAdjuster = new TableColumnAdjuster(unknownModsTable); + + public VersionCheckDialog(Window parent) { + super(parent, "Update Check", ModalityType.DOCUMENT_MODAL); + + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + initComponents(); + pack(); + setLocationRelativeTo(parent); + } + + private void initComponents() { + updateTableAdjuster.adjustColumns(); + updateTableAdjuster.setDynamicAdjustment(true); + + unknownTableAdjuster.adjustColumns(); + unknownTableAdjuster.setDynamicAdjustment(true); + + knownModsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + knownModsTable.setAutoCreateRowSorter(true); + + unknownModsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + unknownModsTable.setAutoCreateRowSorter(true); + + JPanel container = new JPanel(); + container.setLayout(new MigLayout("insets dialog, fill")); + + container.add(new JLabel("With Potential Updates:"), "span"); + container.add(SwingHelper.wrapScrollPane(knownModsTable), "grow, pushy, span, w 500:900, h 230"); + + container.add(new JLabel("Unknown Status:"), "span"); + container.add(SwingHelper.wrapScrollPane(unknownModsTable), "grow, pushy, span, w 500:900, h 150, gapbottom unrel, wrap"); + + container.add(new JLabel("Version data is sourced from NotEnoughMods.com."), ""); + container.add(closeButton, "tag cancel, sizegroup bttn"); + + add(container, BorderLayout.CENTER); + + getRootPane().registerKeyboardAction(e -> closeButton.doClick(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); + + TableSearchable tableSearchable; + + tableSearchable = SearchableUtils.installSearchable(knownModsTable); + tableSearchable.setMainIndex(-1); + + tableSearchable = SearchableUtils.installSearchable(unknownModsTable); + tableSearchable.setMainIndex(-1); + } + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/model/creator/ModFile.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/model/creator/ModFile.java new file mode 100644 index 0000000..45843ab --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/model/creator/ModFile.java @@ -0,0 +1,38 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.model.creator; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.io.File; +import java.net.URL; +import java.util.regex.Pattern; + +@Data +public class ModFile { + + private File file; + private String modId; + private String name; + private String gameVersion; + private String version; + private String latestVersion; + private String latestDevVersion; + private URL url; + + @JsonIgnore + public String getCleanVersion() { + String version = getVersion(); + return version != null ? + version + .replaceAll("^" + Pattern.quote(gameVersion) + "\\-", "") + .replaceAll("\\-" + Pattern.quote(gameVersion) + "$", "") + : null; + } + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/model/swing/ModFileTableModel.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/model/swing/ModFileTableModel.java new file mode 100644 index 0000000..497ea16 --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/model/swing/ModFileTableModel.java @@ -0,0 +1,101 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.model.swing; + +import com.skcraft.launcher.creator.Creator; +import com.skcraft.launcher.creator.model.creator.ModFile; +import com.skcraft.launcher.swing.SwingHelper; + +import javax.annotation.Nullable; +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ModFileTableModel extends AbstractTableModel { + + private static final ImageIcon WWW_ICON; + private final List mods; + + static { + WWW_ICON = SwingHelper.readImageIcon(Creator.class, "www_icon.png"); + } + + public ModFileTableModel(List mods) { + checkNotNull(mods, "mods"); + this.mods = mods; + } + + @Override + public String getColumnName(int columnIndex) { + switch (columnIndex) { + case 0: return ""; + case 1: return "Mod"; + case 2: return "Version"; + case 3: return "Latest Release"; + case 4: return "Latest Dev"; + case 5: return "Mod ID"; + case 6: return "Filename"; + default: return null; + } + } + + @Override + public Class getColumnClass(int columnIndex) { + switch (columnIndex) { + case 0: + return ImageIcon.class; + default: + return String.class; + } + } + + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) { + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public int getRowCount() { + return mods.size(); + } + + @Override + public int getColumnCount() { + return 7; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + @Nullable ModFile mod = mods.get(rowIndex); + + if (mod == null) { + return null; + } + + switch (columnIndex) { + case 0: return mod.getUrl() != null ? WWW_ICON : null; + case 1: return mod.getName() != null ? mod.getName() : mod.getFile().getName(); + case 2: return mod.getCleanVersion(); + case 3: return mod.getLatestVersion(); + case 4: return mod.getLatestDevVersion(); + case 5: return mod.getModId(); + case 6: return mod.getFile().getName(); + default: return null; + } + } + + public ModFile getMod(int index) { + return mods.get(index); + } + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/util/ModInfoReader.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/util/ModInfoReader.java new file mode 100644 index 0000000..a0363f7 --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/util/ModInfoReader.java @@ -0,0 +1,169 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.util; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.io.CharStreams; +import com.google.common.io.Closer; +import lombok.Data; +import lombok.extern.java.Log; + +import java.io.*; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Reads the mod information file from a mod .jar, with support for both Forge + * and LiteLoader. + */ +@Log +public class ModInfoReader { + + private static final String FORGE_INFO_FILENAME = "mcmod.info"; + private static final String LITELOADER_INFO_FILENAME = "litemod.json"; + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * Detect the mods listed in the given .jar + * + * @param file The file + * @return A list of detected mods + */ + public List detectMods(File file) { + Closer closer = Closer.create(); + + try { + FileInputStream fis = closer.register(new FileInputStream(file)); + BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); + ZipInputStream zis = closer.register(new ZipInputStream(bis)); + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equalsIgnoreCase(FORGE_INFO_FILENAME)) { + List mods; + String content = CharStreams.toString(new InputStreamReader(zis, Charsets.UTF_8)); + + try { + mods = mapper.readValue(content, ForgeModManifest.class).getMods(); + } catch (JsonMappingException | JsonParseException e) { + mods = mapper.readValue(content, new TypeReference>() {}); + } + + if (mods != null) { + // Ignore "examplemod" + return Collections.unmodifiableList( + mods.stream() + .filter(info -> !info.getModId().equals("examplemod")) + .collect(Collectors.toList())); + } else { + return Collections.emptyList(); + } + + } else if (entry.getName().equalsIgnoreCase(LITELOADER_INFO_FILENAME)) { + String content = CharStreams.toString(new InputStreamReader(zis, Charsets.UTF_8)); + return new ImmutableList.Builder().add(mapper.readValue(content, LiteLoaderModInfo.class)).build(); + } + } + + return Collections.emptyList(); + } catch (JsonMappingException e) { + log.log(Level.WARNING, "Unknown format " + FORGE_INFO_FILENAME + " file in " + file.getAbsolutePath(), e); + return Collections.emptyList(); + } catch (JsonParseException e) { + log.log(Level.WARNING, "Corrupt " + FORGE_INFO_FILENAME + " file in " + file.getAbsolutePath(), e); + return Collections.emptyList(); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to read " + file.getAbsolutePath(), e); + return Collections.emptyList(); + } finally { + try { + closer.close(); + } catch (IOException ignored) { + } + } + } + + public interface ModInfo { + + String getModId(); + String getName(); + String getDescription(); + String getVersion(); + String getGameVersion(); + String getUrl(); + + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + private static class ForgeModInfo implements ModInfo { + + @JsonProperty("modid") + private String modId; + private String name; + private String description; + private String version; + @JsonProperty("mcversion") + private String gameVersion; + private String url; + private String updateUrl; + private List authorList; + private String credits; + private List dependencies; + + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + private static class ForgeModManifest { + + @JsonProperty("modListVersion") + private int version; + @JsonProperty("modList") + private List mods; + + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + private static class LiteLoaderModInfo implements ModInfo { + + private String name; + private String version; + @JsonProperty("mcversion") + private String gameVersion; + private String revision; + private String author; + private String description; + + @JsonIgnore + @Override + public String getModId() { + return null; + } + + @Override + public String getUrl() { + return null; + } + + } + + +} diff --git a/creator-tools/src/main/java/com/skcraft/launcher/creator/util/NemModList.java b/creator-tools/src/main/java/com/skcraft/launcher/creator/util/NemModList.java new file mode 100644 index 0000000..89a3d82 --- /dev/null +++ b/creator-tools/src/main/java/com/skcraft/launcher/creator/util/NemModList.java @@ -0,0 +1,91 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.creator.util; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.skcraft.launcher.util.HttpRequest; +import lombok.Data; +import lombok.Getter; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class NemModList { + + @Getter + private Map mods = ImmutableMap.of(); + + public void load(String version) throws IOException, InterruptedException { + checkNotNull(version, "version"); + + List mods = HttpRequest.get(HttpRequest.url("https://bot.notenoughmods.com/" + version + ".json")) + .execute() + .expectResponseCode(200) + .returnContent() + .asJson(new TypeReference>() {}); + + Map index = Maps.newHashMap(); + + for (ModEntry entry : mods) { + index.put(entry.getModId(), entry); + } + + this.mods = Collections.unmodifiableMap(index); + } + + @Nullable + public ModEntry get(String modId) { + checkNotNull(modId, "modId"); + return mods.get(modId); + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ModEntry { + + @JsonProperty("modid") + private String modId; + private String name; + private List aliases; + + @JsonProperty("version") + private String latestVersion; + @JsonProperty("dev") + private String latestDevVersion; + @JsonProperty("prevversion") + private String previousVersion; + + private List dependencies; + + @JsonProperty("longurl") + private URL url; + private List tags; + private String comment; + private String author; + + private String license; + private URL repo; + + @JsonProperty("added_at") + private Date addedAt; + @JsonProperty("lastupdated") + private Date lastUpdated; + + } + +} diff --git a/creator-tools/src/main/resources/com/skcraft/launcher/creator/www_icon.png b/creator-tools/src/main/resources/com/skcraft/launcher/creator/www_icon.png new file mode 100644 index 0000000..9a60fed --- /dev/null +++ b/creator-tools/src/main/resources/com/skcraft/launcher/creator/www_icon.png Binary files differ diff --git a/launcher/build.gradle b/launcher/build.gradle index a3dd1fe..ea2b642 100644 --- a/launcher/build.gradle +++ b/launcher/build.gradle @@ -14,6 +14,7 @@ compile 'com.google.guava:guava:15.0' compile 'com.beust:jcommander:1.32' compile 'com.miglayout:miglayout:3.7.4' + compile 'com.google.code.findbugs:jsr305:3.0.0' } processResources { @@ -30,4 +31,4 @@ } } -build.dependsOn(shadowJar) \ No newline at end of file +build.dependsOn(shadowJar)