Private shop review.

This commit is contained in:
MobiusDevelopment 2022-12-19 07:51:48 +00:00
parent df75af3580
commit 224d1b3757
48 changed files with 3512 additions and 7 deletions

View File

@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `item_transaction_history`;
CREATE TABLE IF NOT EXISTS `item_transaction_history` (
`created_time` BIGINT UNSIGNED NOT NULL,
`item_id` INT NOT NULL,
`transaction_type` INT NOT NULL,
`enchant_level` INT NOT NULL,
`price` BIGINT UNSIGNED NOT NULL,
`count` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`created_time`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@ -289,6 +289,12 @@ GridNeighborTurnOffTime = 90
# Default: True
CorrectPrices = True
# Private store review limit.
PrivateStoreReviewLimit = 30
# Private store review cache time in milliseconds.
PrivateStoreReviewCacheTime = 5000
# ---------------------------------------------------------------------------
# Falling Damage

View File

@ -511,6 +511,8 @@ public class Config
public static boolean ALLOW_WEAR;
public static int WEAR_DELAY;
public static int WEAR_PRICE;
public static int STORE_REVIEW_LIMIT;
public static int STORE_REVIEW_CACHE_TIME;
public static int INSTANCE_FINISH_TIME;
public static boolean RESTORE_PLAYER_INSTANCE;
public static int EJECT_DEAD_PLAYER_TIME;
@ -2187,6 +2189,8 @@ public class Config
ALLOW_WEAR = generalConfig.getBoolean("AllowWear", true);
WEAR_DELAY = generalConfig.getInt("WearDelay", 5);
WEAR_PRICE = generalConfig.getInt("WearPrice", 10);
STORE_REVIEW_LIMIT = generalConfig.getInt("PrivateStoreReviewLimit", 30);
STORE_REVIEW_CACHE_TIME = generalConfig.getInt("PrivateStoreReviewCacheTime", 5000);
INSTANCE_FINISH_TIME = generalConfig.getInt("DefaultFinishTime", 5);
RESTORE_PLAYER_INSTANCE = generalConfig.getBoolean("RestorePlayerInstance", false);
EJECT_DEAD_PLAYER_TIME = generalConfig.getInt("EjectDeadPlayerTime", 1);

View File

@ -148,6 +148,7 @@ import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager;
import org.l2jmobius.gameserver.instancemanager.PetitionManager;
import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager;
import org.l2jmobius.gameserver.instancemanager.PremiumManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PunishmentManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager;
import org.l2jmobius.gameserver.instancemanager.RankManager;
@ -294,6 +295,7 @@ public class GameServer
AlchemyData.getInstance();
ItemCommissionManager.getInstance();
WorldExchangeManager.getInstance();
PrivateStoreHistoryManager.getInstance().restore();
LuckyGameData.getInstance();
AttendanceRewardData.getInstance();

View File

@ -125,6 +125,7 @@ public class DailyTaskManager
resetDailyMissionRewards();
resetTimedHuntingZonesWeekly();
resetVitalityWeekly();
resetPrivateStoreHistory();
resetThroneOfHeroesWeekly();
}
else // All days, except Wednesday.
@ -741,6 +742,20 @@ public class DailyTaskManager
LOGGER.info("LimitShopData has been resetted.");
}
public void resetPrivateStoreHistory()
{
try
{
PrivateStoreHistoryManager.getInstance().reset();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Could not reset private store history! " + e);
}
LOGGER.info("Private store history has been resetted.");
}
public static DailyTaskManager getInstance()
{
return SingletonHolder.INSTANCE;

View File

@ -0,0 +1,294 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.l2jmobius.Config;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Mobius
*/
public class PrivateStoreHistoryManager
{
private static final Logger LOGGER = Logger.getLogger(PrivateStoreHistoryManager.class.getName());
private static final String SELECT = "SELECT * FROM item_transaction_history";
private static final String INSERT = "INSERT INTO item_transaction_history (created_time,item_id,transaction_type,enchant_level,price,count) VALUES (?,?,?,?,?,?)";
private static final String TRUNCATE = "TRUNCATE TABLE item_transaction_history";
private static final ArrayList<ItemHistoryTransaction> _items = new ArrayList<>();
public void registerTransaction(PrivateStoreType transactionType, Item item, long count, long price)
{
try
{
final ItemHistoryTransaction historyItem = new ItemHistoryTransaction(transactionType, count, price, item);
_items.add(historyItem);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not store history for item: " + item, e);
}
}
public void restore()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(SELECT))
{
try (ResultSet rs = statement.executeQuery())
{
while (rs.next())
{
final ItemHistoryTransaction item = new ItemHistoryTransaction(rs);
_items.add(item);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not restore history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " items history.");
}
public void reset()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(TRUNCATE))
{
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not reset history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": weekly reset.");
}
public List<ItemHistoryTransaction> getHistory()
{
return getHistory(false);
}
public List<ItemHistoryTransaction> getHistory(boolean full)
{
if (!full)
{
final List<ItemHistoryTransaction> list = new ArrayList<>(_items);
final Map<Integer, Integer> uniqueItemIds = list.stream().map(transaction -> transaction.getItemId()).collect(Collectors.toSet()).stream().collect(Collectors.toMap(Integer::intValue, e -> 0));
list.sort(new SortByDate());
final List<ItemHistoryTransaction> finalList = new ArrayList<>();
for (ItemHistoryTransaction transaction : list)
{
if (uniqueItemIds.get(transaction.getItemId()) < Config.STORE_REVIEW_LIMIT)
{
finalList.add(transaction);
uniqueItemIds.put(transaction.getItemId(), uniqueItemIds.get(transaction.getItemId()) + 1);
}
}
return finalList;
}
return _items;
}
public List<ItemHistoryTransaction> getTopHighestItem()
{
final LinkedList<ItemHistoryTransaction> list = new LinkedList<>(_items);
list.sort(new SortByPrice());
return list;
}
public List<ItemHistoryTransaction> getTopMostItem()
{
final Map<Integer, ItemHistoryTransaction> map = new HashMap<>();
for (ItemHistoryTransaction transaction : _items)
{
if (map.get(transaction.getItemId()) == null)
{
map.put(transaction.getItemId(), new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), 0, false));
}
else
{
map.get(transaction.getItemId()).addCount(transaction.getCount());
}
}
final List<ItemHistoryTransaction> list = new LinkedList<>();
map.forEach((itemID, transaction) -> list.add(transaction));
list.sort(new SortByQuantity());
return list;
}
private static class SortByPrice implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getPrice() > b.getPrice() ? -1 : a.getPrice() == b.getPrice() ? 0 : 1;
}
}
private static class SortByQuantity implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getCount() > b.getCount() ? -1 : a.getCount() == b.getCount() ? 0 : 1;
}
}
private static class SortByDate implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getTransactionDate() > b.getTransactionDate() ? -1 : a.getTransactionDate() == b.getTransactionDate() ? 0 : 1;
}
}
public static class ItemHistoryTransaction
{
private final long _transactionDate;
private final int _itemId;
private final PrivateStoreType _transactionType;
private final int _enchantLevel;
private final long _price;
private long _count;
public ItemHistoryTransaction(ResultSet rs) throws SQLException
{
_transactionDate = rs.getLong("created_time");
_itemId = rs.getInt("item_id");
_transactionType = rs.getInt("transaction_type") == 0 ? PrivateStoreType.SELL : PrivateStoreType.BUY;
_enchantLevel = rs.getInt("enchant_level");
_price = rs.getLong("price");
_count = rs.getLong("count");
}
private ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, Item item)
{
this(transactionType, count, price, item.getId(), item.getEnchantLevel(), true);
}
public ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, int itemId, int enchantLevel, boolean saveToDB)
{
_transactionDate = System.currentTimeMillis();
_itemId = itemId;
_transactionType = transactionType;
_enchantLevel = enchantLevel;
_price = price;
_count = count;
if (saveToDB)
{
storeInDB();
}
}
public long getTransactionDate()
{
return _transactionDate;
}
public PrivateStoreType getTransactionType()
{
return _transactionType;
}
public int getItemId()
{
return _itemId;
}
public int getEnchantLevel()
{
return _enchantLevel;
}
public long getPrice()
{
return _price;
}
public long getCount()
{
return _count;
}
public void addCount(long count)
{
_count += count;
}
private void storeInDB()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(INSERT))
{
ps.setLong(1, _transactionDate);
ps.setInt(2, _itemId);
ps.setInt(3, _transactionType == PrivateStoreType.SELL ? 0 : 1);
ps.setInt(4, _enchantLevel);
ps.setLong(5, _price);
ps.setLong(6, _count);
ps.executeQuery();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "Could not insert history item " + this + " into DB: Reason: " + e.getMessage(), e);
}
}
@Override
public String toString()
{
return _transactionDate + "(" + _transactionType + ")" + "[" + _itemId + " +" + _enchantLevel + " c:" + _count + " p:" + _price + " ]";
}
}
public static PrivateStoreHistoryManager getInstance()
{
return PrivateStoreHistoryManager.SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final PrivateStoreHistoryManager INSTANCE = new PrivateStoreHistoryManager();
}
}

View File

@ -87,6 +87,8 @@ public class World
private static final Map<Integer, Player> _allGoodPlayers = new ConcurrentHashMap<>();
/** Map containing all the Evil players in game. */
private static final Map<Integer, Player> _allEvilPlayers = new ConcurrentHashMap<>();
/** Map containing all the players in Store Buy or Sell mode. */
private static final Map<Integer, Player> _allStoreModeBuySellPlayers = new ConcurrentHashMap<>();
/** Map containing all visible objects. */
private static final Map<Integer, WorldObject> _allObjects = new ConcurrentHashMap<>();
/** Map with the pets instances and their owner ID. */
@ -100,6 +102,8 @@ public class World
private static final WorldRegion[][] _worldRegions = new WorldRegion[REGIONS_X + 1][REGIONS_Y + 1];
private static long _nextPrivateStoreUpdate = 0;
/** Constructor of World. */
protected World()
{
@ -283,6 +287,18 @@ public class World
return _petsInstance.get(ownerId);
}
public synchronized Collection<Player> getSellingOrBuyingPlayers()
{
final long currentTime = System.currentTimeMillis();
if (currentTime > _nextPrivateStoreUpdate)
{
_nextPrivateStoreUpdate = currentTime + Config.STORE_REVIEW_CACHE_TIME;
_allStoreModeBuySellPlayers.clear();
_allPlayers.values().stream().filter(Player::isInStoreSellOrBuyMode).forEach(player -> _allStoreModeBuySellPlayers.put(player.getObjectId(), player));
}
return _allStoreModeBuySellPlayers.values();
}
/**
* Add the given pet instance from the given ownerId.
* @param ownerId ID of the owner

View File

@ -1360,6 +1360,11 @@ public class Player extends Playable
return _privateStoreType != PrivateStoreType.NONE;
}
public boolean isInStoreSellOrBuyMode()
{
return (_privateStoreType == PrivateStoreType.BUY) || (_privateStoreType == PrivateStoreType.SELL) || (_privateStoreType == PrivateStoreType.PACKAGE_SELL);
}
public boolean isCrafting()
{
return _isCrafting;

View File

@ -178,6 +178,8 @@ import org.l2jmobius.gameserver.network.clientpackets.shuttle.CannotMoveAnymoreI
import org.l2jmobius.gameserver.network.clientpackets.shuttle.MoveToLocationInShuttle;
import org.l2jmobius.gameserver.network.clientpackets.shuttle.RequestShuttleGetOff;
import org.l2jmobius.gameserver.network.clientpackets.shuttle.RequestShuttleGetOn;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchStatistics;
import org.l2jmobius.gameserver.network.clientpackets.teleports.ExRequestTeleport;
import org.l2jmobius.gameserver.network.clientpackets.teleports.ExRequestTeleportFavoriteList;
import org.l2jmobius.gameserver.network.clientpackets.teleports.ExRequestTeleportFavoritesAddDel;
@ -735,8 +737,8 @@ public enum ExClientPackets
EX_WORLDCASTLEWAR_PLEDGE_MERCENARY_MEMBER_JOIN(0x211, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_TELEPORT(0x212, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_OBSERVER_START(0x213, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x214, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x215, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x214, ExRequestPrivateStoreSearchList::new, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x215, ExRequestPrivateStoreSearchStatistics::new, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_HOST_CASTLE_SIEGE_RANKING_INFO(0x216, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_CASTLE_SIEGE_RANKING_INFO(0x217, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_SIEGE_MAINBATTLE_HUD_INFO(0x218, null, ConnectionState.IN_GAME),

View File

@ -0,0 +1,519 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.l2jmobius.commons.network.ReadablePacket;
import org.l2jmobius.gameserver.data.xml.CollectionData;
import org.l2jmobius.gameserver.data.xml.EnsoulData;
import org.l2jmobius.gameserver.data.xml.HennaData;
import org.l2jmobius.gameserver.data.xml.VariationData;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.handler.IItemHandler;
import org.l2jmobius.gameserver.handler.ItemHandler;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.TradeItem;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.CollectionDataHolder;
import org.l2jmobius.gameserver.model.holders.ItemEnchantHolder;
import org.l2jmobius.gameserver.model.item.EtcItem;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchHistory;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchItem;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchList implements ClientPacket
{
public static final int MAX_ITEM_PER_PAGE = 120;
private String _searchWord;
private StoreType _storeType;
private StoreItemType _itemType;
private StoreSubItemType _itemSubtype;
private int _searchCollection;
@Override
public void read(ReadablePacket packet)
{
_searchWord = packet.readSizedString();
_storeType = StoreType.findById(packet.readByte());
_itemType = StoreItemType.findById(packet.readByte());
_itemSubtype = StoreSubItemType.findById(packet.readByte());
_searchCollection = packet.readByte();
}
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
final Collection<Player> stores = World.getInstance().getSellingOrBuyingPlayers();
final List<ShopItem> items = new ArrayList<>();
final List<Integer> itemIds = new ArrayList<>();
stores.forEach(vendor ->
{
for (TradeItem item : vendor.getPrivateStoreType() == PrivateStoreType.BUY ? vendor.getBuyList().getItems() : vendor.getSellList().getItems())
{
// Filter by storeType.
if ((_storeType == StoreType.ALL) || ((_storeType == StoreType.SELL) && ((vendor.getPrivateStoreType() == PrivateStoreType.SELL) || (vendor.getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL))) || ((_storeType == StoreType.BUY) && (vendor.getPrivateStoreType() == PrivateStoreType.BUY)))
{
if (isItemVisibleForShop(item))
{
// Filter by Word if supplied.
if (_searchWord.equals("") || (!_searchWord.equals("") && (item.getItem().getName().toLowerCase().contains(_searchWord.toLowerCase()))))
{
items.add(new ShopItem(item, vendor, vendor.getPrivateStoreType()));
itemIds.add(item.getItem().getId());
}
}
}
}
});
int nSize = items.size();
int maxPage = Math.max(1, (nSize / (MAX_ITEM_PER_PAGE * 1F)) > (nSize / MAX_ITEM_PER_PAGE) ? (nSize / MAX_ITEM_PER_PAGE) + 1 : nSize / MAX_ITEM_PER_PAGE);
for (int page = 1; page <= maxPage; page++)
{
final int nsize = page == maxPage ? ((nSize % MAX_ITEM_PER_PAGE) > 0) || (nSize == 0) ? nSize % MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE;
player.sendPacket(new ExPrivateStoreSearchItem(page, maxPage, nsize, items));
}
final List<ItemHistoryTransaction> history = new ArrayList<>();
final List<ItemHistoryTransaction> historyTemp = new ArrayList<>();
PrivateStoreHistoryManager.getInstance().getHistory().forEach(transaction ->
{
if (itemIds.contains(transaction.getItemId()))
{
history.add(new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), transaction.getEnchantLevel(), false));
}
});
int page = 1;
maxPage = Math.max(1, (history.size() / (MAX_ITEM_PER_PAGE * 1F)) > (history.size() / MAX_ITEM_PER_PAGE) ? (history.size() / MAX_ITEM_PER_PAGE) + 1 : history.size() / MAX_ITEM_PER_PAGE);
for (int index = 0; index < history.size(); index++)
{
historyTemp.add(history.get(index));
if ((index == (history.size() - 1)) || (index == (MAX_ITEM_PER_PAGE - 1)) || ((index > 0) && ((index % (MAX_ITEM_PER_PAGE - 1)) == 0)))
{
player.sendPacket(new ExPrivateStoreSearchHistory(page, maxPage, historyTemp));
page++;
historyTemp.clear();
}
}
if (page == 1)
{
player.sendPacket(new ExPrivateStoreSearchHistory(1, 1, historyTemp));
}
}
private boolean isItemVisibleForShop(TradeItem item)
{
/**
* Equipement
*/
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Equipment - All
return item.getItem().isEquipable();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.WEAPON) && (_searchCollection == 0))
{
// Equipment - Weapon
return item.getItem().isEquipable() && item.getItem().isWeapon();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ARMOR) && (_searchCollection == 0))
{
// Equipment - Armor
return item.getItem().isEquipable() && isEquipmentArmor(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ACCESSORY) && (_searchCollection == 0))
{
// Equipment - Accessory
return item.getItem().isEquipable() && isAccessory(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.EQUIPMENT_MISC) && (_searchCollection == 0))
{
// Equipment - Misc
return item.getItem().isEquipable() && !item.getItem().isWeapon() && !isEquipmentArmor(item.getItem()) && !isAccessory(item.getItem());
}
/**
* Exping / Enhancement
*/
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Exping / Enhancement - All
return isEnhancementItem(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENCHANT_SCROLL) && (_searchCollection == 0))
{
// Exping / Enhancement - Enchant Scroll
return isEnchantScroll(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.CRYSTAL) && (_searchCollection == 0))
{
// Exping / Enhancement - Crystal
return isCrystal(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.LIFE_STONE) && (_searchCollection == 0))
{
// Exping / Enhancement - Life Stone
return isLifeStone(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.DYES) && (_searchCollection == 0))
{
// Exping / Enhancement - Dyes
return isDye(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.SPELLBOOK) && (_searchCollection == 0))
{
// Exping / Enhancement - SpellBooks
return isSpellBook(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENHANCEMENT_MISC) && (_searchCollection == 0))
{
// Exping / Enhancement - Misc
return isEnhancementMisc(item.getItem());
}
/**
* Groceries
*/
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Groceries - All
return item.getItem().isPotion() || item.getItem().isScroll() || isTicket(item.getItem()) || isPackOrCraft(item.getItem()) || isGroceryMisc(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.POTION_SCROLL) && (_searchCollection == 0))
{
// Groceries - Potion/Scroll
return item.getItem().isPotion() || item.getItem().isScroll();
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.TICKET) && (_searchCollection == 0))
{
// Groceries - Ticket
return isTicket(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.PACK_CRAFT) && (_searchCollection == 0))
{
// Groceries - Pack/Craft
return isPackOrCraft(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.GROCERY_MISC) && (_searchCollection == 0))
{
// Groceries - Misc
return isGroceryMisc(item.getItem());
}
/**
* Collections
*/
if ((_itemType == StoreItemType.ALL) && (_searchCollection == 1))
{
// Collections - All
return isCollection(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_searchCollection == 1))
{
// Collections - Equipement
return isCollectionEquipement(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_searchCollection == 1))
{
// Collections - Enchanted Item
return isCollectionEnchanted(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_searchCollection == 1))
{
// Collections - Misc
return isCollectionMisc(item.getItem());
}
return true;
}
private boolean isEquipmentArmor(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == ItemTemplate.SLOT_CHEST) || (item.getBodyPart() == ItemTemplate.SLOT_FULL_ARMOR) || (item.getBodyPart() == ItemTemplate.SLOT_HEAD) || (item.getBodyPart() == ItemTemplate.SLOT_LEGS) || (item.getBodyPart() == ItemTemplate.SLOT_FEET) || (item.getBodyPart() == ItemTemplate.SLOT_GLOVES) || (item.getBodyPart() == (ItemTemplate.SLOT_CHEST | ItemTemplate.SLOT_LEGS)));
}
private boolean isAccessory(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == (ItemTemplate.SLOT_L_BRACELET | ItemTemplate.SLOT_R_BRACELET | ItemTemplate.SLOT_BROOCH)) || (item.getBodyPart() == (ItemTemplate.SLOT_R_FINGER | ItemTemplate.SLOT_L_FINGER)) || (item.getBodyPart() == ItemTemplate.SLOT_NECK) || (item.getBodyPart() == (ItemTemplate.SLOT_R_EAR | ItemTemplate.SLOT_L_EAR)));
}
private boolean isEnchantScroll(ItemTemplate item)
{
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("EnchantScrolls");
}
private boolean isCrystal(ItemTemplate item)
{
return EnsoulData.getInstance().getStone(item.getId()) != null;
}
private boolean isLifeStone(ItemTemplate item)
{
return VariationData.getInstance().getVariation(item.getId()) != null;
}
private boolean isDye(ItemTemplate item)
{
return HennaData.getInstance().getHenna(item.getId()) != null;
}
private boolean isSpellBook(ItemTemplate item)
{
return item.getName().contains("Spellbook: ");
}
private boolean isEnhancementMisc(ItemTemplate item)
{
return (item.getId() >= 91031) && (item.getId() <= 91038);
}
private boolean isEnhancementItem(ItemTemplate item)
{
return isEnchantScroll(item) || isCrystal(item) || isLifeStone(item) || isDye(item) || isSpellBook(item) || isEnhancementMisc(item);
}
private boolean isTicket(ItemTemplate item)
{
return (item.getId() == 90045) || (item.getId() == 91462) || (item.getId() == 91463) || (item.getId() == 91972) || (item.getId() == 93903);
}
private boolean isPackOrCraft(ItemTemplate item)
{
if ((item.getId() == 92477) || (item.getId() == 91462) || (item.getId() == 92478) || (item.getId() == 92479) || (item.getId() == 92480) || (item.getId() == 92481) || (item.getId() == 49756) || (item.getId() == 93906) || (item.getId() == 93907) || (item.getId() == 93908) || (item.getId() == 93909) || (item.getId() == 93910) || (item.getId() == 91076))
{
return true;
}
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("ExtractableItems");
}
private boolean isGroceryMisc(ItemTemplate item)
{
// Kinda fallback trash category to ensure no skipping any items.
return !item.isEquipable() && !isEnhancementItem(item) && !isCollection(item) && !item.isPotion() && !item.isScroll() && !isTicket(item) && !isPackOrCraft(item);
}
private boolean isCollection(ItemTemplate item)
{
for (CollectionDataHolder collectionHolder : CollectionData.getInstance().getCollections())
{
for (ItemEnchantHolder itemData : collectionHolder.getItems())
{
if (itemData.getId() == item.getId())
{
return true;
}
}
}
return false;
}
private boolean isCollectionEquipement(ItemTemplate item)
{
return isCollection(item) && item.isEquipable();
}
private boolean isCollectionEnchanted(ItemTemplate item)
{
return isCollection(item) && item.getName().contains("Spellbook: ");
}
private boolean isCollectionMisc(ItemTemplate item)
{
return (item.getId() >= 93906) && (item.getId() <= 93910);
}
private enum StoreType
{
SELL((byte) 0x00),
BUY((byte) 0x01),
ALL((byte) 0x03);
private final byte _storeType;
StoreType(byte storeType)
{
_storeType = storeType;
}
public static StoreType findById(int id)
{
for (StoreType storeType : values())
{
if (storeType.getValue() == id)
{
return storeType;
}
}
return null;
}
public byte getValue()
{
return _storeType;
}
}
private enum StoreItemType
{
ALL((byte) 0xFF),
EQUIPMENT((byte) 0x00),
ENHANCEMENT_OR_EXPING((byte) 0x02),
GROCERY_OR_COLLECTION_MISC((byte) 0x04);
private final byte _storeItemType;
StoreItemType(byte storeItemType)
{
_storeItemType = storeItemType;
}
public static StoreItemType findById(int id)
{
for (StoreItemType storeItemType : values())
{
if (storeItemType.getValue() == id)
{
return storeItemType;
}
}
return null;
}
public byte getValue()
{
return _storeItemType;
}
}
private enum StoreSubItemType
{
ALL((byte) 0xFF),
WEAPON((byte) 0x00),
ARMOR((byte) 0x01),
ACCESSORY((byte) 0x02),
EQUIPMENT_MISC((byte) 0x03),
ENCHANT_SCROLL((byte) 0x08),
LIFE_STONE((byte) 0x0F),
DYES((byte) 0x10),
CRYSTAL((byte) 0x11),
SPELLBOOK((byte) 0x12),
ENHANCEMENT_MISC((byte) 0x13),
POTION_SCROLL((byte) 0x14),
TICKET((byte) 0x15),
PACK_CRAFT((byte) 0x16),
GROCERY_MISC((byte) 0x18);
private final byte _storeSubItemType;
StoreSubItemType(byte storeSubItemType)
{
_storeSubItemType = storeSubItemType;
}
public static StoreSubItemType findById(int id)
{
for (StoreSubItemType storeSubItemType : values())
{
if (storeSubItemType.getValue() == id)
{
return storeSubItemType;
}
}
return null;
}
public byte getValue()
{
return _storeSubItemType;
}
}
public static class ShopItem
{
private final TradeItem _item;
private final Player _owner;
private final PrivateStoreType _storeType;
private ShopItem(TradeItem item, Player owner, PrivateStoreType storeType)
{
_item = item;
_owner = owner;
_storeType = storeType;
}
public long getCount()
{
return _item.getCount();
}
public ItemInfo getItemInfo()
{
return new ItemInfo(_item);
}
public Player getOwner()
{
return _owner;
}
public PrivateStoreType getStoreType()
{
return _storeType;
}
public long getPrice()
{
return _item.getPrice();
}
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.RequestPrivateStoreSearchStatistics;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchStatistics implements ClientPacket
{
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
player.sendPacket(new RequestPrivateStoreSearchStatistics());
}
}

View File

@ -308,4 +308,61 @@ public abstract class AbstractItemPacket extends AbstractMaskPacket<ItemListType
writeShort(0);
}
}
protected int calculatePacketSize(ItemInfo item)
{
final int mask = calculateMask(item);
int size = 0;
size += 2; // writeShort(mask);
size += 4; // writeInt(item.getObjectId()); // ObjectId
size += 4; // writeInt(item.getItem().getDisplayId()); // ItemId
size += 1; // writeByte(item.getItem().isQuestItem() || (item.getEquipped() == 1) ? 0xFF : item.getLocation()); // T1
size += 8; // writeLong(item.getCount()); // Quantity
size += 1; // writeByte(item.getItem().getType2()); // Item Type 2 : 00-weapon, 01-shield/armor, 02-ring/earring/necklace, 03-questitem, 04-adena, 05-item
size += 1; // writeByte(item.getCustomType1()); // Filler (always 0)
size += 2; // writeShort(item.getEquipped()); // Equipped : 00-No, 01-yes
size += 8; // writeLong(item.getItem().getBodyPart()); // Slot : 0006-lr.ear, 0008-neck, 0030-lr.finger, 0040-head, 0100-l.hand, 0200-gloves, 0400-chest, 0800-pants, 1000-feet, 4000-r.hand, 8000-r.hand
size += 2; // writeShort(item.getEnchantLevel()); // Enchant level (pet level shown in control item)
size += 4; // writeInt(item.getMana());
size += 1; // writeByte(0); // 270 protocol
size += 4; // writeInt(item.getTime());
size += 1; // writeByte(item.isAvailable()); // GOD Item enabled = 1 disabled (red) = 0
size += 2; // writeShort(0); // 140 - locked
if (containsMask(mask, ItemListType.AUGMENT_BONUS))
{
size += 8;
}
if (containsMask(mask, ItemListType.ELEMENTAL_ATTRIBUTE))
{
size += 16;
}
// 362 - Removed
// if (containsMask(mask, ItemListType.ENCHANT_EFFECT))
// {
// size += (item.getEnchantOptions().length * 4);
// }
if (containsMask(mask, ItemListType.VISUAL_ID))
{
size += 4;
}
if (containsMask(mask, ItemListType.SOUL_CRYSTAL))
{
size += 1;
size += (item.getSoulCrystalOptions().size() * 4);
size += 1;
size += (item.getSoulCrystalSpecialOptions().size() * 4);
}
// TODO:
// if (containsMask(mask, ItemListType.REUSE_DELAY))
// {
// size += 4;
// }
if (containsMask(mask, ItemListType.BLESSED))
{
size += 1;
}
return size;
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchHistory extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final List<ItemHistoryTransaction> _history;
public ExPrivateStoreSearchHistory(int page, int maxPage, List<ItemHistoryTransaction> history)
{
_page = page;
_maxPage = maxPage;
_history = history;
}
/**
* 338 struct _S_EX_PRIVATE_STORE_SEARCH_HISTORY { var int cCurrentPage; var int cMaxPage; var array<_pkPSSearchHistory> histories; }; struct _pkPSSearchHistory { var int nClassID; var int cStoreType; var int cEnchant; var INT64 nPrice; var INT64 nAmount; }; // S: FE D502 01 - cPage 01 -
* cMaxPage E6000000 - nSize nClassID cStoreType cEnchant nPrice nAmount 4E000000 00 00 7F96980000000000 0100000000000000 4F000000 00 00 7F96980000000000 0100000000000000 5B000000 00 00 80C3C90100000000 0100000000000000 62000000 00 00 002D310100000000 0100000000000000 6E000000 00 00
* 80841E0000000000 0100000000000000 C6000000 00 00 FF117A0000000000 0100000000000000
*/
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_HISTORY.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_history.size()); // nSize -> Items count for loop below
for (int i = 0; i < _history.size(); i++)
{
final ItemHistoryTransaction transaction = _history.get(i);
writeInt(transaction.getItemId()); // itemId
writeByte(transaction.getTransactionType() == PrivateStoreType.SELL ? 0x00 : 0x01); // cStoreType
writeByte(transaction.getEnchantLevel()); // cEnchant
writeLong(transaction.getPrice() / transaction.getCount()); // nPrice
writeLong(transaction.getCount()); // nAmount
}
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList.ShopItem;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchItem extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final int _nSize;
private final List<ShopItem> _items;
public ExPrivateStoreSearchItem(int page, int maxPage, int nSize, List<ShopItem> items)
{
_page = page;
_maxPage = maxPage;
_nSize = nSize;
_items = items;
}
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_ITEM.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_nSize); // nSize
if (_nSize > 0)
{
for (int itemIndex = (_page - 1) * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE; (itemIndex < (_page * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE)) && (itemIndex < _items.size()); itemIndex++)
{
final ShopItem shopItem = _items.get(itemIndex);
writeSizedString(shopItem.getOwner().getName()); // Vendor name
writeByte(shopItem.getStoreType() == PrivateStoreType.PACKAGE_SELL ? 0x02 : shopItem.getStoreType() == PrivateStoreType.SELL ? 0x00 : 0x01); // store type (maybe "sold"/buy/Package (translated as Total Score...))
writeLong(shopItem.getPrice()); // Price
writeInt(shopItem.getOwner().getX()); // X
writeInt(shopItem.getOwner().getY()); // Y
writeInt(shopItem.getOwner().getZ()); // Z
writeInt(calculatePacketSize(shopItem.getItemInfo() /* , shopItem.getCount() */)); // size
writeItem(shopItem.getItemInfo(), shopItem.getCount()); // itemAssemble
}
}
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.item.instance.Item;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class RequestPrivateStoreSearchStatistics extends AbstractItemPacket
{
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_STATISTICS.writeId(this);
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> mostItems = PrivateStoreHistoryManager.getInstance().getTopMostItem();
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> highestItems = PrivateStoreHistoryManager.getInstance().getTopHighestItem();
writeInt(Math.min(mostItems.size(), 5));
for (int i = 0; i < Math.min(mostItems.size(), 5); i++)
{
writeInt((int) mostItems.get(i).getCount());
ItemInfo itemInfo = new ItemInfo(new Item(mostItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , mostItems.get(i).getCount() */));
writeItem(itemInfo, mostItems.get(i).getCount());
}
writeInt(Math.min(highestItems.size(), 5));
for (int i = 0; i < Math.min(highestItems.size(), 5); i++)
{
writeLong(highestItems.get(i).getPrice());
ItemInfo itemInfo = new ItemInfo(new Item(highestItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , highestItems.get(i).getCount() */));
writeItem(itemInfo, highestItems.get(i).getCount());
}
}
}

View File

@ -133,6 +133,9 @@ Master Class Chapter 2: https://eu.4game.com/patchnotes/lineage2/374/
Master Class Chapter 3: https://eu.4game.com/patchnotes/lineage2/417/
-Tales of Hero system
-Private shop review
-World exchange system
-Chest and box auto-use
Events:
-Birth of Draco

View File

@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `item_transaction_history`;
CREATE TABLE IF NOT EXISTS `item_transaction_history` (
`created_time` BIGINT UNSIGNED NOT NULL,
`item_id` INT NOT NULL,
`transaction_type` INT NOT NULL,
`enchant_level` INT NOT NULL,
`price` BIGINT UNSIGNED NOT NULL,
`count` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`created_time`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@ -289,6 +289,12 @@ GridNeighborTurnOffTime = 90
# Default: True
CorrectPrices = True
# Private store review limit.
PrivateStoreReviewLimit = 30
# Private store review cache time in milliseconds.
PrivateStoreReviewCacheTime = 5000
# ---------------------------------------------------------------------------
# Falling Damage

View File

@ -543,6 +543,8 @@ public class Config
public static boolean ALLOW_WEAR;
public static int WEAR_DELAY;
public static int WEAR_PRICE;
public static int STORE_REVIEW_LIMIT;
public static int STORE_REVIEW_CACHE_TIME;
public static int INSTANCE_FINISH_TIME;
public static boolean RESTORE_PLAYER_INSTANCE;
public static int EJECT_DEAD_PLAYER_TIME;
@ -2257,6 +2259,8 @@ public class Config
ALLOW_WEAR = generalConfig.getBoolean("AllowWear", true);
WEAR_DELAY = generalConfig.getInt("WearDelay", 5);
WEAR_PRICE = generalConfig.getInt("WearPrice", 10);
STORE_REVIEW_LIMIT = generalConfig.getInt("PrivateStoreReviewLimit", 30);
STORE_REVIEW_CACHE_TIME = generalConfig.getInt("PrivateStoreReviewCacheTime", 5000);
INSTANCE_FINISH_TIME = generalConfig.getInt("DefaultFinishTime", 5);
RESTORE_PLAYER_INSTANCE = generalConfig.getBoolean("RestorePlayerInstance", false);
EJECT_DEAD_PLAYER_TIME = generalConfig.getInt("EjectDeadPlayerTime", 1);

View File

@ -155,6 +155,7 @@ import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager;
import org.l2jmobius.gameserver.instancemanager.PetitionManager;
import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager;
import org.l2jmobius.gameserver.instancemanager.PremiumManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PunishmentManager;
import org.l2jmobius.gameserver.instancemanager.PurgeRankingManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager;
@ -317,6 +318,7 @@ public class GameServer
AppearanceItemData.getInstance();
ItemCommissionManager.getInstance();
WorldExchangeManager.getInstance();
PrivateStoreHistoryManager.getInstance().restore();
LuckyGameData.getInstance();
AttendanceRewardData.getInstance();
MagicLampData.getInstance();

View File

@ -118,6 +118,7 @@ public class DailyTaskManager
resetMonsterArenaWeekly();
resetTimedHuntingZonesWeekly();
resetVitalityWeekly();
resetPrivateStoreHistory();
}
else // All days, except Wednesday.
{
@ -707,6 +708,20 @@ public class DailyTaskManager
LOGGER.info("Daily payment resurrection count for player has been resetted.");
}
public void resetPrivateStoreHistory()
{
try
{
PrivateStoreHistoryManager.getInstance().reset();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Could not reset private store history! " + e);
}
LOGGER.info("Private store history has been resetted.");
}
private void resetDailyHennaPattern()
{
// Update data for offline players.

View File

@ -0,0 +1,294 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.l2jmobius.Config;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Mobius
*/
public class PrivateStoreHistoryManager
{
private static final Logger LOGGER = Logger.getLogger(PrivateStoreHistoryManager.class.getName());
private static final String SELECT = "SELECT * FROM item_transaction_history";
private static final String INSERT = "INSERT INTO item_transaction_history (created_time,item_id,transaction_type,enchant_level,price,count) VALUES (?,?,?,?,?,?)";
private static final String TRUNCATE = "TRUNCATE TABLE item_transaction_history";
private static final ArrayList<ItemHistoryTransaction> _items = new ArrayList<>();
public void registerTransaction(PrivateStoreType transactionType, Item item, long count, long price)
{
try
{
final ItemHistoryTransaction historyItem = new ItemHistoryTransaction(transactionType, count, price, item);
_items.add(historyItem);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not store history for item: " + item, e);
}
}
public void restore()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(SELECT))
{
try (ResultSet rs = statement.executeQuery())
{
while (rs.next())
{
final ItemHistoryTransaction item = new ItemHistoryTransaction(rs);
_items.add(item);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not restore history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " items history.");
}
public void reset()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(TRUNCATE))
{
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not reset history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": weekly reset.");
}
public List<ItemHistoryTransaction> getHistory()
{
return getHistory(false);
}
public List<ItemHistoryTransaction> getHistory(boolean full)
{
if (!full)
{
final List<ItemHistoryTransaction> list = new ArrayList<>(_items);
final Map<Integer, Integer> uniqueItemIds = list.stream().map(transaction -> transaction.getItemId()).collect(Collectors.toSet()).stream().collect(Collectors.toMap(Integer::intValue, e -> 0));
list.sort(new SortByDate());
final List<ItemHistoryTransaction> finalList = new ArrayList<>();
for (ItemHistoryTransaction transaction : list)
{
if (uniqueItemIds.get(transaction.getItemId()) < Config.STORE_REVIEW_LIMIT)
{
finalList.add(transaction);
uniqueItemIds.put(transaction.getItemId(), uniqueItemIds.get(transaction.getItemId()) + 1);
}
}
return finalList;
}
return _items;
}
public List<ItemHistoryTransaction> getTopHighestItem()
{
final LinkedList<ItemHistoryTransaction> list = new LinkedList<>(_items);
list.sort(new SortByPrice());
return list;
}
public List<ItemHistoryTransaction> getTopMostItem()
{
final Map<Integer, ItemHistoryTransaction> map = new HashMap<>();
for (ItemHistoryTransaction transaction : _items)
{
if (map.get(transaction.getItemId()) == null)
{
map.put(transaction.getItemId(), new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), 0, false));
}
else
{
map.get(transaction.getItemId()).addCount(transaction.getCount());
}
}
final List<ItemHistoryTransaction> list = new LinkedList<>();
map.forEach((itemID, transaction) -> list.add(transaction));
list.sort(new SortByQuantity());
return list;
}
private static class SortByPrice implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getPrice() > b.getPrice() ? -1 : a.getPrice() == b.getPrice() ? 0 : 1;
}
}
private static class SortByQuantity implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getCount() > b.getCount() ? -1 : a.getCount() == b.getCount() ? 0 : 1;
}
}
private static class SortByDate implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getTransactionDate() > b.getTransactionDate() ? -1 : a.getTransactionDate() == b.getTransactionDate() ? 0 : 1;
}
}
public static class ItemHistoryTransaction
{
private final long _transactionDate;
private final int _itemId;
private final PrivateStoreType _transactionType;
private final int _enchantLevel;
private final long _price;
private long _count;
public ItemHistoryTransaction(ResultSet rs) throws SQLException
{
_transactionDate = rs.getLong("created_time");
_itemId = rs.getInt("item_id");
_transactionType = rs.getInt("transaction_type") == 0 ? PrivateStoreType.SELL : PrivateStoreType.BUY;
_enchantLevel = rs.getInt("enchant_level");
_price = rs.getLong("price");
_count = rs.getLong("count");
}
private ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, Item item)
{
this(transactionType, count, price, item.getId(), item.getEnchantLevel(), true);
}
public ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, int itemId, int enchantLevel, boolean saveToDB)
{
_transactionDate = System.currentTimeMillis();
_itemId = itemId;
_transactionType = transactionType;
_enchantLevel = enchantLevel;
_price = price;
_count = count;
if (saveToDB)
{
storeInDB();
}
}
public long getTransactionDate()
{
return _transactionDate;
}
public PrivateStoreType getTransactionType()
{
return _transactionType;
}
public int getItemId()
{
return _itemId;
}
public int getEnchantLevel()
{
return _enchantLevel;
}
public long getPrice()
{
return _price;
}
public long getCount()
{
return _count;
}
public void addCount(long count)
{
_count += count;
}
private void storeInDB()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(INSERT))
{
ps.setLong(1, _transactionDate);
ps.setInt(2, _itemId);
ps.setInt(3, _transactionType == PrivateStoreType.SELL ? 0 : 1);
ps.setInt(4, _enchantLevel);
ps.setLong(5, _price);
ps.setLong(6, _count);
ps.executeQuery();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "Could not insert history item " + this + " into DB: Reason: " + e.getMessage(), e);
}
}
@Override
public String toString()
{
return _transactionDate + "(" + _transactionType + ")" + "[" + _itemId + " +" + _enchantLevel + " c:" + _count + " p:" + _price + " ]";
}
}
public static PrivateStoreHistoryManager getInstance()
{
return PrivateStoreHistoryManager.SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final PrivateStoreHistoryManager INSTANCE = new PrivateStoreHistoryManager();
}
}

View File

@ -86,6 +86,8 @@ public class World
private static final Map<Integer, Player> _allGoodPlayers = new ConcurrentHashMap<>();
/** Map containing all the Evil players in game. */
private static final Map<Integer, Player> _allEvilPlayers = new ConcurrentHashMap<>();
/** Map containing all the players in Store Buy or Sell mode. */
private static final Map<Integer, Player> _allStoreModeBuySellPlayers = new ConcurrentHashMap<>();
/** Map containing all visible objects. */
private static final Map<Integer, WorldObject> _allObjects = new ConcurrentHashMap<>();
/** Map with the pets instances and their owner ID. */
@ -96,6 +98,8 @@ public class World
private static final WorldRegion[][] _worldRegions = new WorldRegion[REGIONS_X + 1][REGIONS_Y + 1];
private static long _nextPrivateStoreUpdate = 0;
/** Constructor of World. */
protected World()
{
@ -279,6 +283,18 @@ public class World
return _petsInstance.get(ownerId);
}
public synchronized Collection<Player> getSellingOrBuyingPlayers()
{
final long currentTime = System.currentTimeMillis();
if (currentTime > _nextPrivateStoreUpdate)
{
_nextPrivateStoreUpdate = currentTime + Config.STORE_REVIEW_CACHE_TIME;
_allStoreModeBuySellPlayers.clear();
_allPlayers.values().stream().filter(Player::isInStoreSellOrBuyMode).forEach(player -> _allStoreModeBuySellPlayers.put(player.getObjectId(), player));
}
return _allStoreModeBuySellPlayers.values();
}
/**
* Add the given pet instance from the given ownerId.
* @param ownerId ID of the owner

View File

@ -1408,6 +1408,11 @@ public class Player extends Playable
return _privateStoreType != PrivateStoreType.NONE;
}
public boolean isInStoreSellOrBuyMode()
{
return (_privateStoreType == PrivateStoreType.BUY) || (_privateStoreType == PrivateStoreType.SELL) || (_privateStoreType == PrivateStoreType.PACKAGE_SELL);
}
public boolean isCrafting()
{
return _isCrafting;

View File

@ -189,6 +189,8 @@ import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyBox
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyGetReward;
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyOpenBox;
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyOpenSlot;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchStatistics;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationGacha;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationGachaUI;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationList;
@ -756,8 +758,8 @@ public enum ExClientPackets
EX_WORLDCASTLEWAR_PLEDGE_MERCENARY_MEMBER_JOIN(0x210, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_TELEPORT(0x211, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_OBSERVER_START(0x212, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x213, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x214, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x213, ExRequestPrivateStoreSearchList::new, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x214, ExRequestPrivateStoreSearchStatistics::new, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_HOST_CASTLE_SIEGE_RANKING_INFO(0x215, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_CASTLE_SIEGE_RANKING_INFO(0x216, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_SIEGE_MAINBATTLE_HUD_INFO(0x217, null, ConnectionState.IN_GAME),

View File

@ -0,0 +1,519 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.l2jmobius.commons.network.ReadablePacket;
import org.l2jmobius.gameserver.data.xml.CollectionData;
import org.l2jmobius.gameserver.data.xml.EnsoulData;
import org.l2jmobius.gameserver.data.xml.HennaData;
import org.l2jmobius.gameserver.data.xml.VariationData;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.handler.IItemHandler;
import org.l2jmobius.gameserver.handler.ItemHandler;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.TradeItem;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.CollectionDataHolder;
import org.l2jmobius.gameserver.model.holders.ItemEnchantHolder;
import org.l2jmobius.gameserver.model.item.EtcItem;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchHistory;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchItem;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchList implements ClientPacket
{
public static final int MAX_ITEM_PER_PAGE = 120;
private String _searchWord;
private StoreType _storeType;
private StoreItemType _itemType;
private StoreSubItemType _itemSubtype;
private int _searchCollection;
@Override
public void read(ReadablePacket packet)
{
_searchWord = packet.readSizedString();
_storeType = StoreType.findById(packet.readByte());
_itemType = StoreItemType.findById(packet.readByte());
_itemSubtype = StoreSubItemType.findById(packet.readByte());
_searchCollection = packet.readByte();
}
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
final Collection<Player> stores = World.getInstance().getSellingOrBuyingPlayers();
final List<ShopItem> items = new ArrayList<>();
final List<Integer> itemIds = new ArrayList<>();
stores.forEach(vendor ->
{
for (TradeItem item : vendor.getPrivateStoreType() == PrivateStoreType.BUY ? vendor.getBuyList().getItems() : vendor.getSellList().getItems())
{
// Filter by storeType.
if ((_storeType == StoreType.ALL) || ((_storeType == StoreType.SELL) && ((vendor.getPrivateStoreType() == PrivateStoreType.SELL) || (vendor.getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL))) || ((_storeType == StoreType.BUY) && (vendor.getPrivateStoreType() == PrivateStoreType.BUY)))
{
if (isItemVisibleForShop(item))
{
// Filter by Word if supplied.
if (_searchWord.equals("") || (!_searchWord.equals("") && (item.getItem().getName().toLowerCase().contains(_searchWord.toLowerCase()))))
{
items.add(new ShopItem(item, vendor, vendor.getPrivateStoreType()));
itemIds.add(item.getItem().getId());
}
}
}
}
});
int nSize = items.size();
int maxPage = Math.max(1, (nSize / (MAX_ITEM_PER_PAGE * 1F)) > (nSize / MAX_ITEM_PER_PAGE) ? (nSize / MAX_ITEM_PER_PAGE) + 1 : nSize / MAX_ITEM_PER_PAGE);
for (int page = 1; page <= maxPage; page++)
{
final int nsize = page == maxPage ? ((nSize % MAX_ITEM_PER_PAGE) > 0) || (nSize == 0) ? nSize % MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE;
player.sendPacket(new ExPrivateStoreSearchItem(page, maxPage, nsize, items));
}
final List<ItemHistoryTransaction> history = new ArrayList<>();
final List<ItemHistoryTransaction> historyTemp = new ArrayList<>();
PrivateStoreHistoryManager.getInstance().getHistory().forEach(transaction ->
{
if (itemIds.contains(transaction.getItemId()))
{
history.add(new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), transaction.getEnchantLevel(), false));
}
});
int page = 1;
maxPage = Math.max(1, (history.size() / (MAX_ITEM_PER_PAGE * 1F)) > (history.size() / MAX_ITEM_PER_PAGE) ? (history.size() / MAX_ITEM_PER_PAGE) + 1 : history.size() / MAX_ITEM_PER_PAGE);
for (int index = 0; index < history.size(); index++)
{
historyTemp.add(history.get(index));
if ((index == (history.size() - 1)) || (index == (MAX_ITEM_PER_PAGE - 1)) || ((index > 0) && ((index % (MAX_ITEM_PER_PAGE - 1)) == 0)))
{
player.sendPacket(new ExPrivateStoreSearchHistory(page, maxPage, historyTemp));
page++;
historyTemp.clear();
}
}
if (page == 1)
{
player.sendPacket(new ExPrivateStoreSearchHistory(1, 1, historyTemp));
}
}
private boolean isItemVisibleForShop(TradeItem item)
{
/**
* Equipement
*/
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Equipment - All
return item.getItem().isEquipable();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.WEAPON) && (_searchCollection == 0))
{
// Equipment - Weapon
return item.getItem().isEquipable() && item.getItem().isWeapon();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ARMOR) && (_searchCollection == 0))
{
// Equipment - Armor
return item.getItem().isEquipable() && isEquipmentArmor(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ACCESSORY) && (_searchCollection == 0))
{
// Equipment - Accessory
return item.getItem().isEquipable() && isAccessory(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.EQUIPMENT_MISC) && (_searchCollection == 0))
{
// Equipment - Misc
return item.getItem().isEquipable() && !item.getItem().isWeapon() && !isEquipmentArmor(item.getItem()) && !isAccessory(item.getItem());
}
/**
* Exping / Enhancement
*/
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Exping / Enhancement - All
return isEnhancementItem(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENCHANT_SCROLL) && (_searchCollection == 0))
{
// Exping / Enhancement - Enchant Scroll
return isEnchantScroll(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.CRYSTAL) && (_searchCollection == 0))
{
// Exping / Enhancement - Crystal
return isCrystal(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.LIFE_STONE) && (_searchCollection == 0))
{
// Exping / Enhancement - Life Stone
return isLifeStone(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.DYES) && (_searchCollection == 0))
{
// Exping / Enhancement - Dyes
return isDye(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.SPELLBOOK) && (_searchCollection == 0))
{
// Exping / Enhancement - SpellBooks
return isSpellBook(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENHANCEMENT_MISC) && (_searchCollection == 0))
{
// Exping / Enhancement - Misc
return isEnhancementMisc(item.getItem());
}
/**
* Groceries
*/
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Groceries - All
return item.getItem().isPotion() || item.getItem().isScroll() || isTicket(item.getItem()) || isPackOrCraft(item.getItem()) || isGroceryMisc(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.POTION_SCROLL) && (_searchCollection == 0))
{
// Groceries - Potion/Scroll
return item.getItem().isPotion() || item.getItem().isScroll();
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.TICKET) && (_searchCollection == 0))
{
// Groceries - Ticket
return isTicket(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.PACK_CRAFT) && (_searchCollection == 0))
{
// Groceries - Pack/Craft
return isPackOrCraft(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.GROCERY_MISC) && (_searchCollection == 0))
{
// Groceries - Misc
return isGroceryMisc(item.getItem());
}
/**
* Collections
*/
if ((_itemType == StoreItemType.ALL) && (_searchCollection == 1))
{
// Collections - All
return isCollection(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_searchCollection == 1))
{
// Collections - Equipement
return isCollectionEquipement(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_searchCollection == 1))
{
// Collections - Enchanted Item
return isCollectionEnchanted(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_searchCollection == 1))
{
// Collections - Misc
return isCollectionMisc(item.getItem());
}
return true;
}
private boolean isEquipmentArmor(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == ItemTemplate.SLOT_CHEST) || (item.getBodyPart() == ItemTemplate.SLOT_FULL_ARMOR) || (item.getBodyPart() == ItemTemplate.SLOT_HEAD) || (item.getBodyPart() == ItemTemplate.SLOT_LEGS) || (item.getBodyPart() == ItemTemplate.SLOT_FEET) || (item.getBodyPart() == ItemTemplate.SLOT_GLOVES) || (item.getBodyPart() == (ItemTemplate.SLOT_CHEST | ItemTemplate.SLOT_LEGS)));
}
private boolean isAccessory(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == (ItemTemplate.SLOT_L_BRACELET | ItemTemplate.SLOT_R_BRACELET | ItemTemplate.SLOT_BROOCH)) || (item.getBodyPart() == (ItemTemplate.SLOT_R_FINGER | ItemTemplate.SLOT_L_FINGER)) || (item.getBodyPart() == ItemTemplate.SLOT_NECK) || (item.getBodyPart() == (ItemTemplate.SLOT_R_EAR | ItemTemplate.SLOT_L_EAR)));
}
private boolean isEnchantScroll(ItemTemplate item)
{
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("EnchantScrolls");
}
private boolean isCrystal(ItemTemplate item)
{
return EnsoulData.getInstance().getStone(item.getId()) != null;
}
private boolean isLifeStone(ItemTemplate item)
{
return VariationData.getInstance().getVariation(item.getId()) != null;
}
private boolean isDye(ItemTemplate item)
{
return HennaData.getInstance().getHennaByItemId(item.getId()) != null;
}
private boolean isSpellBook(ItemTemplate item)
{
return item.getName().contains("Spellbook: ");
}
private boolean isEnhancementMisc(ItemTemplate item)
{
return (item.getId() >= 91031) && (item.getId() <= 91038);
}
private boolean isEnhancementItem(ItemTemplate item)
{
return isEnchantScroll(item) || isCrystal(item) || isLifeStone(item) || isDye(item) || isSpellBook(item) || isEnhancementMisc(item);
}
private boolean isTicket(ItemTemplate item)
{
return (item.getId() == 90045) || (item.getId() == 91462) || (item.getId() == 91463) || (item.getId() == 91972) || (item.getId() == 93903);
}
private boolean isPackOrCraft(ItemTemplate item)
{
if ((item.getId() == 92477) || (item.getId() == 91462) || (item.getId() == 92478) || (item.getId() == 92479) || (item.getId() == 92480) || (item.getId() == 92481) || (item.getId() == 49756) || (item.getId() == 93906) || (item.getId() == 93907) || (item.getId() == 93908) || (item.getId() == 93909) || (item.getId() == 93910) || (item.getId() == 91076))
{
return true;
}
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("ExtractableItems");
}
private boolean isGroceryMisc(ItemTemplate item)
{
// Kinda fallback trash category to ensure no skipping any items.
return !item.isEquipable() && !isEnhancementItem(item) && !isCollection(item) && !item.isPotion() && !item.isScroll() && !isTicket(item) && !isPackOrCraft(item);
}
private boolean isCollection(ItemTemplate item)
{
for (CollectionDataHolder collectionHolder : CollectionData.getInstance().getCollections())
{
for (ItemEnchantHolder itemData : collectionHolder.getItems())
{
if (itemData.getId() == item.getId())
{
return true;
}
}
}
return false;
}
private boolean isCollectionEquipement(ItemTemplate item)
{
return isCollection(item) && item.isEquipable();
}
private boolean isCollectionEnchanted(ItemTemplate item)
{
return isCollection(item) && item.getName().contains("Spellbook: ");
}
private boolean isCollectionMisc(ItemTemplate item)
{
return (item.getId() >= 93906) && (item.getId() <= 93910);
}
private enum StoreType
{
SELL((byte) 0x00),
BUY((byte) 0x01),
ALL((byte) 0x03);
private final byte _storeType;
StoreType(byte storeType)
{
_storeType = storeType;
}
public static StoreType findById(int id)
{
for (StoreType storeType : values())
{
if (storeType.getValue() == id)
{
return storeType;
}
}
return null;
}
public byte getValue()
{
return _storeType;
}
}
private enum StoreItemType
{
ALL((byte) 0xFF),
EQUIPMENT((byte) 0x00),
ENHANCEMENT_OR_EXPING((byte) 0x02),
GROCERY_OR_COLLECTION_MISC((byte) 0x04);
private final byte _storeItemType;
StoreItemType(byte storeItemType)
{
_storeItemType = storeItemType;
}
public static StoreItemType findById(int id)
{
for (StoreItemType storeItemType : values())
{
if (storeItemType.getValue() == id)
{
return storeItemType;
}
}
return null;
}
public byte getValue()
{
return _storeItemType;
}
}
private enum StoreSubItemType
{
ALL((byte) 0xFF),
WEAPON((byte) 0x00),
ARMOR((byte) 0x01),
ACCESSORY((byte) 0x02),
EQUIPMENT_MISC((byte) 0x03),
ENCHANT_SCROLL((byte) 0x08),
LIFE_STONE((byte) 0x0F),
DYES((byte) 0x10),
CRYSTAL((byte) 0x11),
SPELLBOOK((byte) 0x12),
ENHANCEMENT_MISC((byte) 0x13),
POTION_SCROLL((byte) 0x14),
TICKET((byte) 0x15),
PACK_CRAFT((byte) 0x16),
GROCERY_MISC((byte) 0x18);
private final byte _storeSubItemType;
StoreSubItemType(byte storeSubItemType)
{
_storeSubItemType = storeSubItemType;
}
public static StoreSubItemType findById(int id)
{
for (StoreSubItemType storeSubItemType : values())
{
if (storeSubItemType.getValue() == id)
{
return storeSubItemType;
}
}
return null;
}
public byte getValue()
{
return _storeSubItemType;
}
}
public static class ShopItem
{
private final TradeItem _item;
private final Player _owner;
private final PrivateStoreType _storeType;
private ShopItem(TradeItem item, Player owner, PrivateStoreType storeType)
{
_item = item;
_owner = owner;
_storeType = storeType;
}
public long getCount()
{
return _item.getCount();
}
public ItemInfo getItemInfo()
{
return new ItemInfo(_item);
}
public Player getOwner()
{
return _owner;
}
public PrivateStoreType getStoreType()
{
return _storeType;
}
public long getPrice()
{
return _item.getPrice();
}
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.RequestPrivateStoreSearchStatistics;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchStatistics implements ClientPacket
{
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
player.sendPacket(new RequestPrivateStoreSearchStatistics());
}
}

View File

@ -305,4 +305,61 @@ public abstract class AbstractItemPacket extends AbstractMaskPacket<ItemListType
writeShort(0);
}
}
protected int calculatePacketSize(ItemInfo item)
{
final int mask = calculateMask(item);
int size = 0;
size += 2; // writeShort(mask);
size += 4; // writeInt(item.getObjectId()); // ObjectId
size += 4; // writeInt(item.getItem().getDisplayId()); // ItemId
size += 1; // writeByte(item.getItem().isQuestItem() || (item.getEquipped() == 1) ? 0xFF : item.getLocation()); // T1
size += 8; // writeLong(item.getCount()); // Quantity
size += 1; // writeByte(item.getItem().getType2()); // Item Type 2 : 00-weapon, 01-shield/armor, 02-ring/earring/necklace, 03-questitem, 04-adena, 05-item
size += 1; // writeByte(item.getCustomType1()); // Filler (always 0)
size += 2; // writeShort(item.getEquipped()); // Equipped : 00-No, 01-yes
size += 8; // writeLong(item.getItem().getBodyPart()); // Slot : 0006-lr.ear, 0008-neck, 0030-lr.finger, 0040-head, 0100-l.hand, 0200-gloves, 0400-chest, 0800-pants, 1000-feet, 4000-r.hand, 8000-r.hand
size += 2; // writeShort(item.getEnchantLevel()); // Enchant level (pet level shown in control item)
size += 4; // writeInt(item.getMana());
size += 1; // writeByte(0); // 270 protocol
size += 4; // writeInt(item.getTime());
size += 1; // writeByte(item.isAvailable()); // GOD Item enabled = 1 disabled (red) = 0
size += 2; // writeShort(0); // 140 - locked
if (containsMask(mask, ItemListType.AUGMENT_BONUS))
{
size += 8;
}
if (containsMask(mask, ItemListType.ELEMENTAL_ATTRIBUTE))
{
size += 16;
}
// 362 - Removed
// if (containsMask(mask, ItemListType.ENCHANT_EFFECT))
// {
// size += (item.getEnchantOptions().length * 4);
// }
if (containsMask(mask, ItemListType.VISUAL_ID))
{
size += 4;
}
if (containsMask(mask, ItemListType.SOUL_CRYSTAL))
{
size += 1;
size += (item.getSoulCrystalOptions().size() * 4);
size += 1;
size += (item.getSoulCrystalSpecialOptions().size() * 4);
}
// TODO:
// if (containsMask(mask, ItemListType.REUSE_DELAY))
// {
// size += 4;
// }
if (containsMask(mask, ItemListType.BLESSED))
{
size += 1;
}
return size;
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchHistory extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final List<ItemHistoryTransaction> _history;
public ExPrivateStoreSearchHistory(int page, int maxPage, List<ItemHistoryTransaction> history)
{
_page = page;
_maxPage = maxPage;
_history = history;
}
/**
* 338 struct _S_EX_PRIVATE_STORE_SEARCH_HISTORY { var int cCurrentPage; var int cMaxPage; var array<_pkPSSearchHistory> histories; }; struct _pkPSSearchHistory { var int nClassID; var int cStoreType; var int cEnchant; var INT64 nPrice; var INT64 nAmount; }; // S: FE D502 01 - cPage 01 -
* cMaxPage E6000000 - nSize nClassID cStoreType cEnchant nPrice nAmount 4E000000 00 00 7F96980000000000 0100000000000000 4F000000 00 00 7F96980000000000 0100000000000000 5B000000 00 00 80C3C90100000000 0100000000000000 62000000 00 00 002D310100000000 0100000000000000 6E000000 00 00
* 80841E0000000000 0100000000000000 C6000000 00 00 FF117A0000000000 0100000000000000
*/
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_HISTORY.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_history.size()); // nSize -> Items count for loop below
for (int i = 0; i < _history.size(); i++)
{
final ItemHistoryTransaction transaction = _history.get(i);
writeInt(transaction.getItemId()); // itemId
writeByte(transaction.getTransactionType() == PrivateStoreType.SELL ? 0x00 : 0x01); // cStoreType
writeByte(transaction.getEnchantLevel()); // cEnchant
writeLong(transaction.getPrice() / transaction.getCount()); // nPrice
writeLong(transaction.getCount()); // nAmount
}
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList.ShopItem;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchItem extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final int _nSize;
private final List<ShopItem> _items;
public ExPrivateStoreSearchItem(int page, int maxPage, int nSize, List<ShopItem> items)
{
_page = page;
_maxPage = maxPage;
_nSize = nSize;
_items = items;
}
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_ITEM.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_nSize); // nSize
if (_nSize > 0)
{
for (int itemIndex = (_page - 1) * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE; (itemIndex < (_page * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE)) && (itemIndex < _items.size()); itemIndex++)
{
final ShopItem shopItem = _items.get(itemIndex);
writeSizedString(shopItem.getOwner().getName()); // Vendor name
writeByte(shopItem.getStoreType() == PrivateStoreType.PACKAGE_SELL ? 0x02 : shopItem.getStoreType() == PrivateStoreType.SELL ? 0x00 : 0x01); // store type (maybe "sold"/buy/Package (translated as Total Score...))
writeLong(shopItem.getPrice()); // Price
writeInt(shopItem.getOwner().getX()); // X
writeInt(shopItem.getOwner().getY()); // Y
writeInt(shopItem.getOwner().getZ()); // Z
writeInt(calculatePacketSize(shopItem.getItemInfo() /* , shopItem.getCount() */)); // size
writeItem(shopItem.getItemInfo(), shopItem.getCount()); // itemAssemble
}
}
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.item.instance.Item;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class RequestPrivateStoreSearchStatistics extends AbstractItemPacket
{
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_STATISTICS.writeId(this);
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> mostItems = PrivateStoreHistoryManager.getInstance().getTopMostItem();
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> highestItems = PrivateStoreHistoryManager.getInstance().getTopHighestItem();
writeInt(Math.min(mostItems.size(), 5));
for (int i = 0; i < Math.min(mostItems.size(), 5); i++)
{
writeInt((int) mostItems.get(i).getCount());
ItemInfo itemInfo = new ItemInfo(new Item(mostItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , mostItems.get(i).getCount() */));
writeItem(itemInfo, mostItems.get(i).getCount());
}
writeInt(Math.min(highestItems.size(), 5));
for (int i = 0; i < Math.min(highestItems.size(), 5); i++)
{
writeLong(highestItems.get(i).getPrice());
ItemInfo itemInfo = new ItemInfo(new Item(highestItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , highestItems.get(i).getCount() */));
writeItem(itemInfo, highestItems.get(i).getCount());
}
}
}

View File

@ -142,10 +142,12 @@ Frost Lord: https://eu.4game.com/patchnotes/lineage2essence/329/
Battle Chronicle: https://eu.4game.com/patchnotes/lineage2essence/353/
-New henna pattern potential system
-Private shop review
Vanguard: https://eu.4game.com/patchnotes/lineage2essence/387/
-Vanguard creation support
-New enchant system
-World exchange system
-Chest and box auto-use
Customs:

View File

@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `item_transaction_history`;
CREATE TABLE IF NOT EXISTS `item_transaction_history` (
`created_time` BIGINT UNSIGNED NOT NULL,
`item_id` INT NOT NULL,
`transaction_type` INT NOT NULL,
`enchant_level` INT NOT NULL,
`price` BIGINT UNSIGNED NOT NULL,
`count` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`created_time`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

View File

@ -289,6 +289,12 @@ GridNeighborTurnOffTime = 90
# Default: True
CorrectPrices = True
# Private store review limit.
PrivateStoreReviewLimit = 30
# Private store review cache time in milliseconds.
PrivateStoreReviewCacheTime = 5000
# ---------------------------------------------------------------------------
# Falling Damage

View File

@ -543,6 +543,8 @@ public class Config
public static boolean ALLOW_WEAR;
public static int WEAR_DELAY;
public static int WEAR_PRICE;
public static int STORE_REVIEW_LIMIT;
public static int STORE_REVIEW_CACHE_TIME;
public static int INSTANCE_FINISH_TIME;
public static boolean RESTORE_PLAYER_INSTANCE;
public static int EJECT_DEAD_PLAYER_TIME;
@ -2257,6 +2259,8 @@ public class Config
ALLOW_WEAR = generalConfig.getBoolean("AllowWear", true);
WEAR_DELAY = generalConfig.getInt("WearDelay", 5);
WEAR_PRICE = generalConfig.getInt("WearPrice", 10);
STORE_REVIEW_LIMIT = generalConfig.getInt("PrivateStoreReviewLimit", 30);
STORE_REVIEW_CACHE_TIME = generalConfig.getInt("PrivateStoreReviewCacheTime", 5000);
INSTANCE_FINISH_TIME = generalConfig.getInt("DefaultFinishTime", 5);
RESTORE_PLAYER_INSTANCE = generalConfig.getBoolean("RestorePlayerInstance", false);
EJECT_DEAD_PLAYER_TIME = generalConfig.getInt("EjectDeadPlayerTime", 1);

View File

@ -155,6 +155,7 @@ import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager;
import org.l2jmobius.gameserver.instancemanager.PetitionManager;
import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager;
import org.l2jmobius.gameserver.instancemanager.PremiumManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PunishmentManager;
import org.l2jmobius.gameserver.instancemanager.PurgeRankingManager;
import org.l2jmobius.gameserver.instancemanager.QuestManager;
@ -317,6 +318,7 @@ public class GameServer
AppearanceItemData.getInstance();
ItemCommissionManager.getInstance();
WorldExchangeManager.getInstance();
PrivateStoreHistoryManager.getInstance().restore();
LuckyGameData.getInstance();
AttendanceRewardData.getInstance();
MagicLampData.getInstance();

View File

@ -118,6 +118,7 @@ public class DailyTaskManager
resetMonsterArenaWeekly();
resetTimedHuntingZonesWeekly();
resetVitalityWeekly();
resetPrivateStoreHistory();
}
else // All days, except Wednesday.
{
@ -707,6 +708,20 @@ public class DailyTaskManager
LOGGER.info("Daily payment resurrection count for player has been resetted.");
}
public void resetPrivateStoreHistory()
{
try
{
PrivateStoreHistoryManager.getInstance().reset();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Could not reset private store history! " + e);
}
LOGGER.info("Private store history has been resetted.");
}
private void resetDailyHennaPattern()
{
// Update data for offline players.

View File

@ -0,0 +1,294 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.instancemanager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.l2jmobius.Config;
import org.l2jmobius.commons.database.DatabaseFactory;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.model.item.instance.Item;
/**
* @author Mobius
*/
public class PrivateStoreHistoryManager
{
private static final Logger LOGGER = Logger.getLogger(PrivateStoreHistoryManager.class.getName());
private static final String SELECT = "SELECT * FROM item_transaction_history";
private static final String INSERT = "INSERT INTO item_transaction_history (created_time,item_id,transaction_type,enchant_level,price,count) VALUES (?,?,?,?,?,?)";
private static final String TRUNCATE = "TRUNCATE TABLE item_transaction_history";
private static final ArrayList<ItemHistoryTransaction> _items = new ArrayList<>();
public void registerTransaction(PrivateStoreType transactionType, Item item, long count, long price)
{
try
{
final ItemHistoryTransaction historyItem = new ItemHistoryTransaction(transactionType, count, price, item);
_items.add(historyItem);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not store history for item: " + item, e);
}
}
public void restore()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(SELECT))
{
try (ResultSet rs = statement.executeQuery())
{
while (rs.next())
{
final ItemHistoryTransaction item = new ItemHistoryTransaction(rs);
_items.add(item);
}
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not restore history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " items history.");
}
public void reset()
{
_items.clear();
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement statement = con.prepareStatement(TRUNCATE))
{
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not reset history.", e);
}
LOGGER.info(getClass().getSimpleName() + ": weekly reset.");
}
public List<ItemHistoryTransaction> getHistory()
{
return getHistory(false);
}
public List<ItemHistoryTransaction> getHistory(boolean full)
{
if (!full)
{
final List<ItemHistoryTransaction> list = new ArrayList<>(_items);
final Map<Integer, Integer> uniqueItemIds = list.stream().map(transaction -> transaction.getItemId()).collect(Collectors.toSet()).stream().collect(Collectors.toMap(Integer::intValue, e -> 0));
list.sort(new SortByDate());
final List<ItemHistoryTransaction> finalList = new ArrayList<>();
for (ItemHistoryTransaction transaction : list)
{
if (uniqueItemIds.get(transaction.getItemId()) < Config.STORE_REVIEW_LIMIT)
{
finalList.add(transaction);
uniqueItemIds.put(transaction.getItemId(), uniqueItemIds.get(transaction.getItemId()) + 1);
}
}
return finalList;
}
return _items;
}
public List<ItemHistoryTransaction> getTopHighestItem()
{
final LinkedList<ItemHistoryTransaction> list = new LinkedList<>(_items);
list.sort(new SortByPrice());
return list;
}
public List<ItemHistoryTransaction> getTopMostItem()
{
final Map<Integer, ItemHistoryTransaction> map = new HashMap<>();
for (ItemHistoryTransaction transaction : _items)
{
if (map.get(transaction.getItemId()) == null)
{
map.put(transaction.getItemId(), new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), 0, false));
}
else
{
map.get(transaction.getItemId()).addCount(transaction.getCount());
}
}
final List<ItemHistoryTransaction> list = new LinkedList<>();
map.forEach((itemID, transaction) -> list.add(transaction));
list.sort(new SortByQuantity());
return list;
}
private static class SortByPrice implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getPrice() > b.getPrice() ? -1 : a.getPrice() == b.getPrice() ? 0 : 1;
}
}
private static class SortByQuantity implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getCount() > b.getCount() ? -1 : a.getCount() == b.getCount() ? 0 : 1;
}
}
private static class SortByDate implements Comparator<ItemHistoryTransaction>
{
@Override
public int compare(ItemHistoryTransaction a, ItemHistoryTransaction b)
{
return a.getTransactionDate() > b.getTransactionDate() ? -1 : a.getTransactionDate() == b.getTransactionDate() ? 0 : 1;
}
}
public static class ItemHistoryTransaction
{
private final long _transactionDate;
private final int _itemId;
private final PrivateStoreType _transactionType;
private final int _enchantLevel;
private final long _price;
private long _count;
public ItemHistoryTransaction(ResultSet rs) throws SQLException
{
_transactionDate = rs.getLong("created_time");
_itemId = rs.getInt("item_id");
_transactionType = rs.getInt("transaction_type") == 0 ? PrivateStoreType.SELL : PrivateStoreType.BUY;
_enchantLevel = rs.getInt("enchant_level");
_price = rs.getLong("price");
_count = rs.getLong("count");
}
private ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, Item item)
{
this(transactionType, count, price, item.getId(), item.getEnchantLevel(), true);
}
public ItemHistoryTransaction(PrivateStoreType transactionType, long count, long price, int itemId, int enchantLevel, boolean saveToDB)
{
_transactionDate = System.currentTimeMillis();
_itemId = itemId;
_transactionType = transactionType;
_enchantLevel = enchantLevel;
_price = price;
_count = count;
if (saveToDB)
{
storeInDB();
}
}
public long getTransactionDate()
{
return _transactionDate;
}
public PrivateStoreType getTransactionType()
{
return _transactionType;
}
public int getItemId()
{
return _itemId;
}
public int getEnchantLevel()
{
return _enchantLevel;
}
public long getPrice()
{
return _price;
}
public long getCount()
{
return _count;
}
public void addCount(long count)
{
_count += count;
}
private void storeInDB()
{
try (Connection con = DatabaseFactory.getConnection();
PreparedStatement ps = con.prepareStatement(INSERT))
{
ps.setLong(1, _transactionDate);
ps.setInt(2, _itemId);
ps.setInt(3, _transactionType == PrivateStoreType.SELL ? 0 : 1);
ps.setInt(4, _enchantLevel);
ps.setLong(5, _price);
ps.setLong(6, _count);
ps.executeQuery();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "Could not insert history item " + this + " into DB: Reason: " + e.getMessage(), e);
}
}
@Override
public String toString()
{
return _transactionDate + "(" + _transactionType + ")" + "[" + _itemId + " +" + _enchantLevel + " c:" + _count + " p:" + _price + " ]";
}
}
public static PrivateStoreHistoryManager getInstance()
{
return PrivateStoreHistoryManager.SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final PrivateStoreHistoryManager INSTANCE = new PrivateStoreHistoryManager();
}
}

View File

@ -86,6 +86,8 @@ public class World
private static final Map<Integer, Player> _allGoodPlayers = new ConcurrentHashMap<>();
/** Map containing all the Evil players in game. */
private static final Map<Integer, Player> _allEvilPlayers = new ConcurrentHashMap<>();
/** Map containing all the players in Store Buy or Sell mode. */
private static final Map<Integer, Player> _allStoreModeBuySellPlayers = new ConcurrentHashMap<>();
/** Map containing all visible objects. */
private static final Map<Integer, WorldObject> _allObjects = new ConcurrentHashMap<>();
/** Map with the pets instances and their owner ID. */
@ -96,6 +98,8 @@ public class World
private static final WorldRegion[][] _worldRegions = new WorldRegion[REGIONS_X + 1][REGIONS_Y + 1];
private static long _nextPrivateStoreUpdate = 0;
/** Constructor of World. */
protected World()
{
@ -279,6 +283,18 @@ public class World
return _petsInstance.get(ownerId);
}
public synchronized Collection<Player> getSellingOrBuyingPlayers()
{
final long currentTime = System.currentTimeMillis();
if (currentTime > _nextPrivateStoreUpdate)
{
_nextPrivateStoreUpdate = currentTime + Config.STORE_REVIEW_CACHE_TIME;
_allStoreModeBuySellPlayers.clear();
_allPlayers.values().stream().filter(Player::isInStoreSellOrBuyMode).forEach(player -> _allStoreModeBuySellPlayers.put(player.getObjectId(), player));
}
return _allStoreModeBuySellPlayers.values();
}
/**
* Add the given pet instance from the given ownerId.
* @param ownerId ID of the owner

View File

@ -1408,6 +1408,11 @@ public class Player extends Playable
return _privateStoreType != PrivateStoreType.NONE;
}
public boolean isInStoreSellOrBuyMode()
{
return (_privateStoreType == PrivateStoreType.BUY) || (_privateStoreType == PrivateStoreType.SELL) || (_privateStoreType == PrivateStoreType.PACKAGE_SELL);
}
public boolean isCrafting()
{
return _isCrafting;

View File

@ -189,6 +189,8 @@ import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyBox
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyGetReward;
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyOpenBox;
import org.l2jmobius.gameserver.network.clientpackets.steadybox.RequestSteadyOpenSlot;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchStatistics;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationGacha;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationGachaUI;
import org.l2jmobius.gameserver.network.clientpackets.subjugation.RequestSubjugationList;
@ -757,8 +759,8 @@ public enum ExClientPackets
EX_WORLDCASTLEWAR_PLEDGE_MERCENARY_MEMBER_JOIN(0x211, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_TELEPORT(0x212, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_OBSERVER_START(0x213, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x214, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x215, null, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_LIST(0x214, ExRequestPrivateStoreSearchList::new, ConnectionState.IN_GAME),
EX_PRIVATE_STORE_SEARCH_STATISTICS(0x215, ExRequestPrivateStoreSearchStatistics::new, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_HOST_CASTLE_SIEGE_RANKING_INFO(0x216, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_CASTLE_SIEGE_RANKING_INFO(0x217, null, ConnectionState.IN_GAME),
EX_WORLDCASTLEWAR_SIEGE_MAINBATTLE_HUD_INFO(0x218, null, ConnectionState.IN_GAME),

View File

@ -0,0 +1,519 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.l2jmobius.commons.network.ReadablePacket;
import org.l2jmobius.gameserver.data.xml.CollectionData;
import org.l2jmobius.gameserver.data.xml.EnsoulData;
import org.l2jmobius.gameserver.data.xml.HennaData;
import org.l2jmobius.gameserver.data.xml.VariationData;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.handler.IItemHandler;
import org.l2jmobius.gameserver.handler.ItemHandler;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.TradeItem;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.CollectionDataHolder;
import org.l2jmobius.gameserver.model.holders.ItemEnchantHolder;
import org.l2jmobius.gameserver.model.item.EtcItem;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchHistory;
import org.l2jmobius.gameserver.network.serverpackets.storereview.ExPrivateStoreSearchItem;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchList implements ClientPacket
{
public static final int MAX_ITEM_PER_PAGE = 120;
private String _searchWord;
private StoreType _storeType;
private StoreItemType _itemType;
private StoreSubItemType _itemSubtype;
private int _searchCollection;
@Override
public void read(ReadablePacket packet)
{
_searchWord = packet.readSizedString();
_storeType = StoreType.findById(packet.readByte());
_itemType = StoreItemType.findById(packet.readByte());
_itemSubtype = StoreSubItemType.findById(packet.readByte());
_searchCollection = packet.readByte();
}
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
final Collection<Player> stores = World.getInstance().getSellingOrBuyingPlayers();
final List<ShopItem> items = new ArrayList<>();
final List<Integer> itemIds = new ArrayList<>();
stores.forEach(vendor ->
{
for (TradeItem item : vendor.getPrivateStoreType() == PrivateStoreType.BUY ? vendor.getBuyList().getItems() : vendor.getSellList().getItems())
{
// Filter by storeType.
if ((_storeType == StoreType.ALL) || ((_storeType == StoreType.SELL) && ((vendor.getPrivateStoreType() == PrivateStoreType.SELL) || (vendor.getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL))) || ((_storeType == StoreType.BUY) && (vendor.getPrivateStoreType() == PrivateStoreType.BUY)))
{
if (isItemVisibleForShop(item))
{
// Filter by Word if supplied.
if (_searchWord.equals("") || (!_searchWord.equals("") && (item.getItem().getName().toLowerCase().contains(_searchWord.toLowerCase()))))
{
items.add(new ShopItem(item, vendor, vendor.getPrivateStoreType()));
itemIds.add(item.getItem().getId());
}
}
}
}
});
int nSize = items.size();
int maxPage = Math.max(1, (nSize / (MAX_ITEM_PER_PAGE * 1F)) > (nSize / MAX_ITEM_PER_PAGE) ? (nSize / MAX_ITEM_PER_PAGE) + 1 : nSize / MAX_ITEM_PER_PAGE);
for (int page = 1; page <= maxPage; page++)
{
final int nsize = page == maxPage ? ((nSize % MAX_ITEM_PER_PAGE) > 0) || (nSize == 0) ? nSize % MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE : MAX_ITEM_PER_PAGE;
player.sendPacket(new ExPrivateStoreSearchItem(page, maxPage, nsize, items));
}
final List<ItemHistoryTransaction> history = new ArrayList<>();
final List<ItemHistoryTransaction> historyTemp = new ArrayList<>();
PrivateStoreHistoryManager.getInstance().getHistory().forEach(transaction ->
{
if (itemIds.contains(transaction.getItemId()))
{
history.add(new ItemHistoryTransaction(transaction.getTransactionType(), transaction.getCount(), transaction.getPrice(), transaction.getItemId(), transaction.getEnchantLevel(), false));
}
});
int page = 1;
maxPage = Math.max(1, (history.size() / (MAX_ITEM_PER_PAGE * 1F)) > (history.size() / MAX_ITEM_PER_PAGE) ? (history.size() / MAX_ITEM_PER_PAGE) + 1 : history.size() / MAX_ITEM_PER_PAGE);
for (int index = 0; index < history.size(); index++)
{
historyTemp.add(history.get(index));
if ((index == (history.size() - 1)) || (index == (MAX_ITEM_PER_PAGE - 1)) || ((index > 0) && ((index % (MAX_ITEM_PER_PAGE - 1)) == 0)))
{
player.sendPacket(new ExPrivateStoreSearchHistory(page, maxPage, historyTemp));
page++;
historyTemp.clear();
}
}
if (page == 1)
{
player.sendPacket(new ExPrivateStoreSearchHistory(1, 1, historyTemp));
}
}
private boolean isItemVisibleForShop(TradeItem item)
{
/**
* Equipement
*/
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Equipment - All
return item.getItem().isEquipable();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.WEAPON) && (_searchCollection == 0))
{
// Equipment - Weapon
return item.getItem().isEquipable() && item.getItem().isWeapon();
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ARMOR) && (_searchCollection == 0))
{
// Equipment - Armor
return item.getItem().isEquipable() && isEquipmentArmor(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.ACCESSORY) && (_searchCollection == 0))
{
// Equipment - Accessory
return item.getItem().isEquipable() && isAccessory(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_itemSubtype == StoreSubItemType.EQUIPMENT_MISC) && (_searchCollection == 0))
{
// Equipment - Misc
return item.getItem().isEquipable() && !item.getItem().isWeapon() && !isEquipmentArmor(item.getItem()) && !isAccessory(item.getItem());
}
/**
* Exping / Enhancement
*/
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Exping / Enhancement - All
return isEnhancementItem(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENCHANT_SCROLL) && (_searchCollection == 0))
{
// Exping / Enhancement - Enchant Scroll
return isEnchantScroll(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.CRYSTAL) && (_searchCollection == 0))
{
// Exping / Enhancement - Crystal
return isCrystal(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.LIFE_STONE) && (_searchCollection == 0))
{
// Exping / Enhancement - Life Stone
return isLifeStone(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.DYES) && (_searchCollection == 0))
{
// Exping / Enhancement - Dyes
return isDye(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.SPELLBOOK) && (_searchCollection == 0))
{
// Exping / Enhancement - SpellBooks
return isSpellBook(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_itemSubtype == StoreSubItemType.ENHANCEMENT_MISC) && (_searchCollection == 0))
{
// Exping / Enhancement - Misc
return isEnhancementMisc(item.getItem());
}
/**
* Groceries
*/
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.ALL) && (_searchCollection == 0))
{
// Groceries - All
return item.getItem().isPotion() || item.getItem().isScroll() || isTicket(item.getItem()) || isPackOrCraft(item.getItem()) || isGroceryMisc(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.POTION_SCROLL) && (_searchCollection == 0))
{
// Groceries - Potion/Scroll
return item.getItem().isPotion() || item.getItem().isScroll();
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.TICKET) && (_searchCollection == 0))
{
// Groceries - Ticket
return isTicket(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.PACK_CRAFT) && (_searchCollection == 0))
{
// Groceries - Pack/Craft
return isPackOrCraft(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_itemSubtype == StoreSubItemType.GROCERY_MISC) && (_searchCollection == 0))
{
// Groceries - Misc
return isGroceryMisc(item.getItem());
}
/**
* Collections
*/
if ((_itemType == StoreItemType.ALL) && (_searchCollection == 1))
{
// Collections - All
return isCollection(item.getItem());
}
if ((_itemType == StoreItemType.EQUIPMENT) && (_searchCollection == 1))
{
// Collections - Equipement
return isCollectionEquipement(item.getItem());
}
if ((_itemType == StoreItemType.ENHANCEMENT_OR_EXPING) && (_searchCollection == 1))
{
// Collections - Enchanted Item
return isCollectionEnchanted(item.getItem());
}
if ((_itemType == StoreItemType.GROCERY_OR_COLLECTION_MISC) && (_searchCollection == 1))
{
// Collections - Misc
return isCollectionMisc(item.getItem());
}
return true;
}
private boolean isEquipmentArmor(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == ItemTemplate.SLOT_CHEST) || (item.getBodyPart() == ItemTemplate.SLOT_FULL_ARMOR) || (item.getBodyPart() == ItemTemplate.SLOT_HEAD) || (item.getBodyPart() == ItemTemplate.SLOT_LEGS) || (item.getBodyPart() == ItemTemplate.SLOT_FEET) || (item.getBodyPart() == ItemTemplate.SLOT_GLOVES) || (item.getBodyPart() == (ItemTemplate.SLOT_CHEST | ItemTemplate.SLOT_LEGS)));
}
private boolean isAccessory(ItemTemplate item)
{
return item.isArmor() && ((item.getBodyPart() == (ItemTemplate.SLOT_L_BRACELET | ItemTemplate.SLOT_R_BRACELET | ItemTemplate.SLOT_BROOCH)) || (item.getBodyPart() == (ItemTemplate.SLOT_R_FINGER | ItemTemplate.SLOT_L_FINGER)) || (item.getBodyPart() == ItemTemplate.SLOT_NECK) || (item.getBodyPart() == (ItemTemplate.SLOT_R_EAR | ItemTemplate.SLOT_L_EAR)));
}
private boolean isEnchantScroll(ItemTemplate item)
{
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("EnchantScrolls");
}
private boolean isCrystal(ItemTemplate item)
{
return EnsoulData.getInstance().getStone(item.getId()) != null;
}
private boolean isLifeStone(ItemTemplate item)
{
return VariationData.getInstance().getVariation(item.getId()) != null;
}
private boolean isDye(ItemTemplate item)
{
return HennaData.getInstance().getHennaByItemId(item.getId()) != null;
}
private boolean isSpellBook(ItemTemplate item)
{
return item.getName().contains("Spellbook: ");
}
private boolean isEnhancementMisc(ItemTemplate item)
{
return (item.getId() >= 91031) && (item.getId() <= 91038);
}
private boolean isEnhancementItem(ItemTemplate item)
{
return isEnchantScroll(item) || isCrystal(item) || isLifeStone(item) || isDye(item) || isSpellBook(item) || isEnhancementMisc(item);
}
private boolean isTicket(ItemTemplate item)
{
return (item.getId() == 90045) || (item.getId() == 91462) || (item.getId() == 91463) || (item.getId() == 91972) || (item.getId() == 93903);
}
private boolean isPackOrCraft(ItemTemplate item)
{
if ((item.getId() == 92477) || (item.getId() == 91462) || (item.getId() == 92478) || (item.getId() == 92479) || (item.getId() == 92480) || (item.getId() == 92481) || (item.getId() == 49756) || (item.getId() == 93906) || (item.getId() == 93907) || (item.getId() == 93908) || (item.getId() == 93909) || (item.getId() == 93910) || (item.getId() == 91076))
{
return true;
}
if (!(item instanceof EtcItem))
{
return false;
}
final IItemHandler ih = ItemHandler.getInstance().getHandler((EtcItem) item);
return (ih != null) && ih.getClass().getSimpleName().equals("ExtractableItems");
}
private boolean isGroceryMisc(ItemTemplate item)
{
// Kinda fallback trash category to ensure no skipping any items.
return !item.isEquipable() && !isEnhancementItem(item) && !isCollection(item) && !item.isPotion() && !item.isScroll() && !isTicket(item) && !isPackOrCraft(item);
}
private boolean isCollection(ItemTemplate item)
{
for (CollectionDataHolder collectionHolder : CollectionData.getInstance().getCollections())
{
for (ItemEnchantHolder itemData : collectionHolder.getItems())
{
if (itemData.getId() == item.getId())
{
return true;
}
}
}
return false;
}
private boolean isCollectionEquipement(ItemTemplate item)
{
return isCollection(item) && item.isEquipable();
}
private boolean isCollectionEnchanted(ItemTemplate item)
{
return isCollection(item) && item.getName().contains("Spellbook: ");
}
private boolean isCollectionMisc(ItemTemplate item)
{
return (item.getId() >= 93906) && (item.getId() <= 93910);
}
private enum StoreType
{
SELL((byte) 0x00),
BUY((byte) 0x01),
ALL((byte) 0x03);
private final byte _storeType;
StoreType(byte storeType)
{
_storeType = storeType;
}
public static StoreType findById(int id)
{
for (StoreType storeType : values())
{
if (storeType.getValue() == id)
{
return storeType;
}
}
return null;
}
public byte getValue()
{
return _storeType;
}
}
private enum StoreItemType
{
ALL((byte) 0xFF),
EQUIPMENT((byte) 0x00),
ENHANCEMENT_OR_EXPING((byte) 0x02),
GROCERY_OR_COLLECTION_MISC((byte) 0x04);
private final byte _storeItemType;
StoreItemType(byte storeItemType)
{
_storeItemType = storeItemType;
}
public static StoreItemType findById(int id)
{
for (StoreItemType storeItemType : values())
{
if (storeItemType.getValue() == id)
{
return storeItemType;
}
}
return null;
}
public byte getValue()
{
return _storeItemType;
}
}
private enum StoreSubItemType
{
ALL((byte) 0xFF),
WEAPON((byte) 0x00),
ARMOR((byte) 0x01),
ACCESSORY((byte) 0x02),
EQUIPMENT_MISC((byte) 0x03),
ENCHANT_SCROLL((byte) 0x08),
LIFE_STONE((byte) 0x0F),
DYES((byte) 0x10),
CRYSTAL((byte) 0x11),
SPELLBOOK((byte) 0x12),
ENHANCEMENT_MISC((byte) 0x13),
POTION_SCROLL((byte) 0x14),
TICKET((byte) 0x15),
PACK_CRAFT((byte) 0x16),
GROCERY_MISC((byte) 0x18);
private final byte _storeSubItemType;
StoreSubItemType(byte storeSubItemType)
{
_storeSubItemType = storeSubItemType;
}
public static StoreSubItemType findById(int id)
{
for (StoreSubItemType storeSubItemType : values())
{
if (storeSubItemType.getValue() == id)
{
return storeSubItemType;
}
}
return null;
}
public byte getValue()
{
return _storeSubItemType;
}
}
public static class ShopItem
{
private final TradeItem _item;
private final Player _owner;
private final PrivateStoreType _storeType;
private ShopItem(TradeItem item, Player owner, PrivateStoreType storeType)
{
_item = item;
_owner = owner;
_storeType = storeType;
}
public long getCount()
{
return _item.getCount();
}
public ItemInfo getItemInfo()
{
return new ItemInfo(_item);
}
public Player getOwner()
{
return _owner;
}
public PrivateStoreType getStoreType()
{
return _storeType;
}
public long getPrice()
{
return _item.getPrice();
}
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.clientpackets.storereview;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.clientpackets.ClientPacket;
import org.l2jmobius.gameserver.network.serverpackets.storereview.RequestPrivateStoreSearchStatistics;
/**
* @author Mobius
*/
public class ExRequestPrivateStoreSearchStatistics implements ClientPacket
{
@Override
public void run(GameClient client)
{
final Player player = client.getPlayer();
if (player == null)
{
return;
}
player.sendPacket(new RequestPrivateStoreSearchStatistics());
}
}

View File

@ -305,4 +305,61 @@ public abstract class AbstractItemPacket extends AbstractMaskPacket<ItemListType
writeShort(0);
}
}
protected int calculatePacketSize(ItemInfo item)
{
final int mask = calculateMask(item);
int size = 0;
size += 2; // writeShort(mask);
size += 4; // writeInt(item.getObjectId()); // ObjectId
size += 4; // writeInt(item.getItem().getDisplayId()); // ItemId
size += 1; // writeByte(item.getItem().isQuestItem() || (item.getEquipped() == 1) ? 0xFF : item.getLocation()); // T1
size += 8; // writeLong(item.getCount()); // Quantity
size += 1; // writeByte(item.getItem().getType2()); // Item Type 2 : 00-weapon, 01-shield/armor, 02-ring/earring/necklace, 03-questitem, 04-adena, 05-item
size += 1; // writeByte(item.getCustomType1()); // Filler (always 0)
size += 2; // writeShort(item.getEquipped()); // Equipped : 00-No, 01-yes
size += 8; // writeLong(item.getItem().getBodyPart()); // Slot : 0006-lr.ear, 0008-neck, 0030-lr.finger, 0040-head, 0100-l.hand, 0200-gloves, 0400-chest, 0800-pants, 1000-feet, 4000-r.hand, 8000-r.hand
size += 2; // writeShort(item.getEnchantLevel()); // Enchant level (pet level shown in control item)
size += 4; // writeInt(item.getMana());
size += 1; // writeByte(0); // 270 protocol
size += 4; // writeInt(item.getTime());
size += 1; // writeByte(item.isAvailable()); // GOD Item enabled = 1 disabled (red) = 0
size += 2; // writeShort(0); // 140 - locked
if (containsMask(mask, ItemListType.AUGMENT_BONUS))
{
size += 8;
}
if (containsMask(mask, ItemListType.ELEMENTAL_ATTRIBUTE))
{
size += 16;
}
// 362 - Removed
// if (containsMask(mask, ItemListType.ENCHANT_EFFECT))
// {
// size += (item.getEnchantOptions().length * 4);
// }
if (containsMask(mask, ItemListType.VISUAL_ID))
{
size += 4;
}
if (containsMask(mask, ItemListType.SOUL_CRYSTAL))
{
size += 1;
size += (item.getSoulCrystalOptions().size() * 4);
size += 1;
size += (item.getSoulCrystalSpecialOptions().size() * 4);
}
// TODO:
// if (containsMask(mask, ItemListType.REUSE_DELAY))
// {
// size += 4;
// }
if (containsMask(mask, ItemListType.BLESSED))
{
size += 1;
}
return size;
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager.ItemHistoryTransaction;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchHistory extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final List<ItemHistoryTransaction> _history;
public ExPrivateStoreSearchHistory(int page, int maxPage, List<ItemHistoryTransaction> history)
{
_page = page;
_maxPage = maxPage;
_history = history;
}
/**
* 338 struct _S_EX_PRIVATE_STORE_SEARCH_HISTORY { var int cCurrentPage; var int cMaxPage; var array<_pkPSSearchHistory> histories; }; struct _pkPSSearchHistory { var int nClassID; var int cStoreType; var int cEnchant; var INT64 nPrice; var INT64 nAmount; }; // S: FE D502 01 - cPage 01 -
* cMaxPage E6000000 - nSize nClassID cStoreType cEnchant nPrice nAmount 4E000000 00 00 7F96980000000000 0100000000000000 4F000000 00 00 7F96980000000000 0100000000000000 5B000000 00 00 80C3C90100000000 0100000000000000 62000000 00 00 002D310100000000 0100000000000000 6E000000 00 00
* 80841E0000000000 0100000000000000 C6000000 00 00 FF117A0000000000 0100000000000000
*/
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_HISTORY.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_history.size()); // nSize -> Items count for loop below
for (int i = 0; i < _history.size(); i++)
{
final ItemHistoryTransaction transaction = _history.get(i);
writeInt(transaction.getItemId()); // itemId
writeByte(transaction.getTransactionType() == PrivateStoreType.SELL ? 0x00 : 0x01); // cStoreType
writeByte(transaction.getEnchantLevel()); // cEnchant
writeLong(transaction.getPrice() / transaction.getCount()); // nPrice
writeLong(transaction.getCount()); // nAmount
}
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.enums.PrivateStoreType;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList;
import org.l2jmobius.gameserver.network.clientpackets.storereview.ExRequestPrivateStoreSearchList.ShopItem;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class ExPrivateStoreSearchItem extends AbstractItemPacket
{
private final int _page;
private final int _maxPage;
private final int _nSize;
private final List<ShopItem> _items;
public ExPrivateStoreSearchItem(int page, int maxPage, int nSize, List<ShopItem> items)
{
_page = page;
_maxPage = maxPage;
_nSize = nSize;
_items = items;
}
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_ITEM.writeId(this);
writeByte(_page); // cPage
writeByte(_maxPage); // cMaxPage
writeInt(_nSize); // nSize
if (_nSize > 0)
{
for (int itemIndex = (_page - 1) * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE; (itemIndex < (_page * ExRequestPrivateStoreSearchList.MAX_ITEM_PER_PAGE)) && (itemIndex < _items.size()); itemIndex++)
{
final ShopItem shopItem = _items.get(itemIndex);
writeSizedString(shopItem.getOwner().getName()); // Vendor name
writeByte(shopItem.getStoreType() == PrivateStoreType.PACKAGE_SELL ? 0x02 : shopItem.getStoreType() == PrivateStoreType.SELL ? 0x00 : 0x01); // store type (maybe "sold"/buy/Package (translated as Total Score...))
writeLong(shopItem.getPrice()); // Price
writeInt(shopItem.getOwner().getX()); // X
writeInt(shopItem.getOwner().getY()); // Y
writeInt(shopItem.getOwner().getZ()); // Z
writeInt(calculatePacketSize(shopItem.getItemInfo() /* , shopItem.getCount() */)); // size
writeItem(shopItem.getItemInfo(), shopItem.getCount()); // itemAssemble
}
}
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.l2jmobius.gameserver.network.serverpackets.storereview;
import java.util.List;
import org.l2jmobius.gameserver.instancemanager.PrivateStoreHistoryManager;
import org.l2jmobius.gameserver.model.ItemInfo;
import org.l2jmobius.gameserver.model.item.instance.Item;
import org.l2jmobius.gameserver.network.ServerPackets;
import org.l2jmobius.gameserver.network.serverpackets.AbstractItemPacket;
/**
* @author Mobius
*/
public class RequestPrivateStoreSearchStatistics extends AbstractItemPacket
{
@Override
public void write()
{
ServerPackets.EX_PRIVATE_STORE_SEARCH_STATISTICS.writeId(this);
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> mostItems = PrivateStoreHistoryManager.getInstance().getTopMostItem();
final List<PrivateStoreHistoryManager.ItemHistoryTransaction> highestItems = PrivateStoreHistoryManager.getInstance().getTopHighestItem();
writeInt(Math.min(mostItems.size(), 5));
for (int i = 0; i < Math.min(mostItems.size(), 5); i++)
{
writeInt((int) mostItems.get(i).getCount());
ItemInfo itemInfo = new ItemInfo(new Item(mostItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , mostItems.get(i).getCount() */));
writeItem(itemInfo, mostItems.get(i).getCount());
}
writeInt(Math.min(highestItems.size(), 5));
for (int i = 0; i < Math.min(highestItems.size(), 5); i++)
{
writeLong(highestItems.get(i).getPrice());
ItemInfo itemInfo = new ItemInfo(new Item(highestItems.get(i).getItemId()));
writeInt(calculatePacketSize(itemInfo /* , highestItems.get(i).getCount() */));
writeItem(itemInfo, highestItems.get(i).getCount());
}
}
}

View File

@ -142,14 +142,16 @@ Frost Lord: https://eu.4game.com/patchnotes/lineage2essence/329/
Battle Chronicle: https://eu.4game.com/patchnotes/lineage2essence/353/
-New henna pattern potential system
-Private shop review
Vanguard: https://eu.4game.com/patchnotes/lineage2essence/387/
-Vanguard creation support
-New enchant system
-World exchange system
-Chest and box auto-use
Crusader: https://eu.4game.com/patchnotes/lineage2essence/416/
-Login support
-New character creation templates
Customs:
-Newbie Helper NPC location info