package javastory.login; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import javastory.client.GameCharacter; import javastory.db.Database; import javastory.db.DatabaseException; import javastory.game.Equip; import javastory.game.Gender; import javastory.game.Inventory; import javastory.game.InventoryType; import javastory.game.Item; import javastory.game.PlayerStats; import javastory.game.Skills; import com.google.common.collect.Lists; /** * * @author shoftee */ public class LoginCharacter implements GameCharacter { private static final int[] KeyIdDefaults = { 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 23, 25, 26, 27, 31, 34, 37, 38, 41, 44, 45, 46, 50, 57, 59, 60, 61, 62, 63, 64, 65, 8, 9, 24, 30 }; private static final int[] KeyTypeDefaults = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 4, 5, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4 }; private static final int[] KeyActionDefaults = { 10, 12, 13, 18, 6, 11, 8, 5, 0, 4, 1, 19, 14, 15, 3, 17, 9, 20, 22, 50, 51, 52, 7, 53, 100, 101, 102, 103, 104, 105, 106, 16, 23, 24, 2 }; private int id; private String name; private int level; private int fame; private int exp; private byte gmLevel; private Gender gender; private int worldId; // private final PlayerStats stats; // private int accountId, meso, jobId, hairId, faceId, skinColorId, remainingAp, mapId, initialSpawnPoint, subcategory; // private int worldRank, worldRankMove, jobRank, jobRankMove; private final int[] remainingSp; // private final Inventory equips; private LoginCharacter() { this.stats = new PlayerStats(); this.equips = new Inventory(InventoryType.EQUIPPED); this.remainingSp = new int[10]; } private void loadFromRecord(final ResultSet rs) throws SQLException { this.id = rs.getInt("id"); this.name = rs.getString("name"); this.level = rs.getShort("level"); this.fame = rs.getInt("fame"); this.stats.STR = rs.getInt("str"); this.stats.DEX = rs.getInt("dex"); this.stats.INT = rs.getInt("int"); this.stats.LUK = rs.getInt("luk"); this.stats.MaxHP = rs.getInt("maxhp"); this.stats.MaxMP = rs.getInt("maxmp"); this.stats.HP = rs.getInt("hp"); this.stats.MP = rs.getInt("mp"); this.exp = rs.getInt("exp"); final String[] sp = rs.getString("sp").split(","); for (int i = 0; i < this.remainingSp.length; i++) { this.remainingSp[i] = Integer.parseInt(sp[i]); } this.remainingAp = rs.getInt("ap"); this.subcategory = rs.getInt("subcategory"); this.meso = rs.getInt("meso"); this.gmLevel = rs.getByte("gm"); this.skinColorId = rs.getInt("skincolor"); this.gender = Gender.fromNumber(rs.getByte("gender")); this.jobId = rs.getInt("job"); this.hairId = rs.getInt("hair"); this.faceId = rs.getInt("face"); this.mapId = rs.getInt("map"); this.initialSpawnPoint = rs.getInt("spawnpoint"); this.worldId = rs.getInt("world"); this.worldRank = rs.getInt("rank"); this.worldRankMove = rs.getInt("rankMove"); this.jobRank = rs.getInt("jobRank"); this.jobRankMove = rs.getInt("jobRankMove"); try ( PreparedStatement selectEquips = getSelectEquippedItems(); ResultSet equipsResultSet = selectEquips.executeQuery()) { while (equipsResultSet.next()) { final int itemId = equipsResultSet.getInt("itemid"); final byte slot = equipsResultSet.getByte("position"); final int ringId = equipsResultSet.getInt("ringid"); final byte flags = equipsResultSet.getByte("flag"); final Equip equip = new Equip(itemId, slot, ringId, flags); this.equips.addFromDb(equip); } } } private PreparedStatement getSelectEquippedItems() throws SQLException { final String sql = "SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING (inventoryitemid) " + "WHERE `characterid` = ? AND `inventorytype` = ?"; final PreparedStatement ps = Database.getConnection().prepareStatement(sql); ps.setInt(1, this.id); // Equipped inventory type. ps.setInt(2, -1); return ps; } public static List<LoginCharacter> loadCharacters(final int accountId, final int worldId) { final List<LoginCharacter> characters = Lists.newLinkedList(); final Connection connection = Database.getConnection(); try ( final PreparedStatement ps = getSelectCharacters(connection, accountId, worldId); ResultSet rs = ps.executeQuery()) { while (rs.next()) { final LoginCharacter character = new LoginCharacter(); character.loadFromRecord(rs); characters.add(character); } } catch (final SQLException e) { System.err.println("error loading characters internal" + e); } return characters; } private static PreparedStatement getSelectCharacters(final Connection connection, final int accountId, final int worldId) throws SQLException { PreparedStatement ps = connection.prepareStatement("SELECT * FROM `characters` WHERE `accountid` = ? AND `world` = ? ORDER BY `id` ASC"); ps.setInt(1, accountId); ps.setInt(2, worldId); return ps; } public static LoginCharacter getDefault(final int type) { final LoginCharacter character = new LoginCharacter(); character.exp = 0; character.gmLevel = 0; character.jobId = type == 0 ? 1000 : type == 1 ? 0 : type == 2 ? 2000 : 2001; character.meso = 0; character.level = 1; character.remainingAp = 0; character.fame = 0; character.stats.STR = 12; character.stats.DEX = 4; character.stats.INT = 4; character.stats.LUK = 4; character.stats.HP = 50; character.stats.MaxHP = 50; character.stats.MP = 5; character.stats.MaxMP = 5; return character; } public static void saveNewCharacterToDb(final LoginCharacter chr, final int type, final boolean isDualBlader) { final Connection con = Database.getConnection(); try { con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); con.setAutoCommit(false); try (final PreparedStatement ps = chr.getInsertCharacterData(type, isDualBlader, con)) { ps.executeUpdate(); try (final ResultSet rs = ps.getGeneratedKeys()) { if (rs.next()) { chr.id = rs.getInt(1); } else { throw new DatabaseException(":: Failed to create new character ::"); } } } try (final PreparedStatement capacitiesStatement = chr.getInsertInventoryCapacities(con)) { capacitiesStatement.execute(); } try (final PreparedStatement mountStatement = chr.getInsertMountData(con)) { mountStatement.execute(); } try ( final PreparedStatement itemStatement = chr.getInsertInventoryItems(con); final PreparedStatement equipStatement = chr.getInsertEquip(con)) { for (final Item item : chr.equips) { setItemData(itemStatement, item); itemStatement.executeUpdate(); int newItemId; try (final ResultSet rs = itemStatement.getGeneratedKeys()) { if (rs.next()) { newItemId = rs.getInt(1); } else { throw new DatabaseException("Inserting char failed."); } } final Equip equip = (Equip) item; setEquipData(equipStatement, newItemId, equip); equipStatement.executeUpdate(); } } try (final PreparedStatement ps = getInsertKeyMapping(chr, con)) { for (int i = 0; i < KeyIdDefaults.length; i++) { setKeyMappingData(ps, i); ps.execute(); } } con.commit(); } catch (SQLException | DatabaseException e) { System.err.println("[charsave] Error saving character data: " + e); try { con.rollback(); } catch (final SQLException ex) { System.err.println("[charsave] Error Rolling Back: " + ex); } } finally { try { con.setAutoCommit(true); con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); } catch (final SQLException e) { System.err.println("[charsave] Error going back to autocommit mode: " + e); } } } private static void setKeyMappingData(final PreparedStatement ps, int i) throws SQLException { ps.setInt(2, KeyIdDefaults[i]); ps.setInt(3, KeyTypeDefaults[i]); ps.setInt(4, KeyActionDefaults[i]); } private static PreparedStatement getInsertKeyMapping(final LoginCharacter chr, final Connection con) throws SQLException { final PreparedStatement ps = con.prepareStatement("INSERT INTO `keymap` (`characterid`, `key`, `type`, `action`) VALUES (?, ?, ?, ?)"); ps.setInt(1, chr.id); return ps; } private PreparedStatement getInsertEquip(final Connection con) throws SQLException { final String sql = "INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; final PreparedStatement ps = con.prepareStatement(sql); return ps; } private static void setEquipData(final PreparedStatement ps, int newItemId, final Equip equip) throws SQLException { ps.setInt(1, newItemId); ps.setInt(2, equip.getUpgradeSlots()); ps.setInt(3, equip.getLevel()); ps.setInt(4, equip.getStr()); ps.setInt(5, equip.getDex()); ps.setInt(6, equip.getInt()); ps.setInt(7, equip.getLuk()); ps.setInt(8, equip.getHp()); ps.setInt(9, equip.getMp()); ps.setInt(10, equip.getWatk()); ps.setInt(11, equip.getMatk()); ps.setInt(12, equip.getWdef()); ps.setInt(13, equip.getMdef()); ps.setInt(14, equip.getAcc()); ps.setInt(15, equip.getAvoid()); ps.setInt(16, equip.getHands()); ps.setInt(17, equip.getSpeed()); ps.setInt(18, equip.getJump()); ps.setInt(19, equip.getRingId()); ps.setInt(20, equip.getViciousHammer()); ps.setInt(21, 0); ps.setInt(22, 0); } private static void setItemData(final PreparedStatement ps, final Item item) throws SQLException { ps.setInt(3, item.getItemId()); ps.setInt(4, item.getPosition()); ps.setInt(5, item.getQuantity()); ps.setString(6, item.getOwner()); ps.setString(7, item.getGMLog()); ps.setInt(8, -1); // Pet cant be loaded on logins + new char doesn't have. ps.setLong(9, item.getExpiration()); ps.setByte(10, item.getFlag()); } private PreparedStatement getInsertInventoryItems(final Connection con) throws SQLException { final String sql = "INSERT INTO `inventoryitems` (`characterid`, `inventorytype`, `itemid`, `position`, `quantity`, `owner`, `GM_Log`, `petid`, `expiredate`, `flag`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; final PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setInt(1, this.id); ps.setInt(2, this.equips.getType().asNumber()); return ps; } private PreparedStatement getInsertMountData(final Connection con) throws SQLException { final String sql = "INSERT INTO mountdata (characterid, `Level`, `Exp`, `Fatigue`) VALUES (?, ?, ?, ?)"; final PreparedStatement ps = con.prepareStatement(sql); ps.setInt(1, this.id); ps.setInt(2, 1); ps.setInt(3, 0); ps.setInt(4, 0); return ps; } private PreparedStatement getInsertInventoryCapacities(final Connection con) throws SQLException { final String sql = "INSERT INTO inventoryslot (characterid, `equip`, `use`, `setup`, `etc`, `cash`) VALUES (?, ?, ?, ?, ?, ?)"; PreparedStatement ps = con.prepareStatement(sql); ps.setInt(1, this.id); ps.setInt(2, 24); // Eq ps.setInt(3, 24); // Use ps.setInt(4, 24); // Setup ps.setInt(5, 24); // ETC ps.setInt(6, 96); // Cash return ps; } private PreparedStatement getInsertCharacterData(final int type, final boolean isDualBlader, final Connection con) throws SQLException { final String sql = "INSERT INTO characters (level, fame, str, dex, luk, `int`, exp, hp, mp, maxhp, maxmp, sp, ap, gm, skincolor, gender, job, hair, face, map, meso, hpApUsed, mpApUsed, spawnpoint, party, buddyCapacity, messengerid, messengerposition, monsterbookcover, dojo_pts, dojoRecord, accountid, name, world, reborns, subcategory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setInt(1, 1); // Level ps.setInt(2, 0); // Fame ps.setInt(3, this.stats.STR); // Str ps.setInt(4, this.stats.DEX); // Dex ps.setInt(5, this.stats.INT); // Int ps.setInt(6, this.stats.LUK); // Luk ps.setInt(7, 0); // EXP ps.setInt(8, this.stats.HP); ps.setInt(9, this.stats.MP); ps.setInt(10, this.stats.MaxHP); ps.setInt(11, this.stats.MaxMP); ps.setString(12, "0,0,0,0,0,0,0,0,0,0"); // Remaining SP ps.setInt(13, 0); // Remaining AP ps.setInt(14, 0); // GM Level ps.setInt(15, this.skinColorId); ps.setInt(16, this.gender.asNumber()); ps.setInt(17, this.jobId); ps.setInt(18, this.hairId); ps.setInt(19, this.faceId); // TODO: proper constants. ps.setInt(20, type == 1 ? 10000 : type == 0 ? 130030000 : type == 2 ? 914000000 : 900010000); // 0-KOC 1-Adventurer 2-Aran 3-Evan 4-DB ps.setInt(21, this.meso); // Meso ps.setInt(22, 0); // HP ap used ps.setInt(23, 0); // MP ap used ps.setInt(24, 0); // Spawnpoint ps.setInt(25, -1); // Party ps.setInt(26, 20); // Buddy list capacity ps.setInt(27, 0); // MessengerId ps.setInt(28, 4); // Messenger Position ps.setInt(29, 0); // Monster book cover ps.setInt(30, 0); // Dojo ps.setInt(31, 0); // Dojo record ps.setInt(32, this.getAccountId()); ps.setString(33, this.name); ps.setInt(34, this.worldId); ps.setInt(35, 0); ps.setInt(36, isDualBlader ? 1 : 0); //for now return ps; } @Override public int getExp() { return this.exp; } @Override public int getRemainingAp() { return this.remainingAp; } @Override public int[] getRemainingSps() { return this.remainingSp; } @Override public int getRemainingSp() { return this.remainingSp[Skills.getSkillbook(this.jobId)]; //default } @Override public int getRemainingSp(final int skillbook) { return this.remainingSp[skillbook]; } @Override public int getRemainingSpSize() { int ret = 0; for (final int element : this.remainingSp) { if (element > 0) { ret++; } } return ret; } @Override public int getId() { return this.id; } @Override public String getName() { return this.name; } @Override public int getLevel() { return this.level; } @Override public int getFame() { return this.fame; } @Override public int getGmLevel() { return this.gmLevel; } @Override public Gender getGender() { return this.gender; } @Override public int getWorldId() { return this.worldId; } public int getAccountId() { return this.accountId; } @Override public int getMeso() { return this.meso; } @Override public int getJobId() { return this.jobId; } @Override public int getHairId() { return this.hairId; } @Override public int getFaceId() { return this.faceId; } @Override public int getSkinColorId() { return this.skinColorId; } @Override public int getMapId() { return this.mapId; } @Override public int getInitialSpawnPoint() { return this.initialSpawnPoint; } @Override public int getSubcategory() { return this.subcategory; } public int getWorldRank() { return this.worldRank; } public int getWorldRankMove() { return this.worldRankMove; } public int getJobRank() { return this.jobRank; } public int getJobRankMove() { return this.jobRankMove; } public void setName(final String name) { this.name = name; } public void setLevel(final int level) { this.level = (short) level; } public void setFame(final int fame) { this.fame = fame; } public void setExp(final int exp) { this.exp = exp; } public void setGmLevel(final byte gmLevel) { this.gmLevel = gmLevel; } public void setGender(final Gender gender) { this.gender = gender; } public void setWorldId(final int worldId) { this.worldId = worldId; } public void setMeso(final int meso) { this.meso = meso; } public void setJobId(final int jobId) { this.jobId = jobId; } public void setHairId(final int hairId) { this.hairId = hairId; } public void setFaceId(final int faceId) { this.faceId = faceId; } public void setSkinColorId(final int skinColorId) { this.skinColorId = skinColorId; } public void setRemainingAp(final int remainingAp) { this.remainingAp = remainingAp; } public void setSubcategory(final int subcategory) { this.subcategory = subcategory; } @Override public Inventory getEquippedItemsInventory() { return this.equips; } @Override public PlayerStats getStats() { return this.stats; } }