From 43ec8d94399602dffb8bab7b609d50c52bc16258 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 2 Feb 2018 05:12:33 -0700 Subject: [PATCH 1/2] move session management into the SessionManager from the AccountPlugin --- .../client/account/SessionManager.java | 93 +++++++++++++++- .../client/plugins/account/AccountPlugin.java | 100 ++---------------- 2 files changed, 100 insertions(+), 93 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java index 87527a8f43..ee74cc1fae 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java @@ -25,12 +25,17 @@ package net.runelite.client.account; import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; import com.google.gson.Gson; +import java.awt.Desktop; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Instant; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import javax.inject.Singleton; @@ -41,6 +46,8 @@ import net.runelite.client.config.ConfigManager; import net.runelite.api.events.SessionClose; import net.runelite.api.events.SessionOpen; import net.runelite.http.api.account.AccountClient; +import net.runelite.http.api.account.OAuthResponse; +import net.runelite.http.api.ws.messages.LoginResponse; @Singleton @Slf4j @@ -52,8 +59,7 @@ public class SessionManager @Getter private AccountSession accountSession; - @Inject - private EventBus eventBus; + private final EventBus eventBus; @Inject private ConfigManager configManager; @@ -61,6 +67,15 @@ public class SessionManager @Inject private ScheduledExecutorService executor; + private final AccountClient loginClient = new AccountClient(); + + @Inject + public SessionManager(EventBus eventBus) + { + this.eventBus = eventBus; + eventBus.register(this); + } + public void loadSession() { if (!SESSION_FILE.exists()) @@ -165,6 +180,16 @@ public class SessionManager log.debug("Logging out of account {}", accountSession.getUsername()); + AccountClient client = new AccountClient(accountSession.getUuid()); + try + { + client.logout(); + } + catch (IOException ex) + { + log.warn("Unable to logout of session", ex); + } + accountSession = null; // No more account // Restore config @@ -172,4 +197,68 @@ public class SessionManager eventBus.post(new SessionClose()); } + + public void login() + { + OAuthResponse login; + + try + { + login = loginClient.login(); + } + catch (IOException ex) + { + log.warn("Unable to get oauth url", ex); + return; + } + + // Create new session + openSession(new AccountSession(login.getUid(), Instant.now())); + + if (!Desktop.isDesktopSupported()) + { + log.info("Desktop is not supported. Visit {}", login.getOauthUrl()); + return; + } + + Desktop desktop = Desktop.getDesktop(); + if (!desktop.isSupported(Desktop.Action.BROWSE)) + { + log.info("Desktop browser is not supported. Visit {}", login.getOauthUrl()); + return; + } + + try + { + desktop.browse(new URI(login.getOauthUrl())); + + log.debug("Opened browser to {}", login.getOauthUrl()); + } + catch (IOException | URISyntaxException ex) + { + log.warn("Unable to open login page", ex); + } + } + + @Subscribe + public void onLogin(LoginResponse loginResponse) + { + log.debug("Now logged in as {}", loginResponse.getUsername()); + + AccountSession session = getAccountSession(); + session.setUsername(loginResponse.getUsername()); + + // Open session, again, now that we have a username + // This triggers onSessionOpen + openSession(session); + + // Save session to disk + saveSession(); + } + + public void logout() + { + closeSession(); + deleteSession(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java index 23890a8cea..b912aef0b9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java @@ -25,16 +25,12 @@ package net.runelite.client.plugins.account; import com.google.common.eventbus.Subscribe; -import java.awt.Desktop; import java.awt.event.ActionEvent; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.time.Instant; import java.util.concurrent.ScheduledExecutorService; import javax.imageio.ImageIO; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.SessionClose; import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; import net.runelite.api.events.SessionOpen; @@ -44,9 +40,6 @@ import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.PluginToolbar; import net.runelite.client.util.RunnableExceptionLogger; -import net.runelite.http.api.account.AccountClient; -import net.runelite.http.api.account.OAuthResponse; -import net.runelite.http.api.ws.messages.LoginResponse; @PluginDescriptor( name = "Account plugin", @@ -67,8 +60,6 @@ public class AccountPlugin extends Plugin private NavigationButton loginButton; private NavigationButton logoutButton; - private final AccountClient loginClient = new AccountClient(); - @Override protected void startUp() throws Exception { @@ -88,91 +79,22 @@ public class AccountPlugin extends Plugin private void loginClick(ActionEvent ae) { - executor.execute(RunnableExceptionLogger.wrap(this::openLoginPage)); + executor.execute(RunnableExceptionLogger.wrap(sessionManager::login)); } private void logoutClick(ActionEvent ae) { - // Destroy session - AccountSession session = sessionManager.getAccountSession(); - if (session != null) - { - AccountClient client = new AccountClient(session.getUuid()); - try - { - client.logout(); - } - catch (IOException ex) - { - log.warn("Unable to logout of session", ex); - } - } - - sessionManager.closeSession(); // remove session from client - sessionManager.deleteSession(); // delete saved session file + sessionManager.logout(); + } + @Subscribe + public void onSessionClose(SessionClose e) + { // Replace logout nav button with login PluginToolbar navigationPanel = ui.getPluginToolbar(); navigationPanel.removeNavigation(logoutButton); navigationPanel.addNavigation(loginButton); - } - - private void openLoginPage() - { - OAuthResponse login; - - try - { - login = loginClient.login(); - } - catch (IOException ex) - { - log.warn("Unable to get oauth url", ex); - return; - } - - // Create new session - sessionManager.openSession(new AccountSession(login.getUid(), Instant.now())); - - if (!Desktop.isDesktopSupported()) - { - log.info("Desktop is not supported. Visit {}", login.getOauthUrl()); - return; - } - - Desktop desktop = Desktop.getDesktop(); - if (!desktop.isSupported(Desktop.Action.BROWSE)) - { - log.info("Desktop browser is not supported. Visit {}", login.getOauthUrl()); - return; - } - - try - { - desktop.browse(new URI(login.getOauthUrl())); - - log.debug("Opened browser to {}", login.getOauthUrl()); - } - catch (IOException | URISyntaxException ex) - { - log.warn("Unable to open login page", ex); - } - } - - @Subscribe - public void onLogin(LoginResponse loginResponse) - { - log.debug("Now logged in as {}", loginResponse.getUsername()); - - AccountSession session = sessionManager.getAccountSession(); - session.setUsername(loginResponse.getUsername()); - - // Open session, again, now that we have a username - // This triggers onSessionOpen - sessionManager.openSession(session); - - // Save session to disk - sessionManager.saveSession(); + navigationPanel.repaint(); } @Subscribe @@ -187,15 +109,11 @@ public class AccountPlugin extends Plugin log.debug("Session opened as {}", session.getUsername()); - replaceLoginWithLogout(); - } - - private void replaceLoginWithLogout() - { // Replace login nav button with logout PluginToolbar navigationPanel = ui.getPluginToolbar(); navigationPanel.removeNavigation(loginButton); navigationPanel.addNavigation(logoutButton); + navigationPanel.repaint(); } } From a4914cf7eec7b200d16a9bc435876bbed77af4dd Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 2 Feb 2018 05:13:21 -0700 Subject: [PATCH 2/2] Add info panel plugin --- .../runelite/client/RuneLiteProperties.java | 6 + .../client/plugins/info/InfoPanel.java | 173 ++++++++++++++++++ .../client/plugins/info/InfoPlugin.java | 64 +++++++ .../client/plugins/info/JRichTextPane.java | 99 ++++++++++ .../client/plugins/info/info_icon.png | Bin 0 -> 1810 bytes .../net/runelite/client/runelite.properties | 2 +- 6 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/info/info_icon.png 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 557bc675b1..f01c70a946 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -38,6 +38,7 @@ public class RuneLiteProperties { private static final String RUNELITE_TITLE = "runelite.title"; private static final String RUNELITE_VERSION = "runelite.version"; + private static final String RUNESCAPE_VERSION = "runescape.version"; private final Properties properties = new Properties(); @@ -64,4 +65,9 @@ public class RuneLiteProperties { return properties.getProperty(RUNELITE_VERSION); } + + public String getRunescapeVersion() + { + return properties.getProperty(RUNESCAPE_VERSION); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java new file mode 100644 index 0000000000..e58c12c5f6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 Abex + * 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.plugins.info; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import java.awt.Font; +import com.google.inject.Inject; +import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; +import javax.swing.GroupLayout; +import javax.swing.JLabel; +import javax.swing.LayoutStyle; +import javax.swing.event.HyperlinkEvent; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.events.SessionClose; +import net.runelite.api.events.SessionOpen; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.account.SessionManager; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.RunnableExceptionLogger; + +@Slf4j +public class InfoPanel extends PluginPanel +{ + private final static String RUNELITE_LOGIN = "https://runelite_login/"; + + @Inject + @Nullable + private Client client; + + @Inject + private RuneLiteProperties runeLiteProperties; + + @Inject + private EventBus eventBus; + + @Inject + private SessionManager sessionManager; + + @Inject + private ScheduledExecutorService executor; + + private final GroupLayout layout = new GroupLayout(this); + + private final JLabel usernameHeader = new JLabel(); + private final JRichTextPane username = new JRichTextPane(); + + void init() + { + setLayout(layout); + + final Font smallFont = FontManager.getRunescapeSmallFont(); + + final JLabel runeliteVersionHeader = new JLabel("RuneLite version"); + runeliteVersionHeader.setFont(smallFont); + final JLabel runeliteVersion = new JLabel(runeLiteProperties.getVersion()); + + final JLabel runescapeVersionHeader = new JLabel("OldSchool Engine"); + runescapeVersionHeader.setFont(smallFont); + + String engineVer = "Unknown"; + if (client != null) + { + engineVer = String.format("Rev %s", runeLiteProperties.getRunescapeVersion()); + } + final JLabel runescapeVersion = new JLabel(engineVer); + + usernameHeader.setFont(smallFont); + username.enableAutoLinkHandler(false); + username.addHyperlinkListener(e -> + { + if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null) + { + if (e.getURL().toString().equals(RUNELITE_LOGIN)) + { + executor.execute(RunnableExceptionLogger.wrap(sessionManager::login)); + } + } + }); + setNotLoggedIn(); + + final JRichTextPane issueLink = new JRichTextPane("text/html", + "RuneLite is open source!
" + + "Found an issue? Want a feature?
" + + "" + + "Open an issue on GitHub!" + + "" + ); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup() + .addComponent(runeliteVersionHeader) + .addComponent(runescapeVersionHeader) + ).addGroup(layout.createParallelGroup() + .addComponent(runeliteVersion) + .addComponent(runescapeVersion) + ).addGap(6) + .addComponent(usernameHeader) + .addGroup(layout.createParallelGroup() + .addComponent(username) + ) + .addGap(12) + .addComponent(issueLink) + ); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addGroup(layout.createSequentialGroup() + .addComponent(runeliteVersionHeader) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(runescapeVersionHeader) + ).addGroup(layout.createSequentialGroup() + .addComponent(runeliteVersion) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(runescapeVersion) + ) + .addComponent(usernameHeader) + .addComponent(username) + .addComponent(issueLink) + ); + + eventBus.register(this); + } + + private void setNotLoggedIn() + { + username.setContentType("text/html"); + username.setText("Login to sync settings to the cloud."); + usernameHeader.setText("Not logged in"); + } + + @Subscribe + private void onSessionOpen(SessionOpen sessionOpen) + { + String name = sessionManager.getAccountSession().getUsername(); + if (name != null) + { + username.setContentType("text/plain"); + username.setText(name); + usernameHeader.setText("Logged in as"); + } + } + + @Subscribe + private void onSessionClose(SessionClose e) + { + setNotLoggedIn(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java new file mode 100644 index 0000000000..8a1a1163b4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Abex + * 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.plugins.info; + +import com.google.inject.Binder; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientUI; +import net.runelite.client.ui.NavigationButton; + +@PluginDescriptor( + name = "Info panel plugin", + loadWhenOutdated = true +) +public class InfoPlugin extends Plugin +{ + @Inject + private ClientUI ui; + + @Override + public void configure(Binder binder) + { + binder.bind(InfoPanel.class); + } + + @Override + protected void startUp() throws Exception + { + final InfoPanel panel = injector.getInstance(InfoPanel.class); + panel.init(); + + final NavigationButton navButton = new NavigationButton( + "Info", + ImageIO.read(getClass().getResourceAsStream("info_icon.png")), + () -> panel + ); + + ui.getPluginToolbar().addNavigation(navButton); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java new file mode 100644 index 0000000000..21b8f46cd5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Abex + * 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.plugins.info; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URISyntaxException; +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.HTMLEditorKit; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class JRichTextPane extends JEditorPane +{ + private HyperlinkListener linkHandler; + + public JRichTextPane() + { + super(); + setHighlighter(null); + setEditable(false); + setOpaque(false); + enableAutoLinkHandler(true); + setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + HTMLEditorKit ek = (HTMLEditorKit) getEditorKitForContentType("text/html"); + ek.getStyleSheet().addRule("a {color: #DDDDDD }"); + } + + public JRichTextPane(String type, String text) + { + this(); + setContentType(type); + setText(text); + } + + public void enableAutoLinkHandler(boolean enable) + { + if (enable != (linkHandler != null)) + { + if (enable) + { + linkHandler = e -> + { + log.info(e.toString()); + if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null) + { + if (Desktop.isDesktopSupported()) + { + try + { + Desktop.getDesktop().browse(e.getURL().toURI()); + } + catch (URISyntaxException | IOException ex) + { + log.info("Error opening link", ex); + } + } + } + }; + addHyperlinkListener(linkHandler); + } + else + { + removeHyperlinkListener(linkHandler); + linkHandler = null; + } + } + } + + public boolean getAutoLinkHandlerEnabled() + { + return linkHandler != null; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/info_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/info/info_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..565f2a48b259a09ac4f977bcb06d33910dab5275 GIT binary patch literal 1810 zcmcIlU2NM_6n0y+T2rlLgVC-<)Oa9Z;Mh)L$M%*sZPG4bxRfqZyBD@=-1|DLx_gym=Y2sUI|B^q{F-rc}a~ZbmPa6Xb#M&)2WF@EOHfR z3Z7@H99J%vL*+;aJ9$n}6ouo%TsX`U1nVwY9;mREyQ9gFLayPMwr66C_8EbW7rZz_ zl-5%y*)3YjZIp==jH>{f6GFV7QWL0Y>qASW7TWd37fF%}Z27^Xxuf5|`-6HjSEU zve3yS{n(6YL0onp_w_X3CH;cBFKmpfg-aqjEbzB(*di*cpxcyRECCJ(?#~D zu(9A5&);8fT8It8(W;LMctMFlS!SUj32Y2SAuB6UorR(tlXVG2WGUBVd&V)zdjpEM zv-+waVa)L|5TiNBN&?EUl1NnPQBh`fL6pLL4u}ybGBh;Q9Ck{8bctC4dBoXPo}vFy zlf*^rXc&^3NBmwU>?Dh{=@R3X8gIuwG~ZYiO}f@{Dlq&`k28jEF@j9vwt0u@ZA@pK zwp>7jbRF~SFc;^%GH}pno>cljT8q1zbQdhHFMMOR|8K$@B{d4b$|Eu|ImS58Zu@85&U+rX07oy29-F+ac?@do6KIuJnXteTZ@cTld02V2VeRdh8sn- literal 0 HcmV?d00001 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 b8516ea64c..684a13c581 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -1,3 +1,3 @@ runelite.title=RuneLite runelite.version=${project.version} - +runescape.version=${rs.version}