diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java index b929ff035a..ee868491d4 100644 --- a/src/main/java/client/Client.java +++ b/src/main/java/client/Client.java @@ -21,7 +21,6 @@ along with this program. If not, see . */ package client; -import client.inventory.InventoryType; import config.YamlConfig; import constants.game.GameConstants; import constants.id.MapId; @@ -59,9 +58,7 @@ import server.MapleLeafLogger; import server.ThreadManager; import server.TimerManager; import server.life.Monster; -import server.maps.FieldLimit; import server.maps.MapleMap; -import server.maps.MiniDungeonInfo; import tools.BCrypt; import tools.DatabaseConnection; import tools.HexTool; @@ -69,7 +66,6 @@ import tools.PacketCreator; import javax.script.ScriptEngine; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -974,10 +970,9 @@ public class Client extends ChannelInboundHandlerAdapter { 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 int fid = player.getFamilyId(); final BuddyList bl = player.getBuddylist(); - final MessengerCharacter chrm = new MessengerCharacter(player, 0); - final GuildCharacter chrg = player.getMGC(); + final MessengerCharacter messengerChr = new MessengerCharacter(player, 0); + final GuildCharacter guildChr = player.getMGC(); final Guild guild = player.getGuild(); player.cancelMagicDoor(); @@ -990,14 +985,8 @@ public class Client extends ChannelInboundHandlerAdapter { if (!cashshop) { if (!this.serverTransition) { // meaning not changing channels if (messengerid > 0) { - wserv.leaveMessenger(messengerid, chrm); + wserv.leaveMessenger(messengerid, messengerChr); } - /* - if (fid > 0) { - final Family family = worlda.getFamily(fid); - family. - } - */ player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time @@ -1022,8 +1011,8 @@ public class Client extends ChannelInboundHandlerAdapter { log.error("Account stuck", e); } finally { if (!this.serverTransition) { - if (chrg != null) { - chrg.setCharacter(null); + if (guildChr != null) { + guildChr.setCharacter(null); } wserv.removePlayer(player); //getChannelServer().removePlayer(player); already being done @@ -1462,61 +1451,6 @@ public class Client extends ChannelInboundHandlerAdapter { sendPacket(PacketCreator.enableActions()); } - public void changeChannel(int channel) { - Server server = Server.getInstance(); - if (player.isBanned()) { - disconnect(false, false); - return; - } - if (!player.isAlive() || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) { - sendPacket(PacketCreator.enableActions()); - return; - } else if (MiniDungeonInfo.isDungeonMap(player.getMapId())) { - sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon.")); - sendPacket(PacketCreator.enableActions()); - return; - } - - String[] socket = Server.getInstance().getInetSocket(this, getWorld(), channel); - if (socket == null) { - sendPacket(PacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel.")); - sendPacket(PacketCreator.enableActions()); - return; - } - - player.closePlayerInteractions(); - player.closePartySearchInteractions(); - - player.unregisterChairBuff(); - server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); - server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases()); - player.setDisconnectedFromChannelWorld(); - player.notifyMapTransferToPartner(-1); - player.removeIncomingInvites(); - player.cancelAllBuffs(true); - player.cancelAllDebuffs(); - player.cancelBuffExpireTask(); - player.cancelDiseaseExpireTask(); - player.cancelSkillCooldownTask(); - player.cancelQuestExpirationTask(); - //Cancelling magicdoor? Nope - //Cancelling mounts? Noty - - player.getInventory(InventoryType.EQUIPPED).checked(false); //test - player.getMap().removePlayer(player); - player.clearBanishPlayerData(); - player.getClient().getChannelServer().removePlayer(player); - - player.saveCharToDB(); - - player.setSessionTransitionState(); - try { - sendPacket(PacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]))); - } catch (IOException e) { - e.printStackTrace(); - } - } - public long getSessionId() { return this.sessionId; } @@ -1585,4 +1519,4 @@ public class Client extends ChannelInboundHandlerAdapter { public void setLanguage(int lingua) { this.lang = lingua; } -} \ No newline at end of file +} diff --git a/src/main/java/client/command/CommandContext.java b/src/main/java/client/command/CommandContext.java index 458c013141..8858626dfc 100644 --- a/src/main/java/client/command/CommandContext.java +++ b/src/main/java/client/command/CommandContext.java @@ -2,13 +2,15 @@ package client.command; import database.drop.DropProvider; import server.shop.ShopFactory; +import service.ChannelService; /** * @author Ponk */ -public record CommandContext(CommandsExecutor commandsExecutor, DropProvider dropProvider, ShopFactory shopFactory) { +public record CommandContext(CommandsExecutor commandsExecutor, DropProvider dropProvider, ShopFactory shopFactory, + ChannelService channelService) { public CommandContext with(CommandsExecutor ce) { - return new CommandContext(ce, this.dropProvider, this.shopFactory); + return new CommandContext(ce, this.dropProvider, this.shopFactory, this.channelService); } } diff --git a/src/main/java/client/command/commands/gm2/SummonCommand.java b/src/main/java/client/command/commands/gm2/SummonCommand.java index 058ca081e4..338085d52a 100644 --- a/src/main/java/client/command/commands/gm2/SummonCommand.java +++ b/src/main/java/client/command/commands/gm2/SummonCommand.java @@ -63,7 +63,7 @@ public class SummonCommand extends Command { if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed. victim.dropMessage("Changing channel, please wait a moment."); - victim.getClient().changeChannel(player.getClient().getChannel()); + ctx.channelService().changeChannel(victim.getClient(), player.getClient().getChannel()); } try { diff --git a/src/main/java/database/JdbiConfig.java b/src/main/java/database/JdbiConfig.java index 2ea283739d..3f4c6875ab 100644 --- a/src/main/java/database/JdbiConfig.java +++ b/src/main/java/database/JdbiConfig.java @@ -5,6 +5,7 @@ import database.drop.MonsterDropRowMapper; import database.maker.MakerIngredientRowMapper; import database.maker.MakerReagentRowMapper; import database.maker.MakerRecipeRowMapper; +import database.monsterbook.MonsterCardRowMapper; import database.note.NoteRowMapper; import database.shop.ShopItemRowMapper; import database.shop.ShopRowMapper; @@ -32,7 +33,8 @@ public final class JdbiConfig { new MonsterDropRowMapper(), new GlobalMonsterDropRowMapper(), new ShopRowMapper(), - new ShopItemRowMapper() + new ShopItemRowMapper(), + new MonsterCardRowMapper() ); } } diff --git a/src/main/java/database/character/CharacterSaver.java b/src/main/java/database/character/CharacterSaver.java new file mode 100644 index 0000000000..493248089a --- /dev/null +++ b/src/main/java/database/character/CharacterSaver.java @@ -0,0 +1,20 @@ +package database.character; + +import client.Character; +import database.monsterbook.MonsterCardDao; + +public class CharacterSaver { + private final MonsterCardDao monsterCardDao; + + public CharacterSaver(MonsterCardDao monsterCardDao) { + this.monsterCardDao = monsterCardDao; + } + + public void save(Character chr) { + chr.saveCharToDB(false); + + // Saving monster cards to both MySQL and Postgres for now + monsterCardDao.save(chr.getId(), chr.getMonsterBook().getCards()); + } + +} diff --git a/src/main/java/database/monsterbook/MonsterCardDao.java b/src/main/java/database/monsterbook/MonsterCardDao.java index 7c51e929c1..f9d896a158 100644 --- a/src/main/java/database/monsterbook/MonsterCardDao.java +++ b/src/main/java/database/monsterbook/MonsterCardDao.java @@ -4,6 +4,7 @@ import database.DaoException; import database.PgDatabaseConnection; import org.jdbi.v3.core.Handle; import org.jdbi.v3.core.JdbiException; +import org.jdbi.v3.core.statement.PreparedBatch; import java.util.List; @@ -24,7 +25,26 @@ public class MonsterCardDao { .mapTo(MonsterCard.class) .list(); } catch (JdbiException e) { - throw new DaoException("Failed to find monster cards for chr id %d".formatted(chrId), e); + throw new DaoException("Failed to find monster cards (chrId %d)".formatted(chrId), e); + } + } + + public void save(int chrId, List cards) { + try (Handle handle = connection.getHandle()) { + PreparedBatch batch = handle.prepareBatch(""" + INSERT INTO monster_card (chr_id, card_id, level) + VALUES (?, ?, ?) + ON CONFLICT (chr_id, card_id) + DO UPDATE SET level = excluded.level;"""); + batch.bind(0, chrId); + cards.forEach(card -> { + batch.bind(1, card.cardId()); + batch.bind(2, card.level()); + batch.add(); + }); + batch.execute(); + } catch (JdbiException e) { + throw new DaoException("Failed to save monster cards (chrId %d)".formatted(chrId), e); } } } diff --git a/src/main/java/net/ChannelDependencies.java b/src/main/java/net/ChannelDependencies.java index 1c8d7db0f3..7da41b04a7 100644 --- a/src/main/java/net/ChannelDependencies.java +++ b/src/main/java/net/ChannelDependencies.java @@ -3,8 +3,10 @@ package net; import client.command.CommandsExecutor; import client.processor.action.MakerProcessor; import client.processor.npc.FredrickProcessor; +import database.character.CharacterSaver; import database.drop.DropProvider; import server.shop.ShopFactory; +import service.ChannelService; import service.NoteService; import java.util.Objects; @@ -13,16 +15,19 @@ import java.util.Objects; * @author Ponk */ public record ChannelDependencies( - NoteService noteService, FredrickProcessor fredrickProcessor, MakerProcessor makerProcessor, - DropProvider dropProvider, CommandsExecutor commandsExecutor, ShopFactory shopFactory + CharacterSaver characterSaver, NoteService noteService, FredrickProcessor fredrickProcessor, + MakerProcessor makerProcessor, DropProvider dropProvider, CommandsExecutor commandsExecutor, + ShopFactory shopFactory, ChannelService channelService ) { public ChannelDependencies { + Objects.requireNonNull(characterSaver); Objects.requireNonNull(noteService); Objects.requireNonNull(fredrickProcessor); Objects.requireNonNull(makerProcessor); Objects.requireNonNull(dropProvider); Objects.requireNonNull(commandsExecutor); Objects.requireNonNull(shopFactory); + Objects.requireNonNull(channelService); } } diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 96698bad6e..b5a5ccaf3c 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -140,7 +140,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler()); registerHandler(RecvOpcode.CHECK_CHAR_NAME, new TransferNameResultHandler()); registerHandler(RecvOpcode.WORLD_TRANSFER, new TransferWorldHandler()); - registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler()); + registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler(channelDeps.channelService())); registerHandler(RecvOpcode.STRANGE_DATA, LoginRequiringNoOpHandler.getInstance()); registerHandler(RecvOpcode.GENERAL_CHAT, new GeneralChatHandler(channelDeps.commandsExecutor())); registerHandler(RecvOpcode.WHISPER, new WhisperHandler()); @@ -160,7 +160,8 @@ public final class PacketProcessor { registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler(channelDeps.dropProvider())); registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler()); registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler()); - registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler(channelDeps.noteService(), channelDeps.shopFactory())); + registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler(channelDeps.noteService(), + channelDeps.shopFactory())); registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler()); registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler()); registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler()); diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 348a90f4f4..10e41e0e3f 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -43,11 +43,13 @@ import constants.net.OpcodeConstants; import constants.net.ServerConstants; import database.PgDatabaseConfig; import database.PgDatabaseConnection; +import database.character.CharacterSaver; import database.drop.DropDao; import database.drop.DropProvider; import database.maker.MakerDao; import database.maker.MakerInfoProvider; import database.migration.FlywayRunner; +import database.monsterbook.MonsterCardDao; import database.note.NoteDao; import database.shop.ShopDao; import net.ChannelDependencies; @@ -73,6 +75,7 @@ import server.expeditions.ExpeditionBossLog; import server.life.PlayerNPCFactory; import server.quest.Quest; import server.shop.ShopFactory; +import service.ChannelService; import service.NoteService; import tools.DatabaseConnection; import tools.Pair; @@ -975,15 +978,19 @@ public class Server { } private ChannelDependencies registerChannelDependencies(PgDatabaseConnection connection) { + MonsterCardDao monsterCardDao = new MonsterCardDao(connection); + CharacterSaver characterSaver = new CharacterSaver(monsterCardDao); + ChannelService channelService = new ChannelService(characterSaver); NoteService noteService = new NoteService(new NoteDao(connection)); MakerProcessor makerProcessor = new MakerProcessor(new MakerInfoProvider(new MakerDao(connection))); FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService); DropProvider dropProvider = new DropProvider(new DropDao(connection)); ShopFactory shopFactory = new ShopFactory(new ShopDao(connection)); - CommandContext commandContext = new CommandContext(null, dropProvider, shopFactory); + CommandContext commandContext = new CommandContext(null, dropProvider, shopFactory, + channelService); CommandsExecutor commandsExecutor = new CommandsExecutor(commandContext); - ChannelDependencies channelDependencies = new ChannelDependencies(noteService, fredrickProcessor, - makerProcessor, dropProvider, commandsExecutor, shopFactory); + ChannelDependencies channelDependencies = new ChannelDependencies(characterSaver, noteService, + fredrickProcessor, makerProcessor, dropProvider, commandsExecutor, shopFactory, channelService); PacketProcessor.registerGameHandlerDependencies(channelDependencies); diff --git a/src/main/java/net/server/channel/handlers/ChangeChannelHandler.java b/src/main/java/net/server/channel/handlers/ChangeChannelHandler.java index 418537094b..ffa92e4086 100644 --- a/src/main/java/net/server/channel/handlers/ChangeChannelHandler.java +++ b/src/main/java/net/server/channel/handlers/ChangeChannelHandler.java @@ -26,11 +26,17 @@ import client.autoban.AutobanFactory; import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.Server; +import service.ChannelService; /** * @author Matze */ public final class ChangeChannelHandler extends AbstractPacketHandler { + private final ChannelService channelService; + + public ChangeChannelHandler(ChannelService channelService) { + this.channelService = channelService; + } @Override public final void handlePacket(InPacket p, Client c) { @@ -45,6 +51,6 @@ public final class ChangeChannelHandler extends AbstractPacketHandler { return; } - c.changeChannel(channel); + channelService.changeChannel(c, channel); } -} \ No newline at end of file +} diff --git a/src/main/java/service/ChannelService.java b/src/main/java/service/ChannelService.java new file mode 100644 index 0000000000..9cbc6c5fb5 --- /dev/null +++ b/src/main/java/service/ChannelService.java @@ -0,0 +1,79 @@ +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(); + } + } +}