Added cash shop name changes and world transfers. (#495)

* Name changes

* World transfers. Fix NPE. Applied Arnuh's suggestions. More logging.
This commit is contained in:
Ubaware
2019-07-31 16:22:48 -07:00
committed by Ronan Lana
parent 85812ba489
commit 153f5de4fa
14 changed files with 733 additions and 20 deletions

View File

@@ -103,6 +103,7 @@ import server.life.MaplePlayerNPCFactory;
import server.quest.MapleQuest;
import tools.AutoJCE;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.Pair;
import org.apache.mina.core.session.IoSession;
@@ -881,7 +882,8 @@ public class Server {
} catch (SQLException sqle) {
sqle.printStackTrace();
}
applyAllNameChanges(); //name changes can be missed by INSTANT_NAME_CHANGE
applyAllWorldTransfers();
MaplePet.clearMissingPetsFromDb();
MapleCashidGenerator.loadExistentCashIdsFromDb();
@@ -1554,6 +1556,82 @@ public class Server {
}
}
private static void applyAllNameChanges() {
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM namechanges WHERE completionTime IS NULL")) {
ResultSet rs = ps.executeQuery();
List<Pair<String, String>> changedNames = new LinkedList<Pair<String, String>>(); //logging only
while(rs.next()) {
con.setAutoCommit(false);
int nameChangeId = rs.getInt("id");
int characterId = rs.getInt("characterId");
String oldName = rs.getString("old");
String newName = rs.getString("new");
boolean success = MapleCharacter.doNameChange(con, characterId, oldName, newName, nameChangeId);
if(!success) con.rollback(); //discard changes
else changedNames.add(new Pair<String, String>(oldName, newName));
con.setAutoCommit(true);
}
//log
for(Pair<String, String> namePair : changedNames) {
FilePrinter.print(FilePrinter.CHANGE_CHARACTER_NAME, "Name change applied : from \"" + namePair.getLeft() + "\" to \"" + namePair.getRight() + "\" at " + Calendar.getInstance().getTime().toString());
}
} catch(SQLException e) {
e.printStackTrace();
FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to retrieve list of pending name changes.");
}
}
private static void applyAllWorldTransfers() {
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM worldtransfers WHERE completionTime IS NULL")) {
ResultSet rs = ps.executeQuery();
List<Integer> removedTransfers = new LinkedList<Integer>();
while(rs.next()) {
int nameChangeId = rs.getInt("id");
int characterId = rs.getInt("characterId");
int oldWorld = rs.getInt("from");
int newWorld = rs.getInt("to");
String reason = MapleCharacter.checkWorldTransferEligibility(con, characterId, oldWorld, newWorld); //check if character is still eligible
if(reason != null) {
removedTransfers.add(nameChangeId);
FilePrinter.print(FilePrinter.WORLD_TRANSFER, "World transfer cancelled : Character ID " + characterId + " at " + Calendar.getInstance().getTime().toString() + ", Reason : " + reason);
try (PreparedStatement delPs = con.prepareStatement("DELETE FROM worldtransfers WHERE id = ?")) {
delPs.setInt(1, nameChangeId);
delPs.executeUpdate();
} catch(SQLException e) {
e.printStackTrace();
FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to delete world transfer for character ID " + characterId);
}
}
}
rs.beforeFirst();
List<Pair<Integer, Pair<Integer, Integer>>> worldTransfers = new LinkedList<Pair<Integer, Pair<Integer, Integer>>>(); //logging only <charid, <oldWorld, newWorld>>
while(rs.next()) {
con.setAutoCommit(false);
int nameChangeId = rs.getInt("id");
if(removedTransfers.contains(nameChangeId)) continue;
int characterId = rs.getInt("characterId");
int oldWorld = rs.getInt("from");
int newWorld = rs.getInt("to");
boolean success = MapleCharacter.doWorldTransfer(con, characterId, oldWorld, newWorld, nameChangeId);
if(!success) con.rollback();
else worldTransfers.add(new Pair<Integer, Pair<Integer, Integer>>(characterId, new Pair<Integer, Integer>(oldWorld, newWorld)));
con.setAutoCommit(true);
}
//log
for(Pair<Integer, Pair<Integer, Integer>> worldTransferPair : worldTransfers) {
int charId = worldTransferPair.getLeft();
int oldWorld = worldTransferPair.getRight().getLeft();
int newWorld = worldTransferPair.getRight().getRight();
FilePrinter.print(FilePrinter.WORLD_TRANSFER, "World transfer applied : Character ID " + charId + " from World " + oldWorld + " to World " + newWorld + " at " + Calendar.getInstance().getTime().toString());
}
} catch(SQLException e) {
e.printStackTrace();
FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to retrieve list of pending world transfers.");
}
}
public void loadAccountCharacters(MapleClient c) {
Integer accId = c.getAccID();
if (!isFirstAccountLogin(accId)) {

View File

@@ -34,6 +34,7 @@ import java.util.Calendar;
import java.util.List;
import java.util.Map;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.CashShop;
import server.CashShop.CashItem;
import server.CashShop.CashItemFactory;
@@ -343,7 +344,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
slea.readByte();
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(sentTo);
if (partner == null) {
chr.dropMessage(5, "The partner you specified cannot be found. Please make sure your partner is online and in the same channel.");
c.announce(MaplePacketCreator.showCashShopMessage((byte)0xBE));
} else {
// Need to check to make sure its actually an equip and the right SN...
if(itemRing.toItem() instanceof Equip) {
@@ -351,7 +352,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
Pair<Integer, Integer> rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
c.announce(MaplePacketCreator.showBoughtCashRing(eqp, partner.getName(), c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(payment, -itemRing.getPrice());
chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft()));
@@ -368,6 +369,65 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x2E) { //name change
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
c.enableCSActions();
return;
}
if(cItem.getSN() == 50600000 && ServerConstants.ALLOW_CASHSHOP_NAME_CHANGE) {
slea.readMapleAsciiString(); //old name
String newName = slea.readMapleAsciiString();
if(!MapleCharacter.canCreateChar(newName) || chr.getLevel() < 10) { //(longest ban duration isn't tracked currently)
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
c.enableCSActions();
return;
} else if(c.getTempBanCalendar() != null && c.getTempBanCalendar().getTimeInMillis() + (30*24*60*60*1000) > Calendar.getInstance().getTimeInMillis()) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
c.enableCSActions();
return;
}
if(chr.registerNameChange(newName)) { //success
Item item = cItem.toItem();
c.announce(MaplePacketCreator.showNameChangeSuccess(item, c.getAccID()));
cs.addToInventory(item);
cs.gainCash(4, cItem, chr.getWorld());
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
}
}
c.enableCSActions();
} else if(action == 0x31) { //world transfer
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
c.enableCSActions();
return;
}
if(cItem.getSN() == 50600001 && ServerConstants.ALLOW_CASHSHOP_WORLD_TRANSFER) {
int newWorldSelection = slea.readInt();
int worldTransferError = chr.checkWorldTransferEligibility();
if(worldTransferError != 0 || newWorldSelection >= Server.getInstance().getWorldsSize() || Server.getInstance().getWorldsSize() <= 1) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
return;
} else if(newWorldSelection == c.getWorld()) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0xDC));
return;
} else if(c.getAvailableCharacterWorldSlots(newWorldSelection) < 1 || Server.getInstance().getAccountWorldCharacterCount(c.getAccID(), newWorldSelection) >= 3) {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0xDF));
return;
} else if(chr.registerWorldTransfer(newWorldSelection)) {
Item item = cItem.toItem();
c.announce(MaplePacketCreator.showWorldTransferSuccess(item, c.getAccID()));
cs.addToInventory(item);
cs.gainCash(4, cItem, chr.getWorld());
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte)0));
}
}
c.enableCSActions();
} else {
System.out.println("Unhandled action: " + action + "\n" + slea);
}

View File

@@ -20,14 +20,25 @@
package net.server.channel.handlers;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.sql.Connection;
import client.MapleCharacter;
import client.MapleClient;
import constants.ServerConstants;
import net.AbstractMaplePacketHandler;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Ronan
* @author Ubaware
*/
public final class TransferNameHandler extends AbstractMaplePacketHandler {
@@ -40,7 +51,37 @@ public final class TransferNameHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions());
return;
}
c.announce(MaplePacketCreator.sendNameTransferRules(4));
if(!ServerConstants.ALLOW_CASHSHOP_NAME_CHANGE) {
c.announce(MaplePacketCreator.sendNameTransferRules(4));
return;
}
MapleCharacter chr = c.getPlayer();
if(chr.getLevel() < 10) {
c.announce(MaplePacketCreator.sendNameTransferRules(4));
return;
} else if(c.getTempBanCalendar() != null && c.getTempBanCalendar().getTimeInMillis() + (30*24*60*60*1000) < Calendar.getInstance().getTimeInMillis()) {
c.announce(MaplePacketCreator.sendNameTransferRules(2));
return;
}
//sql queries
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM namechanges WHERE characterid=?")) { //double check, just in case
ps.setInt(1, chr.getId());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
Timestamp completedTimestamp = rs.getTimestamp("completionTime");
if(completedTimestamp == null) { //has pending name request
c.announce(MaplePacketCreator.sendNameTransferRules(1));
return;
} else if(completedTimestamp.getTime() + ServerConstants.NAME_CHANGE_COOLDOWN > System.currentTimeMillis()) {
c.announce(MaplePacketCreator.sendNameTransferRules(3));
return;
};
}
} catch(SQLException e) {
e.printStackTrace();
return;
}
c.announce(MaplePacketCreator.sendNameTransferRules(0));
}
}

View File

@@ -35,6 +35,6 @@ public final class TransferNameResultHandler extends AbstractMaplePacketHandler
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String name = slea.readMapleAsciiString();
c.announce(MaplePacketCreator.sendNameTransferCheck(MapleCharacter.canCreateChar(name)));
c.announce(MaplePacketCreator.sendNameTransferCheck(name, MapleCharacter.canCreateChar(name)));
}
}

View File

@@ -20,14 +20,25 @@
package net.server.channel.handlers;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import client.MapleCharacter;
import client.MapleClient;
import constants.ServerConstants;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Ronan
* @author Ubaware
*/
public final class TransferWorldHandler extends AbstractMaplePacketHandler {
@@ -40,7 +51,34 @@ public final class TransferWorldHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions());
return;
}
c.announce(MaplePacketCreator.sendWorldTransferRules(9));
MapleCharacter chr = c.getPlayer();
if(!ServerConstants.ALLOW_CASHSHOP_WORLD_TRANSFER || Server.getInstance().getWorldsSize() <= 1) {
c.announce(MaplePacketCreator.sendWorldTransferRules(9, c));
return;
}
int worldTransferError = chr.checkWorldTransferEligibility();
if(worldTransferError != 0) {
c.announce(MaplePacketCreator.sendWorldTransferRules(worldTransferError, c));
return;
}
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM worldtransfers WHERE characterid=?")) {
ps.setInt(1, chr.getId());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
Timestamp completedTimestamp = rs.getTimestamp("completionTime");
if(completedTimestamp == null) { //has pending world transfer
c.announce(MaplePacketCreator.sendWorldTransferRules(6, c));
return;
} else if(completedTimestamp.getTime() + ServerConstants.WORLD_TRANSFER_COOLDOWN > System.currentTimeMillis()) {
c.announce(MaplePacketCreator.sendWorldTransferRules(7, c));
return;
};
}
} catch(SQLException e) {
e.printStackTrace();
return;
}
c.announce(MaplePacketCreator.sendWorldTransferRules(0, c));
}
}

View File

@@ -423,6 +423,24 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
}
}, 1000 * 10);
remove(c, position, itemId);
} else if (itemType == 540) {
slea.readByte();
slea.readInt();
if(itemId == 5400000) { //name change
if(player.cancelPendingNameChange()) {
player.dropMessage(1, "Successfully canceled pending name change.");
} else {
player.dropMessage(1, "You do not have a pending name change.");
}
} else if(itemId == 5401000) { //world transfer
if(player.cancelPendingWorldTranfer()) {
player.dropMessage(1, "Successfully canceled pending world transfer.");
} else {
player.dropMessage(1, "You do not have a pending world transfer.");
}
}
remove(c, position, itemId);
c.announce(MaplePacketCreator.enableActions());
} else if (itemType == 543) {
if(itemId == 5432000 && !c.gainCharacterSlot()) {
player.dropMessage(1, "You have already used up all 12 extra character slots.");

View File

@@ -135,7 +135,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
c.announce(MaplePacketCreator.getLoginFailed(3));
return;
}
Calendar tempban = c.getTempBanCalendar();
Calendar tempban = c.getTempBanCalendarFromDB();
if (tempban != null) {
if (tempban.getTimeInMillis() > Calendar.getInstance().getTimeInMillis()) {
c.announce(MaplePacketCreator.getTempBan(tempban.getTimeInMillis(), c.getGReason()));