From 7304750a5203bb2d0ddbecda4522bc03647560ce Mon Sep 17 00:00:00 2001 From: "marcins78@gmail.com" Date: Mon, 12 Mar 2012 23:50:50 +0000 Subject: [PATCH] Work on merchant. --- .../lotro/cards/packs/DefaultSetRarity.java | 6 + .../gempukku/lotro/cards/packs/SetRarity.java | 3 + .../java/com/gempukku/lotro/game/Player.java | 1 + .../lotro/collection/CollectionsManager.java | 38 ++++ .../com/gempukku/lotro/db/GameHistoryDAO.java | 30 ++++ .../com/gempukku/lotro/db/MerchantDAO.java | 27 +++ .../lotro/league/ConstructedLeagueData.java | 3 +- .../gempukku/lotro/league/LeaguePrizes.java | 2 + .../lotro/league/SealedLeagueData.java | 3 +- .../com/gempukku/lotro/merchant/Merchant.java | 13 ++ .../lotro/merchant/MerchantException.java | 7 + .../lotro/merchant/MerchantPriceModel.java | 9 - .../lotro/merchant/MerchantService.java | 162 ++++++++++++++++++ .../merchant/ParameterMerchantPriceModel.java | 21 --- .../lotro/merchant/ParametrizedMerchant.java | 73 ++++++++ .../lotro/merchant/MockMerchantDAO.java | 25 +++ .../merchant/ParametrizedMerchantTest.java | 26 +++ .../lotro/server/CollectionResource.java | 6 +- .../lotro/server/MerchantResource.java | 126 ++++++++++++++ .../gempukku/lotro/server/ServerProvider.java | 25 ++- .../gempukku/lotro/server/ServerResource.java | 1 + 21 files changed, 568 insertions(+), 39 deletions(-) create mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/MerchantDAO.java create mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/Merchant.java create mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantException.java delete mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantPriceModel.java create mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantService.java delete mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParameterMerchantPriceModel.java create mode 100644 gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParametrizedMerchant.java create mode 100644 gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/MockMerchantDAO.java create mode 100644 gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/ParametrizedMerchantTest.java create mode 100644 gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/MerchantResource.java diff --git a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/DefaultSetRarity.java b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/DefaultSetRarity.java index 381927483..31519cdf0 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/DefaultSetRarity.java +++ b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/DefaultSetRarity.java @@ -3,6 +3,7 @@ package com.gempukku.lotro.cards.packs; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; public class DefaultSetRarity implements SetRarity { private List _tengwarCards; @@ -34,4 +35,9 @@ public class DefaultSetRarity implements SetRarity { public String getCardRarity(String cardId) { return _cardsRarity.get(cardId); } + + @Override + public Set getAllCards() { + return Collections.unmodifiableSet(_cardsRarity.keySet()); + } } diff --git a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/SetRarity.java b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/SetRarity.java index e51d29179..d4057dd90 100644 --- a/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/SetRarity.java +++ b/gemp-lotr/gemp-lotr-cards/src/main/java/com/gempukku/lotro/cards/packs/SetRarity.java @@ -1,6 +1,7 @@ package com.gempukku.lotro.cards.packs; import java.util.List; +import java.util.Set; public interface SetRarity { public List getCardsOfRarity(String rarity); @@ -8,4 +9,6 @@ public interface SetRarity { public List getTengwarCards(); public String getCardRarity(String cardId); + + public Set getAllCards(); } diff --git a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/game/Player.java b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/game/Player.java index eed37b2fd..5f1555d47 100644 --- a/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/game/Player.java +++ b/gemp-lotr/gemp-lotr-logic/src/main/java/com/gempukku/lotro/game/Player.java @@ -10,6 +10,7 @@ public class Player { _id = id; _name = name; _type = type; + _lastLoginReward = lastLoginReward; } public int getId() { diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/CollectionsManager.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/CollectionsManager.java index 4a7a1e023..3b28e3a83 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/CollectionsManager.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/CollectionsManager.java @@ -109,6 +109,44 @@ public class CollectionsManager { addItemsToPlayerCollection(_playerDAO.getPlayer(player), collectionType, items); } + public boolean buyCardToPlayerCollection(Player player, CollectionType collectionType, String blueprintId, int currency) { + _readWriteLock.writeLock().lock(); + try { + final CardCollection playerCollection = getPlayerCollection(player, collectionType.getCode()); + if (playerCollection != null) { + MutableCardCollection mutableCardCollection = new DefaultCardCollection(playerCollection); + if (!mutableCardCollection.removeCurrency(currency)) + return false; + mutableCardCollection.addItem(blueprintId, 1); + + _collectionDAO.setCollectionForPlayer(player.getId(), collectionType.getCode(), mutableCardCollection); + return true; + } + return false; + } finally { + _readWriteLock.writeLock().unlock(); + } + } + + public boolean sellCardInPlayerCollection(Player player, CollectionType collectionType, String blueprintId, int currency) { + _readWriteLock.writeLock().lock(); + try { + final CardCollection playerCollection = getPlayerCollection(player, collectionType.getCode()); + if (playerCollection != null) { + MutableCardCollection mutableCardCollection = new DefaultCardCollection(playerCollection); + if (!mutableCardCollection.removeItem(blueprintId, 1)) + return false; + mutableCardCollection.addCurrency(currency); + + _collectionDAO.setCollectionForPlayer(player.getId(), collectionType.getCode(), mutableCardCollection); + return true; + } + return false; + } finally { + _readWriteLock.writeLock().unlock(); + } + } + public void addCurrencyToPlayerCollection(Player player, CollectionType collectionType, int currency) { _readWriteLock.writeLock().lock(); try { diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java index 3b38a5bc0..47aaa1552 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/GameHistoryDAO.java @@ -120,6 +120,36 @@ public class GameHistoryDAO { } } + public int getActivePlayersInLastMs(long ms) { + try { + Connection connection = _dbAccess.getDataSource().getConnection(); + try { + PreparedStatement statement = connection.prepareStatement( + "select count(*) from (SELECT winner FROM game_history where end_date>=? union select loser from game_history where end_date>=?) as u"); + try { + long time = System.currentTimeMillis() - ms; + statement.setLong(1, time); + statement.setLong(2, time); + ResultSet rs = statement.executeQuery(); + try { + if (rs.next()) + return rs.getInt(1); + else + return -1; + } finally { + rs.close(); + } + } finally { + statement.close(); + } + } finally { + connection.close(); + } + } catch (SQLException exp) { + throw new RuntimeException("Unable to get count of active players", exp); + } + } + public int getGamesPlayedCountInLastMs(long ms) { try { Connection connection = _dbAccess.getDataSource().getConnection(); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/MerchantDAO.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/MerchantDAO.java new file mode 100644 index 000000000..d50575851 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/db/MerchantDAO.java @@ -0,0 +1,27 @@ +package com.gempukku.lotro.db; + +import java.util.Date; + +public interface MerchantDAO { + public Transaction getLastTransaction(String blueprintId); + + public void addTransaction(String blueprintId, float price, Date date); + + public static class Transaction { + private float _price; + private Date _date; + + public Transaction(Date date, float price) { + _date = date; + _price = price; + } + + public Date getDate() { + return _date; + } + + public float getPrice() { + return _price; + } + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/ConstructedLeagueData.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/ConstructedLeagueData.java index 4cd06595d..9382c7ed3 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/ConstructedLeagueData.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/ConstructedLeagueData.java @@ -59,7 +59,8 @@ public class ConstructedLeagueData implements LeagueData { if (currentTime > DateUtils.offsetDate(lastSerie.getEnd(), 1)) { for (LeagueStanding leagueStanding : leagueStandings) { CardCollection leaguePrize = _leaguePrizes.getPrizeForLeague(leagueStanding.getStanding(), leagueStandings.size(), _prizeMultiplier, _leaguePrizePool); - collectionsManager.addItemsToPlayerCollection(leagueStanding.getPlayerName(), _prizeCollectionType, leaguePrize.getAll()); + if (leaguePrize != null) + collectionsManager.addItemsToPlayerCollection(leagueStanding.getPlayerName(), _prizeCollectionType, leaguePrize.getAll()); } status++; } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/LeaguePrizes.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/LeaguePrizes.java index aaaa92433..26a6ad360 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/LeaguePrizes.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/LeaguePrizes.java @@ -149,6 +149,8 @@ public class LeaguePrizes { addPrizes(leaguePrize, getRandom(_blockPromos.get(format), 1)); } + if (leaguePrize.getAll().size() == 0) + return null; return leaguePrize; } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/SealedLeagueData.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/SealedLeagueData.java index 85c7fcce5..2b44ad3d7 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/SealedLeagueData.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/league/SealedLeagueData.java @@ -84,7 +84,8 @@ public class SealedLeagueData implements LeagueData { if (currentTime > DateUtils.offsetDate(lastSerie.getEnd(), 1)) { for (LeagueStanding leagueStanding : leagueStandings) { CardCollection leaguePrize = _leaguePrizes.getPrizeForLeague(leagueStanding.getStanding(), leagueStandings.size(), 1f, _format); - collectionsManager.addItemsToPlayerCollection(leagueStanding.getPlayerName(), _prizeCollectionType, leaguePrize.getAll()); + if (leaguePrize != null) + collectionsManager.addItemsToPlayerCollection(leagueStanding.getPlayerName(), _prizeCollectionType, leaguePrize.getAll()); } for (LeagueStanding leagueStanding : leagueStandings) { collectionsManager.moveCollectionToCollection(leagueStanding.getPlayerName(), _collectionType, _prizeCollectionType); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/Merchant.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/Merchant.java new file mode 100644 index 000000000..9d7a2bf5f --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/Merchant.java @@ -0,0 +1,13 @@ +package com.gempukku.lotro.merchant; + +import java.util.Date; + +public interface Merchant { + public Integer getCardSellPrice(String blueprintId, Date currentTime); + + public Integer getCardBuyPrice(String blueprintId, Date currentTime); + + public void cardSold(String blueprintId, Date currentTime, int price); + + public void cardBought(String blueprintId, Date currentTime, int price); +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantException.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantException.java new file mode 100644 index 000000000..e71d7e7ff --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantException.java @@ -0,0 +1,7 @@ +package com.gempukku.lotro.merchant; + +public class MerchantException extends Exception { + public MerchantException(String message) { + super(message); + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantPriceModel.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantPriceModel.java deleted file mode 100644 index 2e1ee97df..000000000 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantPriceModel.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gempukku.lotro.merchant; - -import java.util.Date; - -public interface MerchantPriceModel { - public int getCardSellPrice(int lastTransactionValue, Date lastTransactionTime, Date openingTime, Date currentTime); - - public int getCardBuyPrice(int lastTransactionValue, Date lastTransactionTime, Date openingTime, Date currentTime); -} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantService.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantService.java new file mode 100644 index 000000000..bac20d1ea --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/MerchantService.java @@ -0,0 +1,162 @@ +package com.gempukku.lotro.merchant; + +import com.gempukku.lotro.cards.packs.RarityReader; +import com.gempukku.lotro.cards.packs.SetRarity; +import com.gempukku.lotro.collection.CollectionsManager; +import com.gempukku.lotro.db.vo.CollectionType; +import com.gempukku.lotro.game.CardItem; +import com.gempukku.lotro.game.LotroCardBlueprintLibrary; +import com.gempukku.lotro.game.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class MerchantService { + private Merchant _merchant; + private long _priceGuaranteeExpire = 1000 * 60 * 5; + private Map _priceGuarantees = new ConcurrentHashMap(); + + private ReadWriteLock _lock = new ReentrantReadWriteLock(true); + private Set _merchantableItems = new HashSet(); + private CollectionType _permanentCollection = new CollectionType("permanent", "My cards"); + private CollectionsManager _collectionsManager; + + public MerchantService(LotroCardBlueprintLibrary library, CollectionsManager collectionsManager) { + _collectionsManager = collectionsManager; + ParametrizedMerchant parametrizedMerchant = new ParametrizedMerchant(); + parametrizedMerchant.setMerchantSetupDate(new Date()); + _merchant = parametrizedMerchant; + + RarityReader rarityReader = new RarityReader(); + for (int i = 0; i <= 19; i++) { + SetRarity rarity = rarityReader.getSetRarity(String.valueOf(i)); + for (String blueprintId : rarity.getAllCards()) + _merchantableItems.add(new BasicCardItem(library.getBaseBlueprintId(blueprintId))); + } + } + + public Set getSellableItems() { + return Collections.unmodifiableSet(_merchantableItems); + } + + public PriceGuarantee priceCards(Player player, Collection cardBlueprintIds) { + Lock lock = _lock.readLock(); + lock.lock(); + try { + Date currentTime = new Date(); + Map buyPrices = new HashMap(); + Map sellPrices = new HashMap(); + for (CardItem cardItem : cardBlueprintIds) { + Integer buyPrice = _merchant.getCardBuyPrice(cardItem.getBlueprintId(), currentTime); + if (buyPrice != null) + buyPrices.put(cardItem.getBlueprintId(), buyPrice); + Integer sellPrice = _merchant.getCardSellPrice(cardItem.getBlueprintId(), currentTime); + if (sellPrice != null) + sellPrices.put(cardItem.getBlueprintId(), sellPrice); + } + PriceGuarantee priceGuarantee = new PriceGuarantee(sellPrices, buyPrices, currentTime); + _priceGuarantees.put(player, priceGuarantee); + return priceGuarantee; + } finally { + lock.unlock(); + } + } + + public void sellCard(Player player, String blueprintId, int price) throws MerchantException { + Date currentTime = new Date(); + Lock lock = _lock.writeLock(); + lock.lock(); + try { + PriceGuarantee guarantee = _priceGuarantees.get(player); + if (guarantee == null || guarantee.getDate().getTime() + _priceGuaranteeExpire < currentTime.getTime()) + throw new MerchantException("Price guarantee has expired"); + Integer guaranteedPrice = guarantee.getSellPrices().get(blueprintId); + if (guaranteedPrice == null || price != guaranteedPrice) + throw new MerchantException("Guaranteed price does not match the user asked price"); + + boolean success = _collectionsManager.sellCardInPlayerCollection(player, _permanentCollection, blueprintId, price); + if (!success) + throw new MerchantException("Unable to remove the sold card from your collection"); + } finally { + lock.unlock(); + } + } + + public void buyCard(Player player, String blueprintId, int price) throws MerchantException { + Date currentTime = new Date(); + Lock lock = _lock.writeLock(); + lock.lock(); + try { + PriceGuarantee guarantee = _priceGuarantees.get(player); + if (guarantee == null || guarantee.getDate().getTime() + _priceGuaranteeExpire < currentTime.getTime()) + throw new MerchantException("Price guarantee has expired"); + Integer guaranteedPrice = guarantee.getBuyPrices().get(blueprintId); + if (guaranteedPrice == null || price != guaranteedPrice) + throw new MerchantException("Guaranteed price does not match the user asked price"); + + boolean success = _collectionsManager.buyCardToPlayerCollection(player, _permanentCollection, blueprintId, price); + if (!success) + throw new MerchantException("Unable to remove required currency from your collection"); + } finally { + lock.unlock(); + } + } + + private static class BasicCardItem implements CardItem { + private String _blueprintId; + + private BasicCardItem(String blueprintId) { + _blueprintId = blueprintId; + } + + @Override + public String getBlueprintId() { + return _blueprintId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BasicCardItem that = (BasicCardItem) o; + + if (_blueprintId != null ? !_blueprintId.equals(that._blueprintId) : that._blueprintId != null) + return false; + + return true; + } + + @Override + public int hashCode() { + return _blueprintId != null ? _blueprintId.hashCode() : 0; + } + } + + public static class PriceGuarantee { + private Map _sellPrices; + private Map _buyPrices; + private Date _date; + + private PriceGuarantee(Map sellPrices, Map buyPrices, Date date) { + _sellPrices = sellPrices; + _buyPrices = buyPrices; + _date = date; + } + + public Date getDate() { + return _date; + } + + public Map getBuyPrices() { + return _buyPrices; + } + + public Map getSellPrices() { + return _sellPrices; + } + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParameterMerchantPriceModel.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParameterMerchantPriceModel.java deleted file mode 100644 index a8f49d4bc..000000000 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParameterMerchantPriceModel.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gempukku.lotro.merchant; - -import java.util.Date; - -public class ParameterMerchantPriceModel implements MerchantPriceModel { - private static final int DAY = 24 * 60 * 60 * 1000; - private float _profitMargin = 0.7f; - private long _priceRevertTime = 5 * DAY; - private long _easingTime = 30 * DAY; - - @Override - public int getCardBuyPrice(int lastTransactionValue, Date lastTransactionTime, Date openingTime, Date currentTime) { - return (int) Math.floor(_profitMargin - * getCardSellPrice(lastTransactionValue, lastTransactionTime, openingTime, currentTime)); - } - - @Override - public int getCardSellPrice(int lastTransactionValue, Date lastTransactionTime, Date openingTime, Date currentTime) { - return lastTransactionValue / (1 + ()); - } -} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParametrizedMerchant.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParametrizedMerchant.java new file mode 100644 index 000000000..d8fdfe416 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/merchant/ParametrizedMerchant.java @@ -0,0 +1,73 @@ +package com.gempukku.lotro.merchant; + +import com.gempukku.lotro.cards.packs.RarityReader; +import com.gempukku.lotro.cards.packs.SetRarity; +import com.gempukku.lotro.db.MerchantDAO; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class ParametrizedMerchant implements Merchant { + private static final long DAY = 1000 * 60 * 60 * 24; + + private Date _merchantSetupDate; + + private float _fluctuationValue = 0.1f; + private long _priceRevertTimeMs = 5 * DAY; + private long _easingTimeMs = 30 * DAY; + private float _profitMargin = 0.7f; + + private MerchantDAO _merchantDao; + private Map _rarity = new HashMap(); + + public ParametrizedMerchant() { + RarityReader rarityReader = new RarityReader(); + for (int i = 0; i <= 19; i++) + _rarity.put(i, rarityReader.getSetRarity(String.valueOf(i))); + } + + public void setMerchantSetupDate(Date merchantSetupDate) { + _merchantSetupDate = merchantSetupDate; + } + + public void setMerchantDao(MerchantDAO merchantDao) { + _merchantDao = merchantDao; + } + + @Override + public Integer getCardBuyPrice(String blueprintId, Date currentTime) { + double normalPrice = getNormalPrice(blueprintId, currentTime); + return (int) Math.floor(_profitMargin * normalPrice / getSetupComponent(currentTime)); + } + + @Override + public Integer getCardSellPrice(String blueprintId, Date currentTime) { + double normalPrice = getNormalPrice(blueprintId, currentTime); + double setupComponent = getSetupComponent(currentTime); + System.out.println(setupComponent); + return (int) Math.ceil(normalPrice * setupComponent); + } + + private double getSetupComponent(Date currentTime) { + if (currentTime.getTime() < _merchantSetupDate.getTime() + _easingTimeMs) + return 1 + ((_easingTimeMs - (currentTime.getTime() - _merchantSetupDate.getTime())) / (5d * DAY)); + return 1; + } + + private double getNormalPrice(String blueprintId, Date currentTime) { + MerchantDAO.Transaction lastTrans = _merchantDao.getLastTransaction(blueprintId); + // (stored price)/(1+(fluctuation value * ms since last transaction)/(price revert time in ms)) + return lastTrans.getPrice() / (1 + (_fluctuationValue * Math.pow(1f * (currentTime.getTime() - lastTrans.getDate().getTime()) / _priceRevertTimeMs, 0.9))); + } + + @Override + public void cardBought(String blueprintId, Date currentTime, int price) { + _merchantDao.addTransaction(blueprintId, (price / _profitMargin) * (1 + _fluctuationValue), currentTime); + } + + @Override + public void cardSold(String blueprintId, Date currentTime, int price) { + _merchantDao.addTransaction(blueprintId, price * (1 + _fluctuationValue), currentTime); + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/MockMerchantDAO.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/MockMerchantDAO.java new file mode 100644 index 000000000..eb1bceba9 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/MockMerchantDAO.java @@ -0,0 +1,25 @@ +package com.gempukku.lotro.merchant; + +import com.gempukku.lotro.db.MerchantDAO; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class MockMerchantDAO implements MerchantDAO { + private Map _prices = new HashMap(); + private Map _dates = new HashMap(); + + @Override + public void addTransaction(String blueprintId, float price, Date date) { + _prices.put(blueprintId, price); + _dates.put(blueprintId, date); + } + + @Override + public Transaction getLastTransaction(String blueprintId) { + if (_dates.containsKey(blueprintId)) + return new Transaction(_dates.get(blueprintId), _prices.get(blueprintId)); + return null; + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/ParametrizedMerchantTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/ParametrizedMerchantTest.java new file mode 100644 index 000000000..674429a75 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/merchant/ParametrizedMerchantTest.java @@ -0,0 +1,26 @@ +package com.gempukku.lotro.merchant; + +import com.gempukku.lotro.db.MerchantDAO; +import org.junit.Test; + +import java.util.Date; + +public class ParametrizedMerchantTest { + @Test + public void checkBuying() { + Date setupDate = new Date(0); + Date firstTrans = new Date(0); + + MerchantDAO merchantDao = new MockMerchantDAO(); + + ParametrizedMerchant merchant = new ParametrizedMerchant(); + merchant.setMerchantDao(merchantDao); + merchant.setMerchantSetupDate(setupDate); + + merchant.cardSold("1_1", firstTrans, 1000); + + Date buyTime = new Date(1000 * 60 * 60 * 12); + System.out.println(merchant.getCardBuyPrice("1_1", buyTime)); + System.out.println(merchant.getCardSellPrice("1_1", buyTime)); + } +} diff --git a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/CollectionResource.java b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/CollectionResource.java index fc46be88e..3ffd3ea47 100644 --- a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/CollectionResource.java +++ b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/CollectionResource.java @@ -108,8 +108,9 @@ public class CollectionResource extends AbstractResource { doc.appendChild(collectionElem); int index = 0; - for (CardCollection.Item item : filteredResult) { - if (index >= start && index < start + count) { + for (int i = start; i < start + count; i++) { + if (i >= 0 && i < filteredResult.size()) { + CardCollection.Item item = filteredResult.get(i); String blueprintId = item.getBlueprintId(); if (item.getType() == CardCollection.Item.Type.CARD) { Element card = doc.createElement("card"); @@ -134,7 +135,6 @@ public class CollectionResource extends AbstractResource { collectionElem.appendChild(pack); } } - index++; } processDeliveryServiceNotification(request, response); diff --git a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/MerchantResource.java b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/MerchantResource.java new file mode 100644 index 000000000..2642d87ea --- /dev/null +++ b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/MerchantResource.java @@ -0,0 +1,126 @@ +package com.gempukku.lotro.server; + +import com.gempukku.lotro.cards.packs.RarityReader; +import com.gempukku.lotro.cards.packs.SetRarity; +import com.gempukku.lotro.collection.CollectionsManager; +import com.gempukku.lotro.game.*; +import com.gempukku.lotro.merchant.MerchantService; +import com.sun.jersey.spi.resource.Singleton; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Singleton +@Path("/merchant") +public class MerchantResource extends AbstractResource { + @Context + private CollectionsManager _collectionsManager; + @Context + private MerchantService _merchantService; + @Context + private LotroCardBlueprintLibrary _library; + + private Map _rarities; + private SortAndFilterCards _sortAndFilterCards = new SortAndFilterCards(); + + public MerchantResource() { + _rarities = new HashMap(); + RarityReader reader = new RarityReader(); + _rarities.put("0", reader.getSetRarity("0")); + _rarities.put("1", reader.getSetRarity("1")); + _rarities.put("2", reader.getSetRarity("2")); + _rarities.put("3", reader.getSetRarity("3")); + _rarities.put("4", reader.getSetRarity("4")); + _rarities.put("5", reader.getSetRarity("5")); + _rarities.put("6", reader.getSetRarity("6")); + _rarities.put("7", reader.getSetRarity("7")); + _rarities.put("8", reader.getSetRarity("8")); + _rarities.put("9", reader.getSetRarity("9")); + _rarities.put("10", reader.getSetRarity("10")); + _rarities.put("11", reader.getSetRarity("11")); + _rarities.put("12", reader.getSetRarity("12")); + _rarities.put("13", reader.getSetRarity("13")); + _rarities.put("14", reader.getSetRarity("14")); + _rarities.put("15", reader.getSetRarity("15")); + _rarities.put("16", reader.getSetRarity("16")); + _rarities.put("17", reader.getSetRarity("17")); + _rarities.put("18", reader.getSetRarity("18")); + _rarities.put("19", reader.getSetRarity("19")); + } + + @Path("/list") + @GET + @Produces(MediaType.APPLICATION_XML) + public Document getCollection( + @QueryParam("participantId") String participantId, + @QueryParam("filter") String filter, + @QueryParam("start") int start, + @QueryParam("count") int count, + @Context HttpServletRequest request, + @Context HttpServletResponse response) throws ParserConfigurationException { + Player resourceOwner = getResourceOwnerSafely(request, participantId); + + CardCollection collection = _collectionsManager.getPlayerCollection(resourceOwner, "permanent"); + + List cardItems = new ArrayList(); + cardItems.addAll(collection.getAllItems()); + cardItems.addAll(_merchantService.getSellableItems()); + List filteredResult = _sortAndFilterCards.process(filter, cardItems, _library, _rarities); + + List pageToDisplay = new ArrayList(); + for (int i = start; i < start + count; i++) { + if (i >= 0 && i < filteredResult.size()) + pageToDisplay.add(filteredResult.get(i)); + } + + MerchantService.PriceGuarantee priceGuarantee = _merchantService.priceCards(resourceOwner, pageToDisplay); + Map buyPrices = priceGuarantee.getBuyPrices(); + Map sellPrices = priceGuarantee.getSellPrices(); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document doc = documentBuilder.newDocument(); + + Element collectionElem = doc.createElement("merchant"); + collectionElem.setAttribute("count", String.valueOf(filteredResult.size())); + doc.appendChild(collectionElem); + + for (CardItem cardItem : pageToDisplay) { + String blueprintId = cardItem.getBlueprintId(); + + Element elem; + if (blueprintId.contains("_")) + elem = doc.createElement("card"); + else + elem = doc.createElement("pack"); + + elem.setAttribute("count", String.valueOf(collection.getItemCount(blueprintId))); + elem.setAttribute("blueprintId", blueprintId); + Integer buyPrice = buyPrices.get(blueprintId); + if (buyPrice != null) + elem.setAttribute("buyPrice", buyPrice.toString()); + Integer sellPrice = sellPrices.get(blueprintId); + if (sellPrice != null) + elem.setAttribute("sellPrice", sellPrice.toString()); + collectionElem.appendChild(elem); + } + + return doc; + } +} diff --git a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerProvider.java b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerProvider.java index 63bab4239..f4720c9b1 100644 --- a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerProvider.java +++ b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerProvider.java @@ -9,6 +9,7 @@ import com.gempukku.lotro.game.LotroServer; import com.gempukku.lotro.game.formats.LotroFormatLibrary; import com.gempukku.lotro.hall.HallServer; import com.gempukku.lotro.league.LeagueService; +import com.gempukku.lotro.merchant.MerchantService; import com.gempukku.lotro.trade.TradeServer; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.core.spi.component.ComponentScope; @@ -22,7 +23,8 @@ import java.lang.reflect.Type; @Provider public class ServerProvider implements InjectableProvider { private Injectable _chatServerInjectable; - private Injectable _leagueServerInjectable; + private Injectable _leagueServiceInjectable; + private Injectable _merchantServiceInjectable; private Injectable _hallServerInjectable; private Injectable _lotroServerInjectable; private Injectable _tradeServerInjectable; @@ -65,6 +67,8 @@ public class ServerProvider implements InjectableProvider { return getDeliveryServiceInjectable(); if (type.equals(LotroFormatLibrary.class)) return getLotroFormatLibraryInjectable(); + if (type.equals(MerchantService.class)) + return getMerchantServiceInjectable(); return null; } @@ -82,16 +86,29 @@ public class ServerProvider implements InjectableProvider { } private synchronized Injectable getLeagueServiceInjectable() { - if (_leagueServerInjectable == null) { + if (_leagueServiceInjectable == null) { final LeagueService leagueService = new LeagueService(_leagueDao, _leaguePointsDao, _leagueMatchDao, getCollectionsManagerInjectable().getValue()); - _leagueServerInjectable = new Injectable() { + _leagueServiceInjectable = new Injectable() { @Override public LeagueService getValue() { return leagueService; } }; } - return _leagueServerInjectable; + return _leagueServiceInjectable; + } + + private synchronized Injectable getMerchantServiceInjectable() { + if (_merchantServiceInjectable == null) { + final MerchantService merchantService = new MerchantService(_library, getCollectionsManagerInjectable().getValue()); + _merchantServiceInjectable = new Injectable() { + @Override + public MerchantService getValue() { + return merchantService; + } + }; + } + return _merchantServiceInjectable; } private synchronized Injectable getDeliveryServiceInjectable() { diff --git a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java index 4c879d96d..126ebed6c 100644 --- a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java +++ b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java @@ -190,6 +190,7 @@ public class ServerResource extends AbstractResource { sb.append("You are not logged in, log in below or ."); sb.append("
Tables count: ").append(_hallServer.getTablesCount()).append(", players in hall: ").append(_chatServer.getChatRoom("Game Hall").getUsersInRoom().size()) .append(", games played in last 24 hours: ").append(_gameHistoryDao.getGamesPlayedCountInLastMs(1000 * 60 * 60 * 24)) + .append(",
active players in last week: ").append(_gameHistoryDao.getActivePlayersInLastMs(1000 * 60 * 60 * 24 * 7)) .append("
"); sb.append(getLoginHTML()); }