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;
import com.google.common.hash.Hashing;
import com.openosrs.injector.injection.InjectData;
import com.openosrs.injector.injection.InjectTaskHandler;
import com.openosrs.injector.injectors.CreateAnnotations;
@@ -27,6 +28,7 @@ import com.openosrs.injector.transformers.Java8Ifier;
import com.openosrs.injector.transformers.SourceChanger;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
@@ -78,7 +80,8 @@ public class Injector extends InjectData implements InjectTaskHandler
OptionSet options = parser.parse(args);
String oprsVer = options.valueOf(oprsVerOption);
injector.vanilla = load(options.valueOf(vanillaFileOption));
File vanillaFile = options.valueOf(vanillaFileOption);
injector.vanilla = load(vanillaFile);
injector.deobfuscated = load(
new File("../runescape-client/build/libs/runescape-client-" + oprsVer + ".jar"));
injector.rsApi = new RSApi(Objects.requireNonNull(
@@ -95,7 +98,7 @@ public class Injector extends InjectData implements InjectTaskHandler
injector.initToVanilla();
injector.injectVanilla();
save(injector.getVanilla(), options.valueOf(outFileOption), options.valueOf(outModeOption));
save(injector.getVanilla(), options.valueOf(outFileOption), options.valueOf(outModeOption), vanillaFile);
}
public void injectVanilla()
@@ -179,7 +182,7 @@ public class Injector extends InjectData implements InjectTaskHandler
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())
{
@@ -189,7 +192,7 @@ public class Injector extends InjectData implements InjectTaskHandler
}
catch (IOException e)
{
log.info("Failed to delete output directory contents.");
log.lifecycle("Failed to delete output directory contents.");
throw new RuntimeException(e);
}
}
@@ -204,6 +207,18 @@ public class Injector extends InjectData implements InjectTaskHandler
JarUtil.save(group, output);
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)

View File

@@ -351,6 +351,7 @@ public class RuneLite
oprsExternalPluginManager.setupInstance();
oprsExternalPluginManager.startExternalUpdateManager();
oprsExternalPluginManager.startExternalPluginManager();
oprsExternalPluginManager.setOutdated(isOutdated);
// Update external plugins
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
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
@@ -408,10 +413,6 @@ public class RuneLite
SplashScreen.stop();
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

View File

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

View File

@@ -71,6 +71,7 @@ import javax.inject.Singleton;
import javax.swing.JOptionPane;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import net.runelite.client.config.Config;
@@ -127,6 +128,8 @@ public class OPRSExternalPluginManager
@Inject
@Named("safeMode")
private boolean safeMode;
@Setter
boolean isOutdated;
public void setupInstance()
{
@@ -456,6 +459,11 @@ public class OPRSExternalPluginManager
continue;
}
if (!pluginDescriptor.loadWhenOutdated() && isOutdated)
{
continue;
}
if (safeMode && !pluginDescriptor.loadInSafeMode())
{
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 com.openosrs.client.config.OpenOSRSConfig;
import net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel;
import java.awt.image.BufferedImage;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
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.plugins.Plugin;
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.NavigationButton;
import net.runelite.client.util.HotkeyListener;
@@ -61,6 +62,7 @@ public class OpenOSRSPlugin extends Plugin
@Inject
private KeyManager keyManager;
@Nullable
@Inject
private Client client;
@@ -89,6 +91,11 @@ public class OpenOSRSPlugin extends Plugin
@Override
protected void startUp()
{
if (client == null)
{
return;
}
ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class);
final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "externalmanager_icon.png");
@@ -108,7 +115,10 @@ public class OpenOSRSPlugin extends Plugin
@Override
protected void shutDown()
{
clientToolbar.removeNavigation(navButton);
if (navButton != null)
{
clientToolbar.removeNavigation(navButton);
}
}
@Subscribe

View File

@@ -27,7 +27,9 @@
package net.runelite.client.rs;
import com.google.common.base.Strings;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.openosrs.client.OpenOSRS;
import java.applet.Applet;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -37,6 +39,8 @@ import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
@@ -57,6 +61,7 @@ import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties;
import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO;
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.SplashScreen;
import net.runelite.client.util.CountingInputStream;
@@ -66,7 +71,6 @@ import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.FileUtils;
@Slf4j
@SuppressWarnings("deprecation")
@@ -130,18 +134,44 @@ public class ClientLoader implements Supplier<Applet>
StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
FileLock flock = lockfile.lock())
{
SplashScreen.stage(.40, null, "Loading client");
File jarFile = updateCheckMode == AUTO ? PATCHED_CACHE : 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.
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())
SplashScreen.stage(.15, null, "Downloading Old School RuneScape");
try
{
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");
@@ -153,7 +183,7 @@ public class ClientLoader implements Supplier<Applet>
return rs;
}
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
| SecurityException e)
| VerificationException | SecurityException 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
{
try (JarFile jarFile = new JarFile(jar))
@@ -529,7 +601,7 @@ public class ClientLoader implements Supplier<Applet>
if (rs instanceof Client)
{
//.info("client-patch {}", ((Client) rs).getBuildID());
log.info("injected-client {}", OpenOSRS.SYSTEM_VERSION);
}
return rs;