diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index 5d9d2586ca..7a4cbc1f74 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -630,6 +630,20 @@ public class Client extends ChannelInboundHandlerAdapter { } } + public void setLoginState(int newState) { + if (newState == LoginState.NOT_LOGGED_IN) { + loggedIn = false; + serverTransition = false; + setAccID(0); + } else if (newState == LoginState.SERVER_TRANSITION) { + loggedIn = false; + serverTransition = true; + } else { + loggedIn = true; + serverTransition = false; + } + } + public byte getLoginState(Account account) { byte loginState = account.loginState(); if (loginState == LoginState.SERVER_TRANSITION && lastLoginOverThirtySecondsAgo(account)) { diff --git a/src/main/java/database/account/AccountRepository.java b/src/main/java/database/account/AccountRepository.java index 9c63517ed1..5368abfc78 100644 --- a/src/main/java/database/account/AccountRepository.java +++ b/src/main/java/database/account/AccountRepository.java @@ -3,6 +3,7 @@ package database.account; import database.PgDatabaseConnection; import org.jdbi.v3.core.Handle; +import java.time.Instant; import java.util.Optional; /** @@ -124,4 +125,18 @@ public class AccountRepository { .execute() > 0; } } + + public boolean setLoginState(int accountId, byte loginState, Instant lastLogin) { + String sql = """ + UPDATE account + SET login_state = :loginState, last_login = :lastLogin + WHERE id = :id"""; + try (Handle handle = connection.getHandle()) { + return handle.createUpdate(sql) + .bind("id", accountId) + .bind("loginState", loginState) + .bind("lastLogin", lastLogin) + .execute() > 0; + } + } } diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 8d0678586d..f302b8a204 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -277,7 +277,7 @@ public final class PacketProcessor { private void registerLoginHandlers() { registerHandler(RecvOpcode.ACCEPT_TOS, new AcceptToSHandler(channelDeps.accountService())); - registerHandler(RecvOpcode.AFTER_LOGIN, new AfterLoginHandler()); + registerHandler(RecvOpcode.AFTER_LOGIN, new AfterLoginHandler(channelDeps.accountService())); registerHandler(RecvOpcode.SERVERLIST_REREQUEST, new ServerlistRequestHandler()); registerHandler(RecvOpcode.CHARLIST_REQUEST, new CharlistRequestHandler()); registerHandler(RecvOpcode.CHAR_SELECT, new CharSelectedHandler()); diff --git a/src/main/java/net/server/handlers/login/AfterLoginHandler.java b/src/main/java/net/server/handlers/login/AfterLoginHandler.java index 1fc822283f..1b0a74fc12 100644 --- a/src/main/java/net/server/handlers/login/AfterLoginHandler.java +++ b/src/main/java/net/server/handlers/login/AfterLoginHandler.java @@ -22,13 +22,17 @@ package net.server.handlers.login; import client.Client; -import client.LoginState; import net.AbstractPacketHandler; import net.packet.InPacket; -import net.server.coordinator.session.SessionCoordinator; +import service.AccountService; import tools.PacketCreator; public final class AfterLoginHandler extends AbstractPacketHandler { + private final AccountService accountService; + + public AfterLoginHandler(final AccountService accountService) { + this.accountService = accountService; + } @Override public final void handlePacket(InPacket p, Client c) { @@ -58,8 +62,7 @@ public final class AfterLoginHandler extends AbstractPacketHandler { c.sendPacket(PacketCreator.requestPinAfterFailure()); } } else if (c2 == 0 && c3 == 5) { - SessionCoordinator.getInstance().closeSession(c, false); - c.updateLoginState(LoginState.NOT_LOGGED_IN); + accountService.logOut(c); } } } diff --git a/src/main/java/net/server/handlers/login/RegisterPinHandler.java b/src/main/java/net/server/handlers/login/RegisterPinHandler.java index a5b5577f3e..c1293fc9bd 100644 --- a/src/main/java/net/server/handlers/login/RegisterPinHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPinHandler.java @@ -22,10 +22,8 @@ package net.server.handlers.login; import client.Client; -import client.LoginState; import net.AbstractPacketHandler; import net.packet.InPacket; -import net.server.coordinator.session.SessionCoordinator; import service.AccountService; import tools.PacketCreator; @@ -44,8 +42,7 @@ public final class RegisterPinHandler extends AbstractPacketHandler { public void handlePacket(InPacket p, Client c) { boolean cancel = p.readByte() == 0; if (cancel) { - SessionCoordinator.getInstance().closeSession(c, false); - c.updateLoginState(LoginState.NOT_LOGGED_IN); + accountService.logOut(c); return; } @@ -54,7 +51,6 @@ public final class RegisterPinHandler extends AbstractPacketHandler { c.setPin(pin); c.sendPacket(PacketCreator.pinRegistered()); - SessionCoordinator.getInstance().closeSession(c, false); - c.updateLoginState(LoginState.NOT_LOGGED_IN); + accountService.logOut(c); } } diff --git a/src/main/java/net/server/handlers/login/SetGenderHandler.java b/src/main/java/net/server/handlers/login/SetGenderHandler.java index 9b46e3b230..41c8a519a9 100644 --- a/src/main/java/net/server/handlers/login/SetGenderHandler.java +++ b/src/main/java/net/server/handlers/login/SetGenderHandler.java @@ -24,11 +24,9 @@ package net.server.handlers.login; import client.Client; import client.Gender; -import client.LoginState; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.Server; -import net.server.coordinator.session.SessionCoordinator; import service.AccountService; import tools.PacketCreator; @@ -46,24 +44,24 @@ public class SetGenderHandler extends AbstractPacketHandler { @Override public void handlePacket(InPacket p, Client c) { if (c.getGender() != Gender.NOT_SET) { // Packet shouldn't come if Gender isn't 10. - close(c); + logOut(c); return; } byte confirmed = p.readByte(); if (confirmed != 0x01) { - close(c); + logOut(c); return; } byte gender = p.readByte(); if (gender != Gender.MALE && gender != Gender.FEMALE) { - close(c); + logOut(c); return; } if (!accountService.setGender(c.getAccID(), gender)) { - close(c); + logOut(c); return; } @@ -72,9 +70,8 @@ public class SetGenderHandler extends AbstractPacketHandler { Server.getInstance().registerLoginState(c); } - private void close(Client c) { - SessionCoordinator.getInstance().closeSession(c, false); - c.updateLoginState(LoginState.NOT_LOGGED_IN); + private void logOut(Client c) { + accountService.logOut(c); } } diff --git a/src/main/java/service/AccountService.java b/src/main/java/service/AccountService.java index 56c3df53fb..0ec68c1a59 100644 --- a/src/main/java/service/AccountService.java +++ b/src/main/java/service/AccountService.java @@ -1,9 +1,12 @@ package service; import client.Client; +import client.LoginState; import database.account.Account; import database.account.AccountRepository; import lombok.extern.slf4j.Slf4j; +import net.server.Server; +import net.server.coordinator.session.SessionCoordinator; import tools.BCrypt; import tools.DatabaseConnection; @@ -11,6 +14,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.Instant; import java.time.LocalDate; import java.util.Optional; @@ -34,7 +38,7 @@ public class AccountService { .password(hashPassword(password)) .birthdate(GMS_RELEASE) .chrSlots(INITIAL_CHR_SLOTS) - .loginState((byte) Client.LOGIN_NOTLOGGEDIN) + .loginState(LoginState.NOT_LOGGED_IN) .gender(null) .build(); @@ -209,4 +213,35 @@ public class AccountService { return accountRepository.setChrSlots(accountId, chrSlots); } + public void logOut(Client c) { + SessionCoordinator.getInstance().closeSession(c, false); + byte newState = LoginState.NOT_LOGGED_IN; + int accountId = c.getAccID(); + setLoginStateMysql(accountId, newState); + setLoginStatePostgres(accountId, newState); + c.setLoginState(newState); + } + + private void setLoginStateMysql(int accountId, byte newState) { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) { + // using sql currenttime here could potentially break the login, thanks Arnah for pointing this out + + ps.setInt(1, newState); + ps.setTimestamp(2, new java.sql.Timestamp(Server.getInstance().getCurrentTime())); + ps.setInt(3, accountId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void setLoginStatePostgres(int accountId, byte newState) { + Instant loginTime = Instant.now(); + boolean success = accountRepository.setLoginState(accountId, newState, loginTime); + if (!success) { + log.warn("Failed to set login state - account:{}, newState:{}, loginTime:{}", accountId, newState, loginTime); + } + } + }