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();
+ }
+ }
+}