Working on trades.

This commit is contained in:
marcins78@gmail.com
2012-01-18 18:17:08 +00:00
parent 5d292e7c4b
commit b847d9ea7b
8 changed files with 517 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
package com.gempukku.lotro.trade;
public class TradeRequest {
private String _from;
private String _to;
private long _createdDate;
public TradeRequest(String from, String to) {
_from = from;
_to = to;
_createdDate = System.currentTimeMillis();
}
public String getFrom() {
return _from;
}
public String getTo() {
return _to;
}
public long getCreatedDate() {
return _createdDate;
}
}

View File

@@ -0,0 +1,33 @@
package com.gempukku.lotro.trade;
import com.gempukku.lotro.game.CardCollection;
public class TradeResult {
private String _initiatingPlayer;
private String _joiningPlayer;
private CardCollection _initiatingPlayerItems;
private CardCollection _joiningPlayerItems;
public TradeResult(String initiatingPlayer, CardCollection initiatingPlayerItems, String joiningPlayer, CardCollection joiningPlayerItems) {
_initiatingPlayer = initiatingPlayer;
_initiatingPlayerItems = initiatingPlayerItems;
_joiningPlayer = joiningPlayer;
_joiningPlayerItems = joiningPlayerItems;
}
public String getInitiatingPlayer() {
return _initiatingPlayer;
}
public CardCollection getInitiatingPlayerItems() {
return _initiatingPlayerItems;
}
public String getJoiningPlayer() {
return _joiningPlayer;
}
public CardCollection getJoiningPlayerItems() {
return _joiningPlayerItems;
}
}

View File

@@ -0,0 +1,169 @@
package com.gempukku.lotro.trade;
import com.gempukku.lotro.AbstractServer;
import com.gempukku.lotro.chat.ChatServer;
import com.gempukku.lotro.game.Player;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TradeServer extends AbstractServer {
private Map<String, TradeRequest> _tradeRequestsFrom = new LinkedHashMap<String, TradeRequest>();
private Map<String, List<TradeRequest>> _tradeRequestsTo = new HashMap<String, List<TradeRequest>>();
private Set<TradeState> _ongoingTrades = new HashSet<TradeState>();
private Map<String, TradeState> _playerOngoingTrade = new HashMap<String, TradeState>();
private ReadWriteLock _tradeLock = new ReentrantReadWriteLock();
private static final long TRADE_REQUEST_TIMEOUT_MS = 1000 * 60 * 5; // 5 minutes
private static final long TRADE_TIMEOUT_MS = 1000 * 30; // 30 seconds
private ChatServer _chatServer;
public TradeServer(ChatServer chatServer) {
_chatServer = chatServer;
}
public void sendTradeRequest(Player playerFrom, String playerNameTo) {
_tradeLock.writeLock().lock();
try {
if (noOngoingActivity(playerFrom)) {
createTradeRequest(playerFrom, playerNameTo);
}
} finally {
_tradeLock.writeLock().unlock();
}
}
public void cancelTradeRequest(Player playerFrom) {
_tradeLock.writeLock().lock();
try {
final TradeRequest tradeRequest = _tradeRequestsFrom.get(playerFrom.getName());
if (tradeRequest != null) {
destroyTradeRequest(tradeRequest);
}
} finally {
_tradeLock.writeLock().unlock();
}
}
public void acceptTradeRequest(Player player, String requestFrom) {
_tradeLock.writeLock().lock();
try {
if (noOngoingActivity(player)) {
final TradeRequest tradeRequest = _tradeRequestsFrom.get(requestFrom);
if (tradeRequest != null
&& tradeRequest.getTo().equals(player.getName())) {
destroyTradeRequest(tradeRequest);
createNewOngoingTrade(tradeRequest.getFrom(), tradeRequest.getTo());
}
}
} finally {
_tradeLock.writeLock().unlock();
}
}
public TradeState getOngoingTrade(Player player) {
_tradeLock.readLock().lock();
try {
return _playerOngoingTrade.get(player.getName());
} finally {
_tradeLock.readLock().unlock();
}
}
public void processTradeVisitor(Player player, TradesVisitor tradesVisitor) {
_tradeLock.readLock().lock();
try {
final TradeRequest tradeRequest = _tradeRequestsFrom.get(player.getName());
if (tradeRequest != null)
tradesVisitor.processPendingTradeRequestSent(tradeRequest.getTo());
final List<TradeRequest> incomingTradeRequests = _tradeRequestsTo.get(player.getName());
if (incomingTradeRequests != null) {
for (TradeRequest incomingTradeRequest : incomingTradeRequests)
tradesVisitor.processPendingTradeRequestIncoming(incomingTradeRequest.getFrom());
}
final TradeState ongoingTrade = _playerOngoingTrade.get(player.getName());
if (ongoingTrade != null)
tradesVisitor.processOngoingTrade(ongoingTrade, getChatRoomName(ongoingTrade.getInitiatingPlayer(), ongoingTrade.getJoiningPlayer()));
} finally {
_tradeLock.readLock().unlock();
}
}
private boolean noOngoingActivity(Player player) {
return (!_tradeRequestsFrom.containsKey(player.getName())
&& !_playerOngoingTrade.containsKey(player.getName()));
}
private void createTradeRequest(Player playerFrom, String playerNameTo) {
// Create new trade request
final TradeRequest newTradeRequest = new TradeRequest(playerFrom.getName(), playerNameTo);
// Add it to outgoing
_tradeRequestsFrom.put(playerFrom.getName(), newTradeRequest);
// Add it to incoming
List<TradeRequest> incomingTradeRequests = _tradeRequestsTo.get(playerNameTo);
if (incomingTradeRequests == null) {
incomingTradeRequests = new LinkedList<TradeRequest>();
_tradeRequestsTo.put(playerNameTo, incomingTradeRequests);
}
incomingTradeRequests.add(newTradeRequest);
}
private void destroyTradeRequest(TradeRequest tradeRequest) {
final String tradeFrom = tradeRequest.getFrom();
final String tradeTo = tradeRequest.getTo();
_tradeRequestsFrom.remove(tradeFrom);
_tradeRequestsTo.get(tradeTo).remove(tradeRequest);
if (_tradeRequestsTo.get(tradeTo).size() == 0)
_tradeRequestsTo.remove(tradeTo);
}
private void createNewOngoingTrade(String from, String to) {
TradeState tradeState = new TradeState(from, to);
_ongoingTrades.add(tradeState);
_playerOngoingTrade.put(from, tradeState);
_playerOngoingTrade.put(to, tradeState);
_chatServer.createChatRoom(getChatRoomName(from, to), 30);
}
private void destroyOngoingTrade(TradeState tradeState) {
_ongoingTrades.remove(tradeState);
_playerOngoingTrade.remove(tradeState.getInitiatingPlayer());
_playerOngoingTrade.remove(tradeState.getJoiningPlayer());
_chatServer.destroyChatRoom(getChatRoomName(tradeState.getInitiatingPlayer(), tradeState.getJoiningPlayer()));
}
private String getChatRoomName(String from, String to) {
return "Trade - " + from + " and " + to;
}
@Override
protected void cleanup() {
_tradeLock.writeLock().lock();
try {
long now = System.currentTimeMillis();
for (TradeRequest tradeRequest : new ArrayList<TradeRequest>(_tradeRequestsFrom.values())) {
if (tradeRequest.getCreatedDate() < now + TRADE_REQUEST_TIMEOUT_MS)
destroyTradeRequest(tradeRequest);
}
for (TradeState tradeState : new HashSet<TradeState>(_ongoingTrades)) {
if (tradeState.isTradeFinished() || tradeState.getLastActivity() < now + TRADE_TIMEOUT_MS)
destroyOngoingTrade(tradeState);
}
} finally {
_tradeLock.writeLock().unlock();
}
}
}

View File

@@ -0,0 +1,144 @@
package com.gempukku.lotro.trade;
import com.gempukku.lotro.game.DefaultCardCollection;
import com.gempukku.lotro.game.MutableCardCollection;
public class TradeState {
private String _initiatingPlayer;
private String _joiningPlayer;
private MutableCardCollection _initiatingPlayerItems = new DefaultCardCollection();
private MutableCardCollection _joiningPlayerItems = new DefaultCardCollection();
private boolean _initiatingPlayerAgreed;
private boolean _joiningPlayerAgreed;
private int _tradeState;
private int _initiatingPlayerConfirmedState = -1;
private int _joiningPlayerConfirmedState = -1;
private boolean _tradeFinished;
private long _lastActivity;
public TradeState(String initiatingPlayer, String joiningPlayer) {
_initiatingPlayer = initiatingPlayer;
_joiningPlayer = joiningPlayer;
}
public synchronized void addItemsWanted(String player, String item, int count) {
if (player.equals(_initiatingPlayer))
adjustJoiningItems(item, count);
else if (player.equals(_joiningPlayer))
adjustInitiatingItems(item, count);
}
public synchronized void removeItemsWanted(String player, String item, int count) {
if (player.equals(_initiatingPlayer))
adjustJoiningItems(item, -count);
else if (player.equals(_joiningPlayer))
adjustInitiatingItems(item, -count);
}
public synchronized void addItemsOffered(String player, String item, int count) {
if (player.equals(_initiatingPlayer))
adjustInitiatingItems(item, count);
else if (player.equals(_joiningPlayer))
adjustJoiningItems(item, count);
}
public synchronized void removeItemsOffered(String player, String item, int count) {
if (player.equals(_initiatingPlayer))
adjustInitiatingItems(item, -count);
else if (player.equals(_joiningPlayer))
adjustJoiningItems(item, -count);
}
public synchronized void playerAgreed(String player) {
if (!_tradeFinished) {
if (player.equals(_initiatingPlayer))
_initiatingPlayerAgreed = true;
else if (player.equals(_joiningPlayer))
_joiningPlayerAgreed = true;
if (_initiatingPlayerAgreed && _joiningPlayerAgreed)
agreementStateReached();
}
}
public synchronized TradeResult playerConfirmed(String player, int tradeState) {
if (!_tradeFinished) {
if (player.equals(_initiatingPlayer))
_initiatingPlayerConfirmedState = tradeState;
else if (player.equals(_joiningPlayer))
_joiningPlayerConfirmedState = tradeState;
if (_initiatingPlayerConfirmedState == _joiningPlayerConfirmedState
&& _initiatingPlayerConfirmedState != -1) {
_tradeFinished = true;
return new TradeResult(_initiatingPlayer, _initiatingPlayerItems,
_joiningPlayer, _joiningPlayerItems);
}
}
return null;
}
public synchronized void processTradeStateVisitor(String player, TradeStateVisitor tradeStateVisitor) {
if (player.equals(_initiatingPlayer))
tradeStateVisitor.processTradeState(
_joiningPlayer,
new DefaultCardCollection(_initiatingPlayerItems), new DefaultCardCollection(_joiningPlayerItems),
_initiatingPlayerAgreed, _joiningPlayerAgreed, _tradeState,
_initiatingPlayerConfirmedState == _tradeState, _joiningPlayerConfirmedState == _tradeState);
else if (player.equals(_joiningPlayer))
tradeStateVisitor.processTradeState(
_initiatingPlayer,
new DefaultCardCollection(_joiningPlayerItems), new DefaultCardCollection(_initiatingPlayerItems),
_joiningPlayerAgreed, _initiatingPlayerAgreed, _tradeState,
_joiningPlayerConfirmedState == _tradeState, _initiatingPlayerConfirmedState == _tradeState);
_lastActivity = System.currentTimeMillis();
}
public String getInitiatingPlayer() {
return _initiatingPlayer;
}
public String getJoiningPlayer() {
return _joiningPlayer;
}
public boolean isTradeFinished() {
return _tradeFinished;
}
public long getLastActivity() {
return _lastActivity;
}
private void agreementStateReached() {
_tradeState++;
}
private void adjustJoiningItems(String item, int diff) {
adjustItems(_joiningPlayerItems, item, diff);
}
private void adjustInitiatingItems(String item, int diff) {
adjustItems(_initiatingPlayerItems, item, diff);
}
private void adjustItems(MutableCardCollection collection, String item, int diff) {
if (!_tradeFinished) {
if (diff < 0)
collection.removeItem(item, -diff);
else
collection.addItem(item, diff);
_initiatingPlayerAgreed = false;
_joiningPlayerAgreed = false;
}
}
}

View File

@@ -0,0 +1,9 @@
package com.gempukku.lotro.trade;
import com.gempukku.lotro.game.CardCollection;
public interface TradeStateVisitor {
public void processTradeState(String otherParty, CardCollection offering, CardCollection getting,
boolean selfAccepted, boolean otherAccepted, int tradeState,
boolean selfConfirmed, boolean otherConfirmed);
}

View File

@@ -0,0 +1,9 @@
package com.gempukku.lotro.trade;
public interface TradesVisitor {
public void processPendingTradeRequestSent(String playerTo);
public void processPendingTradeRequestIncoming(String playerFrom);
public void processOngoingTrade(TradeState tradeState, String chatRoomName);
}

View File

@@ -7,6 +7,7 @@ import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.LotroServer;
import com.gempukku.lotro.hall.HallServer;
import com.gempukku.lotro.league.LeagueService;
import com.gempukku.lotro.trade.TradeServer;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
@@ -22,6 +23,7 @@ public class ServerProvider implements InjectableProvider<Context, Type> {
private Injectable<LeagueService> _leagueServerInjectable;
private Injectable<HallServer> _hallServerInjectable;
private Injectable<LotroServer> _lotroServerInjectable;
private Injectable<TradeServer> _tradeServerInjectable;
private Injectable<CollectionsManager> _collectionsManagerInjectable;
@Context
@@ -55,6 +57,8 @@ public class ServerProvider implements InjectableProvider<Context, Type> {
return getLeagueServiceInjectable();
if (type.equals(CollectionsManager.class))
return getCollectionsManagerInjectable();
if (type.equals(TradeServer.class))
return getTradeServerInjectable();
return null;
}
@@ -85,6 +89,20 @@ public class ServerProvider implements InjectableProvider<Context, Type> {
return _hallServerInjectable;
}
private synchronized Injectable<TradeServer> getTradeServerInjectable() {
if (_tradeServerInjectable == null) {
final TradeServer tradeServer = new TradeServer(getChatServerInjectable().getValue());
tradeServer.startServer();
_tradeServerInjectable = new Injectable<TradeServer>() {
@Override
public TradeServer getValue() {
return tradeServer;
}
};
}
return _tradeServerInjectable;
}
private synchronized Injectable<LotroServer> getLotroServerInjectable() {
if (_lotroServerInjectable == null) {
final LotroServer lotroServer = new LotroServer(_deckDao, _gameHistoryDao, _library, getChatServerInjectable().getValue(), false);

View File

@@ -0,0 +1,110 @@
package com.gempukku.lotro.server;
import com.gempukku.lotro.game.CardCollection;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.Player;
import com.gempukku.lotro.trade.TradeServer;
import com.gempukku.lotro.trade.TradeState;
import com.gempukku.lotro.trade.TradeStateVisitor;
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.Map;
@Singleton
@Path("/trade")
public class TradeResource extends AbstractResource {
@Context
private TradeServer _tradeServer;
@Context
private LotroCardBlueprintLibrary _library;
@GET
@Produces(MediaType.APPLICATION_XML)
public Document getOngoingTrade(@QueryParam("participantId") String participantId,
@Context HttpServletRequest request,
@Context HttpServletResponse response) throws ParserConfigurationException {
Player resourceOwner = getResourceOwnerSafely(request, participantId);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element trade = doc.createElement("trade");
final TradeState ongoingTrade = _tradeServer.getOngoingTrade(resourceOwner);
if (ongoingTrade != null)
ongoingTrade.processTradeStateVisitor(resourceOwner.getName(), new OngoingTradeVisitor(doc, trade));
doc.appendChild(trade);
processDeliveryServiceNotification(request, response);
return doc;
}
private class OngoingTradeVisitor implements TradeStateVisitor {
private Document _doc;
private Element _root;
private OngoingTradeVisitor(Document doc, Element root) {
_doc = doc;
_root = root;
}
@Override
public void processTradeState(String otherParty, CardCollection offering, CardCollection getting, boolean selfAccepted, boolean otherAccepted, int tradeState, boolean selfConfirmed, boolean otherConfirmed) {
_root.setAttribute("otherParty", otherParty);
if (selfAccepted)
_root.setAttribute("selfAccepted", "true");
if (otherAccepted)
_root.setAttribute("otherAccepted", "true");
if (selfAccepted && otherAccepted)
_root.setAttribute("tradeState", String.valueOf(tradeState));
if (selfConfirmed)
_root.setAttribute("selfConfirmed", "true");
if (otherConfirmed)
_root.setAttribute("otherConfirmed", "true");
final Element offer = _doc.createElement("offer");
appendItems(_doc, offer, offering);
_root.appendChild(offer);
Element get = _doc.createElement("get");
appendItems(_doc, get, getting);
_root.appendChild(get);
}
private void appendItems(Document doc, Element elem, CardCollection collection) {
final Map<String, Integer> items = collection.getAll();
for (Map.Entry<String, Integer> itemCount : items.entrySet()) {
String blueprintId = itemCount.getKey();
if (blueprintId.contains("_")) {
Element card = doc.createElement("card");
card.setAttribute("count", String.valueOf(itemCount.getValue()));
card.setAttribute("blueprintId", blueprintId);
elem.appendChild(card);
} else {
Element pack = doc.createElement("pack");
pack.setAttribute("count", String.valueOf(itemCount.getValue()));
pack.setAttribute("blueprintId", blueprintId);
elem.appendChild(pack);
}
}
}
}
}