From f41268cdde3d2e1def2d6485e3d3149f5fb6c42e Mon Sep 17 00:00:00 2001 From: P0nk Date: Fri, 13 Sep 2024 22:59:55 +0200 Subject: [PATCH] Handle disconnect solely in TransitionService --- src/main/java/client/Character.java | 53 ++--- src/main/java/client/Client.java | 196 +----------------- .../command/commands/gm2/DcCommand.java | 4 +- .../command/commands/gm3/BanCommand.java | 2 +- .../command/commands/gm6/DCAllCommand.java | 2 +- .../database/character/CharacterSaver.java | 2 +- .../java/net/netty/DisconnectException.java | 9 +- .../java/net/netty/DisconnectHandler.java | 2 +- src/main/java/net/server/Server.java | 2 +- .../channel/handlers/AdminCommandHandler.java | 2 +- .../session/SessionCoordinator.java | 18 -- .../java/net/server/task/TimeoutTask.java | 2 +- src/main/java/service/BanService.java | 2 +- src/main/java/service/TransitionService.java | 64 +++--- 14 files changed, 85 insertions(+), 275 deletions(-) diff --git a/src/main/java/client/Character.java b/src/main/java/client/Character.java index 463710d0fc..2064574463 100644 --- a/src/main/java/client/Character.java +++ b/src/main/java/client/Character.java @@ -1755,32 +1755,31 @@ public class Character extends AbstractCharacterObject { sendPacket(warpPacket); map.removePlayer(this); - if (client.getChannelServer().getPlayerStorage().getCharacterById(getId()) != null) { - map = to; - setPosition(pos); - map.addPlayer(this); - visitMap(map); - - prtLock.lock(); - try { - if (party != null) { - mpc.setMapId(to.getId()); - sendPacket(PacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null)); - updatePartyMemberHPInternal(); - } - } finally { - prtLock.unlock(); - } - if (Character.this.getParty() != null) { - Character.this.getParty().setEnemy(k); - } - silentPartyUpdateInternal(getParty()); // EIM script calls inside - } else { + if (client.getChannelServer().getPlayerStorage().getCharacterById(id) == null) { log.warn("Chr {} got stuck when moving to map {}", getName(), map.getId()); - client.disconnect(true, false); // thanks BHB for noticing a player storage stuck case here return; } + map = to; + setPosition(pos); + map.addPlayer(this); + visitMap(map); + + prtLock.lock(); + try { + if (party != null) { + mpc.setMapId(to.getId()); + sendPacket(PacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null)); + updatePartyMemberHPInternal(); + } + } finally { + prtLock.unlock(); + } + if (Character.this.getParty() != null) { + Character.this.getParty().setEnemy(k); + } + silentPartyUpdateInternal(getParty()); // EIM script calls inside + notifyMapTransferToPartner(map.getId()); //alas, new map has been specified when a warping was being processed... @@ -8175,20 +8174,14 @@ public class Character extends AbstractCharacterObject { return false; } - // TODO: all callers should use CharacterSaver instead. - // It's supposed to act as a proxy to these 2 methods (as a first step towards taking full ownership of character saving) - public void saveCharToDB() { - saveCharToDB(true); - } - //ItemFactory saveItems and monsterbook.saveCards are the most time consuming here. - public synchronized void saveCharToDB(boolean notAutosave) { + public synchronized void saveCharToDB() { if (!loggedIn) { return; } Calendar c = Calendar.getInstance(); - log.debug("Attempting to {} chr {}", notAutosave ? "save" : "autosave", name); + log.debug("Saving chr {}", name); Server.getInstance().updateCharacterEntry(this); diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index 96d45834c9..0b37c89cee 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -22,7 +22,6 @@ along with this program. If not, see . package client; import config.YamlConfig; -import constants.id.MapId; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.IdleStateEvent; @@ -41,34 +40,24 @@ import net.server.coordinator.login.LoginBypassCoordinator; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.SessionCoordinator; import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; -import net.server.guild.Guild; -import net.server.guild.GuildCharacter; -import net.server.guild.GuildPackets; -import net.server.world.MessengerCharacter; import net.server.world.Party; -import net.server.world.PartyCharacter; -import net.server.world.PartyOperation; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.AbstractPlayerInteraction; -import scripting.event.EventInstanceManager; import scripting.event.EventManager; import scripting.npc.NPCConversationManager; import scripting.npc.NPCScriptManager; import scripting.quest.QuestActionManager; import scripting.quest.QuestScriptManager; -import server.ThreadManager; import server.TimerManager; import server.life.Monster; -import server.maps.MapleMap; import tools.BCrypt; import tools.DatabaseConnection; import tools.HexTool; import tools.PacketCreator; import javax.script.ScriptEngine; -import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -212,7 +201,8 @@ public class Client extends ChannelInboundHandlerAdapter { MonitoredChrLogger.logPacketIfMonitored(this, opcode, packet.getBytes()); handler.handlePacket(packet, this); } catch (GameViolationException gve) { - throw new DisconnectException(this, gve.getMessage()); + log.warn("Game violation (disconnecting): {}", gve.getMessage()); + throw new DisconnectException(this, true); } catch (final Throwable t) { final String chrInfo = player != null ? player.getName() + " on map " + player.getMapId() : "?"; log.warn("Error in packet handler {}. Chr {}, account {}. Packet: {}", handler.getClass().getSimpleName(), @@ -232,15 +222,13 @@ public class Client extends ChannelInboundHandlerAdapter { } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (player != null) { log.warn("Exception caught by {}", player, cause); } if (cause instanceof InvalidPacketHeaderException) { SessionCoordinator.getInstance().closeSession(this, true); - } else if (cause instanceof IOException) { - closeMapleSession(); } else { ctx.fireExceptionCaught(cause); } @@ -248,10 +236,6 @@ public class Client extends ChannelInboundHandlerAdapter { @Override public void channelInactive(ChannelHandlerContext ctx) { - closeMapleSession(); - } - - private void closeMapleSession() { switch (type) { case LOGIN -> SessionCoordinator.getInstance().closeLoginSession(this); case CHANNEL -> SessionCoordinator.getInstance().closeSession(this, false); @@ -260,7 +244,7 @@ public class Client extends ChannelInboundHandlerAdapter { try { // client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex if (!inTransition) { - disconnect(false, false); + ctx.fireExceptionCaught(new DisconnectException(this, false)); } } catch (Throwable t) { log.warn("Account stuck", t); @@ -693,11 +677,6 @@ public class Client extends ChannelInboundHandlerAdapter { } public void updateLoginState(int newState) { - // rules out possibility of multiple account entries - if (newState == LOGIN_LOGGEDIN) { - SessionCoordinator.getInstance().updateOnlineClient(this); - } - 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 @@ -770,85 +749,6 @@ public class Client extends ChannelInboundHandlerAdapter { return date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR) && date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH) && date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH); } - private void removePartyPlayer(World wserv) { - MapleMap map = player.getMap(); - final Party party = player.getParty(); - final int idz = player.getId(); - - if (party != null) { - final PartyCharacter chrp = new PartyCharacter(player); - chrp.setOnline(false); - wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); - if (party.getLeader().getId() == idz && map != null) { - PartyCharacter lchr = null; - for (PartyCharacter pchr : party.getMembers()) { - if (pchr != null && pchr.getId() != idz && (lchr == null || lchr.getLevel() <= pchr.getLevel()) && map.getCharacterById(pchr.getId()) != null) { - lchr = pchr; - } - } - if (lchr != null) { - wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); - } - } - } - } - - private void removePlayer(World wserv, boolean serverTransition) { - try { - player.setDisconnectedFromChannelWorld(); - player.notifyMapTransferToPartner(-1); - player.removeIncomingInvites(); - player.cancelAllBuffs(true); - - player.closePlayerInteractions(); - player.closePartySearchInteractions(); - - if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels - removePartyPlayer(wserv); - - EventInstanceManager eim = player.getEventInstance(); - if (eim != null) { - eim.playerDisconnected(player); - } - - if (player.getMonsterCarnival() != null) { - player.getMonsterCarnival().playerDisconnected(getPlayer().getId()); - } - - if (player.getAriantColiseum() != null) { - player.getAriantColiseum().playerDisconnected(getPlayer()); - } - } - - if (player.getMap() != null) { - int mapId = player.getMapId(); - player.getMap().removePlayer(player); - if (MapId.isDojo(mapId)) { - this.getChannelServer().freeDojoSectionIfEmpty(mapId); - } - - if (player.getMap().getHPDec() > 0) { - getWorldServer().removePlayerHpDecrease(player); - } - } - - } catch (final Throwable t) { - log.error("Account stuck", t); - } - } - - public final void disconnect(final boolean shutdown, final boolean cashshop) { - if (tryDisconnect()) { - ThreadManager.getInstance().newTask(() -> disconnectInternal(shutdown, cashshop)); - } - } - - public final void forceDisconnect() { - if (tryDisconnect()) { - disconnectInternal(true, false); - } - } - public synchronized boolean tryDisconnect() { if (disconnecting) { return false; @@ -858,88 +758,6 @@ public class Client extends ChannelInboundHandlerAdapter { return true; } - private void disconnectInternal(boolean shutdown, boolean cashshop) {//once per Client instance - if (player != null && player.isLoggedin() && player.getClient() != null) { - final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId(); - final BuddyList bl = player.getBuddylist(); - final MessengerCharacter messengerChr = new MessengerCharacter(player, 0); - final GuildCharacter guildChr = player.getMGC(); - final Guild guild = player.getGuild(); - - player.cancelMagicDoor(); - - final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it - try { - removePlayer(wserv, this.serverTransition); - - if (!(channel == -1 || shutdown)) { - if (!cashshop) { - if (!this.serverTransition) { // meaning not changing channels - if (messengerid > 0) { - wserv.leaveMessenger(messengerid, messengerChr); - } - - player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time - - if (guild != null) { - final Server server = Server.getInstance(); - server.setGuildMemberOnline(player, false, player.getClient().getChannel()); - player.sendPacket(GuildPackets.showGuildInfo(player)); - } - if (bl != null) { - wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); - } - } - } else { - if (!this.serverTransition) { // if dc inside of cash shop. - if (bl != null) { - wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); - } - } - } - } - } catch (final Exception e) { - log.error("Account stuck", e); - } finally { - if (!this.serverTransition) { - if (guildChr != null) { - guildChr.setCharacter(null); - } - wserv.removePlayer(player); - //getChannelServer().removePlayer(player); already being done - - player.cancelAllDebuffs(); - player.saveCharToDB(); - - player.logOff(); - if (YamlConfig.config.server.INSTANT_NAME_CHANGE) { - player.doPendingNameChange(); - } - clear(); - } else { - getChannelServer().removePlayer(player); - - player.cancelAllDebuffs(); - player.saveCharToDB(); - } - } - } - - SessionCoordinator.getInstance().closeSession(this, false); - - if (!serverTransition && isLoggedIn()) { - updateLoginState(Client.LOGIN_NOTLOGGEDIN); - - clear(); - } else { - if (!Server.getInstance().hasCharacteridInTransition(this)) { - updateLoginState(Client.LOGIN_NOTLOGGEDIN); - } - - engines = null; // thanks Tochi for pointing out a NPE here - } - } - public void clear() { // player hard reference removal thanks to Steve (kaito1410) if (this.player != null) { @@ -956,6 +774,12 @@ public class Client extends ChannelInboundHandlerAdapter { this.player = null; } + public void clearEngines() { + if (engines != null) { + engines.clear(); + } + } + public void setCharacterOnSessionTransitionState(int cid) { this.updateLoginState(Client.LOGIN_SERVER_TRANSITION); this.inTransition = true; diff --git a/src/main/java/client/command/commands/gm2/DcCommand.java b/src/main/java/client/command/commands/gm2/DcCommand.java index 4c8178b0d0..8bf4d27614 100644 --- a/src/main/java/client/command/commands/gm2/DcCommand.java +++ b/src/main/java/client/command/commands/gm2/DcCommand.java @@ -49,7 +49,7 @@ public class DcCommand extends Command { victim = player.getMap().getCharacterByName(chrName); if (victim != null) { try {//sometimes bugged because the map = null - ctx.transitionService().disconnect(victim.getClient(), true, false); + ctx.transitionService().disconnect(victim.getClient(), true); player.getMap().removePlayer(victim); } catch (Exception e) { e.printStackTrace(); @@ -62,6 +62,6 @@ public class DcCommand extends Command { if (player.gmLevel() < victim.gmLevel()) { victim = player; } - ctx.transitionService().disconnect(victim.getClient(), false, false); + ctx.transitionService().disconnect(victim.getClient(), false); } } diff --git a/src/main/java/client/command/commands/gm3/BanCommand.java b/src/main/java/client/command/commands/gm3/BanCommand.java index 6b20d02eaf..a3b23708e6 100644 --- a/src/main/java/client/command/commands/gm3/BanCommand.java +++ b/src/main/java/client/command/commands/gm3/BanCommand.java @@ -78,7 +78,7 @@ public class BanCommand extends Command { c.sendPacket(PacketCreator.getGMEffect(4, (byte) 0)); final Character rip = target; TimerManager.getInstance().schedule( - () -> ctx.transitionService().disconnect(rip.getClient(), false, false), + () -> ctx.transitionService().disconnect(rip.getClient(), false), TimeUnit.SECONDS.toMillis(5) ); Server.getInstance().broadcastMessage(c.getWorld(), PacketCreator.serverNotice(6, "[RIP]: " + ign + " has been banned.")); diff --git a/src/main/java/client/command/commands/gm6/DCAllCommand.java b/src/main/java/client/command/commands/gm6/DCAllCommand.java index d4a94a613e..28518d05a0 100644 --- a/src/main/java/client/command/commands/gm6/DCAllCommand.java +++ b/src/main/java/client/command/commands/gm6/DCAllCommand.java @@ -41,7 +41,7 @@ public class DCAllCommand extends Command { for (World world : Server.getInstance().getWorlds()) { for (Character chr : world.getPlayerStorage().getAllCharacters()) { if (!chr.isGM()) { - ctx.transitionService().disconnect(chr.getClient(), false, false); + ctx.transitionService().disconnect(chr.getClient(), false); } } } diff --git a/src/main/java/database/character/CharacterSaver.java b/src/main/java/database/character/CharacterSaver.java index 493248089a..c6a2da115f 100644 --- a/src/main/java/database/character/CharacterSaver.java +++ b/src/main/java/database/character/CharacterSaver.java @@ -11,7 +11,7 @@ public class CharacterSaver { } public void save(Character chr) { - chr.saveCharToDB(false); + chr.saveCharToDB(); // Saving monster cards to both MySQL and Postgres for now monsterCardDao.save(chr.getId(), chr.getMonsterBook().getCards()); diff --git a/src/main/java/net/netty/DisconnectException.java b/src/main/java/net/netty/DisconnectException.java index 3a840260f7..0467930e13 100644 --- a/src/main/java/net/netty/DisconnectException.java +++ b/src/main/java/net/netty/DisconnectException.java @@ -4,13 +4,18 @@ import client.Client; public class DisconnectException extends RuntimeException { private final Client client; + private final boolean shutdown; - public DisconnectException(Client client, String message) { - super(message); + public DisconnectException(Client client, boolean shutdown) { this.client = client; + this.shutdown = shutdown; } public Client getClient() { return client; } + + public boolean isShutdown() { + return shutdown; + } } diff --git a/src/main/java/net/netty/DisconnectHandler.java b/src/main/java/net/netty/DisconnectHandler.java index 1bf192af76..4edd366064 100644 --- a/src/main/java/net/netty/DisconnectHandler.java +++ b/src/main/java/net/netty/DisconnectHandler.java @@ -17,7 +17,7 @@ public class DisconnectHandler extends ChannelInboundHandlerAdapter { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (cause instanceof DisconnectException de) { var client = de.getClient(); - transitionService.disconnect(client, true, false); + transitionService.disconnect(client, true); } else { ctx.fireExceptionCaught(cause); } diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index b69177b250..187b7a60d8 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -1923,7 +1923,7 @@ public class Server { for (Client c : toDisconnect) { // thanks Lei for pointing a deadlock issue with srvLock if (c.isLoggedIn()) { - channelDependencies.transitionService().disconnect(c, false, false); + channelDependencies.transitionService().disconnect(c, false); } else { SessionCoordinator.getInstance().closeSession(c, true); } diff --git a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java index 44a48a5511..f17508a92c 100644 --- a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java +++ b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java @@ -195,7 +195,7 @@ public final class AdminCommandHandler extends AbstractPacketHandler { private void sendPolice(Client c, String reason) { c.sendPacket(PacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for %s.#k", "Cosmic", reason))); c.getPlayer().setBanned(); - TimerManager.getInstance().schedule(() -> transitionService.disconnect(c, false, false), + TimerManager.getInstance().schedule(() -> transitionService.disconnect(c, false), TimeUnit.SECONDS.toMillis(6)); } } diff --git a/src/main/java/net/server/coordinator/session/SessionCoordinator.java b/src/main/java/net/server/coordinator/session/SessionCoordinator.java index ac37245257..774c3887d7 100644 --- a/src/main/java/net/server/coordinator/session/SessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/SessionCoordinator.java @@ -109,24 +109,6 @@ public class SessionCoordinator { } } - /** - * Overwrites any existing online client for the account id, making sure to disconnect it as well. - */ - public void updateOnlineClient(Client client) { - if (client != null) { - int accountId = client.getAccID(); - disconnectClientIfOnline(accountId); - onlineClients.put(accountId, client); - } - } - - private void disconnectClientIfOnline(int accountId) { - Client ingameClient = onlineClients.get(accountId); - if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature - ingameClient.forceDisconnect(); - } - } - public boolean canStartLoginSession(Client client) { if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { return true; diff --git a/src/main/java/net/server/task/TimeoutTask.java b/src/main/java/net/server/task/TimeoutTask.java index d4fc88f5e0..93ba6ac6c2 100644 --- a/src/main/java/net/server/task/TimeoutTask.java +++ b/src/main/java/net/server/task/TimeoutTask.java @@ -28,7 +28,7 @@ public class TimeoutTask extends BaseTask implements Runnable { for (Character chr : chars) { if (time - chr.getClient().getLastPacket() > YamlConfig.config.server.TIMEOUT_DURATION) { log.info("Chr {} auto-disconnected due to inactivity", chr.getName()); - transitionService.disconnect(chr.getClient(), true, chr.getCashShop().isOpened()); + transitionService.disconnect(chr.getClient(), true); } } } diff --git a/src/main/java/service/BanService.java b/src/main/java/service/BanService.java index bae344c3d1..62254c728b 100644 --- a/src/main/java/service/BanService.java +++ b/src/main/java/service/BanService.java @@ -32,7 +32,7 @@ public class BanService { chr.ban(reason); chr.sendPacket(PacketCreator.sendPolice("You have been blocked by the#b %s Police for HACK reason.#k".formatted("Cosmic"))); - TimerManager.getInstance().schedule(() -> transitionService.disconnect(chr.getClient(), false, false), + TimerManager.getInstance().schedule(() -> transitionService.disconnect(chr.getClient(), false), TimeUnit.SECONDS.toMillis(5)); var bannedName = Character.makeMapleReadable(chr.getName()); diff --git a/src/main/java/service/TransitionService.java b/src/main/java/service/TransitionService.java index c0c1c1fb98..4c883c79f7 100644 --- a/src/main/java/service/TransitionService.java +++ b/src/main/java/service/TransitionService.java @@ -6,7 +6,9 @@ import client.inventory.InventoryType; import config.YamlConfig; import constants.id.MapId; import database.character.CharacterSaver; +import net.netty.LoginServer; import net.server.Server; +import net.server.coordinator.session.SessionCoordinator; import net.server.guild.Guild; import net.server.guild.GuildCharacter; import net.server.guild.GuildPackets; @@ -39,7 +41,7 @@ public class TransitionService { public void changeChannel(Client c, int channel) { var chr = c.getPlayer(); if (chr.isBanned()) { - disconnect(c, false, false); + disconnect(c, false); return; } @@ -93,21 +95,19 @@ public class TransitionService { } } - // TODO: take code from Client#disconnect & forceDisconnect. Move it here. - // It's not gonna be easy to move all instances of c.disconnect, but it has to be done. - public void disconnect(final Client c, final boolean shutdown, final boolean cashShop) { + public void disconnect(final Client c, final boolean shutdown) { if (c.tryDisconnect()) { - ThreadManager.getInstance().newTask(() -> disconnectInternal(c, shutdown, cashShop)); + ThreadManager.getInstance().newTask(() -> disconnectInternal(c, shutdown)); } } public void forceDisconnect(Client c) { if (c.tryDisconnect()) { - disconnectInternal(c, true, false); + disconnectInternal(c, true); } } - private void disconnectInternal(Client c, boolean shutdown, boolean cashShop) { + private void disconnectInternal(Client c, boolean shutdown) { var chr = c.getPlayer(); if (chr != null && chr.isLoggedin() && chr.getClient() != null) { final int messengerid = chr.getMessenger() == null ? 0 : chr.getMessenger().getId(); @@ -123,29 +123,21 @@ public class TransitionService { removePlayer(c, wserv, c.isInTransition()); final int channel = c.getChannel(); - if (!(channel == -1 || shutdown)) { - if (!cashShop) { - if (!c.isInTransition()) { // meaning not changing channels - if (messengerid > 0) { - wserv.leaveMessenger(messengerid, messengerChr); - } - - chr.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time - - if (guild != null) { - final Server server = Server.getInstance(); - server.setGuildMemberOnline(chr, false, chr.getClient().getChannel()); - chr.sendPacket(GuildPackets.showGuildInfo(chr)); - } - if (bl != null) { - wserv.loggedOff(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds()); - } + if (!(channel == LoginServer.CHANNEL_ID || shutdown)) { + if (!c.isInTransition()) { // meaning not changing channels + if (messengerid > 0) { + wserv.leaveMessenger(messengerid, messengerChr); } - } else { - if (!c.isInTransition()) { // if dc inside of cash shop. - if (bl != null) { - wserv.loggedOff(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds()); - } + + chr.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time + + if (guild != null) { + final Server server = Server.getInstance(); + server.setGuildMemberOnline(chr, false, chr.getClient().getChannel()); + chr.sendPacket(GuildPackets.showGuildInfo(chr)); + } + if (bl != null) { + wserv.loggedOff(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds()); } } } @@ -175,6 +167,20 @@ public class TransitionService { } } } + + SessionCoordinator.getInstance().closeSession(c, false); + + + if (!c.isInTransition() && c.isLoggedIn()) { + c.updateLoginState(Client.LOGIN_NOTLOGGEDIN); + c.clear(); + } else { + if (!Server.getInstance().hasCharacteridInTransition(c)) { + c.updateLoginState(Client.LOGIN_NOTLOGGEDIN); + } + + c.clearEngines(); + } } private void removePlayer(Client c, World world, boolean serverTransition) {