Merge pull request #2394 from ThatGamerBlue/external-raw-oprs

externalmanager: add ability to load a repository from any url
This commit is contained in:
Owain van Brakel
2020-03-03 12:10:19 +01:00
committed by GitHub
6 changed files with 323 additions and 92 deletions

View File

@@ -8,6 +8,42 @@ import com.google.inject.CreationException;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Module; 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.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -32,43 +68,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; 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.EXTERNALPLUGIN_DIR;
import static net.runelite.client.RuneLite.SYSTEM_VERSION; 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 @Slf4j
@Singleton @Singleton
@@ -85,7 +86,6 @@ class ExternalPluginManager
private final ConfigManager configManager; private final ConfigManager configManager;
private final List<Plugin> plugins = new CopyOnWriteArrayList<>(); private final List<Plugin> plugins = new CopyOnWriteArrayList<>();
private final Map<String, String> pluginsMap = new HashMap<>(); private final Map<String, String> pluginsMap = new HashMap<>();
@Getter(AccessLevel.PUBLIC) @Getter(AccessLevel.PUBLIC)
private UpdateManager updateManager; private UpdateManager updateManager;
@@ -111,7 +111,9 @@ class ExternalPluginManager
{ {
boolean debug = RuneLiteProperties.getLauncherVersion() == null && RuneLiteProperties.getPluginPath() != null; 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 @Override
protected PluginDescriptorFinder createPluginDescriptorFinder() protected PluginDescriptorFinder createPluginDescriptorFinder()
@@ -189,22 +191,41 @@ class ExternalPluginManager
this.externalPluginManager.setSystemVersion(SYSTEM_VERSION); 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 private static URL toRepositoryUrl(String owner, String name) throws MalformedURLException
{ {
return new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/"); 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<UpdateRepository> repositories = new ArrayList<>();
try try
{ {
repositories.add(new DefaultUpdateRepository("github", new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/"))); return testRepository(toRepositoryUrl(owner, name));
} }
catch (MalformedURLException e) catch (MalformedURLException e)
{ {
return true; e.printStackTrace();
} }
return false;
}
public static boolean testRepository(URL url)
{
final List<UpdateRepository> repositories = new ArrayList<>();
repositories.add(new DefaultUpdateRepository("repository-testing", url));
DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath()); DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath());
UpdateManager updateManager = new UpdateManager(testPluginManager, repositories); UpdateManager updateManager = new UpdateManager(testPluginManager, repositories);
@@ -243,6 +264,58 @@ class ExternalPluginManager
} }
public void startExternalUpdateManager() 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 try
{ {
@@ -260,18 +333,13 @@ class ExternalPluginManager
{ {
e.printStackTrace(); e.printStackTrace();
} }
this.updateManager = new UpdateManager(this.externalPluginManager, repositories);
} }
public void addRepository(String owner, String name) public void addGHRepository(String owner, String name)
{ {
try try
{ {
DefaultUpdateRepository respository = new DefaultUpdateRepository(owner + toRepositoryUrl(owner, name), toRepositoryUrl(owner, name)); addRepository("gh:" + owner + "/" + name, toRepositoryUrl(owner, name));
updateManager.addRepository(respository);
eventBus.post(ExternalRepositoryChanged.class, new ExternalRepositoryChanged(owner + toRepositoryUrl(owner, name), true));
saveConfig();
} }
catch (MalformedURLException e) 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) public void removeRepository(String owner)
{ {
updateManager.removeRepository(owner); updateManager.removeRepository(owner);
@@ -293,8 +369,8 @@ class ExternalPluginManager
for (UpdateRepository repository : updateManager.getRepositories()) for (UpdateRepository repository : updateManager.getRepositories())
{ {
config.append(repository.getId()); config.append(repository.getId());
config.append(":"); config.append("|");
config.append(repository.getUrl().toString()); config.append(MiscUtils.urlToStringEncoded(repository.getUrl()));
config.append(";"); config.append(";");
} }
config.deleteCharAt(config.lastIndexOf(";")); config.deleteCharAt(config.lastIndexOf(";"));
@@ -335,7 +411,8 @@ class ExternalPluginManager
} }
else if (pluginDescriptor.type() == PluginType.EXTERNAL) 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; continue;
} }
@@ -375,16 +452,21 @@ class ExternalPluginManager
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Plugin instantiate(List<Plugin> scannedPlugins, Class<Plugin> clazz, boolean init, boolean initConfig) throws PluginInstantiationException private Plugin instantiate(List<Plugin> scannedPlugins, Class<Plugin> 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<Plugin> deps = new ArrayList<>(); List<Plugin> deps = new ArrayList<>();
for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies) for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies)
{ {
Optional<Plugin> dependency = Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream()).filter(p -> p.getClass() == pluginDependency.value()).findFirst(); Optional<Plugin> dependency =
Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream())
.filter(p -> p.getClass() == pluginDependency.value()).findFirst();
if (!dependency.isPresent()) 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()); deps.add(dependency.get());
} }
@@ -446,7 +528,9 @@ class ExternalPluginManager
{ {
runelitePluginManager.startPlugin(plugin); runelitePluginManager.startPlugin(plugin);
runelitePluginManager.add(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) catch (PluginInstantiationException e)
{ {

View File

@@ -1,12 +1,10 @@
package net.runelite.client.plugins.openosrs.externals; package net.runelite.client.plugins.openosrs.externals;
import java.awt.BorderLayout; import net.runelite.client.eventbus.EventBus;
import java.awt.Color; import net.runelite.client.plugins.ExternalPluginManager;
import java.awt.Dimension; import net.runelite.client.ui.ColorScheme;
import java.awt.event.MouseAdapter; import net.runelite.client.ui.PluginPanel;
import java.awt.event.MouseEvent; import net.runelite.client.util.ImageUtil;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
@@ -16,25 +14,33 @@ import javax.swing.JScrollPane;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import net.runelite.client.eventbus.EventBus; import java.awt.BorderLayout;
import net.runelite.client.plugins.ExternalPluginManager; import java.awt.Color;
import net.runelite.client.ui.ColorScheme; import java.awt.Dimension;
import net.runelite.client.ui.PluginPanel; import java.awt.event.MouseAdapter;
import net.runelite.client.util.ImageUtil; 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 public class ExternalPluginManagerPanel extends PluginPanel
{ {
private static final ImageIcon ADD_ICON; private static final ImageIcon ADD_ICON_RAW;
private static final ImageIcon ADD_HOVER_ICON; private static final ImageIcon ADD_HOVER_ICON_RAW;
private static final ImageIcon ADD_ICON_GH;
private static final ImageIcon ADD_HOVER_ICON_GH;
static static
{ {
final BufferedImage addIcon = final BufferedImage addIconRaw =
ImageUtil.recolorImage( ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_raw_icon.png");
ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE final BufferedImage addIconGh = ImageUtil
); .resizeImage(ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14);
ADD_ICON = new ImageIcon(addIcon); ADD_ICON_RAW = new ImageIcon(addIconRaw);
ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); 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; private final ExternalPluginManager externalPluginManager;
@@ -73,13 +79,17 @@ public class ExternalPluginManagerPanel extends PluginPanel
titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10)); titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10));
JLabel title = new JLabel(); 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.setText("External Plugin Manager");
title.setForeground(Color.WHITE); title.setForeground(Color.WHITE);
addRepo.setToolTipText("Add new repository"); addGHRepo.setToolTipText("Add new GitHub repository");
addRepo.addMouseListener(new MouseAdapter() addGHRepo.addMouseListener(new MouseAdapter()
{ {
@Override @Override
public void mousePressed(MouseEvent mouseEvent) public void mousePressed(MouseEvent mouseEvent)
@@ -91,36 +101,120 @@ public class ExternalPluginManagerPanel extends PluginPanel
"Github Repository name:", name "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("")) if (option != JOptionPane.OK_OPTION || owner.getText().equals("") || name.getText().equals(""))
{ {
return; 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; 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 @Override
public void mouseEntered(MouseEvent mouseEvent) public void mouseEntered(MouseEvent mouseEvent)
{ {
addRepo.setIcon(ADD_HOVER_ICON); addGHRepo.setIcon(ADD_HOVER_ICON_GH);
} }
@Override @Override
public void mouseExited(MouseEvent mouseEvent) 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(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; return titlePanel;
} }

View File

@@ -58,8 +58,12 @@ public class RepositoryBox extends JPanel
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setBackground(ColorScheme.DARKER_GRAY_COLOR); setBackground(ColorScheme.DARKER_GRAY_COLOR);
String name = updateRepository.getId().replace(updateRepository.getUrl().toString(), ""); String name = updateRepository.getId();
String urlString = updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""); String urlString = updateRepository.getUrl().toString();
if (urlString.startsWith("/"))
{
urlString = urlString.substring(1);
}
JPanel titleWrapper = new JPanel(new BorderLayout()); JPanel titleWrapper = new JPanel(new BorderLayout());
titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
@@ -145,11 +149,31 @@ public class RepositoryBox extends JPanel
titleWrapper.add(titleActions, BorderLayout.EAST); titleWrapper.add(titleActions, BorderLayout.EAST);
JMultilineLabel repository = new JMultilineLabel(); JMultilineLabel repository = new JMultilineLabel();
repository.setText(urlString.split("/", 2)[1]); repository.setText(formatURL(urlString));
repository.setFont(smallFont); repository.setFont(smallFont);
repository.setDisabledTextColor(Color.WHITE); 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(titleWrapper, BorderLayout.NORTH);
add(repository, BorderLayout.CENTER); add(repository, BorderLayout.CENTER);
} }
private String formatURL(String url)
{
if (url.contains("githubusercontent"))
{
url = url.replace("raw.githubusercontent", "github").replace("/master/", "");
}
return url;
}
} }

View File

@@ -1,12 +1,17 @@
package net.runelite.client.util; 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.Client;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.WorldType; import net.runelite.api.WorldType;
import net.runelite.api.coords.WorldPoint; 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 public class MiscUtils
{ {
@@ -170,4 +175,28 @@ public class MiscUtils
} }
return str; 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);
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B