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 d69e22c984..058c3605f6 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -38,12 +38,12 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; import joptsimple.OptionParser; import joptsimple.OptionSet; import net.runelite.api.Client; import net.runelite.client.account.AccountSession; +import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.PluginManager; @@ -68,9 +68,9 @@ public class RuneLite private ClientUI gui; private PluginManager pluginManager; - private MenuManager menuManager = new MenuManager(this); + private final MenuManager menuManager = new MenuManager(this); private OverlayRenderer renderer; - private EventBus eventBus = new EventBus(this::eventExceptionHandler); + private final EventBus eventBus = new EventBus(this::eventExceptionHandler); private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4); private WSClient wsclient; @@ -171,8 +171,6 @@ public class RuneLite return; } - SESSION_FILE.getParentFile().mkdirs(); - try (FileWriter fw = new FileWriter(SESSION_FILE)) { new Gson().toJson(accountSession, fw); @@ -185,30 +183,29 @@ public class RuneLite } } + public void deleteSession() + { + SESSION_FILE.delete(); + } + /** * Set the given session as the active session and open a socket to the * server with the given session + * * @param session */ public void openSession(AccountSession session) { - boolean needExecutor = false; - - if (wsclient != null) + // If the ws session already exists, don't need to do anything + if (wsclient == null || !wsclient.getSession().equals(session)) { - wsclient.close(); - } - else - { - needExecutor = true; - } + if (wsclient != null) + { + wsclient.close(); + } - wsclient = new WSClient(session); - wsclient.connect(); - - if (needExecutor) - { - executor.scheduleWithFixedDelay(wsclient::ping, WSClient.PING_TIME.getSeconds(), WSClient.PING_TIME.getSeconds(), TimeUnit.SECONDS); + wsclient = new WSClient(session); + wsclient.connect(); } accountSession = session; @@ -216,6 +213,26 @@ public class RuneLite eventBus.post(new SessionOpen()); } + public void closeSession() + { + if (wsclient != null) + { + wsclient.close(); + wsclient = null; + } + + if (accountSession == null) + { + return; + } + + logger.debug("Logging out of account {}", accountSession.getUsername()); + + accountSession = null; // No more account + + eventBus.post(new SessionClose()); + } + private void eventExceptionHandler(Throwable exception, SubscriberExceptionContext context) { logger.warn("uncaught exception in event subscriber", exception); diff --git a/runelite-client/src/main/java/net/runelite/client/WSClient.java b/runelite-client/src/main/java/net/runelite/client/WSClient.java index 1f8518dd17..82a2141c19 100644 --- a/runelite-client/src/main/java/net/runelite/client/WSClient.java +++ b/runelite-client/src/main/java/net/runelite/client/WSClient.java @@ -28,6 +28,9 @@ import com.google.common.eventbus.EventBus; import com.google.gson.Gson; import java.time.Duration; import java.time.Instant; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import net.runelite.client.account.AccountSession; import net.runelite.http.api.RuneliteAPI; import net.runelite.http.api.ws.messages.Handshake; @@ -46,19 +49,27 @@ public class WSClient extends WebSocketListener implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(WSClient.class); - public static final Duration PING_TIME = Duration.ofSeconds(30); + private static final Duration PING_TIME = Duration.ofSeconds(30); private static final Gson gson = WebsocketGsonFactory.build(); private static final EventBus eventBus = RuneLite.getRunelite().getEventBus(); + private static final ScheduledExecutorService executor = RuneLite.getRunelite().getExecutor(); private final OkHttpClient client = new OkHttpClient(); private final AccountSession session; private WebSocket webSocket; + private final ScheduledFuture pingFuture; public WSClient(AccountSession session) { this.session = session; + this.pingFuture = executor.scheduleWithFixedDelay(this::ping, PING_TIME.getSeconds(), PING_TIME.getSeconds(), TimeUnit.SECONDS); + } + + public AccountSession getSession() + { + return session; } public void connect() @@ -99,6 +110,11 @@ public class WSClient extends WebSocketListener implements AutoCloseable @Override public void close() { + if (pingFuture != null) + { + pingFuture.cancel(true); + } + if (webSocket != null) { webSocket.close(1000, null); diff --git a/runelite-client/src/main/java/net/runelite/client/account/AccountSession.java b/runelite-client/src/main/java/net/runelite/client/account/AccountSession.java index 72fe8768c7..24d9d29356 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/AccountSession.java +++ b/runelite-client/src/main/java/net/runelite/client/account/AccountSession.java @@ -25,6 +25,7 @@ package net.runelite.client.account; import java.time.Instant; +import java.util.Objects; import java.util.UUID; public class AccountSession @@ -33,6 +34,37 @@ public class AccountSession private String username; private Instant created; + @Override + public int hashCode() + { + int hash = 3; + hash = 29 * hash + Objects.hashCode(this.uuid); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final AccountSession other = (AccountSession) obj; + if (!Objects.equals(this.uuid, other.uuid)) + { + return false; + } + return true; + } + public UUID getUuid() { return uuid; diff --git a/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java b/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java new file mode 100644 index 0000000000..83c656f77e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, 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.events; + +public class SessionClose +{ + +} 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 19dbebc262..71a72a99f1 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 @@ -36,10 +36,12 @@ import javax.imageio.ImageIO; import javax.swing.ImageIcon; import net.runelite.client.RuneLite; import net.runelite.client.account.AccountSession; +import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.plugins.Plugin; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.NavigationPanel; import net.runelite.client.util.RunnableExceptionLogger; import net.runelite.http.api.account.LoginClient; import net.runelite.http.api.account.OAuthResponse; @@ -54,17 +56,22 @@ public class AccountPlugin extends Plugin private final RuneLite runelite = RuneLite.getRunelite(); private final ClientUI ui = runelite.getGui(); private final NavigationButton loginButton = new NavigationButton("Login"); + private final NavigationButton logoutButton = new NavigationButton("Logout"); private final LoginClient loginClient = new LoginClient(); @Override protected void startUp() throws Exception { - loginButton.getButton().addActionListener(this::loginClick); - ImageIcon icon = new ImageIcon(ImageIO.read(getClass().getResourceAsStream("login_icon.png"))); loginButton.getButton().setIcon(icon); + icon = new ImageIcon(ImageIO.read(getClass().getResourceAsStream("logout_icon.png"))); + logoutButton.getButton().setIcon(icon); + + loginButton.getButton().addActionListener(this::loginClick); + logoutButton.getButton().addActionListener(this::logoutClick); + ui.getNavigationPanel().addNavigation(loginButton); } @@ -79,6 +86,17 @@ public class AccountPlugin extends Plugin executor.execute(RunnableExceptionLogger.wrap(this::openLoginPage)); } + private void logoutClick(ActionEvent ae) + { + runelite.closeSession(); + runelite.deleteSession(); + + // Replace logout nav button with login + NavigationPanel navigationPanel = ui.getNavigationPanel(); + navigationPanel.removeNavigation(logoutButton); + navigationPanel.addNavigation(loginButton); + } + private void openLoginPage() { OAuthResponse login; @@ -130,11 +148,15 @@ public class AccountPlugin extends Plugin { logger.debug("Now logged in as {}", loginResponse.getUsername()); - runelite.getGui().setTitle("RuneLite (" + loginResponse.getUsername() + ")"); - + //runelite.getGui().setTitle("RuneLite (" + loginResponse.getUsername() + ")"); AccountSession session = runelite.getAccountSession(); session.setUsername(loginResponse.getUsername()); + // Open session, again, now that we have a username + // This triggers onSessionOpen + runelite.openSession(session); + + // Save session to disk runelite.saveSession(); } @@ -151,6 +173,22 @@ public class AccountPlugin extends Plugin logger.debug("Session opened as {}", session.getUsername()); runelite.getGui().setTitle("RuneLite (" + session.getUsername() + ")"); + + replaceLoginWithLogout(); + } + + private void replaceLoginWithLogout() + { + // Replace login nav button with logout + NavigationPanel navigationPanel = ui.getNavigationPanel(); + navigationPanel.removeNavigation(loginButton); + navigationPanel.addNavigation(logoutButton); + } + + @Subscribe + public void onSessionClose(SessionClose sessionClose) + { + runelite.getGui().setTitle("RuneLite"); } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/NavigationPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/NavigationPanel.java index c080f4e9a9..d86d8badda 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/NavigationPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/NavigationPanel.java @@ -52,4 +52,11 @@ public class NavigationPanel extends JPanel add(button.getButton()); revalidate(); } + + public void removeNavigation(NavigationButton button) + { + buttons.remove(button); + remove(button.getButton()); + revalidate(); + } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/account/logout_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/account/logout_icon.png new file mode 100644 index 0000000000..100ae801e6 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/account/logout_icon.png differ