From b37d46e488ff2a3ee131ac149b51f6951b93840a Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Feb 2022 16:53:51 -0500 Subject: [PATCH] client: add runtime config A form of this was added in 1.7.11.2 in e8ea616fc24691177cb2de18698f13ba17448a11, but did not bind the config to the RuneLite guice module. --- .../java/net/runelite/client/RuneLite.java | 2 + .../net/runelite/client/RuneLiteModule.java | 39 ++++++ .../runelite/client/RuneLiteProperties.java | 6 + .../net/runelite/client/RuntimeConfig.java | 35 +++++ .../runelite/client/RuntimeConfigLoader.java | 123 ++++++++++++++++++ .../net/runelite/client/runelite.properties | 3 +- .../client/plugins/PluginManagerTest.java | 2 +- 7 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index fad5c22eb3..6abb3b2208 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -215,6 +215,7 @@ public class RuneLite try { final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config")); + final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient); new Thread(() -> { @@ -249,6 +250,7 @@ public class RuneLite injector = Guice.createInjector(new RuneLiteModule( okHttpClient, clientLoader, + runtimeConfigLoader, developerMode, options.has("safe-mode"), options.valueOf(sessionfile), diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 569f05e20d..04744534c8 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -25,12 +25,15 @@ package net.runelite.client; import com.google.common.base.Strings; +import com.google.common.math.DoubleMath; import com.google.gson.Gson; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.binder.ConstantBindingBuilder; import com.google.inject.name.Names; import java.applet.Applet; import java.io.File; +import java.util.Map; import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -63,6 +66,7 @@ public class RuneLiteModule extends AbstractModule { private final OkHttpClient okHttpClient; private final Supplier clientLoader; + private final Supplier configSupplier; private final boolean developerMode; private final boolean safeMode; private final File sessionfile; @@ -71,12 +75,40 @@ public class RuneLiteModule extends AbstractModule @Override protected void configure() { + // bind properties Properties properties = RuneLiteProperties.getProperties(); for (String key : properties.stringPropertyNames()) { String value = properties.getProperty(key); bindConstant().annotatedWith(Names.named(key)).to(value); } + + // bind runtime config + RuntimeConfig runtimeConfig = configSupplier.get(); + if (runtimeConfig != null && runtimeConfig.getProps() != null) + { + for (Map.Entry entry : runtimeConfig.getProps().entrySet()) + { + if (entry.getValue() instanceof String) + { + ConstantBindingBuilder binder = bindConstant().annotatedWith(Names.named(entry.getKey())); + binder.to((String) entry.getValue()); + } + else if (entry.getValue() instanceof Double) + { + ConstantBindingBuilder binder = bindConstant().annotatedWith(Names.named(entry.getKey())); + if (DoubleMath.isMathematicalInteger((double) entry.getValue())) + { + binder.to((int) (double) entry.getValue()); + } + else + { + binder.to((double) entry.getValue()); + } + } + } + } + bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode); bindConstant().annotatedWith(Names.named("safeMode")).to(safeMode); bind(File.class).annotatedWith(Names.named("sessionfile")).toInstance(sessionfile); @@ -116,6 +148,13 @@ public class RuneLiteModule extends AbstractModule return applet instanceof Client ? (Client) applet : null; } + @Provides + @Singleton + RuntimeConfig provideRuntimeConfig() + { + return configSupplier.get(); + } + @Provides @Singleton RuneLiteConfig provideConfig(ConfigManager configManager) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index e598439c2d..1bead1101c 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -48,6 +48,7 @@ public class RuneLiteProperties private static final String PLUGINHUB_BASE = "runelite.pluginhub.url"; private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final String API_BASE = "runelite.api.base"; + private static final String RUNELITE_CONFIG = "runelite.config"; @Getter(AccessLevel.PACKAGE) private static final Properties properties = new Properties(); @@ -130,4 +131,9 @@ public class RuneLiteProperties { return properties.getProperty(API_BASE); } + + public static String getRuneLiteConfig() + { + return properties.getProperty(RUNELITE_CONFIG); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java new file mode 100644 index 0000000000..5d3fb80d94 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client; + +import java.util.Collections; +import java.util.Map; +import lombok.Data; + +@Data +public class RuntimeConfig +{ + private Map props = Collections.emptyMap(); +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java new file mode 100644 index 0000000000..aa82fe44dc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client; + +import com.google.common.base.Strings; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +class RuntimeConfigLoader implements Supplier +{ + private final OkHttpClient okHttpClient; + private final CompletableFuture configFuture; + + public RuntimeConfigLoader(OkHttpClient okHttpClient) + { + this.okHttpClient = okHttpClient; + configFuture = fetch(); + } + + @Override + public RuntimeConfig get() + { + try + { + return configFuture.get(); + } + catch (InterruptedException | ExecutionException e) + { + log.error("error fetching runtime config", e); + return null; + } + } + + private CompletableFuture fetch() + { + CompletableFuture future = new CompletableFuture<>(); + + String prop = System.getProperty("runelite.rtconf"); + if (!Strings.isNullOrEmpty(prop)) + { + try + { + log.info("Using local runtime config"); + + String strConf = new String(Files.readAllBytes(Paths.get(prop)), StandardCharsets.UTF_8); + RuntimeConfig conf = RuneLiteAPI.GSON.fromJson(strConf, RuntimeConfig.class); + future.complete(conf); + return future; + } + catch (IOException e) + { + throw new RuntimeException("failed to load override runtime config", e); + } + } + + Request request = new Request.Builder() + .url(RuneLiteProperties.getRuneLiteConfig()) + .build(); + + okHttpClient.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + future.completeExceptionally(e); + } + + @Override + public void onResponse(Call call, Response response) + { + try // NOPMD: UseTryWithResources + { + RuntimeConfig config = RuneLiteAPI.GSON.fromJson(response.body().charStream(), RuntimeConfig.class); + future.complete(config); + } + catch (Throwable ex) + { + future.completeExceptionally(ex); + } + finally + { + response.close(); + } + } + }); + return future; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index 8952e01593..049a6443fb 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -18,4 +18,5 @@ runelite.imgur.client.id=30d71e5f6860809 runelite.api.base=https://api.runelite.net/runelite-${project.version} runelite.session=https://api.runelite.net/session runelite.static.base=https://static.runelite.net -runelite.ws=https://api.runelite.net/ws \ No newline at end of file +runelite.ws=https://api.runelite.net/ws +runelite.config=https://static.runelite.net/config.json \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java index 50692a915a..983bf613f2 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -89,7 +89,7 @@ public class PluginManagerTest .thenThrow(new RuntimeException("in plugin manager test")); Injector injector = Guice.createInjector(Modules - .override(new RuneLiteModule(okHttpClient, () -> null, true, false, + .override(new RuneLiteModule(okHttpClient, () -> null, () -> null, true, false, RuneLite.DEFAULT_SESSION_FILE, RuneLite.DEFAULT_CONFIG_FILE)) .with(BoundFieldModule.of(this)));