Autosave feature + Pet Ignore fix

Added experimental autosaver feature. Fixed pet ignore feature not
saving/loading data in some cases. Added concurrency protection for
inventory classes and monster book.
This commit is contained in:
ronancpl
2017-09-11 16:53:40 -03:00
parent e064d5cbfa
commit f387d589b2
55 changed files with 567 additions and 247 deletions
+46 -20
View File
@@ -4529,26 +4529,31 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
PreparedStatement ps2;
ResultSet rs2;
for(byte i = 0; i < 3; i++) {
MaplePet pet = ret.getPet(i);
if(pet == null) continue;
int petId = pet.getUniqueId();
ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?"); // Get pet details..
PreparedStatement ps2, ps3;
ResultSet rs2, rs3;
ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid > -1");
ps3.setInt(1, charid);
rs3 = ps3.executeQuery();
while(rs3.next()) {
int petId = rs3.getInt("petid");
ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?");
ps2.setInt(1, petId);
ret.resetExcluded(petId);
rs2 = ps2.executeQuery();
while(rs2.next()) {
ret.addExcluded(petId, rs2.getInt("itemid"));
}
ps2.close();
rs2.close();
}
ps3.close();
rs3.close();
ret.commitExcludedItems();
if (channelserver) {
@@ -5525,10 +5530,28 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
// synchronize this call instead of trying to give access all at once (?)
public synchronized void saveToDB() {
public void saveToDB() {
if(ServerConstants.USE_AUTOSAVE) {
Runnable r = new Runnable() {
@Override
public void run() {
saveToDB(true);
}
};
Thread t = new Thread(r); //spawns a new thread to deal with this
t.start();
} else {
saveToDB(true);
}
}
public synchronized void saveToDB(boolean notAutosave) {
Calendar c = Calendar.getInstance();
FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString());
if(notAutosave) FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString());
else FilePrinter.print(FilePrinter.AUTOSAVING_CHARACTER, "Attempting to autosave " + name + " at " + c.getTime().toString());
Connection con = null;
try {
con = DatabaseConnection.getConnection();
@@ -5577,7 +5600,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
ps.setInt(22, meso.get());
ps.setInt(23, hpMpApUsed);
if (map == null || map.getId() == 610020000 || map.getId() == 610020001) {
if (map == null || map.getId() == 610020000 || map.getId() == 610020001) { // reset to first spawnpoint on those maps
ps.setInt(24, 0);
} else {
MaplePortal closest = map.findClosestPlayerSpawnpoint(getPosition());
@@ -5648,7 +5671,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
petLock.unlock();
}
for(Entry<Integer, Set<Integer>> es: getExcluded().entrySet()) {
for(Entry<Integer, Set<Integer>> es: getExcluded().entrySet()) { // this set is already protected
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM petignores WHERE petid=?")) {
ps2.setInt(1, es.getKey());
ps2.executeUpdate();
@@ -5667,8 +5690,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
deleteWhereCharacterId(con, "DELETE FROM keymap WHERE characterid = ?");
ps = con.prepareStatement("INSERT INTO keymap (characterid, `key`, `type`, `action`) VALUES (?, ?, ?, ?)");
ps.setInt(1, id);
for (Entry<Integer, MapleKeyBinding> keybinding : keymap.entrySet()) {
ps.setInt(2, keybinding.getKey().intValue());
Set<Entry<Integer, MapleKeyBinding>> keybindingItems = Collections.unmodifiableSet(keymap.entrySet());
for (Entry<Integer, MapleKeyBinding> keybinding : keybindingItems) {
ps.setInt(2, keybinding.getKey());
ps.setInt(3, keybinding.getValue().getType());
ps.setInt(4, keybinding.getValue().getAction());
ps.addBatch();
@@ -5691,14 +5716,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
ps.executeBatch();
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
for (MapleInventory iv : inventory) {
for (Item item : iv.list()) {
itemsWithType.add(new Pair<>(item, iv.getType()));
}
}
ItemFactory.INVENTORY.saveItems(itemsWithType, id, con);
deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
@@ -6283,7 +6307,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if (slots <= 96) {
inventory[type].setSlotLimit(slots);
saveToDB();
this.saveToDB();
if (update) {
client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots));
}
@@ -6552,6 +6576,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
getMap().broadcastMessage(this, MaplePacketCreator.showPet(this, pet, true, hunger), true);
removePet(pet, shift_left);
commitExcludedItems();
client.announce(MaplePacketCreator.petStatUpdate(this));
client.announce(MaplePacketCreator.enableActions());
}
+99 -40
View File
@@ -25,9 +25,13 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
@@ -36,85 +40,140 @@ public final class MonsterBook {
private int normalCard = 0;
private int bookLevel = 1;
private Map<Integer, Integer> cards = new LinkedHashMap<>();
private Lock lock = new ReentrantLock();
private Set<Entry<Integer, Integer>> getCardSet() {
lock.lock();
try {
return Collections.unmodifiableSet(cards.entrySet());
} finally {
lock.unlock();
}
}
public void addCard(final MapleClient c, final int cardid) {
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignCardEffect(c.getPlayer().getId()), false);
for (Entry<Integer, Integer> all : cards.entrySet()) {
if (all.getKey() == cardid) {
if (all.getValue() > 4) {
c.announce(MaplePacketCreator.addCard(true, cardid, all.getValue()));
Integer qty;
lock.lock();
try {
qty = cards.get(cardid);
if(qty != null) {
if(qty < 5) {
cards.put(cardid, qty + 1);
}
} else {
cards.put(cardid, 1);
qty = 0;
if (cardid / 1000 >= 2388) {
specialCard++;
} else {
all.setValue(all.getValue() + 1);
c.announce(MaplePacketCreator.addCard(false, cardid, all.getValue()));
c.announce(MaplePacketCreator.showGainCard());
calculateLevel();
normalCard++;
}
return;
}
} finally {
lock.unlock();
}
cards.put(cardid, 1);
c.announce(MaplePacketCreator.addCard(false, cardid, 1));
c.announce(MaplePacketCreator.showGainCard());
calculateLevel();
if (cardid / 1000 >= 2388) {
specialCard++;
if(qty < 5) {
calculateLevel(); // current leveling system only accounts unique cards...
c.announce(MaplePacketCreator.addCard(false, cardid, qty + 1));
c.announce(MaplePacketCreator.showGainCard());
} else {
normalCard++;
c.announce(MaplePacketCreator.addCard(true, cardid, 5));
}
//c.getPlayer().saveToDB(); //is it REALLY needed to save to DB every new entry?
}
private void calculateLevel() {
bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5));
lock.lock();
try {
bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5));
} finally {
lock.unlock();
}
}
public int getBookLevel() {
return bookLevel;
lock.lock();
try {
return bookLevel;
} finally {
lock.unlock();
}
}
public Map<Integer, Integer> getCards() {
return cards;
lock.lock();
try {
return Collections.unmodifiableMap(cards);
} finally {
lock.unlock();
}
}
public int getTotalCards() {
return specialCard + normalCard;
lock.lock();
try {
return specialCard + normalCard;
} finally {
lock.unlock();
}
}
public int getNormalCard() {
return normalCard;
lock.lock();
try {
return normalCard;
} finally {
lock.unlock();
}
}
public int getSpecialCard() {
return specialCard;
lock.lock();
try {
return specialCard;
} finally {
lock.unlock();
}
}
public void loadCards(final int charid) throws SQLException {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT cardid, level FROM monsterbook WHERE charid = ? ORDER BY cardid ASC")) {
ps.setInt(1, charid);
try (ResultSet rs = ps.executeQuery()) {
int cardid, level;
while (rs.next()) {
cardid = rs.getInt("cardid");
level = rs.getInt("level");
if (cardid / 1000 >= 2388) {
specialCard++;
} else {
normalCard++;
lock.lock();
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT cardid, level FROM monsterbook WHERE charid = ? ORDER BY cardid ASC")) {
ps.setInt(1, charid);
try (ResultSet rs = ps.executeQuery()) {
int cardid, level;
while (rs.next()) {
cardid = rs.getInt("cardid");
level = rs.getInt("level");
if (cardid / 1000 >= 2388) {
specialCard++;
} else {
normalCard++;
}
cards.put(cardid, level);
}
cards.put(cardid, level);
}
}
con.close();
} finally {
lock.unlock();
}
con.close();
calculateLevel();
}
public void saveCards(final int charid) {
if (cards.isEmpty()) {
Set<Entry<Integer, Integer>> cardSet = getCardSet();
if (cardSet.isEmpty()) {
return;
}
try {
@@ -125,7 +184,7 @@ public final class MonsterBook {
ps.close();
boolean first = true;
StringBuilder query = new StringBuilder();
for (Entry<Integer, Integer> all : cards.entrySet()) {
for (Entry<Integer, Integer> all : cardSet) {
if (first) {
query.append("INSERT INTO monsterbook VALUES (");
first = false;
-1
View File
@@ -141,7 +141,6 @@ public enum ItemFactory {
ResultSet rs = null;
lock.lock();
try {
StringBuilder query = new StringBuilder();
query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
+131 -54
View File
@@ -29,6 +29,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.Pair;
import client.MapleCharacter;
@@ -36,6 +38,7 @@ import client.MapleClient;
import constants.ItemConstants;
import server.MapleItemInformationProvider;
import server.MapleInventoryManipulator;
import tools.FilePrinter;
/**
*
@@ -47,6 +50,7 @@ public class MapleInventory implements Iterable<Item> {
private byte slotLimit;
private MapleInventoryType type;
private boolean checked = false;
private Lock lock = new ReentrantLock();
public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) {
this.owner = mc;
@@ -64,15 +68,34 @@ public class MapleInventory implements Iterable<Item> {
}
public byte getSlotLimit() {
return slotLimit;
lock.lock();
try {
return slotLimit;
} finally {
lock.unlock();
}
}
public void setSlotLimit(int newLimit) {
slotLimit = (byte) newLimit;
lock.lock();
try {
slotLimit = (byte) newLimit;
} finally {
lock.unlock();
}
}
public Collection<Item> list() {
lock.lock();
try {
return Collections.unmodifiableCollection(inventory.values());
} finally {
lock.unlock();
}
}
public Item findById(int itemId) {
for (Item item : inventory.values()) {
for (Item item : list()) {
if (item.getItemId() == itemId) {
return item;
}
@@ -81,10 +104,10 @@ public class MapleInventory implements Iterable<Item> {
}
public Item findByName(String name) {
for (Item item : inventory.values()) {
for (Item item : list()) {
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
if(itemName == null) {
System.out.println("[CRITICAL] Item " + item.getItemId() + " has no name.");
FilePrinter.printError(FilePrinter.EXCEPTION, "[CRITICAL] Item " + item.getItemId() + " has no name.");
continue;
}
@@ -97,7 +120,7 @@ public class MapleInventory implements Iterable<Item> {
public int countById(int itemId) {
int qty = 0;
for (Item item : inventory.values()) {
for (Item item : list()) {
if (item.getItemId() == itemId) {
qty += item.getQuantity();
}
@@ -138,7 +161,7 @@ public class MapleInventory implements Iterable<Item> {
public List<Item> listById(int itemId) {
List<Item> ret = new ArrayList<>();
for (Item item : inventory.values()) {
for (Item item : list()) {
if (item.getItemId() == itemId) {
ret.add(item);
}
@@ -149,10 +172,6 @@ public class MapleInventory implements Iterable<Item> {
return ret;
}
public Collection<Item> list() {
return inventory.values();
}
public short addItem(Item item) {
short slotId = getNextFreeSlot();
if (slotId < 0 || item == null) {
@@ -171,29 +190,34 @@ public class MapleInventory implements Iterable<Item> {
}
public void move(short sSlot, short dSlot, short slotMax) {
Item source = (Item) inventory.get(sSlot);
Item target = (Item) inventory.get(dSlot);
if (source == null) {
return;
}
if (target == null) {
source.setPosition(dSlot);
inventory.put(dSlot, source);
inventory.remove(sSlot);
} else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargable(source.getItemId())) {
if (type.getType() == MapleInventoryType.EQUIP.getType()) {
lock.lock();
try {
Item source = (Item) inventory.get(sSlot);
Item target = (Item) inventory.get(dSlot);
if (source == null) {
return;
}
if (target == null) {
source.setPosition(dSlot);
inventory.put(dSlot, source);
inventory.remove(sSlot);
} else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargable(source.getItemId())) {
if (type.getType() == MapleInventoryType.EQUIP.getType()) {
swap(target, source);
}
if (source.getQuantity() + target.getQuantity() > slotMax) {
short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax);
source.setQuantity(rest);
target.setQuantity(slotMax);
} else {
target.setQuantity((short) (source.getQuantity() + target.getQuantity()));
inventory.remove(sSlot);
}
} else {
swap(target, source);
}
if (source.getQuantity() + target.getQuantity() > slotMax) {
short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax);
source.setQuantity(rest);
target.setQuantity(slotMax);
} else {
target.setQuantity((short) (source.getQuantity() + target.getQuantity()));
inventory.remove(sSlot);
}
} else {
swap(target, source);
} finally {
lock.unlock();
}
}
@@ -208,7 +232,12 @@ public class MapleInventory implements Iterable<Item> {
}
public Item getItem(short slot) {
return inventory.get(slot);
lock.lock();
try {
return inventory.get(slot);
} finally {
lock.unlock();
}
}
public void removeItem(short slot) {
@@ -216,7 +245,7 @@ public class MapleInventory implements Iterable<Item> {
}
public void removeItem(short slot, short quantity, boolean allowZero) {
Item item = inventory.get(slot);
Item item = getItem(slot);
if (item == null) {// TODO is it ok not to throw an exception here?
return;
}
@@ -230,7 +259,12 @@ public class MapleInventory implements Iterable<Item> {
}
public void addSlot(short slot, Item item) {
inventory.put(slot, item);
lock.lock();
try {
inventory.put(slot, item);
} finally {
lock.unlock();
}
if(ItemConstants.isRateCoupon(item.getItemId())) {
owner.updateCouponRates();
@@ -238,7 +272,13 @@ public class MapleInventory implements Iterable<Item> {
}
public void removeSlot(short slot) {
Item item = inventory.remove(slot);
Item item;
lock.lock();
try {
item = inventory.remove(slot);
} finally {
lock.unlock();
}
if(item != null && ItemConstants.isRateCoupon(item.getItemId())) {
owner.updateCouponRates();
@@ -246,40 +286,67 @@ public class MapleInventory implements Iterable<Item> {
}
public boolean isFull() {
return inventory.size() >= slotLimit;
lock.lock();
try {
return inventory.size() >= slotLimit;
} finally {
lock.unlock();
}
}
public boolean isFull(int margin) {
return inventory.size() + margin >= slotLimit;
lock.lock();
try {
return inventory.size() + margin >= slotLimit;
} finally {
lock.unlock();
}
}
public boolean isFullAfterSomeItems(int margin, int used) {
return inventory.size() + margin >= slotLimit - used;
lock.lock();
try {
return inventory.size() + margin >= slotLimit - used;
} finally {
lock.unlock();
}
}
public short getNextFreeSlot() {
if (isFull()) {
return -1;
}
for (short i = 1; i <= slotLimit; i++) {
if (!inventory.keySet().contains(i)) {
return i;
lock.lock();
try {
for (short i = 1; i <= slotLimit; i++) {
if (!inventory.containsKey(i)) {
return i;
}
}
return -1;
} finally {
lock.unlock();
}
return -1;
}
public short getNumFreeSlot() {
if (isFull()) {
return 0;
}
short free = 0;
for (short i = 1; i <= slotLimit; i++) {
if (!inventory.keySet().contains(i)) {
free++;
}
}
return free;
lock.lock();
try {
short free = 0;
for (short i = 1; i <= slotLimit; i++) {
if (!inventory.containsKey(i)) {
free++;
}
}
return free;
} finally {
lock.unlock();
}
}
public static boolean checkSpot(MapleCharacter chr, Item item) {
@@ -394,7 +461,7 @@ public class MapleInventory implements Iterable<Item> {
@Override
public Iterator<Item> iterator() {
return Collections.unmodifiableCollection(inventory.values()).iterator();
return Collections.unmodifiableCollection(list()).iterator();
}
public Collection<MapleInventory> allInventories() {
@@ -404,7 +471,7 @@ public class MapleInventory implements Iterable<Item> {
public Item findByCashId(int cashId) {
boolean isRing = false;
Equip equip = null;
for (Item item : inventory.values()) {
for (Item item : list()) {
if (item.getType() == MapleInventoryType.EQUIP.getType()) {
equip = (Equip) item;
isRing = equip.getRingId() > -1;
@@ -417,10 +484,20 @@ public class MapleInventory implements Iterable<Item> {
}
public boolean checked() {
return checked;
lock.lock();
try {
return checked;
} finally {
lock.unlock();
}
}
public void checked(boolean yes) {
checked = yes;
lock.lock();
try {
checked = yes;
} finally {
lock.unlock();
}
}
}
+1
View File
@@ -44,6 +44,7 @@ public class ServerConstants {
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_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 to required secondary stat on equipments.
public static final boolean USE_REFRESH_RANK_MOVE = true;
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)
@@ -0,0 +1,50 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server;
import net.server.world.World;
import client.MapleCharacter;
import constants.ServerConstants;
/**
* @author Ronan
*/
public class CharacterAutosaverWorker implements Runnable {
private World wserv;
@Override
public void run() {
if(!ServerConstants.USE_AUTOSAVE) return;
PlayerStorage ps = wserv.getPlayerStorage();
for(MapleCharacter chr: ps.getAllCharacters()) {
if(chr != null && chr.isLoggedin()) {
chr.saveToDB(false);
}
}
}
public CharacterAutosaverWorker(World world) {
wserv = world;
}
}
@@ -61,7 +61,6 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
chr.cancelExpirationTask();
chr.saveToDB();
System.out.println("STRANGE SAVE TO DB");
chr.getMap().removePlayer(c.getPlayer());
try {
c.announce(MaplePacketCreator.openCashShop(c, true));
@@ -90,7 +90,8 @@ public final class SpawnPetHandler extends AbstractMaplePacketHandler {
chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
c.announce(MaplePacketCreator.enableActions());
chr.commitExcludedItems();
chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
}
}
+6 -2
View File
@@ -45,8 +45,9 @@ import java.util.HashSet;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.PetFullnessWorker;
import net.server.CharacterAutosaverWorker;
import net.server.MountTirednessWorker;
import net.server.PetFullnessWorker;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.channel.Channel;
@@ -82,7 +83,9 @@ public class World {
private Map<Integer, Byte> activeMounts = new LinkedHashMap<>();
private ScheduledFuture<?> mountsSchedule;
private long mountUpdate;
private ScheduledFuture<?> charactersSchedule;
public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int bossdroprate) {
this.id = world;
this.flag = flag;
@@ -99,6 +102,7 @@ public class World {
petsSchedule = TimerManager.getInstance().register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
mountsSchedule = TimerManager.getInstance().register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000);
charactersSchedule = TimerManager.getInstance().register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000);
}
public List<Channel> getChannels() {
+33 -12
View File
@@ -30,6 +30,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import provider.MapleData;
import provider.MapleDataProvider;
@@ -43,6 +45,7 @@ import client.inventory.ItemFactory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.ItemConstants;
import java.util.Collections;
/*
* @author Flav
@@ -99,12 +102,13 @@ public class CashShop {
item = new Item(itemId, (byte) 0, count, petid);
}
if (ItemConstants.EXPIRING_ITEMS)
if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4));
} else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period));
}
if (ItemConstants.EXPIRING_ITEMS) {
if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4));
} else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period));
}
}
item.setSN(sn);
return item;
}
@@ -237,6 +241,7 @@ public class CashShop {
private List<Item> inventory = new ArrayList<>();
private List<Integer> wishList = new ArrayList<>();
private int notes = 0;
private Lock lock = new ReentrantLock();
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
this.accountId = accountId;
@@ -325,13 +330,18 @@ public class CashShop {
}
public List<Item> getInventory() {
return inventory;
lock.lock();
try {
return Collections.unmodifiableList(inventory);
} finally {
lock.unlock();
}
}
public Item findByCashId(int cashId) {
boolean isRing = false;
Equip equip = null;
for (Item item : inventory) {
for (Item item : getInventory()) {
if (item.getType() == 1) {
equip = (Equip) item;
isRing = equip.getRingId() > -1;
@@ -345,11 +355,21 @@ public class CashShop {
}
public void addToInventory(Item item) {
inventory.add(item);
lock.lock();
try {
inventory.add(item);
} finally {
lock.unlock();
}
}
public void removeFromInventory(Item item) {
inventory.remove(item);
lock.lock();
try {
inventory.remove(item);
} finally {
lock.unlock();
}
}
public List<Integer> getWishList() {
@@ -458,7 +478,8 @@ public class CashShop {
ps.close();
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
for (Item item : inventory) {
List<Item> inv = getInventory();
for (Item item : inv) {
itemsWithType.add(new Pair<>(item, MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId())));
}
@@ -466,7 +487,7 @@ public class CashShop {
ps = con.prepareStatement("DELETE FROM `wishlists` WHERE `charid` = ?");
ps.setInt(1, characterId);
ps.executeUpdate();
ps.close();
ps.close();
ps = con.prepareStatement("INSERT INTO `wishlists` VALUES (DEFAULT, ?, ?)");
ps.setInt(1, characterId);
+66 -22
View File
@@ -36,6 +36,8 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
@@ -47,10 +49,11 @@ import tools.Pair;
public class MapleStorage {
private int id;
private List<Item> items;
private int meso;
private byte slots;
private Map<MapleInventoryType, List<Item>> typeItems = new HashMap<>();
private List<Item> items;
private Lock lock = new ReentrantLock();
private MapleStorage(int id, byte slots, int meso) {
this.id = id;
@@ -135,7 +138,8 @@ public class MapleStorage {
}
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
for (Item item : items) {
List<Item> list = getItems();
for (Item item : list) {
itemsWithType.add(new Pair<>(item, MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId())));
}
@@ -146,30 +150,56 @@ public class MapleStorage {
}
public Item getItem(byte slot) {
return items.get(slot);
lock.lock();
try {
return items.get(slot);
} finally {
lock.unlock();
}
}
public Item takeOut(byte slot) {
Item ret = items.remove(slot);
Item ret;
lock.lock();
try {
ret = items.remove(slot);
} finally {
lock.unlock();
}
MapleInventoryType type = MapleItemInformationProvider.getInstance().getInventoryType(ret.getItemId());
typeItems.put(type, new ArrayList<>(filterItems(type)));
return ret;
}
public void store(Item item) {
items.add(item);
lock.lock();
try {
items.add(item);
} finally {
lock.unlock();
}
MapleInventoryType type = MapleItemInformationProvider.getInstance().getInventoryType(item.getItemId());
typeItems.put(type, new ArrayList<>(filterItems(type)));
}
public List<Item> getItems() {
return Collections.unmodifiableList(items);
lock.lock();
try {
return Collections.unmodifiableList(items);
} finally {
lock.unlock();
}
}
private List<Item> filterItems(MapleInventoryType type) {
List<Item> storageItems = getItems();
List<Item> ret = new LinkedList<>();
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
for (Item item : items) {
for (Item item : storageItems) {
if (ii.getInventoryType(item.getItemId()) == type) {
ret.add(item);
}
@@ -179,7 +209,8 @@ public class MapleStorage {
public byte getSlot(MapleInventoryType type, byte slot) {
byte ret = 0;
for (Item item : items) {
List<Item> storageItems = getItems();
for (Item item : storageItems) {
if (item == typeItems.get(type).get(slot)) {
return ret;
}
@@ -190,21 +221,29 @@ public class MapleStorage {
public void sendStorage(MapleClient c, int npcId) {
final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
Collections.sort(items, new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
if (ii.getInventoryType(o1.getItemId()).getType() < ii.getInventoryType(o2.getItemId()).getType()) {
return -1;
} else if (ii.getInventoryType(o1.getItemId()) == ii.getInventoryType(o2.getItemId())) {
return 0;
lock.lock();
try {
Collections.sort(items, new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
if (ii.getInventoryType(o1.getItemId()).getType() < ii.getInventoryType(o2.getItemId()).getType()) {
return -1;
} else if (ii.getInventoryType(o1.getItemId()) == ii.getInventoryType(o2.getItemId())) {
return 0;
}
return 1;
}
return 1;
}
});
for (MapleInventoryType type : MapleInventoryType.values()) {
typeItems.put(type, new ArrayList<>(items));
});
} finally {
lock.unlock();
}
c.announce(MaplePacketCreator.getStorage(npcId, slots, items, meso));
List<Item> storageItems = getItems();
for (MapleInventoryType type : MapleInventoryType.values()) {
typeItems.put(type, new ArrayList<>(storageItems));
}
c.announce(MaplePacketCreator.getStorage(npcId, slots, storageItems, meso));
}
public void sendStored(MapleClient c, MapleInventoryType type) {
@@ -231,7 +270,12 @@ public class MapleStorage {
}
public boolean isFull() {
return items.size() >= slots;
lock.lock();
try {
return items.size() >= slots;
} finally {
lock.unlock();
}
}
public void close() {
+93 -86
View File
@@ -55,100 +55,107 @@ public class MapleLifeFactory {
}
public static MapleMonster getMonster(int mid) {
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
if (stats == null) {
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
if (monsterData == null) {
return null;
}
MapleData monsterInfoData = monsterData.getChildByPath("info");
stats = new MapleMonsterStats();
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
MapleData special = monsterInfoData.getChildByPath("coolDamage");
if (special != null) {
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
stats.setCool(new Pair<>(coolDmg, coolProb));
}
special = monsterInfoData.getChildByPath("loseItem");
if (special != null) {
for (MapleData liData : special.getChildren()) {
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
try {
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
if (stats == null) {
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
if (monsterData == null) {
return null;
}
}
special = monsterInfoData.getChildByPath("selfDestruction");
if (special != null) {
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
}
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
int firstAttack = 0;
if (firstAttackData != null) {
if (firstAttackData.getType() == MapleDataType.FLOAT) {
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
} else {
firstAttack = MapleDataTool.getInt(firstAttackData);
}
}
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
MapleData monsterInfoData = monsterData.getChildByPath("info");
stats = new MapleMonsterStats();
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {
int delay = 0;
for (MapleData pic : idata.getChildren()) {
delay += MapleDataTool.getIntConvert("delay", pic, 0);
MapleData special = monsterInfoData.getChildByPath("coolDamage");
if (special != null) {
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
stats.setCool(new Pair<>(coolDmg, coolProb));
}
special = monsterInfoData.getChildByPath("loseItem");
if (special != null) {
for (MapleData liData : special.getChildren()) {
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
}
stats.setAnimationTime(idata.getName(), delay);
}
}
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
if (reviveInfo != null) {
List<Integer> revives = new LinkedList<>();
for (MapleData data_ : reviveInfo) {
revives.add(MapleDataTool.getInt(data_));
special = monsterInfoData.getChildByPath("selfDestruction");
if (special != null) {
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
}
stats.setRevives(revives);
}
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
if (monsterSkillData != null) {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
i++;
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
int firstAttack = 0;
if (firstAttackData != null) {
if (firstAttackData.getType() == MapleDataType.FLOAT) {
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
} else {
firstAttack = MapleDataTool.getInt(firstAttackData);
}
}
stats.setSkills(skills);
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {
int delay = 0;
for (MapleData pic : idata.getChildren()) {
delay += MapleDataTool.getIntConvert("delay", pic, 0);
}
stats.setAnimationTime(idata.getName(), delay);
}
}
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
if (reviveInfo != null) {
List<Integer> revives = new LinkedList<>();
for (MapleData data_ : reviveInfo) {
revives.add(MapleDataTool.getInt(data_));
}
stats.setRevives(revives);
}
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
if (monsterSkillData != null) {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
i++;
}
stats.setSkills(skills);
}
MapleData banishData = monsterInfoData.getChildByPath("ban");
if (banishData != null) {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
monsterStats.put(Integer.valueOf(mid), stats);
}
MapleData banishData = monsterInfoData.getChildByPath("ban");
if (banishData != null) {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
monsterStats.put(Integer.valueOf(mid), stats);
MapleMonster ret = new MapleMonster(mid, stats);
return ret;
} catch(NullPointerException npe) {
System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n");
npe.printStackTrace();
return null;
}
MapleMonster ret = new MapleMonster(mid, stats);
return ret;
}
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
+2 -2
View File
@@ -2325,11 +2325,11 @@ public class MapleMap {
}
public void setMapPointBoundings(int px, int py, int h, int w) {
mapArea.setBounds(px, py, w, h);
mapArea.setBounds(px + 7, py, w - 14, h);
}
public void setMapLineBoundings(int vrTop, int vrBottom, int vrLeft, int vrRight) {
mapArea.setBounds(vrLeft, vrTop, vrRight - vrLeft, vrBottom - vrTop);
mapArea.setBounds(vrLeft + 7, vrTop, vrRight - vrLeft - 14, vrBottom - vrTop);
}
/**
+1
View File
@@ -41,6 +41,7 @@ public class FilePrinter {
FREDRICK = "fredrick/",
NPC_UNCODED = "uncodedNPCs.txt",
QUEST_UNCODED = "uncodedQuests.txt",
AUTOSAVING_CHARACTER = "saveCharAuto.txt",
SAVING_CHARACTER = "saveChar.txt",
USED_COMMANDS = "usedCommands.txt";//more to come (maps)