Merge pull request #3002 from open-osrs/bug/client-loader-vanilla

client: dont bug out if vanilla changes without bumping rev number
This commit is contained in:
ThatGamerBlue
2021-06-15 18:04:02 +01:00
committed by GitHub
6 changed files with 129 additions and 23 deletions

View File

@@ -7,6 +7,7 @@
*/ */
package com.openosrs.injector; package com.openosrs.injector;
import com.google.common.hash.Hashing;
import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.injection.InjectData;
import com.openosrs.injector.injection.InjectTaskHandler; import com.openosrs.injector.injection.InjectTaskHandler;
import com.openosrs.injector.injectors.CreateAnnotations; import com.openosrs.injector.injectors.CreateAnnotations;
@@ -27,6 +28,7 @@ import com.openosrs.injector.transformers.Java8Ifier;
import com.openosrs.injector.transformers.SourceChanger; import com.openosrs.injector.transformers.SourceChanger;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Comparator; import java.util.Comparator;
@@ -78,7 +80,8 @@ public class Injector extends InjectData implements InjectTaskHandler
OptionSet options = parser.parse(args); OptionSet options = parser.parse(args);
String oprsVer = options.valueOf(oprsVerOption); String oprsVer = options.valueOf(oprsVerOption);
injector.vanilla = load(options.valueOf(vanillaFileOption)); File vanillaFile = options.valueOf(vanillaFileOption);
injector.vanilla = load(vanillaFile);
injector.deobfuscated = load( injector.deobfuscated = load(
new File("../runescape-client/build/libs/runescape-client-" + oprsVer + ".jar")); new File("../runescape-client/build/libs/runescape-client-" + oprsVer + ".jar"));
injector.rsApi = new RSApi(Objects.requireNonNull( injector.rsApi = new RSApi(Objects.requireNonNull(
@@ -95,7 +98,7 @@ public class Injector extends InjectData implements InjectTaskHandler
injector.initToVanilla(); injector.initToVanilla();
injector.injectVanilla(); injector.injectVanilla();
save(injector.getVanilla(), options.valueOf(outFileOption), options.valueOf(outModeOption)); save(injector.getVanilla(), options.valueOf(outFileOption), options.valueOf(outModeOption), vanillaFile);
} }
public void injectVanilla() public void injectVanilla()
@@ -179,7 +182,7 @@ public class Injector extends InjectData implements InjectTaskHandler
log.lifecycle("{} {}", name, transformer.getCompletionMsg()); log.lifecycle("{} {}", name, transformer.getCompletionMsg());
} }
private static void save(ClassGroup group, File output, OutputMode mode) private static void save(ClassGroup group, File output, OutputMode mode, File vanillaFile)
{ {
if (output.exists()) if (output.exists())
{ {
@@ -189,7 +192,7 @@ public class Injector extends InjectData implements InjectTaskHandler
} }
catch (IOException e) catch (IOException e)
{ {
log.info("Failed to delete output directory contents."); log.lifecycle("Failed to delete output directory contents.");
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@@ -204,6 +207,18 @@ public class Injector extends InjectData implements InjectTaskHandler
JarUtil.save(group, output); JarUtil.save(group, output);
break; break;
} }
try
{
String hash = com.google.common.io.Files.asByteSource(vanillaFile).hash(Hashing.sha256()).toString();
log.lifecycle("Writing vanilla hash: {}", hash);
Files.write(output.getParentFile().toPath().resolve("client.hash"), hash.getBytes(StandardCharsets.UTF_8));
}
catch (IOException ex)
{
log.lifecycle("Failed to write vanilla hash file");
throw new RuntimeException(ex);
}
} }
private static void saveFiles(ClassGroup group, File outDir) private static void saveFiles(ClassGroup group, File outDir)

View File

@@ -351,6 +351,7 @@ public class RuneLite
oprsExternalPluginManager.setupInstance(); oprsExternalPluginManager.setupInstance();
oprsExternalPluginManager.startExternalUpdateManager(); oprsExternalPluginManager.startExternalUpdateManager();
oprsExternalPluginManager.startExternalPluginManager(); oprsExternalPluginManager.startExternalPluginManager();
oprsExternalPluginManager.setOutdated(isOutdated);
// Update external plugins // Update external plugins
oprsExternalPluginManager.update(); //TODO: Re-enable after fixing actions for new repo oprsExternalPluginManager.update(); //TODO: Re-enable after fixing actions for new repo
@@ -400,6 +401,10 @@ public class RuneLite
// legacy method, i cant figure out how to make it work without garbage // legacy method, i cant figure out how to make it work without garbage
eventBus.register(xpDropManager.get()); eventBus.register(xpDropManager.get());
//Set the world if specified via CLI args - will not work until clientUI.init is called
Optional<Integer> worldArg = Optional.ofNullable(System.getProperty("cli.world")).map(Integer::parseInt);
worldArg.ifPresent(this::setWorld);
} }
// Start plugins // Start plugins
@@ -408,10 +413,6 @@ public class RuneLite
SplashScreen.stop(); SplashScreen.stop();
clientUI.show(); clientUI.show();
//Set the world if specified via CLI args - will not work until clientUI.init is called
Optional<Integer> worldArg = Optional.ofNullable(System.getProperty("cli.world")).map(Integer::parseInt);
worldArg.ifPresent(this::setWorld);
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -60,7 +60,7 @@ public class WorldService
private WorldResult worlds; private WorldResult worlds;
@Inject @Inject
private WorldService(Client client, ScheduledExecutorService scheduledExecutorService, OkHttpClient okHttpClient, private WorldService(@Nullable Client client, ScheduledExecutorService scheduledExecutorService, OkHttpClient okHttpClient,
EventBus eventBus) EventBus eventBus)
{ {
this.client = client; this.client = client;

View File

@@ -71,6 +71,7 @@ import javax.inject.Singleton;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.config.Config; import net.runelite.client.config.Config;
@@ -127,6 +128,8 @@ public class OPRSExternalPluginManager
@Inject @Inject
@Named("safeMode") @Named("safeMode")
private boolean safeMode; private boolean safeMode;
@Setter
boolean isOutdated;
public void setupInstance() public void setupInstance()
{ {
@@ -456,6 +459,11 @@ public class OPRSExternalPluginManager
continue; continue;
} }
if (!pluginDescriptor.loadWhenOutdated() && isOutdated)
{
continue;
}
if (safeMode && !pluginDescriptor.loadInSafeMode()) if (safeMode && !pluginDescriptor.loadInSafeMode())
{ {
log.debug("Disabling {} due to safe mode", clazz); log.debug("Disabling {} due to safe mode", clazz);

View File

@@ -28,8 +28,8 @@ package net.runelite.client.plugins.openosrs;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import com.openosrs.client.config.OpenOSRSConfig; import com.openosrs.client.config.OpenOSRSConfig;
import net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -40,6 +40,7 @@ import net.runelite.client.events.ConfigChanged;
import net.runelite.client.input.KeyManager; import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel;
import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.HotkeyListener; import net.runelite.client.util.HotkeyListener;
@@ -61,6 +62,7 @@ public class OpenOSRSPlugin extends Plugin
@Inject @Inject
private KeyManager keyManager; private KeyManager keyManager;
@Nullable
@Inject @Inject
private Client client; private Client client;
@@ -89,6 +91,11 @@ public class OpenOSRSPlugin extends Plugin
@Override @Override
protected void startUp() protected void startUp()
{ {
if (client == null)
{
return;
}
ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class); ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class);
final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "externalmanager_icon.png"); final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "externalmanager_icon.png");
@@ -108,7 +115,10 @@ public class OpenOSRSPlugin extends Plugin
@Override @Override
protected void shutDown() protected void shutDown()
{ {
clientToolbar.removeNavigation(navButton); if (navButton != null)
{
clientToolbar.removeNavigation(navButton);
}
} }
@Subscribe @Subscribe

View File

@@ -27,7 +27,9 @@
package net.runelite.client.rs; package net.runelite.client.rs;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.openosrs.client.OpenOSRS;
import java.applet.Applet; import java.applet.Applet;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -37,6 +39,8 @@ import java.io.OutputStream;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
@@ -57,6 +61,7 @@ import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties; import net.runelite.client.RuneLiteProperties;
import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO; import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO;
import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.NONE;
import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA;
import net.runelite.client.ui.FatalErrorDialog; import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.ui.SplashScreen; import net.runelite.client.ui.SplashScreen;
import net.runelite.client.util.CountingInputStream; import net.runelite.client.util.CountingInputStream;
@@ -66,7 +71,6 @@ import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.apache.commons.io.FileUtils;
@Slf4j @Slf4j
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@@ -130,18 +134,44 @@ public class ClientLoader implements Supplier<Applet>
StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE); StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
FileLock flock = lockfile.lock()) FileLock flock = lockfile.lock())
{ {
SplashScreen.stage(.40, null, "Loading client"); SplashScreen.stage(.15, null, "Downloading Old School RuneScape");
File jarFile = updateCheckMode == AUTO ? PATCHED_CACHE : VANILLA_CACHE; try
// create the classloader for the jar while we hold the lock, and eagerly load and link all classes
// in the jar. Otherwise the jar can change on disk and can break future classloads.
File oprsInjected = new File(System.getProperty("user.home") + "/.openosrs/cache/injected-client.jar");
InputStream initialStream = RuneLite.class.getResourceAsStream(INJECTED_CLIENT_NAME);
if (!oprsInjected.exists() || oprsInjected.length() != RuneLite.class.getResource(INJECTED_CLIENT_NAME).getFile().length())
{ {
FileUtils.copyInputStreamToFile(initialStream, oprsInjected); updateVanilla(config);
}
catch (IOException ex)
{
// try again with the fallback config and gamepack
if (javConfigUrl.equals(RuneLiteProperties.getJavConfig()) && !config.isFallback())
{
log.warn("Unable to download game client, attempting to use fallback config", ex);
config = downloadFallbackConfig();
updateVanilla(config);
}
else
{
throw ex;
}
} }
classLoader = createJarClassLoader(oprsInjected); if (!checkVanillaHash())
{
log.error("Injected client vanilla hash doesn't match, loading vanilla client.");
updateCheckMode = VANILLA;
}
SplashScreen.stage(.40, null, "Loading client");
File oprsInjected = new File(System.getProperty("user.home") + "/.openosrs/cache/injected-client.jar");
if (updateCheckMode == AUTO)
{
writeInjectedClient(oprsInjected);
}
File jarFile = updateCheckMode == AUTO ? oprsInjected : VANILLA_CACHE;
// create the classloader for the jar while we hold the lock, and eagerly load and link all classes
// in the jar. Otherwise the jar can change on disk and can break future classloads.
classLoader = createJarClassLoader(jarFile);
} }
SplashScreen.stage(.465, "Starting", "Starting Old School RuneScape"); SplashScreen.stage(.465, "Starting", "Starting Old School RuneScape");
@@ -153,7 +183,7 @@ public class ClientLoader implements Supplier<Applet>
return rs; return rs;
} }
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
| SecurityException e) | VerificationException | SecurityException e)
{ {
log.error("Error loading RS!", e); log.error("Error loading RS!", e);
@@ -457,6 +487,48 @@ public class ClientLoader implements Supplier<Applet>
*/ */
} }
private boolean checkVanillaHash()
{
try (InputStream is = ClientLoader.class.getResourceAsStream("/client.hash"))
{
String storedHash = new String(is.readAllBytes(), StandardCharsets.UTF_8);
String vanillaHash = Hashing.sha256().hashBytes(Files.readAllBytes(VANILLA_CACHE.toPath())).toString();
log.debug("Stored vanilla hash: {}", storedHash);
log.debug("Actual vanilla hash: {}", vanillaHash);
return vanillaHash.equals(storedHash);
}
catch (IOException ex)
{
log.error("Failed to compare vanilla hashes, loading vanilla", ex);
}
return false;
}
private void writeInjectedClient(File cachedInjected) throws IOException
{
String cachedHash = "";
try
{
cachedHash = com.google.common.io.Files.asByteSource(cachedInjected).hash(Hashing.sha256()).toString();
}
catch (IOException ex)
{
log.error("Failed to calculate hash for cached file, falling back to vanilla", ex);
updateCheckMode = VANILLA;
return;
}
byte[] currentInjected = ByteStreams.toByteArray(ClientLoader.class.getResourceAsStream(INJECTED_CLIENT_NAME));
String currentHash = Hashing.sha256().hashBytes(currentInjected).toString();
if (!cachedInjected.exists() || !currentHash.equals(cachedHash))
{
Files.write(cachedInjected.toPath(), currentInjected);
}
}
private ClassLoader createJarClassLoader(File jar) throws IOException, ClassNotFoundException private ClassLoader createJarClassLoader(File jar) throws IOException, ClassNotFoundException
{ {
try (JarFile jarFile = new JarFile(jar)) try (JarFile jarFile = new JarFile(jar))
@@ -529,7 +601,7 @@ public class ClientLoader implements Supplier<Applet>
if (rs instanceof Client) if (rs instanceof Client)
{ {
//.info("client-patch {}", ((Client) rs).getBuildID()); log.info("injected-client {}", OpenOSRS.SYSTEM_VERSION);
} }
return rs; return rs;