diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java index e9c3afad37..9237273f11 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java @@ -8,6 +8,42 @@ import com.google.inject.CreationException; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLite; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.OpenOSRSConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.events.ExternalPluginChanged; +import net.runelite.client.events.ExternalRepositoryChanged; +import net.runelite.client.ui.RuneLiteSplashScreen; +import net.runelite.client.util.MiscUtils; +import net.runelite.client.util.SwingUtil; +import org.pf4j.DefaultPluginManager; +import org.pf4j.DependencyResolver; +import org.pf4j.JarPluginLoader; +import org.pf4j.JarPluginRepository; +import org.pf4j.ManifestPluginDescriptorFinder; +import org.pf4j.PluginAlreadyLoadedException; +import org.pf4j.PluginDependency; +import org.pf4j.PluginDescriptorFinder; +import org.pf4j.PluginLoader; +import org.pf4j.PluginRepository; +import org.pf4j.PluginRuntimeException; +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; +import org.pf4j.update.DefaultUpdateRepository; +import org.pf4j.update.PluginInfo; +import org.pf4j.update.UpdateManager; +import org.pf4j.update.UpdateRepository; +import org.pf4j.update.VerifyException; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -32,43 +68,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; import static net.runelite.client.RuneLite.EXTERNALPLUGIN_DIR; import static net.runelite.client.RuneLite.SYSTEM_VERSION; -import net.runelite.client.RuneLiteProperties; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.OpenOSRSConfig; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ExternalPluginChanged; -import net.runelite.client.events.ExternalRepositoryChanged; -import net.runelite.client.ui.RuneLiteSplashScreen; -import net.runelite.client.util.SwingUtil; -import org.pf4j.DefaultPluginManager; -import org.pf4j.DependencyResolver; -import org.pf4j.JarPluginLoader; -import org.pf4j.JarPluginRepository; -import org.pf4j.ManifestPluginDescriptorFinder; -import org.pf4j.PluginAlreadyLoadedException; -import org.pf4j.PluginDependency; -import org.pf4j.PluginDescriptorFinder; -import org.pf4j.PluginLoader; -import org.pf4j.PluginRepository; -import org.pf4j.PluginRuntimeException; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; -import org.pf4j.update.DefaultUpdateRepository; -import org.pf4j.update.PluginInfo; -import org.pf4j.update.UpdateManager; -import org.pf4j.update.UpdateRepository; -import org.pf4j.update.VerifyException; @Slf4j @Singleton @@ -85,7 +86,6 @@ class ExternalPluginManager private final ConfigManager configManager; private final List plugins = new CopyOnWriteArrayList<>(); private final Map pluginsMap = new HashMap<>(); - @Getter(AccessLevel.PUBLIC) private UpdateManager updateManager; @@ -111,7 +111,9 @@ class ExternalPluginManager { boolean debug = RuneLiteProperties.getLauncherVersion() == null && RuneLiteProperties.getPluginPath() != null; - this.externalPluginManager = new DefaultPluginManager(debug ? Paths.get(RuneLiteProperties.getPluginPath() + File.separator + "release") : EXTERNALPLUGIN_DIR.toPath()) + this.externalPluginManager = new DefaultPluginManager( + debug ? Paths.get(RuneLiteProperties.getPluginPath() + File.separator + "release") + : EXTERNALPLUGIN_DIR.toPath()) { @Override protected PluginDescriptorFinder createPluginDescriptorFinder() @@ -189,22 +191,41 @@ class ExternalPluginManager this.externalPluginManager.setSystemVersion(SYSTEM_VERSION); } + public boolean doesGhRepoExist(String owner, String name) + { + return doesRepoExist("gh:" + owner + "/" + name); + } + + /** + * Note that {@link org.pf4j.update.UpdateManager#addRepository} checks if the repo exists, however it throws an exception which is bad + */ + public boolean doesRepoExist(String id) + { + return repositories.stream().anyMatch((repo) -> repo.getId().equals(id)); + } + private static URL toRepositoryUrl(String owner, String name) throws MalformedURLException { return new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/"); } - public static boolean testRepository(String owner, String name) + public static boolean testGHRepository(String owner, String name) { - final List repositories = new ArrayList<>(); try { - repositories.add(new DefaultUpdateRepository("github", new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/"))); + return testRepository(toRepositoryUrl(owner, name)); } catch (MalformedURLException e) { - return true; + e.printStackTrace(); } + return false; + } + + public static boolean testRepository(URL url) + { + final List repositories = new ArrayList<>(); + repositories.add(new DefaultUpdateRepository("repository-testing", url)); DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath()); UpdateManager updateManager = new UpdateManager(testPluginManager, repositories); @@ -243,6 +264,58 @@ class ExternalPluginManager } public void startExternalUpdateManager() + { + if (!tryLoadNewFormat()) + { + loadOldFormat(); + } + + this.updateManager = new UpdateManager(this.externalPluginManager, repositories); + saveConfig(); + } + + public boolean tryLoadNewFormat() + { + try + { + for (String keyval : openOSRSConfig.getExternalRepositories().split(";")) + { + String[] split = keyval.split("\\|"); + if (split.length != 2) + { + repositories.clear(); + return false; + } + String id = split[0]; + String url = split[1]; + if (!url.endsWith("/")) + { + url = url.concat("/"); + } + + if (id.contains("https://raw.githubusercontent.com/")) + { + id = "gh:" + id.substring(id.indexOf("https://raw.githubusercontent.com/")).replace("/master", "") + .replace("https://raw.githubusercontent.com/", ""); + + if (id.endsWith("/")) + { + id = id.substring(0, id.lastIndexOf("/")); + } + } + + repositories.add(new DefaultUpdateRepository(id, new URL(url))); + } + } + catch (ArrayIndexOutOfBoundsException | MalformedURLException e) + { + repositories.clear(); + return false; + } + return true; + } + + public void loadOldFormat() { try { @@ -260,18 +333,13 @@ class ExternalPluginManager { e.printStackTrace(); } - - this.updateManager = new UpdateManager(this.externalPluginManager, repositories); } - public void addRepository(String owner, String name) + public void addGHRepository(String owner, String name) { try { - DefaultUpdateRepository respository = new DefaultUpdateRepository(owner + toRepositoryUrl(owner, name), toRepositoryUrl(owner, name)); - updateManager.addRepository(respository); - eventBus.post(ExternalRepositoryChanged.class, new ExternalRepositoryChanged(owner + toRepositoryUrl(owner, name), true)); - saveConfig(); + addRepository("gh:" + owner + "/" + name, toRepositoryUrl(owner, name)); } catch (MalformedURLException e) { @@ -279,6 +347,14 @@ class ExternalPluginManager } } + public void addRepository(String key, URL url) + { + DefaultUpdateRepository respository = new DefaultUpdateRepository(key, url); + updateManager.addRepository(respository); + eventBus.post(ExternalRepositoryChanged.class, new ExternalRepositoryChanged(key, true)); + saveConfig(); + } + public void removeRepository(String owner) { updateManager.removeRepository(owner); @@ -293,8 +369,8 @@ class ExternalPluginManager for (UpdateRepository repository : updateManager.getRepositories()) { config.append(repository.getId()); - config.append(":"); - config.append(repository.getUrl().toString()); + config.append("|"); + config.append(MiscUtils.urlToStringEncoded(repository.getUrl())); config.append(";"); } config.deleteCharAt(config.lastIndexOf(";")); @@ -335,7 +411,8 @@ class ExternalPluginManager } else if (pluginDescriptor.type() == PluginType.EXTERNAL) { - log.error("Class {} is using the the new external plugin loader, it should not use PluginType.EXTERNAL", clazz); + log.error("Class {} is using the the new external plugin loader, it should not use PluginType.EXTERNAL", + clazz); continue; } @@ -375,16 +452,21 @@ class ExternalPluginManager } @SuppressWarnings("unchecked") - private Plugin instantiate(List scannedPlugins, Class clazz, boolean init, boolean initConfig) throws PluginInstantiationException + private Plugin instantiate(List scannedPlugins, Class clazz, boolean init, boolean initConfig) + throws PluginInstantiationException { - net.runelite.client.plugins.PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); + net.runelite.client.plugins.PluginDependency[] pluginDependencies = + clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); List deps = new ArrayList<>(); for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies) { - Optional dependency = Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream()).filter(p -> p.getClass() == pluginDependency.value()).findFirst(); + Optional dependency = + Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream()) + .filter(p -> p.getClass() == pluginDependency.value()).findFirst(); if (!dependency.isPresent()) { - throw new PluginInstantiationException("Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName()); + throw new PluginInstantiationException( + "Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName()); } deps.add(dependency.get()); } @@ -446,7 +528,9 @@ class ExternalPluginManager { runelitePluginManager.startPlugin(plugin); runelitePluginManager.add(plugin); - eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginsMap.get(plugin.getClass().getSimpleName()), plugin, true)); + eventBus.post(ExternalPluginChanged.class, + new ExternalPluginChanged(pluginsMap.get(plugin.getClass().getSimpleName()), plugin, + true)); } catch (PluginInstantiationException e) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java index e01e2aa509..f079c6de00 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java @@ -1,12 +1,10 @@ package net.runelite.client.plugins.openosrs.externals; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.util.concurrent.ScheduledExecutorService; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.plugins.ExternalPluginManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.ImageUtil; import javax.inject.Inject; import javax.swing.ImageIcon; import javax.swing.JLabel; @@ -16,25 +14,33 @@ import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.PluginPanel; -import net.runelite.client.util.ImageUtil; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.ScheduledExecutorService; public class ExternalPluginManagerPanel extends PluginPanel { - private static final ImageIcon ADD_ICON; - private static final ImageIcon ADD_HOVER_ICON; + private static final ImageIcon ADD_ICON_RAW; + private static final ImageIcon ADD_HOVER_ICON_RAW; + private static final ImageIcon ADD_ICON_GH; + private static final ImageIcon ADD_HOVER_ICON_GH; static { - final BufferedImage addIcon = - ImageUtil.recolorImage( - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE - ); - ADD_ICON = new ImageIcon(addIcon); - ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); + final BufferedImage addIconRaw = + ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_raw_icon.png"); + final BufferedImage addIconGh = ImageUtil + .resizeImage(ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14); + ADD_ICON_RAW = new ImageIcon(addIconRaw); + ADD_HOVER_ICON_RAW = new ImageIcon(ImageUtil.alphaOffset(addIconRaw, 0.53f)); + ADD_ICON_GH = new ImageIcon(addIconGh); + ADD_HOVER_ICON_GH = new ImageIcon(ImageUtil.alphaOffset(addIconGh, 0.53f)); } private final ExternalPluginManager externalPluginManager; @@ -73,13 +79,17 @@ public class ExternalPluginManagerPanel extends PluginPanel titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10)); JLabel title = new JLabel(); - JLabel addRepo = new JLabel(ADD_ICON); + JLabel addGHRepo = new JLabel(ADD_ICON_GH); + JLabel addRawRepo = new JLabel(ADD_ICON_RAW); + + JPanel buttonHolder = new JPanel(new BorderLayout()); + buttonHolder.setBorder(new EmptyBorder(0, 0, 0, 0)); title.setText("External Plugin Manager"); title.setForeground(Color.WHITE); - addRepo.setToolTipText("Add new repository"); - addRepo.addMouseListener(new MouseAdapter() + addGHRepo.setToolTipText("Add new GitHub repository"); + addGHRepo.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent mouseEvent) @@ -91,36 +101,120 @@ public class ExternalPluginManagerPanel extends PluginPanel "Github Repository name:", name }; - int option = JOptionPane.showConfirmDialog(null, message, "Add repository", JOptionPane.OK_CANCEL_OPTION); + int option = + JOptionPane.showConfirmDialog(null, message, "Add repository", JOptionPane.OK_CANCEL_OPTION); if (option != JOptionPane.OK_OPTION || owner.getText().equals("") || name.getText().equals("")) { return; } - if (ExternalPluginManager.testRepository(owner.getText(), name.getText())) + if (externalPluginManager.doesGhRepoExist(owner.getText(), name.getText())) { - JOptionPane.showMessageDialog(null, "This doesn't appear to be a valid repository.", "Error!", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "This repository already exists.", "Error!", + JOptionPane.ERROR_MESSAGE); return; } - externalPluginManager.addRepository(owner.getText(), name.getText()); + if (ExternalPluginManager.testGHRepository(owner.getText(), name.getText())) + { + JOptionPane.showMessageDialog(null, "This doesn't appear to be a valid repository.", "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + + externalPluginManager.addGHRepository(owner.getText(), name.getText()); } @Override public void mouseEntered(MouseEvent mouseEvent) { - addRepo.setIcon(ADD_HOVER_ICON); + addGHRepo.setIcon(ADD_HOVER_ICON_GH); } @Override public void mouseExited(MouseEvent mouseEvent) { - addRepo.setIcon(ADD_ICON); + addGHRepo.setIcon(ADD_ICON_GH); } }); + addGHRepo.setBorder(new EmptyBorder(0, 3, 0, 0)); + + addRawRepo.setToolTipText("Add new raw repository"); + addRawRepo.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + JTextField id = new JTextField(); + JTextField url = new JTextField(); + Object[] message = { + "Repository ID:", id, + "Repository URL:", url + }; + + int option = + JOptionPane.showConfirmDialog(null, message, "Add repository", JOptionPane.OK_CANCEL_OPTION); + if (option != JOptionPane.OK_OPTION || id.getText().equals("") || url.getText().equals("")) + { + return; + } + + if (id.getText().startsWith("gh:") || id.getText().contains("|")) + { + JOptionPane.showMessageDialog(null, + "Repository id cannot begin with \"gh:\"\nor contain the pipe character '|'.", "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + + if (externalPluginManager.doesRepoExist(id.getText())) + { + JOptionPane.showMessageDialog(null, + String.format("The repository with id %s already exists.", id.getText()), "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + + URL urlActual; + try + { + urlActual = new URL(url.getText()); + } + catch (MalformedURLException e) + { + JOptionPane.showMessageDialog(null, "This doesn't appear to be a valid repository.", "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + + if (ExternalPluginManager.testRepository(urlActual)) + { + JOptionPane.showMessageDialog(null, "This doesn't appear to be a valid repository.", "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + + externalPluginManager.addRepository(id.getText(), urlActual); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + addRawRepo.setIcon(ADD_HOVER_ICON_RAW); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + addRawRepo.setIcon(ADD_ICON_RAW); + } + }); + addRawRepo.setBorder(new EmptyBorder(0, 0, 0, 3)); titlePanel.add(title, BorderLayout.WEST); - titlePanel.add(addRepo, BorderLayout.EAST); + buttonHolder.add(addRawRepo, BorderLayout.WEST); + buttonHolder.add(addGHRepo, BorderLayout.EAST); + titlePanel.add(buttonHolder, BorderLayout.EAST); return titlePanel; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java index da989c1015..10b088b3a5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java @@ -58,8 +58,12 @@ public class RepositoryBox extends JPanel setLayout(new BorderLayout()); setBackground(ColorScheme.DARKER_GRAY_COLOR); - String name = updateRepository.getId().replace(updateRepository.getUrl().toString(), ""); - String urlString = updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""); + String name = updateRepository.getId(); + String urlString = updateRepository.getUrl().toString(); + if (urlString.startsWith("/")) + { + urlString = urlString.substring(1); + } JPanel titleWrapper = new JPanel(new BorderLayout()); titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -145,11 +149,31 @@ public class RepositoryBox extends JPanel titleWrapper.add(titleActions, BorderLayout.EAST); JMultilineLabel repository = new JMultilineLabel(); - repository.setText(urlString.split("/", 2)[1]); + repository.setText(formatURL(urlString)); repository.setFont(smallFont); repository.setDisabledTextColor(Color.WHITE); + String finalUrlString = urlString; + repository.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + LinkBrowser.browse(formatURL(finalUrlString)); + } + }); + add(titleWrapper, BorderLayout.NORTH); add(repository, BorderLayout.CENTER); } + + private String formatURL(String url) + { + if (url.contains("githubusercontent")) + { + url = url.replace("raw.githubusercontent", "github").replace("/master/", ""); + } + + return url; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java b/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java index 29e7d7b93f..181e2644dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java +++ b/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java @@ -1,12 +1,17 @@ package net.runelite.client.util; -import java.awt.Polygon; -import java.time.Duration; -import java.time.temporal.ChronoUnit; import net.runelite.api.Client; import net.runelite.api.Player; import net.runelite.api.WorldType; import net.runelite.api.coords.WorldPoint; +import java.awt.Polygon; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class MiscUtils { @@ -170,4 +175,28 @@ public class MiscUtils } return str; } + + /** + * Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)} + * + * @param url URL to encode + * @return URL, with path, query and ref encoded + */ + public static String urlToStringEncoded(URL url) + { + String s; + String path = url.getPath() != null ? Stream.of(url.getPath().split("/")) + .map(s2 -> URLEncoder.encode(s2, StandardCharsets.UTF_8)).collect(Collectors.joining("/")) : ""; + return url.getProtocol() + + ':' + + (((s = url.getAuthority()) != null && s.length() > 0) ? "//" + s : "") + + (path) + + (((s = url.getQuery()) != null) ? '?' + urlEncode(s) : "") + + (((s = url.getRef()) != null) ? '#' + urlEncode(s) : ""); + } + + private static String urlEncode(String s) + { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_raw_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_raw_icon.png new file mode 100644 index 0000000000..7f1afebf35 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_raw_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/gh_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/gh_icon.png new file mode 100644 index 0000000000..4d8e66215e Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/gh_icon.png differ