Protected Hired Merchant + Buff system patch
Fixed some issues with Fredrick not retrieving the right amount of items from Hired Merchants. Added concurrency protection for HM. Patched a minor issue on buff system.
This commit is contained in:
@@ -2574,7 +2574,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
buffExpires.put(sourceid, expirationtime);
|
||||
}
|
||||
|
||||
private void removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) {
|
||||
private boolean removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) {
|
||||
Map<MapleBuffStat, MapleBuffStatValueHolder> lbe = buffEffects.get(sourceid);
|
||||
|
||||
if(lbe.remove(buffStat) != null) {
|
||||
@@ -2584,7 +2584,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
buffEffects.remove(sourceid);
|
||||
buffExpires.remove(sourceid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeItemEffectHolder(Integer sourceid) {
|
||||
@@ -2634,9 +2638,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
private void extractBuffValue(int sourceid, MapleBuffStat stat) {
|
||||
chrLock.lock();
|
||||
try {
|
||||
if(buffEffects.get(sourceid).remove(stat) != null) {
|
||||
buffEffectsCount.put(stat, (byte)(buffEffectsCount.get(stat) - 1));
|
||||
}
|
||||
removeEffectFromItemEffectHolder(sourceid, stat);
|
||||
} finally {
|
||||
chrLock.unlock();
|
||||
}
|
||||
@@ -2755,7 +2757,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
for (Entry<MapleBuffStat, MapleBuffStatValueHolder> stat : stats.entrySet()) {
|
||||
int sourceid = stat.getValue().effect.getBuffSourceId();
|
||||
|
||||
if(buffEffects.get(sourceid) == null) {
|
||||
if(!buffEffects.containsKey(sourceid)) {
|
||||
buffExpires.remove(sourceid);
|
||||
}
|
||||
|
||||
@@ -6537,10 +6539,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
public void addMerchantMesos(int add) {
|
||||
int newAmount;
|
||||
|
||||
try {
|
||||
newAmount = (int)Math.min((long)merchantmeso + add, Integer.MAX_VALUE);
|
||||
System.out.println("adding" + add + " now" + newAmount);
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setInt(1, merchantmeso + add);
|
||||
ps.setInt(1, newAmount);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
@@ -6550,7 +6557,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
merchantmeso += add;
|
||||
merchantmeso = newAmount;
|
||||
}
|
||||
|
||||
public void setMerchantMeso(int set) {
|
||||
|
||||
@@ -57,6 +57,20 @@ public enum ItemFactory {
|
||||
}
|
||||
|
||||
public List<Pair<Item, MapleInventoryType>> loadItems(int id, boolean login) throws SQLException {
|
||||
if(value != 6) return loadItemsCommon(id, login);
|
||||
else return loadItemsMerchant(id, login);
|
||||
}
|
||||
|
||||
public void saveItems(List<Pair<Item, MapleInventoryType>> items, int id, Connection con) throws SQLException {
|
||||
saveItems(items, null, id, con);
|
||||
}
|
||||
|
||||
public synchronized void saveItems(List<Pair<Item, MapleInventoryType>> items, List<Short> bundlesList, int id, Connection con) throws SQLException {
|
||||
if(value != 6) saveItemsCommon(items, id, con);
|
||||
else saveItemsMerchant(items, bundlesList, id, con);
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsCommon(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null;
|
||||
@@ -135,7 +149,7 @@ public enum ItemFactory {
|
||||
return items;
|
||||
}
|
||||
|
||||
public synchronized void saveItems(List<Pair<Item, MapleInventoryType>> items, int id, Connection con) throws SQLException {
|
||||
private void saveItemsCommon(List<Pair<Item, MapleInventoryType>> items, int id, Connection con) throws SQLException {
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement pse = null;
|
||||
ResultSet rs = null;
|
||||
@@ -227,4 +241,212 @@ public enum ItemFactory {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsMerchant(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null, ps2 = null;
|
||||
ResultSet rs = null, rs2 = null;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
|
||||
if (login) {
|
||||
query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
ps2 = con.prepareStatement("SELECT `bundles` FROM `inventorymerchant` WHERE `inventoryitemid` = ?");
|
||||
ps2.setInt(1, rs.getInt("inventoryitemid"));
|
||||
rs2 = ps2.executeQuery();
|
||||
|
||||
short bundles = 0;
|
||||
if(rs2.next()) {
|
||||
bundles = rs2.getShort("bundles");
|
||||
}
|
||||
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
Equip equip = new Equip(rs.getInt("itemid"), (short) rs.getInt("position"));
|
||||
equip.setOwner(rs.getString("owner"));
|
||||
equip.setQuantity((short) rs.getInt("quantity"));
|
||||
equip.setAcc((short) rs.getInt("acc"));
|
||||
equip.setAvoid((short) rs.getInt("avoid"));
|
||||
equip.setDex((short) rs.getInt("dex"));
|
||||
equip.setHands((short) rs.getInt("hands"));
|
||||
equip.setHp((short) rs.getInt("hp"));
|
||||
equip.setInt((short) rs.getInt("int"));
|
||||
equip.setJump((short) rs.getInt("jump"));
|
||||
equip.setVicious((short) rs.getInt("vicious"));
|
||||
equip.setFlag((byte) rs.getInt("flag"));
|
||||
equip.setLuk((short) rs.getInt("luk"));
|
||||
equip.setMatk((short) rs.getInt("matk"));
|
||||
equip.setMdef((short) rs.getInt("mdef"));
|
||||
equip.setMp((short) rs.getInt("mp"));
|
||||
equip.setSpeed((short) rs.getInt("speed"));
|
||||
equip.setStr((short) rs.getInt("str"));
|
||||
equip.setWatk((short) rs.getInt("watk"));
|
||||
equip.setWdef((short) rs.getInt("wdef"));
|
||||
equip.setUpgradeSlots((byte) rs.getInt("upgradeslots"));
|
||||
equip.setLevel((byte) rs.getByte("level"));
|
||||
equip.setItemExp(rs.getInt("itemexp"));
|
||||
equip.setItemLevel(rs.getByte("itemlevel"));
|
||||
equip.setExpiration(rs.getLong("expiration"));
|
||||
equip.setGiftFrom(rs.getString("giftFrom"));
|
||||
equip.setRingId(rs.getInt("ringid"));
|
||||
items.add(new Pair<Item, MapleInventoryType>(equip, mit));
|
||||
} else {
|
||||
if(bundles > 0) {
|
||||
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid"));
|
||||
item.setOwner(rs.getString("owner"));
|
||||
item.setExpiration(rs.getLong("expiration"));
|
||||
item.setGiftFrom(rs.getString("giftFrom"));
|
||||
item.setFlag((byte) rs.getInt("flag"));
|
||||
items.add(new Pair<>(item, mit));
|
||||
}
|
||||
}
|
||||
|
||||
rs2.close();
|
||||
ps2.close();
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} finally {
|
||||
if (rs2 != null && !rs2.isClosed()) {
|
||||
rs2.close();
|
||||
}
|
||||
if (ps2 != null && !ps2.isClosed()) {
|
||||
ps2.close();
|
||||
}
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private void saveItemsMerchant(List<Pair<Item, MapleInventoryType>> items, List<Short> bundlesList, int id, Connection con) throws SQLException {
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement pse = null;
|
||||
ResultSet rs = null;
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
ps = con.prepareStatement("DELETE FROM `inventorymerchant` WHERE `characterid` = ?");
|
||||
ps.setInt(1, id);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
ps = con.prepareStatement("INSERT INTO `inventoryitems` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
int i = 0;
|
||||
for (Pair<Item, MapleInventoryType> pair : items) {
|
||||
Item item = pair.getLeft();
|
||||
Short bundles = bundlesList.get(i);
|
||||
MapleInventoryType mit = pair.getRight();
|
||||
i++;
|
||||
|
||||
ps.setInt(1, value);
|
||||
ps.setString(2, account ? null : String.valueOf(id));
|
||||
ps.setString(3, account ? String.valueOf(id) : null);
|
||||
ps.setInt(4, item.getItemId());
|
||||
ps.setInt(5, mit.getType());
|
||||
ps.setInt(6, item.getPosition());
|
||||
ps.setInt(7, item.getQuantity());
|
||||
ps.setString(8, item.getOwner());
|
||||
ps.setInt(9, item.getPetId());
|
||||
ps.setInt(10, item.getFlag());
|
||||
ps.setLong(11, item.getExpiration());
|
||||
ps.setString(12, item.getGiftFrom());
|
||||
ps.executeUpdate();
|
||||
|
||||
rs = ps.getGeneratedKeys();
|
||||
if (!rs.next()) {
|
||||
throw new RuntimeException("Inserting item failed.");
|
||||
}
|
||||
|
||||
int genKey = rs.getInt(1);
|
||||
rs.close();
|
||||
|
||||
pse = con.prepareStatement("INSERT INTO `inventorymerchant` VALUES (DEFAULT, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
pse.setInt(1, genKey);
|
||||
pse.setInt(2, id);
|
||||
pse.setInt(3, bundles);
|
||||
pse.executeUpdate();
|
||||
pse.close();
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
pse = con.prepareStatement("INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
pse.setInt(1, genKey);
|
||||
|
||||
Equip equip = (Equip) item;
|
||||
pse.setInt(2, equip.getUpgradeSlots());
|
||||
pse.setInt(3, equip.getLevel());
|
||||
pse.setInt(4, equip.getStr());
|
||||
pse.setInt(5, equip.getDex());
|
||||
pse.setInt(6, equip.getInt());
|
||||
pse.setInt(7, equip.getLuk());
|
||||
pse.setInt(8, equip.getHp());
|
||||
pse.setInt(9, equip.getMp());
|
||||
pse.setInt(10, equip.getWatk());
|
||||
pse.setInt(11, equip.getMatk());
|
||||
pse.setInt(12, equip.getWdef());
|
||||
pse.setInt(13, equip.getMdef());
|
||||
pse.setInt(14, equip.getAcc());
|
||||
pse.setInt(15, equip.getAvoid());
|
||||
pse.setInt(16, equip.getHands());
|
||||
pse.setInt(17, equip.getSpeed());
|
||||
pse.setInt(18, equip.getJump());
|
||||
pse.setInt(19, 0);
|
||||
pse.setInt(20, equip.getVicious());
|
||||
pse.setInt(21, equip.getItemLevel());
|
||||
pse.setInt(22, equip.getItemExp());
|
||||
pse.setInt(23, equip.getRingId());
|
||||
pse.executeUpdate();
|
||||
|
||||
pse.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ps.close();
|
||||
} finally {
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (pse != null && !pse.isClosed()) {
|
||||
pse.close();
|
||||
}
|
||||
if(rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,12 @@ public class ServerConstants {
|
||||
public static final boolean USE_ITEM_SORT = true;
|
||||
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
|
||||
public static final boolean USE_PARTY_SEARCH = false;
|
||||
public static final boolean USE_MERCHANT_ANYWHERE = true; //Enables player shops and hired merchants outside FM rooms (except FM entrance).
|
||||
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
|
||||
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
|
||||
public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
|
||||
public static final boolean USE_REFRESH_RANK_MOVE = true;
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster)
|
||||
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
|
||||
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
|
||||
|
||||
@@ -39,6 +39,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import net.MapleServerHandler;
|
||||
import net.mina.MapleCodecFactory;
|
||||
@@ -79,6 +81,7 @@ public class Server implements Runnable {
|
||||
private static Server instance = null;
|
||||
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
|
||||
private final Map<Integer, MapleGuild> guilds = new LinkedHashMap<>();
|
||||
private final Lock shutdownLock = new ReentrantLock();
|
||||
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
|
||||
private final Map<Integer, MapleAlliance> alliances = new LinkedHashMap<>();
|
||||
private boolean online = false;
|
||||
@@ -99,7 +102,8 @@ public class Server implements Runnable {
|
||||
return worldRecommendedList;
|
||||
}
|
||||
|
||||
public void removeChannel(int worldid, int channel) {
|
||||
/*
|
||||
public void removeChannel(int worldid, int channel) { //lol don't!
|
||||
channels.remove(channel);
|
||||
|
||||
World world = worlds.get(worldid);
|
||||
@@ -107,6 +111,7 @@ public class Server implements Runnable {
|
||||
world.removeChannel(channel);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public Channel getChannel(int world, int channel) {
|
||||
return worlds.get(world).getChannel(channel);
|
||||
@@ -702,69 +707,75 @@ public class Server implements Runnable {
|
||||
return worlds;
|
||||
}
|
||||
|
||||
public final Runnable shutdown(final boolean restart) {//only once :D
|
||||
public final Runnable shutdown(final boolean restart) {//no player should be online when trying to shutdown!
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n");
|
||||
if (getWorlds() == null) return;//already shutdown
|
||||
for (World w : getWorlds()) {
|
||||
w.shutdown();
|
||||
}
|
||||
/*for (World w : getWorlds()) {
|
||||
while (w.getPlayerStorage().getAllCharacters().size() > 0) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
System.err.println("FUCK MY LIFE");
|
||||
shutdownLock.lock();
|
||||
|
||||
try {
|
||||
System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n");
|
||||
if (getWorlds() == null) return;//already shutdown
|
||||
for (World w : getWorlds()) {
|
||||
w.shutdown();
|
||||
}
|
||||
/*for (World w : getWorlds()) {
|
||||
while (w.getPlayerStorage().getAllCharacters().size() > 0) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
System.err.println("FUCK MY LIFE");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Channel ch : getAllChannels()) {
|
||||
while (ch.getConnectedClients() > 0) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
System.err.println("FUCK MY LIFE");
|
||||
for (Channel ch : getAllChannels()) {
|
||||
while (ch.getConnectedClients() > 0) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
System.err.println("FUCK MY LIFE");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
TimerManager.getInstance().purge();
|
||||
TimerManager.getInstance().stop();
|
||||
|
||||
for (Channel ch : getAllChannels()) {
|
||||
while (!ch.finishedShutdown()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
System.err.println("FUCK MY LIFE");
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
|
||||
TimerManager.getInstance().purge();
|
||||
TimerManager.getInstance().stop();
|
||||
|
||||
for (Channel ch : getAllChannels()) {
|
||||
while (!ch.finishedShutdown()) {
|
||||
System.out.println("Worlds + Channels are offline.");
|
||||
acceptor.unbind();
|
||||
acceptor = null;
|
||||
if (!restart) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("\r\nRestarting the server....\r\n");
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
System.err.println("FUCK MY LIFE");
|
||||
instance.finalize();//FUU I CAN AND IT'S FREE
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
instance = null;
|
||||
System.gc();
|
||||
getInstance().run();//DID I DO EVERYTHING?! D:
|
||||
}
|
||||
}
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
|
||||
System.out.println("Worlds + Channels are offline.");
|
||||
acceptor.unbind();
|
||||
acceptor = null;
|
||||
if (!restart) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println("\r\nRestarting the server....\r\n");
|
||||
try {
|
||||
instance.finalize();//FUU I CAN AND IT'S FREE
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
instance = null;
|
||||
System.gc();
|
||||
getInstance().run();//DID I DO EVERYTHING?! D:
|
||||
} finally {
|
||||
shutdownLock.unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,10 +72,10 @@ public class FredrickHandler extends AbstractMaplePacketHandler {
|
||||
chr.getHiredMerchant().clearItems();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
Item item = items.get(i).getLeft();
|
||||
Item item = items.get(i).getLeft();
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false);
|
||||
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
|
||||
FilePrinter.printError(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
|
||||
FilePrinter.printError(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
|
||||
}
|
||||
c.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E));
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import client.MapleCharacter;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import client.MapleClient;
|
||||
import constants.ServerConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import tools.MaplePacketCreator;
|
||||
@@ -38,7 +39,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
public final class HiredMerchantRequest extends AbstractMaplePacketHandler {
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && chr.getMapId() > 910000000 && chr.getMapId() < 910000023) {
|
||||
if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && ((ServerConstants.USE_MERCHANT_ANYWHERE && chr.getMapId() != 910000000) || (chr.getMapId() > 910000000 && chr.getMapId() < 910000023))) {
|
||||
if (!chr.hasMerchant()) {
|
||||
try {
|
||||
if (ItemFactory.MERCHANT.loadItems(chr.getId(), false).isEmpty() && chr.getMerchantMeso() == 0) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import constants.ItemConstants;
|
||||
import constants.ServerConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -234,7 +235,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
} else if (!merchant.isOpen()) {
|
||||
chr.dropMessage(1, "This shop is in maintenance, please come by later.");
|
||||
return;
|
||||
} else if (merchant.getFreeSlot() == -1) {
|
||||
} else if (merchant.getFreeSlotThreadsafe() == -1) {
|
||||
chr.dropMessage(1, "This shop has reached it's maximum capacity, please come by later.");
|
||||
return;
|
||||
} else {
|
||||
@@ -259,10 +260,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
game.chat(c, slea.readMapleAsciiString());
|
||||
}
|
||||
} else if (merchant != null) {
|
||||
String message = chr.getName() + " : " + slea.readMapleAsciiString();
|
||||
byte slot = (byte) (merchant.getVisitorSlot(c.getPlayer()) + 1);
|
||||
merchant.getMessages().add(new Pair<>(message, slot));
|
||||
merchant.broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot));
|
||||
merchant.sendMessage(c.getPlayer(), slea.readMapleAsciiString());
|
||||
}
|
||||
} else if (mode == Action.EXIT.getCode()) {
|
||||
if (chr.getTrade() != null) {
|
||||
@@ -408,7 +406,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
} else if (mode == Action.CONFIRM.getCode()) {
|
||||
MapleTrade.completeTrade(c.getPlayer());
|
||||
} else if (mode == Action.ADD_ITEM.getCode() || mode == Action.PUT_ITEM.getCode()) {
|
||||
MapleInventoryType type = MapleInventoryType.getByType(slea.readByte());
|
||||
MapleInventoryType type = MapleInventoryType.getByType(slea.readByte());
|
||||
short slot = slea.readShort();
|
||||
short bundles = slea.readShort();
|
||||
if (chr.getInventory(type).getItem(slot) == null || chr.getItemQuantity(chr.getInventory(type).getItem(slot).getItemId(), false) < bundles || chr.getInventory(type).getItem(slot).getFlag() == ItemConstants.UNTRADEABLE) {
|
||||
@@ -425,6 +423,9 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
Item sellItem = ivItem.copy();
|
||||
if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) {
|
||||
return;
|
||||
} else if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(ivItem.getItemId())) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Shop."));
|
||||
return;
|
||||
}
|
||||
sellItem.setQuantity(perBundle);
|
||||
MaplePlayerShopItem item = new MaplePlayerShopItem(sellItem, bundles, price);
|
||||
@@ -521,7 +522,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
|
||||
} else if (merchant != null) {
|
||||
merchant.buy(c, item, quantity);
|
||||
merchant.broadcastToVisitors(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
|
||||
merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
|
||||
}
|
||||
} else if (mode == Action.TAKE_ITEM_BACK.getCode()) {
|
||||
HiredMerchant merchant = chr.getHiredMerchant();
|
||||
@@ -555,7 +556,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
chr.setHasMerchant(false);
|
||||
}
|
||||
if (merchant != null && merchant.isOwner(c.getPlayer())) {
|
||||
merchant.getMessages().clear();
|
||||
merchant.clearMessages();
|
||||
merchant.setOpen(true);
|
||||
}
|
||||
chr.setHiredMerchant(null);
|
||||
|
||||
@@ -117,8 +117,14 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
return;
|
||||
}
|
||||
} catch(ClassCastException e) {
|
||||
e.printStackTrace();
|
||||
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
|
||||
//this happens due to mob on last map damaging player just before changing maps
|
||||
|
||||
if(ServerConstants.USE_DEBUG) {
|
||||
e.printStackTrace();
|
||||
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
direction = slea.readByte();
|
||||
|
||||
@@ -86,7 +86,6 @@ public class World {
|
||||
private long mountUpdate;
|
||||
|
||||
private Map<HiredMerchant, Byte> activeMerchants = new LinkedHashMap<>();
|
||||
private ScheduledFuture<?> MerchantsSchedule;
|
||||
private long merchantUpdate;
|
||||
|
||||
private ScheduledFuture<?> charactersSchedule;
|
||||
|
||||
@@ -73,7 +73,13 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
this.map = owner.getMap();
|
||||
}
|
||||
|
||||
public void broadcastToVisitors(final byte[] packet) {
|
||||
public void broadcastToVisitorsThreadsafe(final byte[] packet) {
|
||||
synchronized(visitors) {
|
||||
broadcastToVisitors(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastToVisitors(final byte[] packet) {
|
||||
for (MapleCharacter visitor : visitors) {
|
||||
if (visitor != null) {
|
||||
visitor.getClient().announce(packet);
|
||||
@@ -82,27 +88,37 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public void addVisitor(MapleCharacter visitor) {
|
||||
int i = this.getFreeSlot();
|
||||
if (i > -1) {
|
||||
visitors[i] = visitor;
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeVisitor(MapleCharacter visitor) {
|
||||
int slot = getVisitorSlot(visitor);
|
||||
if (slot < 0){ //Not found
|
||||
return;
|
||||
}
|
||||
if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) {
|
||||
visitors[slot] = null;
|
||||
if (slot != -1) {
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
|
||||
synchronized(visitors) {
|
||||
int i = this.getFreeSlot();
|
||||
if (i > -1) {
|
||||
visitors[i] = visitor;
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getVisitorSlot(MapleCharacter visitor) {
|
||||
public void removeVisitor(MapleCharacter visitor) {
|
||||
synchronized(visitors) {
|
||||
int slot = getVisitorSlot(visitor);
|
||||
if (slot < 0){ //Not found
|
||||
return;
|
||||
}
|
||||
if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) {
|
||||
visitors[slot] = null;
|
||||
if (slot != -1) {
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getVisitorSlotThreadsafe(MapleCharacter visitor) {
|
||||
synchronized(visitors) {
|
||||
return getVisitorSlot(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
private int getVisitorSlot(MapleCharacter visitor) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null && visitors[i].getId() == visitor.getId()){
|
||||
return i;
|
||||
@@ -112,21 +128,24 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public void removeAllVisitors(String message) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].setHiredMerchant(null);
|
||||
visitors[i].getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
|
||||
if (message.length() > 0) {
|
||||
visitors[i].dropMessage(1, message);
|
||||
synchronized(visitors) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] != null) {
|
||||
visitors[i].setHiredMerchant(null);
|
||||
visitors[i].getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
|
||||
if (message.length() > 0) {
|
||||
visitors[i].dropMessage(1, message);
|
||||
}
|
||||
visitors[i] = null;
|
||||
}
|
||||
visitors[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void buy(MapleClient c, int item, short quantity) {
|
||||
MaplePlayerShopItem pItem = items.get(item);
|
||||
synchronized (items) {
|
||||
MaplePlayerShopItem pItem = items.get(item);
|
||||
|
||||
Item newItem = pItem.getItem().copy();
|
||||
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
|
||||
if ((newItem.getFlag() & ItemConstants.KARMA) == ItemConstants.KARMA) {
|
||||
@@ -142,11 +161,15 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
int price = pItem.getPrice() * quantity;
|
||||
int price = (int)Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE);
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (MapleInventoryManipulator.addFromDrop(c, newItem, true)) {
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price));
|
||||
|
||||
synchronized (sold) {
|
||||
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price));
|
||||
}
|
||||
|
||||
pItem.setBundles((short) (pItem.getBundles() - quantity));
|
||||
if (pItem.getBundles() < 1) {
|
||||
pItem.setDoesExist(false);
|
||||
@@ -187,7 +210,9 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
|
||||
try {
|
||||
saveItems(true);
|
||||
items.clear();
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
@@ -234,15 +259,19 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
con.close();
|
||||
}
|
||||
|
||||
if (check(c.getPlayer(), getItems()) && !timeout) {
|
||||
for (MaplePlayerShopItem mpsi : getItems()) {
|
||||
List<MaplePlayerShopItem> copyItems = getItems();
|
||||
if (check(c.getPlayer(), copyItems) && !timeout) {
|
||||
for (MaplePlayerShopItem mpsi : copyItems) {
|
||||
if (mpsi.isExist() && (mpsi.getItem().getType() == MapleInventoryType.EQUIP.getType())) {
|
||||
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false);
|
||||
} else if (mpsi.isExist()) {
|
||||
MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), null, -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration());
|
||||
}
|
||||
}
|
||||
items.clear();
|
||||
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -251,7 +280,9 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
items.clear();
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -264,7 +295,9 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public void clearItems() {
|
||||
items.clear();
|
||||
synchronized (items) {
|
||||
items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
@@ -276,15 +309,25 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public MapleCharacter[] getVisitors() {
|
||||
return visitors;
|
||||
synchronized(visitors) {
|
||||
MapleCharacter[] copy = new MapleCharacter[3];
|
||||
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
|
||||
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
public List<MaplePlayerShopItem> getItems() {
|
||||
return Collections.unmodifiableList(items);
|
||||
synchronized (items) {
|
||||
return Collections.unmodifiableList(items);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(MaplePlayerShopItem item) {
|
||||
items.add(item);
|
||||
synchronized (items) {
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (SQLException ex) {
|
||||
@@ -293,7 +336,10 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public void removeFromSlot(int slot) {
|
||||
items.remove(slot);
|
||||
synchronized (items) {
|
||||
items.remove(slot);
|
||||
}
|
||||
|
||||
try {
|
||||
this.saveItems(false);
|
||||
} catch (SQLException ex) {
|
||||
@@ -301,8 +347,13 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public int getFreeSlotThreadsafe() {
|
||||
synchronized(visitors) {
|
||||
return getFreeSlot();
|
||||
}
|
||||
}
|
||||
|
||||
public int getFreeSlot() {
|
||||
private int getFreeSlot() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (visitors[i] == null) {
|
||||
return i;
|
||||
@@ -330,24 +381,38 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
public boolean isOwner(MapleCharacter chr) {
|
||||
return chr.getId() == ownerId;
|
||||
}
|
||||
|
||||
public void sendMessage(MapleCharacter chr, String msg) {
|
||||
String message = chr.getName() + " : " + msg;
|
||||
byte slot = (byte) (getVisitorSlot(chr) + 1);
|
||||
|
||||
synchronized (messages) {
|
||||
messages.add(new Pair<>(message, slot));
|
||||
}
|
||||
broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot));
|
||||
}
|
||||
|
||||
public void saveItems(boolean shutdown) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
|
||||
List<Short> bundles = new ArrayList<>();
|
||||
|
||||
for (MaplePlayerShopItem pItems : items) {
|
||||
for (MaplePlayerShopItem pItems : getItems()) {
|
||||
Item newItem = pItems.getItem();
|
||||
if (shutdown) {
|
||||
newItem.setQuantity((short) (pItems.getItem().getQuantity() * pItems.getBundles()));
|
||||
short newBundle = pItems.getBundles();
|
||||
|
||||
if (shutdown) { //is "shutdown" really necessary?
|
||||
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
|
||||
} else {
|
||||
newItem.setQuantity(pItems.getItem().getQuantity());
|
||||
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
|
||||
}
|
||||
if (pItems.getBundles() > 0) {
|
||||
if (newBundle > 0) {
|
||||
itemsWithType.add(new Pair<>(newItem, MapleInventoryType.getByType(newItem.getType())));
|
||||
bundles.add(newBundle);
|
||||
}
|
||||
}
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ItemFactory.MERCHANT.saveItems(itemsWithType, this.ownerId, con);
|
||||
ItemFactory.MERCHANT.saveItems(itemsWithType, bundles, this.ownerId, con);
|
||||
con.close();
|
||||
}
|
||||
|
||||
@@ -405,8 +470,21 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
return (int) ((System.currentTimeMillis() - start) / 1000);
|
||||
}
|
||||
|
||||
public void clearMessages() {
|
||||
synchronized (messages) {
|
||||
messages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Pair<String, Byte>> getMessages() {
|
||||
return messages;
|
||||
synchronized (messages) {
|
||||
List<Pair<String, Byte>> msgList = new LinkedList<>();
|
||||
for(Pair<String, Byte> m : messages) {
|
||||
msgList.add(m);
|
||||
}
|
||||
|
||||
return msgList;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMapId() {
|
||||
@@ -414,7 +492,9 @@ public class HiredMerchant extends AbstractMapleMapObject {
|
||||
}
|
||||
|
||||
public List<SoldItem> getSold() {
|
||||
return sold;
|
||||
synchronized (sold) {
|
||||
return Collections.unmodifiableList(sold);
|
||||
}
|
||||
}
|
||||
|
||||
public int getMesos() {
|
||||
|
||||
@@ -4959,7 +4959,7 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(item.getBundles());
|
||||
mplew.writeInt(item.getPrice());
|
||||
mplew.writeInt(hm.getOwnerId());
|
||||
mplew.write(hm.getFreeSlot() == -1 ? 1 : 0);
|
||||
mplew.write(hm.getFreeSlotThreadsafe() == -1 ? 1 : 0);
|
||||
MapleCharacter chr = c.getChannelServer().getPlayerStorage().getCharacterById(hm.getOwnerId());
|
||||
if ((chr != null) && (c.getChannel() == hm.getChannel())) {
|
||||
mplew.write(1);
|
||||
@@ -5004,7 +5004,7 @@ public class MaplePacketCreator {
|
||||
mplew.write(PlayerInteractionHandler.Action.ROOM.getCode());
|
||||
mplew.write(0x05);
|
||||
mplew.write(0x04);
|
||||
mplew.writeShort(hm.getVisitorSlot(chr) + 1);
|
||||
mplew.writeShort(hm.getVisitorSlotThreadsafe(chr) + 1);
|
||||
mplew.writeInt(hm.getItemId());
|
||||
mplew.writeMapleAsciiString("Hired Merchant");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -5016,10 +5016,12 @@ public class MaplePacketCreator {
|
||||
}
|
||||
mplew.write(-1);
|
||||
if (hm.isOwner(chr)) {
|
||||
mplew.writeShort(hm.getMessages().size());
|
||||
for (int i = 0; i < hm.getMessages().size(); i++) {
|
||||
mplew.writeMapleAsciiString(hm.getMessages().get(i).getLeft());
|
||||
mplew.write(hm.getMessages().get(i).getRight());
|
||||
List<Pair<String, Byte>> msgList = hm.getMessages();
|
||||
|
||||
mplew.writeShort(msgList.size());
|
||||
for (int i = 0; i < msgList.size(); i++) {
|
||||
mplew.writeMapleAsciiString(msgList.get(i).getLeft());
|
||||
mplew.write(msgList.get(i).getRight());
|
||||
}
|
||||
} else {
|
||||
mplew.writeShort(0);
|
||||
|
||||
Reference in New Issue
Block a user