Add client disconnection logic to TransitionService
Problem: disconnecting requires access to CharacterSaver, which is not available in Client. Having it in a service like this solves that problem. Next step is to migrate all calls to Client#disconnect and Client#forceDisconnect to their TransitionService counterparts.
This commit is contained in:
@@ -305,6 +305,10 @@ public class Client extends ChannelInboundHandlerAdapter {
|
|||||||
return loggedIn;
|
return loggedIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInTransition() {
|
||||||
|
return serverTransition;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasBannedIP() {
|
public boolean hasBannedIP() {
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
try (Connection con = DatabaseConnection.getConnection();
|
try (Connection con = DatabaseConnection.getConnection();
|
||||||
@@ -833,7 +837,7 @@ public class Client extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean tryDisconnect() {
|
public synchronized boolean tryDisconnect() {
|
||||||
if (disconnecting) {
|
if (disconnecting) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -924,7 +928,7 @@ public class Client extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clear() {
|
public void clear() {
|
||||||
// player hard reference removal thanks to Steve (kaito1410)
|
// player hard reference removal thanks to Steve (kaito1410)
|
||||||
if (this.player != null) {
|
if (this.player != null) {
|
||||||
this.player.empty(true); // clears schedules and stuff
|
this.player.empty(true); // clears schedules and stuff
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package client.autoban;
|
|||||||
|
|
||||||
import client.Character;
|
import client.Character;
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
|
import net.netty.GameViolationException;
|
||||||
import net.server.Server;
|
import net.server.Server;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -118,11 +119,10 @@ public class AutobanManager {
|
|||||||
if (this.timestamp[type] == time) {
|
if (this.timestamp[type] == time) {
|
||||||
this.timestampcounter[type]++;
|
this.timestampcounter[type]++;
|
||||||
if (this.timestampcounter[type] >= times) {
|
if (this.timestampcounter[type] >= times) {
|
||||||
if (YamlConfig.config.server.USE_AUTOBAN) {
|
|
||||||
chr.getClient().disconnect(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Autoban - Chr {} was caught spamming TYPE {} and has been disconnected", chr, type);
|
log.info("Autoban - Chr {} was caught spamming TYPE {} and has been disconnected", chr, type);
|
||||||
|
if (YamlConfig.config.server.USE_AUTOBAN) {
|
||||||
|
throw new GameViolationException("Auto ban");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.timestamp[type] = time;
|
this.timestamp[type] = time;
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package client.command;
|
|||||||
import database.character.CharacterSaver;
|
import database.character.CharacterSaver;
|
||||||
import database.drop.DropProvider;
|
import database.drop.DropProvider;
|
||||||
import server.shop.ShopFactory;
|
import server.shop.ShopFactory;
|
||||||
import service.ChannelService;
|
import service.TransitionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ponk
|
* @author Ponk
|
||||||
*/
|
*/
|
||||||
public record CommandContext(CommandsExecutor commandsExecutor, DropProvider dropProvider, ShopFactory shopFactory,
|
public record CommandContext(CommandsExecutor commandsExecutor, DropProvider dropProvider, ShopFactory shopFactory,
|
||||||
CharacterSaver characterSaver, ChannelService channelService) {
|
CharacterSaver characterSaver, TransitionService transitionService) {
|
||||||
|
|
||||||
public CommandContext with(CommandsExecutor ce) {
|
public CommandContext with(CommandsExecutor ce) {
|
||||||
return new CommandContext(ce, this.dropProvider, this.shopFactory, this.characterSaver, this.channelService);
|
return new CommandContext(ce, this.dropProvider, this.shopFactory, this.characterSaver, this.transitionService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class SummonCommand extends Command {
|
|||||||
|
|
||||||
if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed.
|
if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed.
|
||||||
victim.dropMessage("Changing channel, please wait a moment.");
|
victim.dropMessage("Changing channel, please wait a moment.");
|
||||||
ctx.channelService().changeChannel(victim.getClient(), player.getClient().getChannel());
|
ctx.transitionService().changeChannel(victim.getClient(), player.getClient().getChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import database.character.CharacterLoader;
|
|||||||
import database.character.CharacterSaver;
|
import database.character.CharacterSaver;
|
||||||
import database.drop.DropProvider;
|
import database.drop.DropProvider;
|
||||||
import server.shop.ShopFactory;
|
import server.shop.ShopFactory;
|
||||||
import service.ChannelService;
|
|
||||||
import service.NoteService;
|
import service.NoteService;
|
||||||
|
import service.TransitionService;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ import java.util.Objects;
|
|||||||
public record ChannelDependencies(
|
public record ChannelDependencies(
|
||||||
CharacterLoader characterLoader, CharacterSaver characterSaver, NoteService noteService,
|
CharacterLoader characterLoader, CharacterSaver characterSaver, NoteService noteService,
|
||||||
FredrickProcessor fredrickProcessor, MakerProcessor makerProcessor, DropProvider dropProvider,
|
FredrickProcessor fredrickProcessor, MakerProcessor makerProcessor, DropProvider dropProvider,
|
||||||
CommandsExecutor commandsExecutor, ShopFactory shopFactory, ChannelService channelService
|
CommandsExecutor commandsExecutor, ShopFactory shopFactory, TransitionService transitionService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public ChannelDependencies {
|
public ChannelDependencies {
|
||||||
@@ -30,6 +30,6 @@ public record ChannelDependencies(
|
|||||||
Objects.requireNonNull(dropProvider);
|
Objects.requireNonNull(dropProvider);
|
||||||
Objects.requireNonNull(commandsExecutor);
|
Objects.requireNonNull(commandsExecutor);
|
||||||
Objects.requireNonNull(shopFactory);
|
Objects.requireNonNull(shopFactory);
|
||||||
Objects.requireNonNull(channelService);
|
Objects.requireNonNull(transitionService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler());
|
registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler());
|
||||||
registerHandler(RecvOpcode.CHECK_CHAR_NAME, new TransferNameResultHandler());
|
registerHandler(RecvOpcode.CHECK_CHAR_NAME, new TransferNameResultHandler());
|
||||||
registerHandler(RecvOpcode.WORLD_TRANSFER, new TransferWorldHandler());
|
registerHandler(RecvOpcode.WORLD_TRANSFER, new TransferWorldHandler());
|
||||||
registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler(channelDeps.channelService()));
|
registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler(channelDeps.transitionService()));
|
||||||
registerHandler(RecvOpcode.STRANGE_DATA, LoginRequiringNoOpHandler.getInstance());
|
registerHandler(RecvOpcode.STRANGE_DATA, LoginRequiringNoOpHandler.getInstance());
|
||||||
registerHandler(RecvOpcode.GENERAL_CHAT, new GeneralChatHandler(channelDeps.commandsExecutor()));
|
registerHandler(RecvOpcode.GENERAL_CHAT, new GeneralChatHandler(channelDeps.commandsExecutor()));
|
||||||
registerHandler(RecvOpcode.WHISPER, new WhisperHandler());
|
registerHandler(RecvOpcode.WHISPER, new WhisperHandler());
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ import server.expeditions.ExpeditionBossLog;
|
|||||||
import server.life.PlayerNPCFactory;
|
import server.life.PlayerNPCFactory;
|
||||||
import server.quest.Quest;
|
import server.quest.Quest;
|
||||||
import server.shop.ShopFactory;
|
import server.shop.ShopFactory;
|
||||||
import service.ChannelService;
|
|
||||||
import service.NoteService;
|
import service.NoteService;
|
||||||
|
import service.TransitionService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
@@ -982,17 +982,17 @@ public class Server {
|
|||||||
MonsterCardDao monsterCardDao = new MonsterCardDao(connection);
|
MonsterCardDao monsterCardDao = new MonsterCardDao(connection);
|
||||||
CharacterLoader characterLoader = new CharacterLoader(monsterCardDao);
|
CharacterLoader characterLoader = new CharacterLoader(monsterCardDao);
|
||||||
CharacterSaver characterSaver = new CharacterSaver(monsterCardDao);
|
CharacterSaver characterSaver = new CharacterSaver(monsterCardDao);
|
||||||
ChannelService channelService = new ChannelService(characterSaver);
|
TransitionService transitionService = new TransitionService(characterSaver);
|
||||||
NoteService noteService = new NoteService(new NoteDao(connection));
|
NoteService noteService = new NoteService(new NoteDao(connection));
|
||||||
MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection)));
|
MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection)));
|
||||||
FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService);
|
FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService);
|
||||||
DropProvider dropProvider = new DropProvider(new DropDao(connection));
|
DropProvider dropProvider = new DropProvider(new DropDao(connection));
|
||||||
ShopFactory shopFactory = new ShopFactory(new ShopDao(connection));
|
ShopFactory shopFactory = new ShopFactory(new ShopDao(connection));
|
||||||
CommandContext commandContext = new CommandContext(null, dropProvider, shopFactory,
|
CommandContext commandContext = new CommandContext(null, dropProvider, shopFactory,
|
||||||
characterSaver, channelService);
|
characterSaver, transitionService);
|
||||||
CommandsExecutor commandsExecutor = new CommandsExecutor(commandContext);
|
CommandsExecutor commandsExecutor = new CommandsExecutor(commandContext);
|
||||||
ChannelDependencies channelDependencies = new ChannelDependencies(characterLoader, characterSaver, noteService,
|
ChannelDependencies channelDependencies = new ChannelDependencies(characterLoader, characterSaver, noteService,
|
||||||
fredrickProcessor, makerProcessor, dropProvider, commandsExecutor, shopFactory, channelService);
|
fredrickProcessor, makerProcessor, dropProvider, commandsExecutor, shopFactory, transitionService);
|
||||||
|
|
||||||
PacketProcessor.registerGameHandlerDependencies(channelDependencies);
|
PacketProcessor.registerGameHandlerDependencies(channelDependencies);
|
||||||
|
|
||||||
|
|||||||
@@ -27,16 +27,16 @@ import net.AbstractPacketHandler;
|
|||||||
import net.netty.GameViolationException;
|
import net.netty.GameViolationException;
|
||||||
import net.packet.InPacket;
|
import net.packet.InPacket;
|
||||||
import net.server.Server;
|
import net.server.Server;
|
||||||
import service.ChannelService;
|
import service.TransitionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Matze
|
* @author Matze
|
||||||
*/
|
*/
|
||||||
public final class ChangeChannelHandler extends AbstractPacketHandler {
|
public final class ChangeChannelHandler extends AbstractPacketHandler {
|
||||||
private final ChannelService channelService;
|
private final TransitionService transitionService;
|
||||||
|
|
||||||
public ChangeChannelHandler(ChannelService channelService) {
|
public ChangeChannelHandler(TransitionService transitionService) {
|
||||||
this.channelService = channelService;
|
this.transitionService = transitionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -51,6 +51,6 @@ public final class ChangeChannelHandler extends AbstractPacketHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelService.changeChannel(c, channel);
|
transitionService.changeChannel(c, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
package service;
|
|
||||||
|
|
||||||
import client.Client;
|
|
||||||
import client.inventory.InventoryType;
|
|
||||||
import database.character.CharacterSaver;
|
|
||||||
import net.server.Server;
|
|
||||||
import server.maps.FieldLimit;
|
|
||||||
import server.maps.MiniDungeonInfo;
|
|
||||||
import tools.PacketCreator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
|
|
||||||
public class ChannelService {
|
|
||||||
private final Server server = Server.getInstance();
|
|
||||||
private final CharacterSaver chrSaver;
|
|
||||||
|
|
||||||
public ChannelService(CharacterSaver characterSaver) {
|
|
||||||
this.chrSaver = characterSaver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void changeChannel(Client c, int channel) {
|
|
||||||
var chr = c.getPlayer();
|
|
||||||
if (chr.isBanned()) {
|
|
||||||
c.disconnect(false, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chr.isAlive() || FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) {
|
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) {
|
|
||||||
c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
|
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] socket = Server.getInstance().getInetSocket(c, c.getWorld(), channel);
|
|
||||||
if (socket == null) {
|
|
||||||
c.sendPacket(PacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel."));
|
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
chr.closePlayerInteractions();
|
|
||||||
chr.closePartySearchInteractions();
|
|
||||||
|
|
||||||
chr.unregisterChairBuff();
|
|
||||||
server.getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
|
|
||||||
server.getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
|
|
||||||
chr.setDisconnectedFromChannelWorld();
|
|
||||||
chr.notifyMapTransferToPartner(-1);
|
|
||||||
chr.removeIncomingInvites();
|
|
||||||
chr.cancelAllBuffs(true);
|
|
||||||
chr.cancelAllDebuffs();
|
|
||||||
chr.cancelBuffExpireTask();
|
|
||||||
chr.cancelDiseaseExpireTask();
|
|
||||||
chr.cancelSkillCooldownTask();
|
|
||||||
chr.cancelQuestExpirationTask();
|
|
||||||
//Cancelling magicdoor? Nope
|
|
||||||
//Cancelling mounts? Noty
|
|
||||||
|
|
||||||
chr.getInventory(InventoryType.EQUIPPED).checked(false); //test
|
|
||||||
chr.getMap().removePlayer(chr);
|
|
||||||
chr.clearBanishPlayerData();
|
|
||||||
c.getChannelServer().removePlayer(chr);
|
|
||||||
|
|
||||||
chrSaver.save(chr);
|
|
||||||
|
|
||||||
chr.setSessionTransitionState();
|
|
||||||
try {
|
|
||||||
c.sendPacket(PacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
245
src/main/java/service/TransitionService.java
Normal file
245
src/main/java/service/TransitionService.java
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
package service;
|
||||||
|
|
||||||
|
import client.BuddyList;
|
||||||
|
import client.Client;
|
||||||
|
import client.inventory.InventoryType;
|
||||||
|
import config.YamlConfig;
|
||||||
|
import constants.id.MapId;
|
||||||
|
import database.character.CharacterSaver;
|
||||||
|
import net.server.Server;
|
||||||
|
import net.server.guild.Guild;
|
||||||
|
import net.server.guild.GuildCharacter;
|
||||||
|
import net.server.guild.GuildPackets;
|
||||||
|
import net.server.world.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import scripting.event.EventInstanceManager;
|
||||||
|
import server.ThreadManager;
|
||||||
|
import server.maps.FieldLimit;
|
||||||
|
import server.maps.MapleMap;
|
||||||
|
import server.maps.MiniDungeonInfo;
|
||||||
|
import tools.PacketCreator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
public class TransitionService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TransitionService.class);
|
||||||
|
private final Server server = Server.getInstance();
|
||||||
|
private final CharacterSaver chrSaver;
|
||||||
|
|
||||||
|
public TransitionService(CharacterSaver characterSaver) {
|
||||||
|
this.chrSaver = characterSaver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeChannel(Client c, int channel) {
|
||||||
|
var chr = c.getPlayer();
|
||||||
|
if (chr.isBanned()) {
|
||||||
|
disconnect(c, false, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chr.isAlive() || FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) {
|
||||||
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) {
|
||||||
|
c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
|
||||||
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] socket = Server.getInstance().getInetSocket(c, c.getWorld(), channel);
|
||||||
|
if (socket == null) {
|
||||||
|
c.sendPacket(PacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel."));
|
||||||
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chr.closePlayerInteractions();
|
||||||
|
chr.closePartySearchInteractions();
|
||||||
|
|
||||||
|
chr.unregisterChairBuff();
|
||||||
|
server.getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
|
||||||
|
server.getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
|
||||||
|
chr.setDisconnectedFromChannelWorld();
|
||||||
|
chr.notifyMapTransferToPartner(-1);
|
||||||
|
chr.removeIncomingInvites();
|
||||||
|
chr.cancelAllBuffs(true);
|
||||||
|
chr.cancelAllDebuffs();
|
||||||
|
chr.cancelBuffExpireTask();
|
||||||
|
chr.cancelDiseaseExpireTask();
|
||||||
|
chr.cancelSkillCooldownTask();
|
||||||
|
chr.cancelQuestExpirationTask();
|
||||||
|
//Cancelling magicdoor? Nope
|
||||||
|
//Cancelling mounts? Noty
|
||||||
|
|
||||||
|
chr.getInventory(InventoryType.EQUIPPED).checked(false); //test
|
||||||
|
chr.getMap().removePlayer(chr);
|
||||||
|
chr.clearBanishPlayerData();
|
||||||
|
c.getChannelServer().removePlayer(chr);
|
||||||
|
|
||||||
|
chrSaver.save(chr);
|
||||||
|
|
||||||
|
chr.setSessionTransitionState();
|
||||||
|
try {
|
||||||
|
c.sendPacket(PacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (c.tryDisconnect()) {
|
||||||
|
ThreadManager.getInstance().newTask(() -> disconnectInternal(c, shutdown, cashShop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceDisconnect(Client c) {
|
||||||
|
if (c.tryDisconnect()) {
|
||||||
|
disconnectInternal(c, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnectInternal(Client c, boolean shutdown, boolean cashShop) {
|
||||||
|
var chr = c.getPlayer();
|
||||||
|
if (chr != null && chr.isLoggedin() && chr.getClient() != null) {
|
||||||
|
final int messengerid = chr.getMessenger() == null ? 0 : chr.getMessenger().getId();
|
||||||
|
final BuddyList bl = chr.getBuddylist();
|
||||||
|
final MessengerCharacter messengerChr = new MessengerCharacter(chr, 0);
|
||||||
|
final GuildCharacter guildChr = chr.getMGC();
|
||||||
|
final Guild guild = chr.getGuild();
|
||||||
|
|
||||||
|
chr.cancelMagicDoor();
|
||||||
|
|
||||||
|
final World wserv = c.getWorldServer(); // obviously wserv is NOT null if this chr was online on it
|
||||||
|
try {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!c.isInTransition()) { // if dc inside of cash shop.
|
||||||
|
if (bl != null) {
|
||||||
|
wserv.loggedOff(chr.getName(), chr.getId(), channel, chr.getBuddylist().getBuddyIds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Account stuck", e);
|
||||||
|
} finally {
|
||||||
|
if (!c.isInTransition()) {
|
||||||
|
if (guildChr != null) {
|
||||||
|
guildChr.setCharacter(null);
|
||||||
|
}
|
||||||
|
wserv.removePlayer(chr);
|
||||||
|
//getChannelServer().removePlayer(player); already being done
|
||||||
|
|
||||||
|
chr.cancelAllDebuffs();
|
||||||
|
chrSaver.save(chr);
|
||||||
|
|
||||||
|
chr.logOff();
|
||||||
|
if (YamlConfig.config.server.INSTANT_NAME_CHANGE) {
|
||||||
|
chr.doPendingNameChange();
|
||||||
|
}
|
||||||
|
c.clear();
|
||||||
|
} else {
|
||||||
|
c.getChannelServer().removePlayer(chr);
|
||||||
|
|
||||||
|
chr.cancelAllDebuffs();
|
||||||
|
chrSaver.save(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removePlayer(Client c, World world, boolean serverTransition) {
|
||||||
|
var chr = c.getPlayer();
|
||||||
|
try {
|
||||||
|
chr.setDisconnectedFromChannelWorld();
|
||||||
|
chr.notifyMapTransferToPartner(-1);
|
||||||
|
chr.removeIncomingInvites();
|
||||||
|
chr.cancelAllBuffs(true);
|
||||||
|
|
||||||
|
chr.closePlayerInteractions();
|
||||||
|
chr.closePartySearchInteractions();
|
||||||
|
|
||||||
|
if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels
|
||||||
|
removePartyPlayer(c, world);
|
||||||
|
|
||||||
|
EventInstanceManager eim = chr.getEventInstance();
|
||||||
|
if (eim != null) {
|
||||||
|
eim.playerDisconnected(chr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chr.getMonsterCarnival() != null) {
|
||||||
|
chr.getMonsterCarnival().playerDisconnected(chr.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chr.getAriantColiseum() != null) {
|
||||||
|
chr.getAriantColiseum().playerDisconnected(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chr.getMap() != null) {
|
||||||
|
int mapId = chr.getMapId();
|
||||||
|
chr.getMap().removePlayer(chr);
|
||||||
|
if (MapId.isDojo(mapId)) {
|
||||||
|
c.getChannelServer().freeDojoSectionIfEmpty(mapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chr.getMap().getHPDec() > 0) {
|
||||||
|
world.removePlayerHpDecrease(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
log.error("Account stuck", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removePartyPlayer(Client c, World world) {
|
||||||
|
var chr = c.getPlayer();
|
||||||
|
MapleMap map = chr.getMap();
|
||||||
|
final Party party = chr.getParty();
|
||||||
|
final int idz = chr.getId();
|
||||||
|
|
||||||
|
if (party != null) {
|
||||||
|
final PartyCharacter chrp = new PartyCharacter(chr);
|
||||||
|
chrp.setOnline(false);
|
||||||
|
world.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) {
|
||||||
|
world.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user