project(externals): Make the external manager more robust
This commit is contained in:
@@ -88,10 +88,9 @@ import org.pf4j.DependencyResolver;
|
|||||||
import org.pf4j.PluginDependency;
|
import org.pf4j.PluginDependency;
|
||||||
import org.pf4j.PluginRuntimeException;
|
import org.pf4j.PluginRuntimeException;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.pf4j.update.DefaultUpdateRepository;
|
|
||||||
import org.pf4j.update.PluginInfo;
|
import org.pf4j.update.PluginInfo;
|
||||||
import org.pf4j.update.UpdateManager;
|
|
||||||
import org.pf4j.update.UpdateRepository;
|
import org.pf4j.update.UpdateRepository;
|
||||||
|
import org.pf4j.update.VerifyException;
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -107,7 +106,7 @@ public class OPRSExternalPluginManager
|
|||||||
@Getter(AccessLevel.PUBLIC)
|
@Getter(AccessLevel.PUBLIC)
|
||||||
private org.pf4j.PluginManager externalPluginManager;
|
private org.pf4j.PluginManager externalPluginManager;
|
||||||
@Getter(AccessLevel.PUBLIC)
|
@Getter(AccessLevel.PUBLIC)
|
||||||
private final List<UpdateRepository> repositories = new ArrayList<>();
|
private final List<OPRSUpdateRepository> repositories = new ArrayList<>();
|
||||||
@Inject
|
@Inject
|
||||||
private OpenOSRSConfig openOSRSConfig;
|
private OpenOSRSConfig openOSRSConfig;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -124,7 +123,7 @@ public class OPRSExternalPluginManager
|
|||||||
@Inject
|
@Inject
|
||||||
private Groups groups;
|
private Groups groups;
|
||||||
@Getter(AccessLevel.PUBLIC)
|
@Getter(AccessLevel.PUBLIC)
|
||||||
private UpdateManager updateManager;
|
private OPRSUpdateManager updateManager;
|
||||||
@Inject
|
@Inject
|
||||||
@Named("safeMode")
|
@Named("safeMode")
|
||||||
private boolean safeMode;
|
private boolean safeMode;
|
||||||
@@ -160,7 +159,7 @@ public class OPRSExternalPluginManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note that {@link UpdateManager#addRepository} checks if the repo exists, however it throws an exception which is bad
|
* Note that {@link OPRSUpdateManager#addRepository} checks if the repo exists, however it throws an exception which is bad
|
||||||
*/
|
*/
|
||||||
public boolean doesRepoExist(String id)
|
public boolean doesRepoExist(String id)
|
||||||
{
|
{
|
||||||
@@ -192,17 +191,17 @@ public class OPRSExternalPluginManager
|
|||||||
|
|
||||||
public static boolean testRepository(URL url, String pluginsJson)
|
public static boolean testRepository(URL url, String pluginsJson)
|
||||||
{
|
{
|
||||||
final List<UpdateRepository> repositories = new ArrayList<>();
|
final List<OPRSUpdateRepository> repositories = new ArrayList<>();
|
||||||
if (pluginsJson != null)
|
if (pluginsJson != null)
|
||||||
{
|
{
|
||||||
repositories.add(new DefaultUpdateRepository("repository-testing", url, pluginsJson));
|
repositories.add(new OPRSUpdateRepository("repository-testing", url, pluginsJson));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
repositories.add(new DefaultUpdateRepository("repository-testing", url));
|
repositories.add(new OPRSUpdateRepository("repository-testing", url));
|
||||||
}
|
}
|
||||||
DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath());
|
DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath());
|
||||||
UpdateManager updateManager = new UpdateManager(testPluginManager, repositories);
|
OPRSUpdateManager updateManager = new OPRSUpdateManager(testPluginManager, repositories);
|
||||||
|
|
||||||
return updateManager.getPlugins().size() <= 0;
|
return updateManager.getPlugins().size() <= 0;
|
||||||
}
|
}
|
||||||
@@ -258,7 +257,7 @@ public class OPRSExternalPluginManager
|
|||||||
loadOldFormat();
|
loadOldFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateManager = new UpdateManager(externalPluginManager, repositories);
|
updateManager = new OPRSUpdateManager(externalPluginManager, repositories);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,11 +309,11 @@ public class OPRSExternalPluginManager
|
|||||||
|
|
||||||
if (pluginJson == null)
|
if (pluginJson == null)
|
||||||
{
|
{
|
||||||
repositories.add(new DefaultUpdateRepository(id, new URL(url)));
|
repositories.add(new OPRSUpdateRepository(id, new URL(url)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
repositories.add(new DefaultUpdateRepository(id, new URL(url), pluginJson));
|
repositories.add(new OPRSUpdateRepository(id, new URL(url), pluginJson));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +339,7 @@ public class OPRSExternalPluginManager
|
|||||||
String id = keyval.substring(0, keyval.lastIndexOf(":https"));
|
String id = keyval.substring(0, keyval.lastIndexOf(":https"));
|
||||||
String url = keyval.substring(keyval.lastIndexOf("https"));
|
String url = keyval.substring(keyval.lastIndexOf("https"));
|
||||||
|
|
||||||
DefaultUpdateRepository defaultRepo = new DefaultUpdateRepository(id, new URL(url));
|
OPRSUpdateRepository defaultRepo = new OPRSUpdateRepository(id, new URL(url));
|
||||||
repositories.add(defaultRepo);
|
repositories.add(defaultRepo);
|
||||||
log.debug("Added Repo: {}", defaultRepo.getUrl());
|
log.debug("Added Repo: {}", defaultRepo.getUrl());
|
||||||
}
|
}
|
||||||
@@ -355,7 +354,7 @@ public class OPRSExternalPluginManager
|
|||||||
openOSRSConfig.setExternalRepositories(DEFAULT_PLUGIN_REPOS);
|
openOSRSConfig.setExternalRepositories(DEFAULT_PLUGIN_REPOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateManager = new UpdateManager(externalPluginManager, repositories);
|
updateManager = new OPRSUpdateManager(externalPluginManager, repositories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addGHRepository(String owner, String name)
|
public void addGHRepository(String owner, String name)
|
||||||
@@ -377,15 +376,15 @@ public class OPRSExternalPluginManager
|
|||||||
|
|
||||||
public void addRepository(String key, URL url, String pluginsJson)
|
public void addRepository(String key, URL url, String pluginsJson)
|
||||||
{
|
{
|
||||||
DefaultUpdateRepository respository;
|
OPRSUpdateRepository respository;
|
||||||
|
|
||||||
if (pluginsJson != null)
|
if (pluginsJson != null)
|
||||||
{
|
{
|
||||||
respository = new DefaultUpdateRepository(key, url, pluginsJson);
|
respository = new OPRSUpdateRepository(key, url, pluginsJson);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
respository = new DefaultUpdateRepository(key, url);
|
respository = new OPRSUpdateRepository(key, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateManager.addRepository(respository);
|
updateManager.addRepository(respository);
|
||||||
@@ -1030,10 +1029,18 @@ public class OPRSExternalPluginManager
|
|||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (VerifyException ex)
|
||||||
|
{
|
||||||
|
// This should never happen but can crash the client
|
||||||
|
log.error("Cannot update plugin '{}', the SHA512 hash mismatches! {}", plugin.id, ex.getMessage());
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch (PluginRuntimeException ex)
|
catch (PluginRuntimeException ex)
|
||||||
{
|
{
|
||||||
// This should never happen but can crash the client
|
// This should never happen but can crash the client
|
||||||
log.warn("Cannot update plugin '{}', the user probably has another client open", plugin.id);
|
log.warn("Cannot update plugin '{}', the user probably has another client open", plugin.id);
|
||||||
|
log.error(String.valueOf(ex));
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1181,8 +1188,6 @@ public class OPRSExternalPluginManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)}
|
|
||||||
*
|
|
||||||
* @param url URL to encode
|
* @param url URL to encode
|
||||||
* @return URL, with path, query and ref encoded
|
* @return URL, with path, query and ref encoded
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,463 @@
|
|||||||
|
package net.runelite.client.plugins;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.pf4j.PluginManager;
|
||||||
|
import org.pf4j.PluginRuntimeException;
|
||||||
|
import org.pf4j.PluginState;
|
||||||
|
import org.pf4j.PluginWrapper;
|
||||||
|
import org.pf4j.VersionManager;
|
||||||
|
import org.pf4j.update.DefaultUpdateRepository;
|
||||||
|
import org.pf4j.update.FileDownloader;
|
||||||
|
import org.pf4j.update.FileVerifier;
|
||||||
|
import org.pf4j.update.PluginInfo;
|
||||||
|
import org.pf4j.update.SimpleFileDownloader;
|
||||||
|
import org.pf4j.update.UpdateManager;
|
||||||
|
import org.pf4j.update.verifier.CompoundVerifier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class OPRSUpdateManager
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(UpdateManager.class);
|
||||||
|
|
||||||
|
private final PluginManager pluginManager;
|
||||||
|
private final VersionManager versionManager;
|
||||||
|
private final String systemVersion;
|
||||||
|
private final Path repositoriesJson;
|
||||||
|
|
||||||
|
// cache last plugin release per plugin id (the key)
|
||||||
|
private final Map<String, PluginInfo.PluginRelease> lastPluginRelease = new HashMap<>();
|
||||||
|
|
||||||
|
protected List<OPRSUpdateRepository> repositories;
|
||||||
|
|
||||||
|
public OPRSUpdateManager(PluginManager pluginManager)
|
||||||
|
{
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
|
||||||
|
versionManager = pluginManager.getVersionManager();
|
||||||
|
systemVersion = pluginManager.getSystemVersion();
|
||||||
|
repositoriesJson = Paths.get("repositories.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
public OPRSUpdateManager(PluginManager pluginManager, List<OPRSUpdateRepository> repos)
|
||||||
|
{
|
||||||
|
this(pluginManager);
|
||||||
|
|
||||||
|
repositories = repos == null ? new ArrayList<>() : repos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PluginInfo> getAvailablePlugins()
|
||||||
|
{
|
||||||
|
List<PluginInfo> availablePlugins = new ArrayList<>();
|
||||||
|
for (PluginInfo plugin : getPlugins())
|
||||||
|
{
|
||||||
|
if (pluginManager.getPlugin(plugin.id) == null)
|
||||||
|
{
|
||||||
|
availablePlugins.add(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availablePlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of plugins that are newer versions of already installed plugins.
|
||||||
|
*
|
||||||
|
* @return list of plugins that have updates
|
||||||
|
*/
|
||||||
|
public List<PluginInfo> getUpdates()
|
||||||
|
{
|
||||||
|
List<PluginInfo> updates = new ArrayList<>();
|
||||||
|
for (PluginWrapper installed : pluginManager.getPlugins())
|
||||||
|
{
|
||||||
|
String pluginId = installed.getPluginId();
|
||||||
|
if (hasPluginUpdate(pluginId))
|
||||||
|
{
|
||||||
|
updates.add(getPluginsMap().get(pluginId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if Update Repositories has newer versions of some of the installed plugins.
|
||||||
|
*
|
||||||
|
* @return true if updates exist
|
||||||
|
*/
|
||||||
|
public boolean hasUpdates()
|
||||||
|
{
|
||||||
|
return getUpdates().size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of plugins from all repos.
|
||||||
|
*
|
||||||
|
* @return List of plugin info
|
||||||
|
*/
|
||||||
|
public List<PluginInfo> getPlugins()
|
||||||
|
{
|
||||||
|
List<PluginInfo> list = new ArrayList<>(getPluginsMap().values());
|
||||||
|
Collections.sort(list);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a map of all plugins from all repos where key is plugin id.
|
||||||
|
*
|
||||||
|
* @return List of plugin info
|
||||||
|
*/
|
||||||
|
public Map<String, PluginInfo> getPluginsMap()
|
||||||
|
{
|
||||||
|
Map<String, PluginInfo> pluginsMap = new HashMap<>();
|
||||||
|
for (OPRSUpdateRepository repository : getRepositories())
|
||||||
|
{
|
||||||
|
pluginsMap.putAll(repository.getPlugins());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluginsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OPRSUpdateRepository> getRepositories()
|
||||||
|
{
|
||||||
|
if (repositories == null && repositoriesJson != null)
|
||||||
|
{
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return repositories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add one {@link DefaultUpdateRepository}.
|
||||||
|
*
|
||||||
|
* @param id of repo
|
||||||
|
* @param url of repo
|
||||||
|
*/
|
||||||
|
public void addRepository(String id, URL url)
|
||||||
|
{
|
||||||
|
for (OPRSUpdateRepository ur : repositories)
|
||||||
|
{
|
||||||
|
if (ur.getId().equals(id))
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Repository with id " + id + " already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories.add(new OPRSUpdateRepository(id, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a repo that was created by client.
|
||||||
|
*
|
||||||
|
* @param newRepo the new UpdateRepository to add to the list
|
||||||
|
*/
|
||||||
|
public void addRepository(OPRSUpdateRepository newRepo)
|
||||||
|
{
|
||||||
|
for (OPRSUpdateRepository ur : repositories)
|
||||||
|
{
|
||||||
|
if (ur.getId().equals(newRepo.getId()))
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Repository with id " + newRepo.getId() + " already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRepo.refresh();
|
||||||
|
repositories.add(newRepo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a repository by id.
|
||||||
|
*
|
||||||
|
* @param id of repository to remove
|
||||||
|
*/
|
||||||
|
public void removeRepository(String id)
|
||||||
|
{
|
||||||
|
for (OPRSUpdateRepository repo : getRepositories())
|
||||||
|
{
|
||||||
|
if (id.equals(repo.getId()))
|
||||||
|
{
|
||||||
|
repositories.remove(repo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.warn("Repository with id " + id + " not found, doing nothing");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes all repositories, so they are forced to refresh list of plugins.
|
||||||
|
*/
|
||||||
|
public synchronized void refresh()
|
||||||
|
{
|
||||||
|
if (repositoriesJson != null && Files.exists(repositoriesJson))
|
||||||
|
{
|
||||||
|
initRepositoriesFromJson();
|
||||||
|
}
|
||||||
|
for (OPRSUpdateRepository updateRepository : repositories)
|
||||||
|
{
|
||||||
|
updateRepository.refresh();
|
||||||
|
}
|
||||||
|
lastPluginRelease.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a plugin by id and version.
|
||||||
|
*
|
||||||
|
* @param id the id of plugin to install
|
||||||
|
* @param version the version of plugin to install, on SemVer format, or null for latest
|
||||||
|
* @return true if installation successful and plugin started
|
||||||
|
* @throws PluginRuntimeException if plugin does not exist in repos or problems during
|
||||||
|
*/
|
||||||
|
public synchronized boolean installPlugin(String id, String version)
|
||||||
|
{
|
||||||
|
// Download to temporary location
|
||||||
|
Path downloaded = downloadPlugin(id, version);
|
||||||
|
|
||||||
|
Path pluginsRoot = pluginManager.getPluginsRoot();
|
||||||
|
Path file = pluginsRoot.resolve(downloaded.getFileName());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Files.move(downloaded, file, REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException(e, "Failed to write file '{}' to plugins folder", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
String pluginId = pluginManager.loadPlugin(file);
|
||||||
|
PluginState state = pluginManager.startPlugin(pluginId);
|
||||||
|
|
||||||
|
return PluginState.STARTED.equals(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads a plugin with given coordinates, runs all {@link FileVerifier}s
|
||||||
|
* and returns a path to the downloaded file.
|
||||||
|
*
|
||||||
|
* @param id of plugin
|
||||||
|
* @param version of plugin or null to download latest
|
||||||
|
* @return Path to file which will reside in a temporary folder in the system default temp area
|
||||||
|
* @throws PluginRuntimeException if download failed
|
||||||
|
*/
|
||||||
|
protected Path downloadPlugin(String id, String version)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PluginInfo.PluginRelease release = findReleaseForPlugin(id, version);
|
||||||
|
Path downloaded = getFileDownloader(id).downloadFile(new URL(release.url));
|
||||||
|
getFileVerifier(id).verify(new FileVerifier.Context(id, release), downloaded);
|
||||||
|
return downloaded;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException(e, "Error during download of plugin {}", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the {@link FileDownloader} to use for this repository.
|
||||||
|
*
|
||||||
|
* @param pluginId the plugin we wish to download
|
||||||
|
* @return FileDownloader instance
|
||||||
|
*/
|
||||||
|
protected FileDownloader getFileDownloader(String pluginId)
|
||||||
|
{
|
||||||
|
for (OPRSUpdateRepository ur : repositories)
|
||||||
|
{
|
||||||
|
if (ur.getPlugin(pluginId) != null && ur.getFileDownloader() != null)
|
||||||
|
{
|
||||||
|
return ur.getFileDownloader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleFileDownloader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a file verifier to use for this plugin. First tries to use custom verifier
|
||||||
|
* configured for the repository, then fallback to the default {@link CompoundVerifier}
|
||||||
|
*
|
||||||
|
* @param pluginId the plugin we wish to download
|
||||||
|
* @return FileVerifier instance
|
||||||
|
*/
|
||||||
|
protected FileVerifier getFileVerifier(String pluginId)
|
||||||
|
{
|
||||||
|
for (OPRSUpdateRepository ur : repositories)
|
||||||
|
{
|
||||||
|
if (ur.getPlugin(pluginId) != null && ur.getFileVerifier() != null)
|
||||||
|
{
|
||||||
|
return ur.getFileVerifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompoundVerifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves Release from id and version.
|
||||||
|
*
|
||||||
|
* @param id of plugin
|
||||||
|
* @param version of plugin or null to locate latest version
|
||||||
|
* @return PluginRelease for downloading
|
||||||
|
* @throws PluginRuntimeException if id or version does not exist
|
||||||
|
*/
|
||||||
|
protected PluginInfo.PluginRelease findReleaseForPlugin(String id, String version)
|
||||||
|
{
|
||||||
|
PluginInfo pluginInfo = getPluginsMap().get(id);
|
||||||
|
if (pluginInfo == null)
|
||||||
|
{
|
||||||
|
log.info("Plugin with id {} does not exist in any repository", id);
|
||||||
|
throw new PluginRuntimeException("Plugin with id {} not found in any repository", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version == null)
|
||||||
|
{
|
||||||
|
return getLastPluginRelease(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PluginInfo.PluginRelease release : pluginInfo.releases)
|
||||||
|
{
|
||||||
|
if (versionManager.compareVersions(version, release.version) == 0 && release.url != null)
|
||||||
|
{
|
||||||
|
return release;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PluginRuntimeException("Plugin {} with version @{} does not exist in the repository", id, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a plugin id to given version or to latest version if {@code version == null}.
|
||||||
|
*
|
||||||
|
* @param id the id of plugin to update
|
||||||
|
* @param version the version to update to, on SemVer format, or null for latest
|
||||||
|
* @return true if update successful
|
||||||
|
* @throws PluginRuntimeException in case the given version is not available, plugin id not already installed etc
|
||||||
|
*/
|
||||||
|
public boolean updatePlugin(String id, String version)
|
||||||
|
{
|
||||||
|
if (pluginManager.getPlugin(id) == null)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException("Plugin {} cannot be updated since it is not installed", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginInfo pluginInfo = getPluginsMap().get(id);
|
||||||
|
if (pluginInfo == null)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException("Plugin {} does not exist in any repository", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPluginUpdate(id))
|
||||||
|
{
|
||||||
|
log.warn("Plugin {} does not have an update available which is compatible with system version {}", id, systemVersion);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download to temp folder
|
||||||
|
Path downloaded = downloadPlugin(id, version);
|
||||||
|
|
||||||
|
if (!pluginManager.deletePlugin(id))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path pluginsRoot = pluginManager.getPluginsRoot();
|
||||||
|
Path file = pluginsRoot.resolve(downloaded.getFileName());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Files.move(downloaded, file, REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException("Failed to write plugin file {} to plugin folder", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newPluginId = pluginManager.loadPlugin(file);
|
||||||
|
PluginState state = pluginManager.startPlugin(newPluginId);
|
||||||
|
|
||||||
|
return PluginState.STARTED.equals(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last release version of this plugin for given system version, regardless of release date.
|
||||||
|
*
|
||||||
|
* @return PluginRelease which has the highest version number
|
||||||
|
*/
|
||||||
|
public PluginInfo.PluginRelease getLastPluginRelease(String id)
|
||||||
|
{
|
||||||
|
PluginInfo pluginInfo = getPluginsMap().get(id);
|
||||||
|
if (pluginInfo == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastPluginRelease.containsKey(id))
|
||||||
|
{
|
||||||
|
for (PluginInfo.PluginRelease release : pluginInfo.releases)
|
||||||
|
{
|
||||||
|
if (systemVersion.equals("0.0.0") || versionManager.checkVersionConstraint(systemVersion, release.requires))
|
||||||
|
{
|
||||||
|
if (lastPluginRelease.get(id) == null)
|
||||||
|
{
|
||||||
|
lastPluginRelease.put(id, release);
|
||||||
|
}
|
||||||
|
else if (versionManager.compareVersions(release.version, lastPluginRelease.get(id).version) > 0)
|
||||||
|
{
|
||||||
|
lastPluginRelease.put(id, release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastPluginRelease.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds whether the newer version of the plugin.
|
||||||
|
*
|
||||||
|
* @return true if there is a newer version available which is compatible with system
|
||||||
|
*/
|
||||||
|
public boolean hasPluginUpdate(String id)
|
||||||
|
{
|
||||||
|
PluginInfo pluginInfo = getPluginsMap().get(id);
|
||||||
|
if (pluginInfo == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String installedVersion = pluginManager.getPlugin(id).getDescriptor().getVersion();
|
||||||
|
PluginInfo.PluginRelease last = getLastPluginRelease(id);
|
||||||
|
|
||||||
|
return last != null && versionManager.compareVersions(last.version, installedVersion) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void initRepositoriesFromJson()
|
||||||
|
{
|
||||||
|
log.debug("Read repositories from '{}'", repositoriesJson);
|
||||||
|
try (FileReader reader = new FileReader(repositoriesJson.toFile()))
|
||||||
|
{
|
||||||
|
Gson gson = new GsonBuilder().create();
|
||||||
|
OPRSUpdateRepository[] items = gson.fromJson(reader, OPRSUpdateRepository[].class);
|
||||||
|
repositories = Arrays.asList(items);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
repositories = Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
package net.runelite.client.plugins;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.pf4j.update.DefaultUpdateRepository;
|
||||||
|
import org.pf4j.update.FileDownloader;
|
||||||
|
import org.pf4j.update.FileVerifier;
|
||||||
|
import org.pf4j.update.PluginInfo;
|
||||||
|
import org.pf4j.update.SimpleFileDownloader;
|
||||||
|
import org.pf4j.update.UpdateRepository;
|
||||||
|
import org.pf4j.update.util.LenientDateTypeAdapter;
|
||||||
|
import org.pf4j.update.verifier.CompoundVerifier;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class OPRSUpdateRepository implements UpdateRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final String DEFAULT_PLUGINS_JSON_FILENAME = "plugins.json";
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(DefaultUpdateRepository.class);
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final URL url;
|
||||||
|
private String pluginsJsonFileName;
|
||||||
|
|
||||||
|
private Map<String, PluginInfo> plugins;
|
||||||
|
|
||||||
|
public OPRSUpdateRepository(String id, URL url)
|
||||||
|
{
|
||||||
|
this(id, url, DEFAULT_PLUGINS_JSON_FILENAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OPRSUpdateRepository(String id, URL url, String pluginsJsonFileName)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.url = url;
|
||||||
|
this.pluginsJsonFileName = pluginsJsonFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getUrl()
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, PluginInfo> getPlugins()
|
||||||
|
{
|
||||||
|
if (plugins == null)
|
||||||
|
{
|
||||||
|
initPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluginInfo getPlugin(String id)
|
||||||
|
{
|
||||||
|
return getPlugins().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPlugins()
|
||||||
|
{
|
||||||
|
Reader pluginsJsonReader;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL pluginsUrl = new URL(getUrl(), getPluginsJsonFileName());
|
||||||
|
log.debug("Read plugins of '{}' repository from '{}'", id, pluginsUrl);
|
||||||
|
pluginsJsonReader = new InputStreamReader(pluginsUrl.openStream());
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
log.error("{}{} could not be loaded (file missing)", getUrl(), getPluginsJsonFileName());
|
||||||
|
plugins = Collections.emptyMap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
plugins = Collections.emptyMap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new LenientDateTypeAdapter()).create();
|
||||||
|
PluginInfo[] items;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
items = gson.fromJson(pluginsJsonReader, PluginInfo[].class);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException | JsonSyntaxException ex)
|
||||||
|
{
|
||||||
|
items = new PluginInfo[]{};
|
||||||
|
}
|
||||||
|
plugins = new HashMap<>(items.length);
|
||||||
|
for (PluginInfo p : items)
|
||||||
|
{
|
||||||
|
for (PluginInfo.PluginRelease r : p.releases)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r.url = new URL(getUrl(), r.url).toString();
|
||||||
|
if (r.date.getTime() == 0)
|
||||||
|
{
|
||||||
|
log.warn("Illegal release date when parsing {}@{}, setting to epoch", p.id, r.version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e)
|
||||||
|
{
|
||||||
|
log.warn("Skipping release {} of plugin {} due to failure to build valid absolute URL. Url was {}{}", r.version, p.id, getUrl(), r.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.setRepositoryId(getId());
|
||||||
|
plugins.put(p.id, p);
|
||||||
|
}
|
||||||
|
log.debug("Found {} plugins in repository '{}'", plugins.size(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes {@code plugins.json} to be read again to look for new updates from repositories.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void refresh()
|
||||||
|
{
|
||||||
|
plugins = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileDownloader getFileDownloader()
|
||||||
|
{
|
||||||
|
return new SimpleFileDownloader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a file verifier to execute on the downloaded file for it to be claimed valid.
|
||||||
|
* May be a CompoundVerifier in order to chain several verifiers.
|
||||||
|
*
|
||||||
|
* @return list of {@link FileVerifier}s
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public FileVerifier getFileVerifier()
|
||||||
|
{
|
||||||
|
return new CompoundVerifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the plugins json file name. Returns {@code plugins.json} if null.
|
||||||
|
*
|
||||||
|
* @return the plugins json file name
|
||||||
|
*/
|
||||||
|
public String getPluginsJsonFileName()
|
||||||
|
{
|
||||||
|
if (pluginsJsonFileName == null)
|
||||||
|
{
|
||||||
|
pluginsJsonFileName = DEFAULT_PLUGINS_JSON_FILENAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluginsJsonFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import net.runelite.client.plugins.OPRSExternalPluginManager;
|
|||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.openosrs.client.events.OPRSPluginChanged;
|
import com.openosrs.client.events.OPRSPluginChanged;
|
||||||
import com.openosrs.client.events.OPRSRepositoryChanged;
|
import com.openosrs.client.events.OPRSRepositoryChanged;
|
||||||
|
import net.runelite.client.plugins.OPRSUpdateManager;
|
||||||
|
import net.runelite.client.plugins.OPRSUpdateRepository;
|
||||||
import net.runelite.client.util.DeferredDocumentChangedListener;
|
import net.runelite.client.util.DeferredDocumentChangedListener;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
@@ -46,8 +48,6 @@ import net.runelite.client.util.ImageUtil;
|
|||||||
import net.runelite.client.util.SwingUtil;
|
import net.runelite.client.util.SwingUtil;
|
||||||
import org.pf4j.VersionManager;
|
import org.pf4j.VersionManager;
|
||||||
import org.pf4j.update.PluginInfo;
|
import org.pf4j.update.PluginInfo;
|
||||||
import org.pf4j.update.UpdateManager;
|
|
||||||
import org.pf4j.update.UpdateRepository;
|
|
||||||
import org.pf4j.update.VerifyException;
|
import org.pf4j.update.VerifyException;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -84,7 +84,7 @@ public class PluginsPanel extends JPanel
|
|||||||
|
|
||||||
private final OPRSExternalPluginManager externalPluginManager;
|
private final OPRSExternalPluginManager externalPluginManager;
|
||||||
private final VersionManager versionManager;
|
private final VersionManager versionManager;
|
||||||
private final UpdateManager updateManager;
|
private final OPRSUpdateManager updateManager;
|
||||||
|
|
||||||
private final IconTextField searchBar = new IconTextField();
|
private final IconTextField searchBar = new IconTextField();
|
||||||
private final JPanel filterwrapper = new JPanel(new BorderLayout(0, 10));
|
private final JPanel filterwrapper = new JPanel(new BorderLayout(0, 10));
|
||||||
@@ -167,7 +167,7 @@ public class PluginsPanel extends JPanel
|
|||||||
{
|
{
|
||||||
List<String> repositories = new ArrayList<>();
|
List<String> repositories = new ArrayList<>();
|
||||||
repositories.add("All");
|
repositories.add("All");
|
||||||
for (UpdateRepository updateRepository : this.updateManager.getRepositories())
|
for (OPRSUpdateRepository updateRepository : this.updateManager.getRepositories())
|
||||||
{
|
{
|
||||||
repositories.add(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""));
|
repositories.add(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""));
|
||||||
}
|
}
|
||||||
@@ -366,7 +366,7 @@ public class PluginsPanel extends JPanel
|
|||||||
{
|
{
|
||||||
boolean filtered = true;
|
boolean filtered = true;
|
||||||
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
||||||
for (UpdateRepository updateRepository : updateManager.getRepositories())
|
for (OPRSUpdateRepository updateRepository : updateManager.getRepositories())
|
||||||
{
|
{
|
||||||
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
||||||
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
||||||
@@ -424,7 +424,7 @@ public class PluginsPanel extends JPanel
|
|||||||
{
|
{
|
||||||
boolean filtered = true;
|
boolean filtered = true;
|
||||||
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
||||||
for (UpdateRepository updateRepository : updateManager.getRepositories())
|
for (OPRSUpdateRepository updateRepository : updateManager.getRepositories())
|
||||||
{
|
{
|
||||||
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
||||||
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ import javax.swing.JLabel;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import net.runelite.client.plugins.OPRSUpdateRepository;
|
||||||
import net.runelite.client.ui.ColorScheme;
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.ui.FontManager;
|
import net.runelite.client.ui.FontManager;
|
||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
import net.runelite.client.util.LinkBrowser;
|
import net.runelite.client.util.LinkBrowser;
|
||||||
import org.pf4j.update.PluginInfo;
|
import org.pf4j.update.PluginInfo;
|
||||||
import org.pf4j.update.UpdateRepository;
|
|
||||||
|
|
||||||
public class RepositoryBox extends JPanel
|
public class RepositoryBox extends JPanel
|
||||||
{
|
{
|
||||||
@@ -53,7 +53,7 @@ public class RepositoryBox extends JPanel
|
|||||||
DISCORD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(discordImg, 0.53f));
|
DISCORD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(discordImg, 0.53f));
|
||||||
}
|
}
|
||||||
|
|
||||||
RepositoryBox(OPRSExternalPluginManager externalPluginManager, UpdateRepository updateRepository)
|
RepositoryBox(OPRSExternalPluginManager externalPluginManager, OPRSUpdateRepository updateRepository)
|
||||||
{
|
{
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import net.runelite.client.eventbus.EventBus;
|
import net.runelite.client.eventbus.EventBus;
|
||||||
import net.runelite.client.eventbus.Subscribe;
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
|
import net.runelite.client.plugins.OPRSUpdateRepository;
|
||||||
import net.runelite.client.ui.ColorScheme;
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import org.pf4j.update.UpdateRepository;
|
|
||||||
|
|
||||||
public class RepositoryPanel extends JPanel
|
public class RepositoryPanel extends JPanel
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ public class RepositoryPanel extends JPanel
|
|||||||
c.gridy = 0;
|
c.gridy = 0;
|
||||||
c.insets = new Insets(5, 0, 0, 0);
|
c.insets = new Insets(5, 0, 0, 0);
|
||||||
|
|
||||||
for (UpdateRepository repository : externalPluginManager.getRepositories())
|
for (OPRSUpdateRepository repository : externalPluginManager.getRepositories())
|
||||||
{
|
{
|
||||||
final RepositoryBox p = new RepositoryBox(externalPluginManager, repository);
|
final RepositoryBox p = new RepositoryBox(externalPluginManager, repository);
|
||||||
add(p, c);
|
add(p, c);
|
||||||
|
|||||||
Reference in New Issue
Block a user