Move chr MySQL saving to CharacterSaver

This commit is contained in:
P0nk
2024-10-05 22:31:08 +02:00
parent a4f8086da1
commit f2b8ced976
7 changed files with 678 additions and 414 deletions

View File

@@ -118,7 +118,6 @@ import server.events.gm.Ola;
import server.life.BanishInfo;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillId;
import server.life.MobSkillType;
import server.life.Monster;
import server.life.PlayerNPC;
@@ -178,7 +177,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
@@ -236,7 +234,7 @@ public class Character extends AbstractCharacterObject {
private int linkedLevel = 0;
private String linkedName = null;
private boolean finishedDojoTutorial;
private boolean usedStorage = false;
private volatile boolean usedStorage = false; // TODO: fully move this into Storage where usage can be fully controlled.
private String name;
private String chalktext;
private String commandtext;
@@ -1256,6 +1254,10 @@ public class Character extends AbstractCharacterObject {
this.m_pQuickslotKeyMapped = new QuickslotBinding(aQuickslotKeyMapped);
}
public QuickslotBinding getQuickslotBinding() {
return this.m_pQuickslotKeyMapped;
}
public void broadcastStance(int newStance) {
setStance(newStance);
broadcastStance();
@@ -4631,7 +4633,7 @@ public class Character extends AbstractCharacterObject {
return client.getAbstractPlayerInteraction();
}
private List<QuestStatus> getQuests() {
public List<QuestStatus> getQuests() {
synchronized (quests) {
return new ArrayList<>(quests.values());
}
@@ -4995,6 +4997,14 @@ public class Character extends AbstractCharacterObject {
usedStorage = true;
}
public void resetUsedStorage() {
usedStorage = false;
}
public boolean usedStorage() {
return usedStorage;
}
public List<Ring> getFriendshipRings() {
Collections.sort(friendshipRings);
return friendshipRings;
@@ -5169,7 +5179,7 @@ public class Character extends AbstractCharacterObject {
}
public Map<Integer, KeyBinding> getKeymap() {
return keymap;
return Collections.unmodifiableMap(keymap);
}
public long getLastHealed() {
@@ -5777,6 +5787,10 @@ public class Character extends AbstractCharacterObject {
return rankMove;
}
public SavedLocation[] getSavedLocations() {
return Arrays.copyOf(savedLocations, savedLocations.length);
}
public void clearSavedLocation(SavedLocationType type) {
savedLocations[type.ordinal()] = null;
}
@@ -8116,335 +8130,6 @@ public class Character extends AbstractCharacterObject {
return false;
}
//ItemFactory saveItems and monsterbook.saveCards are the most time consuming here.
public synchronized void saveCharToDB(Connection con) throws SQLException {
Server.getInstance().updateCharacterEntry(this);
saveCharacter(con);
monsterbook.saveCards(con, id);
List<Pet> petList = new LinkedList<>();
petLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (pets[i] != null) {
petList.add(pets[i]);
}
}
} finally {
petLock.unlock();
}
for (Pet pet : petList) {
pet.saveToDb();
}
for (Entry<Integer, Set<Integer>> es : getExcluded().entrySet()) { // this set is already protected
try (PreparedStatement psIgnore = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) {
psIgnore.setInt(1, es.getKey());
psIgnore.executeUpdate();
}
try (PreparedStatement psIgnore = con.prepareStatement("INSERT INTO petignores (petid, itemid) VALUES (?, ?)")) {
psIgnore.setInt(1, es.getKey());
for (Integer x : es.getValue()) {
psIgnore.setInt(2, x);
psIgnore.addBatch();
}
psIgnore.executeBatch();
}
}
// Key config
deleteWhereCharacterId(con, "DELETE FROM keymap WHERE characterid = ?");
try (PreparedStatement psKey = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)")) {
psKey.setInt(1, id);
Set<Entry<Integer, KeyBinding>> keybindingItems = Collections.unmodifiableSet(keymap.entrySet());
for (Entry<Integer, KeyBinding> keybinding : keybindingItems) {
psKey.setInt(2, keybinding.getKey());
psKey.setInt(3, keybinding.getValue().getType());
psKey.setInt(4, keybinding.getValue().getAction());
psKey.addBatch();
}
psKey.executeBatch();
}
// No quickslots, or no change.
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.GetKeybindings(), this.m_aQuickslotLoaded));
if (!bQuickslotEquals) {
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.GetKeybindings());
try (final PreparedStatement psQuick = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
psQuick.setInt(1, this.getAccountID());
psQuick.setLong(2, nQuickslotKeymapped);
psQuick.setLong(3, nQuickslotKeymapped);
psQuick.executeUpdate();
}
}
// Skill macros
deleteWhereCharacterId(con, "DELETE FROM skillmacros WHERE characterid = ?");
try (PreparedStatement psMacro = con.prepareStatement("INSERT INTO skillmacros (characterid, skill1, skill2, skill3, name, shout, position) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
psMacro.setInt(1, getId());
for (int i = 0; i < 5; i++) {
SkillMacro macro = skillMacros[i];
if (macro != null) {
psMacro.setInt(2, macro.getSkill1());
psMacro.setInt(3, macro.getSkill2());
psMacro.setInt(4, macro.getSkill3());
psMacro.setString(5, macro.getName());
psMacro.setInt(6, macro.getShout());
psMacro.setInt(7, i);
psMacro.addBatch();
}
}
psMacro.executeBatch();
}
List<Pair<Item, InventoryType>> itemsWithType = new ArrayList<>();
for (Inventory iv : inventory) {
for (Item item : iv.list()) {
itemsWithType.add(new Pair<>(item, iv.getType()));
}
}
// Items
ItemFactory.INVENTORY.saveItems(itemsWithType, id, con);
// Skills
try (PreparedStatement psSkill = con.prepareStatement("REPLACE INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)")) {
psSkill.setInt(1, id);
for (Entry<Skill, SkillEntry> skill : skills.entrySet()) {
psSkill.setInt(2, skill.getKey().getId());
psSkill.setInt(3, skill.getValue().skillevel);
psSkill.setInt(4, skill.getValue().masterlevel);
psSkill.setLong(5, skill.getValue().expiration);
psSkill.addBatch();
}
psSkill.executeBatch();
}
// Saved locations
deleteWhereCharacterId(con, "DELETE FROM savedlocations WHERE characterid = ?");
try (PreparedStatement psLoc = con.prepareStatement("INSERT INTO savedlocations (characterid, `locationtype`, `map`, `portal`) VALUES (?, ?, ?, ?)")) {
psLoc.setInt(1, id);
for (SavedLocationType savedLocationType : SavedLocationType.values()) {
if (savedLocations[savedLocationType.ordinal()] != null) {
psLoc.setString(2, savedLocationType.name());
psLoc.setInt(3, savedLocations[savedLocationType.ordinal()].getMapId());
psLoc.setInt(4, savedLocations[savedLocationType.ordinal()].getPortal());
psLoc.addBatch();
}
}
psLoc.executeBatch();
}
deleteWhereCharacterId(con, "DELETE FROM trocklocations WHERE characterid = ?");
// Vip teleport rocks
try (PreparedStatement psVip = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 0)")) {
for (int i = 0; i < getTrockSize(); i++) {
if (trockmaps.get(i) != MapId.NONE) {
psVip.setInt(1, getId());
psVip.setInt(2, trockmaps.get(i));
psVip.addBatch();
}
}
psVip.executeBatch();
}
// Regular teleport rocks
try (PreparedStatement psReg = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 1)")) {
for (int i = 0; i < getVipTrockSize(); i++) {
if (viptrockmaps.get(i) != MapId.NONE) {
psReg.setInt(1, getId());
psReg.setInt(2, viptrockmaps.get(i));
psReg.addBatch();
}
}
psReg.executeBatch();
}
// Buddy
deleteWhereCharacterId(con, "DELETE FROM buddies WHERE characterid = ? AND pending = 0");
try (PreparedStatement psBuddy = con.prepareStatement("INSERT INTO buddies (characterid, `buddyid`, `pending`, `group`) VALUES (?, ?, 0, ?)")) {
psBuddy.setInt(1, id);
for (BuddylistEntry entry : buddylist.getBuddies()) {
if (entry.isVisible()) {
psBuddy.setInt(2, entry.getCharacterId());
psBuddy.setString(3, entry.getGroup());
psBuddy.addBatch();
}
}
psBuddy.executeBatch();
}
// Area info
deleteWhereCharacterId(con, "DELETE FROM area_info WHERE charid = ?");
try (PreparedStatement psArea = con.prepareStatement("INSERT INTO area_info (id, charid, area, info) VALUES (DEFAULT, ?, ?, ?)")) {
psArea.setInt(1, id);
for (Entry<Short, String> area : area_info.entrySet()) {
psArea.setInt(2, area.getKey());
psArea.setString(3, area.getValue());
psArea.addBatch();
}
psArea.executeBatch();
}
// Event stats
deleteWhereCharacterId(con, "DELETE FROM eventstats WHERE characterid = ?");
try (PreparedStatement psEvent = con.prepareStatement("INSERT INTO eventstats (characterid, name, info) VALUES (?, ?, ?)")) {
psEvent.setInt(1, id);
for (Map.Entry<String, Events> entry : events.entrySet()) {
psEvent.setString(2, entry.getKey());
psEvent.setInt(3, entry.getValue().getInfo());
psEvent.addBatch();
}
psEvent.executeBatch();
}
deleteQuestProgressWhereCharacterId(con, id);
// Quests and medals
try (PreparedStatement psStatus = con.prepareStatement("INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `expires`, `forfeited`, `completed`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
PreparedStatement psProgress = con.prepareStatement("INSERT INTO questprogress VALUES (DEFAULT, ?, ?, ?, ?)");
PreparedStatement psMedal = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)")) {
psStatus.setInt(1, id);
for (QuestStatus qs : getQuests()) {
psStatus.setInt(2, qs.getQuest().getId());
psStatus.setInt(3, qs.getStatus().getId());
psStatus.setInt(4, (int) (qs.getCompletionTime() / 1000));
psStatus.setLong(5, qs.getExpirationTime());
psStatus.setInt(6, qs.getForfeited());
psStatus.setInt(7, qs.getCompleted());
psStatus.executeUpdate();
try (ResultSet rs = psStatus.getGeneratedKeys()) {
rs.next();
for (int mob : qs.getProgress().keySet()) {
psProgress.setInt(1, id);
psProgress.setInt(2, rs.getInt(1));
psProgress.setInt(3, mob);
psProgress.setString(4, qs.getProgress(mob));
psProgress.addBatch();
}
psProgress.executeBatch();
for (int i = 0; i < qs.getMedalMaps().size(); i++) {
psMedal.setInt(1, id);
psMedal.setInt(2, rs.getInt(1));
psMedal.setInt(3, qs.getMedalMaps().get(i));
psMedal.addBatch();
}
psMedal.executeBatch();
}
}
}
FamilyEntry familyEntry = getFamilyEntry(); //save family rep
if (familyEntry != null) {
if (familyEntry.saveReputation(con)) {
familyEntry.savedSuccessfully();
}
FamilyEntry senior = familyEntry.getSenior();
if (senior != null && senior.getChr() == null) { //only save for offline family members
if (senior.saveReputation(con)) {
senior.savedSuccessfully();
}
senior = senior.getSenior(); //save one level up as well
if (senior != null && senior.getChr() == null) {
if (senior.saveReputation(con)) {
senior.savedSuccessfully();
}
}
}
}
saveCooldowns(con);
saveDiseases(con);
if (cashshop != null) {
cashshop.save(con);
}
if (storage != null && usedStorage) {
storage.saveToDB(con);
usedStorage = false;
}
}
private void saveCharacter(Connection con) throws SQLException {
CharacterStats stats = getCharacterStats();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, jailexpire = ?, partnerId = ?, marriageItemId = ?, lastExpGainTime = ?, ariantPoints = ?, partySearch = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, stats.level());
ps.setInt(2, stats.fame());
ps.setInt(3, stats.str());
ps.setInt(4, stats.dex());
ps.setInt(5, stats.luk());
ps.setInt(6, stats.int_());
ps.setInt(7, stats.exp());
ps.setInt(8, stats.gachaExp());
ps.setInt(9, stats.hp());
ps.setInt(10, stats.mp());
ps.setInt(11, stats.maxHp());
ps.setInt(12, stats.maxMp());
ps.setString(13, String.valueOf(stats.sp()));
ps.setInt(14, stats.ap());
ps.setInt(15, stats.gmLevel());
ps.setInt(16, stats.skin());
ps.setInt(17, stats.gender());
ps.setInt(18, stats.job());
ps.setInt(19, stats.hair());
ps.setInt(20, stats.face());
ps.setInt(21, stats.mapId());
ps.setInt(22, stats.meso());
ps.setInt(23, stats.hpMpApUsed());
ps.setInt(24, stats.spawnPortal());
ps.setInt(25, Objects.requireNonNullElse(stats.party(), -1));
ps.setInt(26, stats.buddyCapacity());
ps.setInt(27, Objects.requireNonNullElse(stats.messenger(), 0));
ps.setInt(28, Objects.requireNonNullElse(stats.messengerPosition(), 4));
ps.setInt(29, Objects.requireNonNullElse(stats.mountLevel(), 1));
ps.setInt(30, Objects.requireNonNullElse(stats.mountExp(), 0));
ps.setInt(31, Objects.requireNonNullElse(stats.mountTiredness(), 0));
ps.setInt(32, stats.equipSlots());
ps.setInt(33, stats.useSlots());
ps.setInt(34, stats.setupSlots());
ps.setInt(35, stats.etcSlots());
ps.setInt(36, stats.monsterBookCover());
ps.setInt(37, stats.dojoVanquisherStage());
ps.setInt(38, stats.dojoPoints());
ps.setInt(39, stats.dojoStage());
ps.setInt(40, stats.dojoTutorialComplete() ? 1 : 0);
ps.setInt(41, stats.dojoVanquisherKills());
ps.setInt(42, stats.matchCardWins());
ps.setInt(43, stats.matchCardLosses());
ps.setInt(44, stats.matchCardTies());
ps.setInt(45, stats.omokWins());
ps.setInt(46, stats.omokLosses());
ps.setInt(47, stats.omokTies());
ps.setString(48, stats.dataString());
ps.setLong(49, Objects.requireNonNullElse(stats.jailExpiration(), 0L));
ps.setInt(50, Objects.requireNonNullElse(stats.partnerId(), -1));
ps.setInt(51, Objects.requireNonNullElse(stats.marriageItemId(), -1));
ps.setTimestamp(52, new Timestamp(stats.lastExpGainTime()));
ps.setInt(53, stats.ariantPoints());
ps.setBoolean(54, stats.canRecvPartySearchInvite());
ps.setInt(55, stats.id());
int updateRows = ps.executeUpdate();
if (updateRows < 1) {
throw new RuntimeException("Character not in database (" + id + ")");
}
}
}
public CharacterStats getCharacterStats() {
CharacterStats.CharacterStatsBuilder builder = CharacterStats.builder()
.id(id)
@@ -8560,50 +8245,6 @@ public class Character extends AbstractCharacterObject {
return closest.getId();
}
private void saveCooldowns(Connection con) throws SQLException {
deleteWhereCharacterId(con, "DELETE FROM cooldowns WHERE charid = ?");
List<PlayerCoolDownValueHolder> cooldowns = getAllCooldowns();
if (cooldowns.isEmpty()) {
return;
}
try (PreparedStatement ps = con.prepareStatement("INSERT INTO cooldowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)")) {
ps.setInt(1, getId());
for (PlayerCoolDownValueHolder cooling : cooldowns) {
ps.setInt(2, cooling.skillId);
ps.setLong(3, cooling.startTime);
ps.setLong(4, cooling.length);
ps.addBatch();
}
ps.executeBatch();
}
}
private void saveDiseases(Connection con) throws SQLException {
deleteWhereCharacterId(con, "DELETE FROM playerdiseases WHERE charid = ?");
Map<Disease, Pair<Long, MobSkill>> diseases = getAllDiseases();
if (diseases.isEmpty()) {
return;
}
try (PreparedStatement ps = con.prepareStatement("INSERT INTO playerdiseases (charid, disease, mobskillid, mobskilllv, length) VALUES (?, ?, ?, ?, ?)")) {
ps.setInt(1, getId());
for (Entry<Disease, Pair<Long, MobSkill>> e : diseases.entrySet()) {
ps.setInt(2, e.getKey().ordinal());
MobSkill ms = e.getValue().getRight();
MobSkillId msId = ms.getId();
ps.setInt(3, msId.type().getId());
ps.setInt(4, msId.level());
ps.setInt(5, e.getValue().getLeft().intValue());
ps.addBatch();
}
ps.executeBatch();
}
}
public void sendPolice(String text) {
final String message = getName() + " received this - " + text;
log.info(message);
@@ -8618,17 +8259,6 @@ public class Character extends AbstractCharacterObject {
sendPacket(PacketCreator.getKeymap(keymap));
}
public void sendQuickmap() {
// send quickslots to user
QuickslotBinding pQuickslotKeyMapped = this.m_pQuickslotKeyMapped;
if (pQuickslotKeyMapped == null) {
pQuickslotKeyMapped = new QuickslotBinding(QuickslotBinding.DEFAULT_QUICKSLOTS);
}
this.sendPacket(PacketCreator.QuickslotMappedInit(pQuickslotKeyMapped));
}
public void sendMacros() {
// Always send the macro packet to fix a client side bug when switching characters.
sendPacket(PacketCreator.getMacros(skillMacros));

View File

@@ -109,7 +109,7 @@ public class Pet extends Item {
}
}
public void saveToDb() {
public void saveToDb() { // TODO: throw SQLException
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?")) {
ps.setString(1, getName());
@@ -325,4 +325,4 @@ public class Pet extends Item {
}
}
}
}
}

View File

@@ -1,26 +1,66 @@
package database.character;
import client.BuddylistEntry;
import client.Character;
import client.CharacterStats;
import client.Disease;
import client.FamilyEntry;
import client.QuestStatus;
import client.Skill;
import client.SkillMacro;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.Pet;
import client.keybind.KeyBinding;
import client.keybind.QuickslotBinding;
import constants.id.MapId;
import database.PgDatabaseConnection;
import database.monsterbook.MonsterCardRepository;
import lombok.extern.slf4j.Slf4j;
import net.server.PlayerCoolDownValueHolder;
import net.server.Server;
import org.jdbi.v3.core.Handle;
import server.CashShop;
import server.Storage;
import server.events.Events;
import server.life.MobSkill;
import server.life.MobSkillId;
import server.maps.SavedLocation;
import server.maps.SavedLocationType;
import tools.DatabaseConnection;
import tools.LongTool;
import tools.Pair;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@Slf4j
public class CharacterSaver {
private static final Set<InventoryType> INVENTORIES_TO_SAVE = Set.of(InventoryType.EQUIP, InventoryType.USE,
InventoryType.SETUP, InventoryType.ETC, InventoryType.CASH, InventoryType.EQUIPPED);
private final Server server;
private final PgDatabaseConnection pgConnection;
private final CharacterRepository characterRepository;
private final MonsterCardRepository monsterCardRepository;
public CharacterSaver(PgDatabaseConnection pgConnection,
public CharacterSaver(Server server, PgDatabaseConnection pgConnection,
CharacterRepository characterRepository,
MonsterCardRepository monsterCardRepository) {
this.server = server;
this.pgConnection = pgConnection;
this.characterRepository = characterRepository;
this.monsterCardRepository = monsterCardRepository;
@@ -28,10 +68,12 @@ public class CharacterSaver {
public void save(Character chr) {
if (!chr.isLoggedin()) {
log.debug("Not saving chr {} - not logged in", chr.getName());
return;
}
log.debug("Saving chr {}", chr.getName());
server.updateCharacterEntry(chr);
saveToMysql(chr);
saveToPostgres(chr);
}
@@ -42,7 +84,7 @@ public class CharacterSaver {
con.setAutoCommit(false);
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
try {
chr.saveCharToDB(con);
saveChrMysql(con, chr);
con.commit();
} catch (Exception e) {
con.rollback();
@@ -58,6 +100,589 @@ public class CharacterSaver {
log.debug("Saved {} to MySQL in {} ms", chr.getName(), saveDuration.toMillis());
}
private void saveChrMysql(Connection con, Character chr) throws SQLException {
saveCharacter(con, chr);
saveInventory(con, chr);
saveCashShop(con, chr);
saveStorage(con, chr);
savePets(con, chr);
saveBuddies(con, chr);
saveFamily(con, chr);
saveDiseases(con, chr);
saveKeymap(con, chr);
saveQuickmap(con, chr);
saveSkills(con, chr);
saveCooldowns(con, chr);
saveSkillMacros(con, chr);
saveQuests(con, chr);
saveTeleportRockLocations(con, chr);
savePetIgnoredItems(con, chr);
saveSavedLocations(con, chr);
saveAreaInfo(con, chr);
saveMonsterBook(con, chr);
saveEventStats(con, chr);
}
private void saveCharacter(Connection con, Character chr) throws SQLException {
CharacterStats stats = chr.getCharacterStats();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, jailexpire = ?, partnerId = ?, marriageItemId = ?, lastExpGainTime = ?, ariantPoints = ?, partySearch = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, stats.level());
ps.setInt(2, stats.fame());
ps.setInt(3, stats.str());
ps.setInt(4, stats.dex());
ps.setInt(5, stats.luk());
ps.setInt(6, stats.int_());
ps.setInt(7, stats.exp());
ps.setInt(8, stats.gachaExp());
ps.setInt(9, stats.hp());
ps.setInt(10, stats.mp());
ps.setInt(11, stats.maxHp());
ps.setInt(12, stats.maxMp());
ps.setString(13, String.valueOf(stats.sp()));
ps.setInt(14, stats.ap());
ps.setInt(15, stats.gmLevel());
ps.setInt(16, stats.skin());
ps.setInt(17, stats.gender());
ps.setInt(18, stats.job());
ps.setInt(19, stats.hair());
ps.setInt(20, stats.face());
ps.setInt(21, stats.mapId());
ps.setInt(22, stats.meso());
ps.setInt(23, stats.hpMpApUsed());
ps.setInt(24, stats.spawnPortal());
ps.setInt(25, Objects.requireNonNullElse(stats.party(), -1));
ps.setInt(26, stats.buddyCapacity());
ps.setInt(27, Objects.requireNonNullElse(stats.messenger(), 0));
ps.setInt(28, Objects.requireNonNullElse(stats.messengerPosition(), 4));
ps.setInt(29, Objects.requireNonNullElse(stats.mountLevel(), 1));
ps.setInt(30, Objects.requireNonNullElse(stats.mountExp(), 0));
ps.setInt(31, Objects.requireNonNullElse(stats.mountTiredness(), 0));
ps.setInt(32, stats.equipSlots());
ps.setInt(33, stats.useSlots());
ps.setInt(34, stats.setupSlots());
ps.setInt(35, stats.etcSlots());
ps.setInt(36, stats.monsterBookCover());
ps.setInt(37, stats.dojoVanquisherStage());
ps.setInt(38, stats.dojoPoints());
ps.setInt(39, stats.dojoStage());
ps.setInt(40, stats.dojoTutorialComplete() ? 1 : 0);
ps.setInt(41, stats.dojoVanquisherKills());
ps.setInt(42, stats.matchCardWins());
ps.setInt(43, stats.matchCardLosses());
ps.setInt(44, stats.matchCardTies());
ps.setInt(45, stats.omokWins());
ps.setInt(46, stats.omokLosses());
ps.setInt(47, stats.omokTies());
ps.setString(48, stats.dataString());
ps.setLong(49, Objects.requireNonNullElse(stats.jailExpiration(), 0L));
ps.setInt(50, Objects.requireNonNullElse(stats.partnerId(), -1));
ps.setInt(51, Objects.requireNonNullElse(stats.marriageItemId(), -1));
ps.setTimestamp(52, new Timestamp(stats.lastExpGainTime()));
ps.setInt(53, stats.ariantPoints());
ps.setBoolean(54, stats.canRecvPartySearchInvite());
ps.setInt(55, stats.id());
int updateRows = ps.executeUpdate();
if (updateRows < 1) {
throw new RuntimeException("Character not in database (" + chr.getId() + ")");
}
}
}
private void saveInventory(Connection con, Character chr) throws SQLException {
List<Pair<Item, InventoryType>> itemsWithType = new ArrayList<>();
for (InventoryType type : INVENTORIES_TO_SAVE) {
Inventory inventory = chr.getInventory(type);
for (Item item : inventory.list()) {
itemsWithType.add(new Pair<>(item, type));
}
}
ItemFactory.INVENTORY.saveItems(itemsWithType, chr.getId(), con);
}
private void saveCashShop(Connection con, Character chr) throws SQLException {
CashShop cashShop = chr.getCashShop();
if (cashShop == null) {
return;
}
cashShop.save(con);
}
private void saveStorage(Connection con, Character chr) throws SQLException {
Storage storage = chr.getStorage();
if (storage == null || !chr.usedStorage()) {
return;
}
storage.saveToDB(con);
chr.resetUsedStorage();
}
private void savePets(Connection con, Character chr) throws SQLException {
Pet[] pets = chr.getPets();
for (Pet pet : pets) {
if (pet == null) {
continue;
}
savePet(con, pet);
}
}
private void savePet(Connection con, Pet pet) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?")) {
ps.setString(1, pet.getName());
ps.setInt(2, pet.getLevel());
ps.setInt(3, pet.getTameness());
ps.setInt(4, pet.getFullness());
ps.setInt(5, pet.isSummoned() ? 1 : 0);
ps.setInt(6, pet.getPetAttribute());
ps.setInt(7, pet.getUniqueId());
ps.executeUpdate();
}
}
private void saveBuddies(Connection con, Character chr) throws SQLException {
deleteBuddies(con, chr);
insertBuddies(con, chr);
}
private void deleteBuddies(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM buddies
WHERE characterid = ?
AND pending = 0""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertBuddies(Connection con, Character chr) throws SQLException {
try (PreparedStatement psBuddy = con.prepareStatement("INSERT INTO buddies (characterid, `buddyid`, `pending`, `group`) VALUES (?, ?, 0, ?)")) {
psBuddy.setInt(1, chr.getId());
for (BuddylistEntry entry : chr.getBuddylist().getBuddies()) {
if (entry.isVisible()) {
psBuddy.setInt(2, entry.getCharacterId());
psBuddy.setString(3, entry.getGroup());
psBuddy.addBatch();
}
}
psBuddy.executeBatch();
}
}
private void saveQuests(Connection con, Character chr) throws SQLException {
deleteMedalMaps(con, chr);
deleteQuestProgress(con, chr);
deleteQuestStatus(con, chr);
insertQuestStatusAndQuestProgressAndMedalMaps(con, chr);
}
private void deleteMedalMaps(Connection con, Character chr) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("DELETE FROM medalmaps WHERE characterid = ?")) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void deleteQuestProgress(Connection con, Character chr) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("DELETE FROM questprogress WHERE characterid = ?")) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void deleteQuestStatus(Connection con, Character chr) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("DELETE FROM queststatus WHERE characterid = ?")) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertQuestStatusAndQuestProgressAndMedalMaps(Connection con, Character chr) throws SQLException {
try (PreparedStatement psStatus = con.prepareStatement("INSERT INTO queststatus (`queststatusid`, `characterid`, `quest`, `status`, `time`, `expires`, `forfeited`, `completed`) VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
PreparedStatement psProgress = con.prepareStatement("INSERT INTO questprogress VALUES (DEFAULT, ?, ?, ?, ?)");
PreparedStatement psMedal = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)")) {
psStatus.setInt(1, chr.getId());
for (QuestStatus qs : chr.getQuests()) {
psStatus.setInt(2, qs.getQuest().getId());
psStatus.setInt(3, qs.getStatus().getId());
psStatus.setInt(4, (int) (qs.getCompletionTime() / 1000));
psStatus.setLong(5, qs.getExpirationTime());
psStatus.setInt(6, qs.getForfeited());
psStatus.setInt(7, qs.getCompleted());
psStatus.executeUpdate();
try (ResultSet rs = psStatus.getGeneratedKeys()) {
rs.next();
for (int mob : qs.getProgress().keySet()) {
psProgress.setInt(1, chr.getId());
psProgress.setInt(2, rs.getInt(1));
psProgress.setInt(3, mob);
psProgress.setString(4, qs.getProgress(mob));
psProgress.addBatch();
}
psProgress.executeBatch();
for (int i = 0; i < qs.getMedalMaps().size(); i++) {
psMedal.setInt(1, chr.getId());
psMedal.setInt(2, rs.getInt(1));
psMedal.setInt(3, qs.getMedalMaps().get(i));
psMedal.addBatch();
}
psMedal.executeBatch();
}
}
}
}
private void saveTeleportRockLocations(Connection con, Character chr) throws SQLException {
deleteTeleportRockLocations(con, chr);
insertRegularTeleportRockLocations(con, chr);
insertVipTeleportRockLocations(con, chr);
}
private void deleteTeleportRockLocations(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM trocklocations
WHERE characterid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertRegularTeleportRockLocations(Connection con, Character chr) throws SQLException {
List<Integer> viptrockmaps = chr.getVipTrockMaps();
try (PreparedStatement psReg = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 1)")) {
for (int i = 0; i < chr.getVipTrockSize(); i++) {
if (viptrockmaps.get(i) != MapId.NONE) {
psReg.setInt(1, chr.getId());
psReg.setInt(2, viptrockmaps.get(i));
psReg.addBatch();
}
}
psReg.executeBatch();
}
}
private void insertVipTeleportRockLocations(Connection con, Character chr) throws SQLException {
List<Integer> trockmaps = chr.getTrockMaps();
try (PreparedStatement psVip = con.prepareStatement("INSERT INTO trocklocations(characterid, mapid, vip) VALUES (?, ?, 0)")) {
for (int i = 0; i < chr.getTrockSize(); i++) {
if (trockmaps.get(i) != MapId.NONE) {
psVip.setInt(1, chr.getId());
psVip.setInt(2, trockmaps.get(i));
psVip.addBatch();
}
}
psVip.executeBatch();
}
}
private void saveMonsterBook(Connection con, Character chr) throws SQLException {
chr.getMonsterBook().saveCards(con, chr.getId());
}
private void savePetIgnoredItems(Connection con, Character chr) throws SQLException {
for (Map.Entry<Integer, Set<Integer>> es : chr.getExcluded().entrySet()) {
try (PreparedStatement psIgnore = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) {
psIgnore.setInt(1, es.getKey());
psIgnore.executeUpdate();
}
try (PreparedStatement psIgnore = con.prepareStatement("INSERT INTO petignores (petid, itemid) VALUES (?, ?)")) {
psIgnore.setInt(1, es.getKey());
for (Integer x : es.getValue()) {
psIgnore.setInt(2, x);
psIgnore.addBatch();
}
psIgnore.executeBatch();
}
}
}
private void saveKeymap(Connection con, Character chr) throws SQLException {
deleteKeymap(con, chr.getId());
insertKeymap(con, chr);
}
private void deleteKeymap(Connection con, int chrId) throws SQLException {
String sql = """
DELETE FROM keymap
WHERE characterid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chrId);
ps.executeUpdate();
}
}
private void insertKeymap(Connection con, Character chr) throws SQLException {
try (PreparedStatement psKey = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)")) {
psKey.setInt(1, chr.getId());
for (Map.Entry<Integer, KeyBinding> keybinding : chr.getKeymap().entrySet()) {
psKey.setInt(2, keybinding.getKey());
psKey.setInt(3, keybinding.getValue().getType());
psKey.setInt(4, keybinding.getValue().getAction());
psKey.addBatch();
}
psKey.executeBatch();
}
}
private void saveQuickmap(Connection con, Character chr) throws SQLException {
QuickslotBinding quickslotBinding = chr.getQuickslotBinding();
if (quickslotBinding == null) {
return;
}
long nQuickslotKeymapped = LongTool.BytesToLong(quickslotBinding.GetKeybindings());
try (final PreparedStatement psQuick = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
psQuick.setInt(1, chr.getAccountID());
psQuick.setLong(2, nQuickslotKeymapped);
psQuick.setLong(3, nQuickslotKeymapped);
psQuick.executeUpdate();
}
}
private void saveSkills(Connection con, Character chr) throws SQLException {
try (PreparedStatement psSkill = con.prepareStatement("REPLACE INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)")) {
psSkill.setInt(1, chr.getId());
for (Map.Entry<Skill, Character.SkillEntry> skill : chr.getSkills().entrySet()) {
psSkill.setInt(2, skill.getKey().getId());
psSkill.setInt(3, skill.getValue().skillevel);
psSkill.setInt(4, skill.getValue().masterlevel);
psSkill.setLong(5, skill.getValue().expiration);
psSkill.addBatch();
}
psSkill.executeBatch();
}
}
private void saveCooldowns(Connection con, Character chr) throws SQLException {
deleteCooldowns(con, chr);
insertCooldowns(con, chr);
}
private void deleteCooldowns(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM cooldowns
WHERE charid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertCooldowns(Connection con, Character chr) throws SQLException {
List<PlayerCoolDownValueHolder> cooldowns = chr.getAllCooldowns();
if (cooldowns.isEmpty()) {
return;
}
try (PreparedStatement ps = con.prepareStatement("INSERT INTO cooldowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)")) {
ps.setInt(1, chr.getId());
for (PlayerCoolDownValueHolder cooling : cooldowns) {
ps.setInt(2, cooling.skillId);
ps.setLong(3, cooling.startTime);
ps.setLong(4, cooling.length);
ps.addBatch();
}
ps.executeBatch();
}
}
private void saveSkillMacros(Connection con, Character chr) throws SQLException {
deleteSkillMacros(con, chr.getId());
insertSkillMacros(con, chr);
}
private void deleteSkillMacros(Connection con, int chrId) throws SQLException {
String sql = """
DELETE FROM skillmacros
WHERE characterid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chrId);
ps.executeUpdate();
}
}
private void insertSkillMacros(Connection con, Character chr) throws SQLException {
SkillMacro[] skillMacros = chr.getMacros();
try (PreparedStatement psMacro = con.prepareStatement("INSERT INTO skillmacros (characterid, skill1, skill2, skill3, name, shout, position) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
psMacro.setInt(1, chr.getId());
for (int i = 0; i < 5; i++) {
SkillMacro macro = skillMacros[i];
if (macro != null) {
psMacro.setInt(2, macro.getSkill1());
psMacro.setInt(3, macro.getSkill2());
psMacro.setInt(4, macro.getSkill3());
psMacro.setString(5, macro.getName());
psMacro.setInt(6, macro.getShout());
psMacro.setInt(7, i);
psMacro.addBatch();
}
}
psMacro.executeBatch();
}
}
private void saveFamily(Connection con, Character chr) {
FamilyEntry familyEntry = chr.getFamilyEntry();
if (familyEntry == null) {
return;
}
if (familyEntry.saveReputation(con)) {
familyEntry.savedSuccessfully();
}
FamilyEntry senior = familyEntry.getSenior();
if (senior == null || senior.getChr() != null) { // Only save for offline family members
return;
}
if (senior.saveReputation(con)) {
senior.savedSuccessfully();
}
FamilyEntry seniorsSenior = senior.getSenior();
if (seniorsSenior == null || seniorsSenior.getChr() != null) {
return;
}
if (senior.saveReputation(con)) {
senior.savedSuccessfully();
}
}
private void saveDiseases(Connection con, Character chr) throws SQLException {
deleteDiseases(con, chr);
insertDiseases(con, chr);
}
private void deleteDiseases(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM playerdiseases
WHERE charid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertDiseases(Connection con, Character chr) throws SQLException {
Map<Disease, Pair<Long, MobSkill>> diseases = chr.getAllDiseases();
if (diseases.isEmpty()) {
return;
}
try (PreparedStatement ps = con.prepareStatement("INSERT INTO playerdiseases (charid, disease, mobskillid, mobskilllv, length) VALUES (?, ?, ?, ?, ?)")) {
ps.setInt(1, chr.getId());
for (Map.Entry<Disease, Pair<Long, MobSkill>> e : diseases.entrySet()) {
ps.setInt(2, e.getKey().ordinal());
MobSkill ms = e.getValue().getRight();
MobSkillId msId = ms.getId();
ps.setInt(3, msId.type().getId());
ps.setInt(4, msId.level());
ps.setInt(5, e.getValue().getLeft().intValue());
ps.addBatch();
}
ps.executeBatch();
}
}
private void saveSavedLocations(Connection con, Character chr) throws SQLException {
deleteSavedLocations(con, chr.getId());
insertSavedLocations(con, chr);
}
private void deleteSavedLocations(Connection con, int chrId) throws SQLException {
String sql = """
DELETE FROM savedlocations
WHERE characterid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chrId);
ps.executeUpdate();
}
}
private void insertSavedLocations(Connection con, Character chr) throws SQLException {
SavedLocation[] savedLocations = chr.getSavedLocations();
try (PreparedStatement psLoc = con.prepareStatement("INSERT INTO savedlocations (characterid, `locationtype`, `map`, `portal`) VALUES (?, ?, ?, ?)")) {
psLoc.setInt(1, chr.getId());
for (SavedLocationType savedLocationType : SavedLocationType.values()) {
if (savedLocations[savedLocationType.ordinal()] != null) {
psLoc.setString(2, savedLocationType.name());
psLoc.setInt(3, savedLocations[savedLocationType.ordinal()].getMapId());
psLoc.setInt(4, savedLocations[savedLocationType.ordinal()].getPortal());
psLoc.addBatch();
}
}
psLoc.executeBatch();
}
}
private void saveAreaInfo(Connection con, Character chr) throws SQLException {
deleteAreaInfo(con, chr);
insertAreaInfo(con, chr);
}
private void deleteAreaInfo(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM area_info
WHERE charid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertAreaInfo(Connection con, Character chr) throws SQLException {
try (PreparedStatement psArea = con.prepareStatement("INSERT INTO area_info (id, charid, area, info) VALUES (DEFAULT, ?, ?, ?)")) {
psArea.setInt(1, chr.getId());
for (Map.Entry<Short, String> area : chr.getAreaInfos().entrySet()) {
psArea.setInt(2, area.getKey());
psArea.setString(3, area.getValue());
psArea.addBatch();
}
psArea.executeBatch();
}
}
private void saveEventStats(Connection con, Character chr) throws SQLException {
deleteEventStats(con, chr);
insertEventStats(con, chr);
}
private void deleteEventStats(Connection con, Character chr) throws SQLException {
String sql = """
DELETE FROM eventstats
WHERE characterid = ?""";
try (PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, chr.getId());
ps.executeUpdate();
}
}
private void insertEventStats(Connection con, Character chr) throws SQLException {
try (PreparedStatement psEvent = con.prepareStatement("INSERT INTO eventstats (characterid, name, info) VALUES (?, ?, ?)")) {
psEvent.setInt(1, chr.getId());
for (Map.Entry<String, Events> entry : chr.getEvents().entrySet()) {
psEvent.setString(2, entry.getKey());
psEvent.setInt(3, entry.getValue().getInfo());
psEvent.addBatch();
}
psEvent.executeBatch();
}
}
private void saveToPostgres(Character chr) {
Instant before = Instant.now();

View File

@@ -832,7 +832,8 @@ public class Server {
private ChannelDependencies registerChannelDependencies(PgDatabaseConnection connection) {
CharacterRepository characterRepository = new CharacterRepository();
MonsterCardRepository monsterCardRepository = new MonsterCardRepository(connection);
CharacterSaver characterSaver = new CharacterSaver(connection, characterRepository, monsterCardRepository);
CharacterSaver characterSaver = new CharacterSaver(this, connection, characterRepository,
monsterCardRepository);
AccountService accountService = new AccountService(new AccountRepository(connection));
TransitionService transitionService = new TransitionService(characterSaver, accountService);
NoteService noteService = new NoteService(new NoteDao(connection));

View File

@@ -37,6 +37,7 @@ import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.Pet;
import client.keybind.KeyBinding;
import client.keybind.QuickslotBinding;
import config.YamlConfig;
import constants.game.GameConstants;
import database.account.Account;
@@ -237,7 +238,7 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler {
}
}
player.sendKeymap();
player.sendQuickmap();
sendQuickmap(player);
player.sendMacros();
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
@@ -466,6 +467,15 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler {
}
}
private static void sendQuickmap(Character chr) {
QuickslotBinding quickslotBinding = chr.getQuickslotBinding();
if (quickslotBinding == null) {
quickslotBinding = new QuickslotBinding(QuickslotBinding.DEFAULT_QUICKSLOTS);
}
chr.sendPacket(PacketCreator.QuickslotMappedInit(quickslotBinding));
}
private static List<Pair<Long, PlayerBuffValueHolder>> getLocalStartTimes(List<PlayerBuffValueHolder> lpbvl) {
List<Pair<Long, PlayerBuffValueHolder>> timedBuffs = new ArrayList<>();
long curtime = currentServerTime();

View File

@@ -129,25 +129,21 @@ public class Storage {
}
}
public void saveToDB(Connection con) {
try {
try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) {
ps.setInt(1, slots);
ps.setInt(2, meso);
ps.setInt(3, id);
ps.executeUpdate();
}
List<Pair<Item, InventoryType>> itemsWithType = new ArrayList<>();
List<Item> list = getItems();
for (Item item : list) {
itemsWithType.add(new Pair<>(item, item.getInventoryType()));
}
ItemFactory.STORAGE.saveItems(itemsWithType, id, con);
} catch (SQLException ex) {
ex.printStackTrace();
public void saveToDB(Connection con) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("UPDATE storages SET slots = ?, meso = ? WHERE storageid = ?")) {
ps.setInt(1, slots);
ps.setInt(2, meso);
ps.setInt(3, id);
ps.executeUpdate();
}
List<Pair<Item, InventoryType>> itemsWithType = new ArrayList<>();
List<Item> list = getItems();
for (Item item : list) {
itemsWithType.add(new Pair<>(item, item.getInventoryType()));
}
ItemFactory.STORAGE.saveItems(itemsWithType, id, con);
}
public Item getItem(byte slot) {

View File

@@ -7,6 +7,7 @@ import database.DatabaseTest;
import database.monsterbook.MonsterCardRepository;
import org.jdbi.v3.core.Handle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.testcontainers.junit.jupiter.Testcontainers;
@@ -17,12 +18,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@Testcontainers
@Disabled("Tests fail due to MySQL saving. Keeping this disabled until migrated to PG, no point wasting time on MySQL when it's going to be removed.")
class CharacterSaverTest extends DatabaseTest {
private CharacterSaver characterSaver;
@BeforeEach
void reset() {
this.characterSaver = new CharacterSaver(connection, new CharacterRepository(),
this.characterSaver = new CharacterSaver(Mockito.mock(), connection, new CharacterRepository(),
new MonsterCardRepository(connection));
}