From 2ff71a089328eda5a3c72a0fc1a0f02d1195d51d Mon Sep 17 00:00:00 2001 From: Alyssa Date: Thu, 7 Nov 2024 20:17:24 +0000 Subject: [PATCH 1/9] ...what was I doing again? --- .../src/main/java/online/screen/GameRoom.java | 37 +- .../java/online/util/ResponseHandler.java | 13 +- .../src/main/java/screen/EntropyScreen.java | 9 +- client/src/main/java/screen/GameScreen.java | 71 +-- client/src/main/java/screen/HandPanelMk2.java | 34 +- client/src/main/java/screen/ReplayDialog.java | 40 +- client/src/main/java/util/ApiUtil.java | 11 +- client/src/main/java/util/CpuStrategies.java | 21 +- .../src/main/java/util/EntCpuStrategies.java | 15 +- client/src/main/java/util/GameUtil.java | 4 +- .../java/util/MarkStrategySuitWrapper.java | 3 +- .../src/main/java/util/VectCpuStrategies.java | 7 +- core/src/main/java/object/Bid.java | 17 +- core/src/main/java/object/EntropyBid.java | 7 +- core/src/main/java/object/FakeBid.java | 11 +- core/src/main/java/object/Player.java | 25 +- core/src/main/java/object/VectropyBid.java | 9 +- core/src/main/java/util/CardsUtil.java | 59 +- core/src/main/java/util/EntropyUtil.java | 10 +- core/src/main/java/util/VectropyUtil.java | 29 +- server/src/main/java/object/HandDetails.java | 17 +- server/src/main/java/object/Room.java | 561 ------------------ .../src/main/java/server/EntropyServer.java | 6 +- .../java/server/MessageHandlerRunnable.java | 2 +- .../src/main/java/util/XmlBuilderServer.java | 15 +- server/src/main/kotlin/room/Room.kt | 518 ++++++++++++++++ .../main/kotlin/routes/lobby/LobbyService.kt | 2 +- .../kotlin/routes/session/SessionService.kt | 2 +- 28 files changed, 743 insertions(+), 812 deletions(-) delete mode 100644 server/src/main/java/object/Room.java create mode 100644 server/src/main/kotlin/room/Room.kt diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index 4056432..539d5a0 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -20,6 +20,7 @@ import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.*; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.prefs.Preferences; @@ -60,7 +61,7 @@ public abstract class GameRoom extends JFrame public Bid lastBid = null; private ConcurrentHashMap hmPlayerByAdjustedPlayerNumber = new ConcurrentHashMap<>(); - public ConcurrentHashMap hmHandByAdjustedPlayerNumber = new ConcurrentHashMap<>(); + public ConcurrentHashMap> hmHandByAdjustedPlayerNumber = new ConcurrentHashMap<>(); public ConcurrentHashMap hmBidByPlayerNumber = new ConcurrentHashMap<>(); private int personToStartLocal = -1; private int personToStart = -1; @@ -679,7 +680,7 @@ private void sendGameInitialiseRequest(int delay) } } - public void setHand(int playerNumber, String[] hand) + public void setHand(int playerNumber, List hand) { int playerNumberAdjusted = adjustForMe(playerNumber); hmHandByAdjustedPlayerNumber.put(playerNumberAdjusted, hand); @@ -970,7 +971,7 @@ private void adjustPlayersBasedOnHands() { Map.Entry entry = it.next(); int playerNumberAdjusted = entry.getKey(); - String[] hand = hmHandByAdjustedPlayerNumber.get(playerNumberAdjusted); + List hand = hmHandByAdjustedPlayerNumber.get(playerNumberAdjusted); Player player = entry.getValue(); boolean enabled = hand != null; @@ -978,7 +979,7 @@ private void adjustPlayersBasedOnHands() if (enabled) { - player.setNumberOfCards(hand.length); + player.setNumberOfCards(hand.size()); player.resetHand(); } else if (playerNumberAdjusted == playerNumberLocal) @@ -994,10 +995,10 @@ private int getTotalFromHands() for (int i=0; i hand = hmHandByAdjustedPlayerNumber.get(i); if (hand != null) { - total += hand.length; + total += hand.size(); } } @@ -1112,28 +1113,28 @@ private void saveHands(int roundNumber) int opponentTwoNumberOfCards = 0; int opponentThreeNumberOfCards = 0; - String[] playerHand = hmHandByAdjustedPlayerNumber.get(ADJUSTED_PLAYER_NUMBER_ME); + List playerHand = hmHandByAdjustedPlayerNumber.get(ADJUSTED_PLAYER_NUMBER_ME); if (playerHand != null) { - playerNumberOfCards = playerHand.length; + playerNumberOfCards = playerHand.size(); } - String[] opponentOneHand = hmHandByAdjustedPlayerNumber.get(1); + List opponentOneHand = hmHandByAdjustedPlayerNumber.get(1); if (opponentOneHand != null) { - opponentOneNumberOfCards = opponentOneHand.length; + opponentOneNumberOfCards = opponentOneHand.size(); } - String[] opponentTwoHand = hmHandByAdjustedPlayerNumber.get(2); + List opponentTwoHand = hmHandByAdjustedPlayerNumber.get(2); if (opponentTwoHand != null) { - opponentTwoNumberOfCards = opponentTwoHand.length; + opponentTwoNumberOfCards = opponentTwoHand.size(); } - String[] opponentThreeHand = hmHandByAdjustedPlayerNumber.get(3); + List opponentThreeHand = hmHandByAdjustedPlayerNumber.get(3); if (opponentThreeHand != null) { - opponentThreeNumberOfCards = opponentThreeHand.length; + opponentThreeNumberOfCards = opponentThreeHand.size(); } //save the player hands @@ -1144,19 +1145,19 @@ private void saveHands(int roundNumber) for (int i=0; i hand = getHandFromElement(child); gameRoom.setHand(playerNumber, hand); } } - public static String[] getHandFromElement(Element handElement) + public static List getHandFromElement(Element handElement) { List cards = new ArrayList<>(); @@ -404,14 +404,7 @@ public static String[] getHandFromElement(Element handElement) } } - int size = cards.size(); - String[] hand = new String[size]; - for (int j=0; j playerHand = player.getHand(); + List opponentOneHand = opponentOne.getHand(); + List opponentTwoHand = opponentTwo.getHand(); + List opponentThreeHand = opponentThree.getHand(); int perfectBidSuitCode = EntropyUtil.getPerfectBidSuitCode(playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue, includeStars); int perfectBidAmount = EntropyUtil.getPerfectBidAmount(playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); String suit = CardsUtil.getSuitDesc(perfectBidAmount, perfectBidSuitCode); diff --git a/client/src/main/java/screen/GameScreen.java b/client/src/main/java/screen/GameScreen.java index 04d15a7..4ae3f16 100644 --- a/client/src/main/java/screen/GameScreen.java +++ b/client/src/main/java/screen/GameScreen.java @@ -770,10 +770,10 @@ public void cancelNewRound() protected String getMaxBidsStr() { - String[] playerHand = player.getHand(); - String[] opponentOneHand = opponentOne.getHand(); - String[] opponentTwoHand = opponentTwo.getHand(); - String[] opponentThreeHand = opponentThree.getHand(); + List playerHand = player.getHand(); + List opponentOneHand = opponentOne.getHand(); + List opponentTwoHand = opponentTwo.getHand(); + List opponentThreeHand = opponentThree.getHand(); int maxClubs = CardsUtil.countSuit(CardsUtil.SUIT_CLUBS, playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); int maxDiamonds = CardsUtil.countSuit(CardsUtil.SUIT_DIAMONDS, playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); @@ -807,7 +807,7 @@ protected void randomlyReplaceCardsWithJokers() int jokersToAdd = random.nextInt(5) + 1; //1-5 logger.info("rainingJokers", "Adding " + jokersToAdd + " jokers"); - String[] allCards = getConcatenatedHands(); + List allCards = getConcatenatedHands(); while (CardsUtil.containsNonJoker(allCards) && jokersToAdd > 0) { @@ -824,25 +824,25 @@ private void replaceRandomCardWithJoker() { Random rand = new Random(); String jokerToAdd = "Jo" + rand.nextInt(4); //0, 1, 2, 3 - String[] hand = pickHandWithNonJokerAtRandom(); + List hand = pickHandWithNonJokerAtRandom(); - int length = hand.length; + int length = hand.size(); int position = (rand.nextInt(length)); - String cardToReplace = hand[position]; + String cardToReplace = hand.get(position); if (cardToReplace.contains("Jo")) { replaceRandomCardWithJoker(); return; } - hand[position] = jokerToAdd; + hand.set(position, jokerToAdd); } - private String[] pickHandWithNonJokerAtRandom() + private List pickHandWithNonJokerAtRandom() { - String[][] allHands = {player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), opponentThree.getHand()}; + List[] allHands = new List[]{player.getHand(), opponentOne.getHand(), opponentTwo.getHand(), opponentThree.getHand()}; Random rand = new Random(); - String[] hand = allHands[rand.nextInt(4)]; + List hand = allHands[rand.nextInt(4)]; if (!CardsUtil.containsNonJoker(hand)) { @@ -854,42 +854,19 @@ private String[] pickHandWithNonJokerAtRandom() } } - public String[] getConcatenatedHands() + public List getConcatenatedHands() { - String[] playerHand = player.getHand(); - String[] opponentOneHand = opponentOne.getHand(); - String[] opponentTwoHand = opponentTwo.getHand(); - String[] opponentThreeHand = opponentThree.getHand(); - - int length = playerHand.length + opponentOneHand.length + opponentTwoHand.length + opponentThreeHand.length; - String[] allCards = new String[length]; - - int fromIndex = 0; - for (int i=0; i playerHand = player.getHand(); + List opponentOneHand = opponentOne.getHand(); + List opponentTwoHand = opponentTwo.getHand(); + List opponentThreeHand = opponentThree.getHand(); + + var result = new ArrayList(); + result.addAll(playerHand); + result.addAll(opponentOneHand); + result.addAll(opponentTwoHand); + result.addAll(opponentThreeHand); + return result; } private void processPlayerBid() diff --git a/client/src/main/java/screen/HandPanelMk2.java b/client/src/main/java/screen/HandPanelMk2.java index 6c08992..ba4a36b 100644 --- a/client/src/main/java/screen/HandPanelMk2.java +++ b/client/src/main/java/screen/HandPanelMk2.java @@ -13,6 +13,8 @@ import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; @@ -242,24 +244,24 @@ public HandPanelMk2(RevealListener listener) private final JLabel lblOpponentTwoSeat = new JLabel(""); private final JLabel lblOpponentThreeSeat = new JLabel(""); - public void displayHandsInGame(String[] playerHand, String[] opponentOneHand, - String[] opponentTwoHand, String[] opponentThreeHand) + public void displayHandsInGame(List playerHand, List opponentOneHand, + List opponentTwoHand, List opponentThreeHand) { initialiseCardLabels(playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand); } - public void displayHandsOnline(ConcurrentHashMap hmHandByPlayerNumber) + public void displayHandsOnline(ConcurrentHashMap> hmHandByPlayerNumber) { - String[] playerHand = hmHandByPlayerNumber.get(0); - String[] opponentOneHand = hmHandByPlayerNumber.get(1); - String[] opponentTwoHand = hmHandByPlayerNumber.get(2); - String[] opponentThreeHand = hmHandByPlayerNumber.get(3); + List playerHand = hmHandByPlayerNumber.get(0); + List opponentOneHand = hmHandByPlayerNumber.get(1); + List opponentTwoHand = hmHandByPlayerNumber.get(2); + List opponentThreeHand = hmHandByPlayerNumber.get(3); initialiseCardLabels(playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand); } - private void initialiseCardLabels(String[] playerHand, String[] opponentOneHand, String[] opponentTwoHand, - String[] opponentThreeHand) + private void initialiseCardLabels(List playerHand, List opponentOneHand, List opponentTwoHand, + List opponentThreeHand) { resetPlayerCardPositions(); @@ -279,12 +281,12 @@ private void resetPlayerCardPositions() } } - private void initialiseCardLabelsForPlayer(String[] hand, CardLabel[] labels, boolean faceUp) + private void initialiseCardLabelsForPlayer(List hand, CardLabel[] labels, boolean faceUp) { int size = 0; if (hand != null) { - size = hand.length; + size = hand.size(); } for (int i=0; i(), playerCards, false); break; case 1: opponentOneName = name; - initialiseCardLabelsForPlayer(new String[5], opponentOneCards, false); + initialiseCardLabelsForPlayer(new ArrayList<>(), opponentOneCards, false); break; case 2: opponentTwoName = name; - initialiseCardLabelsForPlayer(new String[5], opponentTwoCards, false); + initialiseCardLabelsForPlayer(new ArrayList<>(), opponentTwoCards, false); break; case 3: opponentThreeName = name; - initialiseCardLabelsForPlayer(new String[5], opponentThreeCards, false); + initialiseCardLabelsForPlayer(new ArrayList<>(), opponentThreeCards, false); break; default: Debug.stackTrace("Unexpected playerNumber [" + playerNumber + "]"); diff --git a/client/src/main/java/screen/ReplayDialog.java b/client/src/main/java/screen/ReplayDialog.java index 37818e4..686952e 100644 --- a/client/src/main/java/screen/ReplayDialog.java +++ b/client/src/main/java/screen/ReplayDialog.java @@ -10,6 +10,8 @@ import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; import java.util.prefs.Preferences; import javax.swing.BorderFactory; @@ -64,10 +66,10 @@ public class ReplayDialog extends JFrame private boolean opponentTwoEnabled = true; private boolean opponentThreeEnabled = true; - private String[] opponentOneHand = new String[5]; - private String[] playerHand = new String[5]; - private String[] opponentTwoHand = new String[5]; - private String[] opponentThreeHand = new String[5]; + private List opponentOneHand = new ArrayList<>(); + private List playerHand = new ArrayList<>(); + private List opponentTwoHand = new ArrayList<>(); + private List opponentThreeHand = new ArrayList<>(); private int jokerValue = 2; private boolean includeMoons = false; @@ -584,26 +586,26 @@ private void populateHands() opponentOneNumberOfCards = replay.getInt(roundNumber + REPLAY_INT_OPPONENT_ONE_NUMBER_OF_CARDS, 0); opponentTwoNumberOfCards = replay.getInt(roundNumber + REPLAY_INT_OPPONENT_TWO_NUMBER_OF_CARDS, 0); opponentThreeNumberOfCards = replay.getInt(roundNumber + REPLAY_INT_OPPONENT_THREE_NUMBER_OF_CARDS, 0); - opponentThreeHand = new String[opponentThreeNumberOfCards]; - opponentTwoHand = new String[opponentTwoNumberOfCards]; - opponentOneHand = new String[opponentOneNumberOfCards]; - playerHand = new String[playerNumberOfCards]; + opponentThreeHand = new ArrayList<>(); + opponentTwoHand = new ArrayList<>(); + opponentOneHand = new ArrayList<>(); + playerHand = new ArrayList<>(); for (int i = 0; i < playerNumberOfCards; i++) { - playerHand[i] = replay.get(roundNumber + REPLAY_STRING_PLAYER_HAND + i, ""); + playerHand.add(replay.get(roundNumber + REPLAY_STRING_PLAYER_HAND + i, "")); } for (int i = 0; i < opponentOneNumberOfCards; i++) { - opponentOneHand[i] = replay.get(roundNumber + REPLAY_STRING_OPPONENT_ONE_HAND + i, ""); + opponentOneHand.add(replay.get(roundNumber + REPLAY_STRING_OPPONENT_ONE_HAND + i, "")); } for (int i = 0; i < opponentTwoNumberOfCards; i++) { - opponentTwoHand[i] = replay.get(roundNumber + REPLAY_STRING_OPPONENT_TWO_HAND + i, ""); + opponentTwoHand.add(replay.get(roundNumber + REPLAY_STRING_OPPONENT_TWO_HAND + i, "")); } for (int i = 0; i < opponentThreeNumberOfCards; i++) { - opponentThreeHand[i] = replay.get(roundNumber + REPLAY_STRING_OPPONENT_THREE_HAND + i, ""); + opponentThreeHand.add(replay.get(roundNumber + REPLAY_STRING_OPPONENT_THREE_HAND + i, "")); } } @@ -633,13 +635,13 @@ private void displayHands() displayHand(opponentThreeCards, opponentThreeHand, opponentThreeNumberOfCards); } - private void displayHand(JLabel[] cardLabels, String hand[], int handSize) + private void displayHand(JLabel[] cardLabels, List hand, int handSize) { for (int i = 0; i < 5; i++) { if (i < handSize) { - ImageIcon card = GameUtil.getImageForCard(hand[i], deckDirectory, jokerDirectory, numberOfColours); + ImageIcon card = GameUtil.getImageForCard(hand.get(i), deckDirectory, jokerDirectory, numberOfColours); cardLabels[i].setIcon(card); } else @@ -657,21 +659,21 @@ private void highlightHands(int suitCode) highlightHand(suitCode, opponentThreeHand, opponentThreeCards); } - private void highlightHand(int suitCode, String[] hand, JLabel[] cards) + private void highlightHand(int suitCode, List hand, JLabel[] cards) { - int size = hand.length; + int size = hand.size(); for (int i=0; i playerHand = player.getHand(); Element handElement = document.createElement("PlayerHand"); - int length = playerHand.length; + int length = playerHand.size(); for (int i=0; i getCombinedArrayOfCardsICanSee(List hand, StrategyParms parms) { ArrayList revealedCards = parms.getCardsOnShowFromOpponents(); - int size = hand.length + revealedCards.size(); - - String[] ret = new String[size]; - for (int i=0; i(); + result.addAll(hand); + result.addAll(revealedCards); + return result; } /** diff --git a/client/src/main/java/util/EntCpuStrategies.java b/client/src/main/java/util/EntCpuStrategies.java index 204191e..77725c9 100644 --- a/client/src/main/java/util/EntCpuStrategies.java +++ b/client/src/main/java/util/EntCpuStrategies.java @@ -1,9 +1,6 @@ package util; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Random; -import java.util.Vector; +import java.util.*; import object.Bid; import object.ChallengeBid; @@ -61,7 +58,7 @@ private static Bid processMarkTurn(Player opponent, StrategyParms parms) EntropyBid bid = (EntropyBid)parms.getLastBid(); Random rand = new Random(); - String hand[] = opponent.getHand(); + List hand = opponent.getHand(); //Parms boolean includeMoons = parms.getIncludeMoons(); @@ -194,7 +191,7 @@ else if (minBiddableSuitCount == 1) { Debug.append("Couldn't automatically minbid any suit", logging); double jokerThreshold = Math.floor((jokerQuantity * jokerValue)/(double)(suitsInPlay*2)); - double threshold = bidSuitCount + totalCards - hand.length + jokerThreshold - 1; + double threshold = bidSuitCount + totalCards - hand.size() + jokerThreshold - 1; Debug.append("Bonkers threshold is " + threshold, logging); if (bidAmountFacedWith > threshold) @@ -302,7 +299,7 @@ private static Bid processBasicTurn(Player opponent, StrategyParms parms) Random coin = new Random(); int decision = coin.nextInt(2); int decisionTwo = coin.nextInt(2); - String[] hand = opponent.getHand(); + List hand = opponent.getHand(); if (bid == null) { @@ -400,9 +397,9 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) Debug.append("EV strategy for this turn", logging); Random coin = new Random(); - String[] hand = opponent.getHand(); + List hand = opponent.getHand(); - int totalOpponentCards = totalCards - hand.length; + int totalOpponentCards = totalCards - hand.size(); if (bid == null) { Debug.append("Starting this round", logging); diff --git a/client/src/main/java/util/GameUtil.java b/client/src/main/java/util/GameUtil.java index 3285aa6..bc7af73 100644 --- a/client/src/main/java/util/GameUtil.java +++ b/client/src/main/java/util/GameUtil.java @@ -108,7 +108,7 @@ public static void populateHand(Player playerToPopulate, List deck, bool { int playerNumberOfCards = playerToPopulate.getNumberOfCards(); int playerToBeDealt = playerNumberOfCards; - String[] playerHand = playerToPopulate.getHand(); + List playerHand = playerToPopulate.getHand(); while (playerToBeDealt > 0) { @@ -118,7 +118,7 @@ public static void populateHand(Player playerToPopulate, List deck, bool } String card = deck.remove(0); - playerHand[(playerNumberOfCards - playerToBeDealt)] = card; + playerHand.add(card); hand += card; playerToBeDealt--; diff --git a/client/src/main/java/util/MarkStrategySuitWrapper.java b/client/src/main/java/util/MarkStrategySuitWrapper.java index dea363b..1038e92 100644 --- a/client/src/main/java/util/MarkStrategySuitWrapper.java +++ b/client/src/main/java/util/MarkStrategySuitWrapper.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Random; import object.EntropyBid; @@ -14,7 +15,7 @@ public class MarkStrategySuitWrapper private int bestSuit = -1; private int worstSuit = -1; - public MarkStrategySuitWrapper(String[] hand, int jokerValue, boolean includeMoons, boolean includeStars, EntropyBid bid) + public MarkStrategySuitWrapper(List hand, int jokerValue, boolean includeMoons, boolean includeStars, EntropyBid bid) { applicableSuits.add(CardsUtil.SUIT_CLUBS); applicableSuits.add(CardsUtil.SUIT_DIAMONDS); diff --git a/client/src/main/java/util/VectCpuStrategies.java b/client/src/main/java/util/VectCpuStrategies.java index 2f08fe2..f9f9912 100644 --- a/client/src/main/java/util/VectCpuStrategies.java +++ b/client/src/main/java/util/VectCpuStrategies.java @@ -1,6 +1,7 @@ package util; import java.util.HashMap; +import java.util.List; import java.util.Random; import java.util.Vector; @@ -57,7 +58,7 @@ private static Bid processBasicTurn(Player opponent, StrategyParms parms) Random coin = new Random(); //Get the variables we're interested in - String hand[] = opponent.getHand(); + List hand = opponent.getHand(); VectropyBid lastBid = (VectropyBid)parms.getLastBid(); double totalCards = parms.getTotalNumberOfCards(); int jokerValue = parms.getJokerValue(); @@ -116,7 +117,7 @@ private static Bid processBasicTurn(Player opponent, StrategyParms parms) else { hand = CpuStrategies.getCombinedArrayOfCardsICanSee(hand, parms); - double unseenCards = parms.getTotalNumberOfCards() - hand.length; + double unseenCards = parms.getTotalNumberOfCards() - hand.size(); double thirdThreshold = Math.floor(totalCards/3); @@ -181,7 +182,7 @@ private static Bid processEvTurn(Player opponent, StrategyParms parms) boolean logging = parms.getLogging(); Debug.append("EV strategy for this turn", logging); Random coin = new Random(); - String hand[] = opponent.getHand(); + List hand = opponent.getHand(); //Parms VectropyBid lastBid = (VectropyBid)parms.getLastBid(); diff --git a/core/src/main/java/object/Bid.java b/core/src/main/java/object/Bid.java index c7c579e..db254da 100644 --- a/core/src/main/java/object/Bid.java +++ b/core/src/main/java/object/Bid.java @@ -2,6 +2,7 @@ package object; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.w3c.dom.Element; @@ -21,10 +22,10 @@ public abstract class Bid public abstract boolean higherThan(Bid bid); public abstract boolean isOverAchievementThreshold(); - public abstract boolean isPerfect(String[] handOne, String[] handTwo, String[] handThree, String[] handFour, + public abstract boolean isPerfect(List handOne, List handTwo, List handThree, List handFour, int jokerValue, boolean includeMoons, boolean includeStars); - public abstract boolean isOverbid(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue); - public abstract boolean isOverbid(String[] handOne, String[] handTwo, String[] handThree, String[] handFour, int jokerValue); + public abstract boolean isOverbid(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue); + public abstract boolean isOverbid(List handOne, List handTwo, List handThree, List handFour, int jokerValue); /** * Used for: @@ -79,13 +80,13 @@ public String toXmlString() return xmlStr; } - public boolean isPerfect(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue, + public boolean isPerfect(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue, boolean includeMoons, boolean includeStars) { - String[] handOne = hmHandByPlayerNumber.get(0); - String[] handTwo = hmHandByPlayerNumber.get(1); - String[] handThree = hmHandByPlayerNumber.get(2); - String[] handFour = hmHandByPlayerNumber.get(3); + List handOne = hmHandByPlayerNumber.get(0); + List handTwo = hmHandByPlayerNumber.get(1); + List handThree = hmHandByPlayerNumber.get(2); + List handFour = hmHandByPlayerNumber.get(3); return isPerfect(handOne, handTwo, handThree, handFour, jokerValue, includeMoons, includeStars); } diff --git a/core/src/main/java/object/EntropyBid.java b/core/src/main/java/object/EntropyBid.java index 11cd3bf..e37776b 100644 --- a/core/src/main/java/object/EntropyBid.java +++ b/core/src/main/java/object/EntropyBid.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.w3c.dom.Element; @@ -115,7 +116,7 @@ public boolean higherThan(Bid bid) } @Override - public boolean isPerfect(String[] handOne, String[] handTwo, String[] handThree, String[] handFour, + public boolean isPerfect(List handOne, List handTwo, List handThree, List handFour, int jokerValue, boolean includeMoons, boolean includeStars) { int perfectBidAmount = EntropyUtil.getPerfectBidAmount(handOne, handTwo, handThree, handFour, jokerValue); @@ -124,14 +125,14 @@ public boolean isPerfect(String[] handOne, String[] handTwo, String[] handThree, return bidSuitCode == perfectBidSuitCode && bidAmount == perfectBidAmount; } @Override - public boolean isOverbid(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue) + public boolean isOverbid(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue) { int total = CardsUtil.countSuit(bidSuitCode, hmHandByPlayerNumber, jokerValue); return bidAmount > total; } @Override - public boolean isOverbid(String[] handOne, String[] handTwo, String[] handThree, String[] handFour, int jokerValue) + public boolean isOverbid(List handOne, List handTwo, List handThree, List handFour, int jokerValue) { int total = CardsUtil.countSuit(bidSuitCode, handOne, handTwo, handThree, handFour, jokerValue); return bidAmount > total; diff --git a/core/src/main/java/object/FakeBid.java b/core/src/main/java/object/FakeBid.java index 7a02892..24e16d4 100644 --- a/core/src/main/java/object/FakeBid.java +++ b/core/src/main/java/object/FakeBid.java @@ -1,5 +1,6 @@ package object; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.w3c.dom.Element; @@ -16,8 +17,8 @@ public boolean higherThan(Bid bid) } @Override - public boolean isPerfect(String[] handOne, String[] handTwo, - String[] handThree, String[] handFour, int jokerValue, + public boolean isPerfect(List handOne, List handTwo, + List handThree, List handFour, int jokerValue, boolean includeMoons, boolean includeStars) { Debug.stackTrace("Calling unimplemented method: isPerfect"); @@ -25,15 +26,15 @@ public boolean isPerfect(String[] handOne, String[] handTwo, } @Override - public boolean isOverbid(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue) + public boolean isOverbid(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue) { Debug.stackTrace("Calling unimplemented method: isOverbid"); return false; } @Override - public boolean isOverbid(String[] handOne, String[] handTwo, - String[] handThree, String[] handFour, int jokerValue) + public boolean isOverbid(List handOne, List handTwo, + List handThree, List handFour, int jokerValue) { Debug.stackTrace("Calling unimplemented method: isOverbid"); return false; diff --git a/core/src/main/java/object/Player.java b/core/src/main/java/object/Player.java index 7eb372a..39856b6 100644 --- a/core/src/main/java/object/Player.java +++ b/core/src/main/java/object/Player.java @@ -1,6 +1,7 @@ package object; import java.util.ArrayList; +import java.util.List; import java.util.prefs.Preferences; /** @@ -13,7 +14,7 @@ public class Player private int cardsToSubtract = 0; private boolean enabled = false; private String strategy = null; - private String[] hand = null; + private List hand = null; private ArrayList revealedCards = new ArrayList<>(); private int playerNumber = -1; @@ -29,7 +30,7 @@ public void saveHandToRegistry(Preferences replay, String replayStr) { for (int i=0; i(); for (int i = 0; i < numberOfCards; i++) { - hand[i] = registry.get(handStr + i, ""); + hand.add(registry.get(handStr + i, "")); String revealedCard = registry.get(revealedCardStr + i, ""); if (!revealedCard.isEmpty()) @@ -74,7 +75,7 @@ public void doSubtraction() public void resetHand() { - hand = new String[numberOfCards]; + hand = new ArrayList<>(); revealedCards = new ArrayList<>(); } @@ -99,9 +100,9 @@ public boolean hasMoreCardsToReveal() } public boolean handContainsCard(String card) { - for (int i=0; i getCardsNotOnShow() { ArrayList ret = new ArrayList<>(); - for (int i=0; i getHand() { return hand; } - public void setHand(String[] hand) + public void setHand(List hand) { - numberOfCards = hand.length; + numberOfCards = hand.size(); this.hand = hand; } public String getColour() diff --git a/core/src/main/java/object/VectropyBid.java b/core/src/main/java/object/VectropyBid.java index 817cce4..9e11b9b 100644 --- a/core/src/main/java/object/VectropyBid.java +++ b/core/src/main/java/object/VectropyBid.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.w3c.dom.Element; @@ -263,7 +264,7 @@ public boolean higherThan(Bid bid) } @Override - public boolean isPerfect(String[] handOne, String[] handTwo, String[] handThree, String[] handFour, + public boolean isPerfect(List handOne, List handTwo, List handThree, List handFour, int jokerValue, boolean includeMoons, boolean includeStars) { int maxClubs = CardsUtil.countSuit(CardsUtil.SUIT_CLUBS, handOne, handTwo, handThree, handFour, jokerValue); @@ -282,14 +283,14 @@ public boolean isPerfect(String[] handOne, String[] handTwo, String[] handThree, } @Override - public boolean isOverbid(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue) + public boolean isOverbid(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue) { return VectropyUtil.isOverbid(this, hmHandByPlayerNumber, jokerValue); } @Override - public boolean isOverbid(String[] handOne, String[] handTwo, - String[] handThree, String[] handFour, int jokerValue) + public boolean isOverbid(List handOne, List handTwo, + List handThree, List handFour, int jokerValue) { return VectropyUtil.isOverbid(this, handOne, handTwo, handThree, handFour, jokerValue); } diff --git a/core/src/main/java/util/CardsUtil.java b/core/src/main/java/util/CardsUtil.java index be78676..36b20a0 100644 --- a/core/src/main/java/util/CardsUtil.java +++ b/core/src/main/java/util/CardsUtil.java @@ -53,7 +53,7 @@ public static boolean isRelevant(String card, int suit) } } - public static int countSuit(String hand[], int suit, int jokerValue) + public static int countSuit(List hand, int suit, int jokerValue) { int count = 0; if (suit == SUIT_CLUBS) @@ -158,7 +158,7 @@ public static List createAndShuffleDeck(boolean includeJokers, int joker return list; } - public static HashMap getEvBySuitHashMapIncludingMyHand(String[] hand, StrategyParms parms) + public static HashMap getEvBySuitHashMapIncludingMyHand(List hand, StrategyParms parms) { double evClubs = getExpectedValueForSuitIncludingMyHand(hand, SUIT_CLUBS, parms); double evDiamonds = getExpectedValueForSuitIncludingMyHand(hand, SUIT_DIAMONDS, parms); @@ -178,7 +178,7 @@ public static HashMap getEvBySuitHashMapIncludingMyHand(String[ return hmEvBySuit; } - private static double getExpectedValueForSuitIncludingMyHand(String[] hand, int suitCode, StrategyParms parms) + private static double getExpectedValueForSuitIncludingMyHand(List hand, int suitCode, StrategyParms parms) { double ev = 0; @@ -191,13 +191,13 @@ private static double getExpectedValueForSuitIncludingMyHand(String[] hand, int List deck = createAndShuffleDeck(includeJokers, jokerQuantity, includeMoons, includeStars, negativeJacks); - int handSize = hand.length; + int handSize = hand.size(); int totalCardsNotIncludingMine = parms.getTotalNumberOfCards() - handSize; //go through the deck and take out the cards that we can see for (int i=0; i cardToCheck = new ArrayList<>(); + cardToCheck.add(deck.get(i)); ev += countSuit(cardToCheck, suitCode, jokerValue); } return countSuit(hand, suitCode, jokerValue) + (ev * totalCardsNotIncludingMine)/remainingCards; } - public static int countClubs(String[] hand, int jokerValue) + public static int countClubs(List hand, int jokerValue) { return actuallyCountSuit("c", hand, jokerValue); } - public static int countDiamonds(String[] hand, int jokerValue) + public static int countDiamonds(List hand, int jokerValue) { return actuallyCountSuit("d", hand, jokerValue); } - public static int countHearts(String[] hand, int jokerValue) + public static int countHearts(List hand, int jokerValue) { return actuallyCountSuit("h", hand, jokerValue); } - public static int countSpades(String[] hand, int jokerValue) + public static int countSpades(List hand, int jokerValue) { return actuallyCountSuit("s", hand, jokerValue); } - public static int countMoons(String[] hand, int jokerValue) + public static int countMoons(List hand, int jokerValue) { return actuallyCountSuit("m", hand, jokerValue); } - public static int countStars(String[] hand, int jokerValue) + public static int countStars(List hand, int jokerValue) { return actuallyCountSuit("x", hand, jokerValue); } - private static int actuallyCountSuit(String suit, String[] hand, int jokerValue) + private static int actuallyCountSuit(String suit, List hand, int jokerValue) { if (hand == null) { @@ -256,25 +257,25 @@ private static int actuallyCountSuit(String suit, String[] hand, int jokerValue) } int count = 0; - for (int i=0; i playerHand, List opponentOneHand, + List opponentTwoHand, List opponentThreeHand, int jokerValue) { return countSuit(playerHand, suitCode, jokerValue) + countSuit(opponentOneHand, suitCode, jokerValue) @@ -291,12 +292,12 @@ public static int countSuit(int suitCode, String[] playerHand, String[] opponent + countSuit(opponentThreeHand, suitCode, jokerValue); } - public static int countSuit(int suitCode, ConcurrentHashMap hmHandByPlayerNumber, int jokerValue) + public static int countSuit(int suitCode, ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue) { int total = 0; for (int i=0; i<4; i++) { - String[] hand = hmHandByPlayerNumber.get(i); + List hand = hmHandByPlayerNumber.get(i); if (hand != null) { total += countSuit(hand, suitCode, jokerValue); @@ -421,12 +422,12 @@ public static ArrayList getSuitCodesVector(boolean includeMoons, boolea return suits; } - public static boolean containsNonJoker(String[] cards) + public static boolean containsNonJoker(List cards) { - int size = cards.length; + int size = cards.size(); for (int i=0; i hand) { - int length = hand.length; + int length = hand.size(); String handStr = "["; for (int i=0; i playerHand, List opponentOneHand, + List opponentTwoHand, List opponentThreeHand, int jokerValue) { int perfectBidAmount = 0; for (int i=0; i<6; i++) @@ -30,8 +32,8 @@ public static int getPerfectBidAmount(String[] playerHand, String[] opponentOneH return perfectBidAmount; } - public static int getPerfectBidSuitCode(String[] playerHand, String[] opponentOneHand, - String[] opponentTwoHand, String[] opponentThreeHand, int jokerValue, boolean includeStars) + public static int getPerfectBidSuitCode(List playerHand, List opponentOneHand, + List opponentTwoHand, List opponentThreeHand, int jokerValue, boolean includeStars) { int diamondsTotal = CardsUtil.countDiamonds(playerHand, jokerValue) + CardsUtil.countDiamonds(opponentOneHand, jokerValue) diff --git a/core/src/main/java/util/VectropyUtil.java b/core/src/main/java/util/VectropyUtil.java index 2e58420..cb8d803 100644 --- a/core/src/main/java/util/VectropyUtil.java +++ b/core/src/main/java/util/VectropyUtil.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.prefs.Preferences; @@ -10,17 +11,17 @@ public class VectropyUtil { - public static boolean isOverbid(VectropyBid bid, ConcurrentHashMap hmHandByPlayerNumber, int jokerValue) + public static boolean isOverbid(VectropyBid bid, ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue) { - String[] playerHand = hmHandByPlayerNumber.get(0); - String[] opponentOneHand = hmHandByPlayerNumber.get(1); - String[] opponentTwoHand = hmHandByPlayerNumber.get(2); - String[] opponentThreeHand = hmHandByPlayerNumber.get(3); + List playerHand = hmHandByPlayerNumber.get(0); + List opponentOneHand = hmHandByPlayerNumber.get(1); + List opponentTwoHand = hmHandByPlayerNumber.get(2); + List opponentThreeHand = hmHandByPlayerNumber.get(3); return isOverbid(bid, playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); } - public static boolean isOverbid(VectropyBid bid, String[] playerHand, String[] opponentOneHand, String[] opponentTwoHand, String[] opponentThreeHand, int jokerValue) + public static boolean isOverbid(VectropyBid bid, List playerHand, List opponentOneHand, List opponentTwoHand, List opponentThreeHand, int jokerValue) { int maxClubs = CardsUtil.countSuit(CardsUtil.SUIT_CLUBS, playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); int maxDiamonds = CardsUtil.countSuit(CardsUtil.SUIT_DIAMONDS, playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue); @@ -37,18 +38,18 @@ public static boolean isOverbid(VectropyBid bid, String[] playerHand, String[] o || bid.getStars() > maxStars; } - public static String getResult(ConcurrentHashMap hmHandByPlayerNumber, int jokerValue, int suitCode, + public static String getResult(ConcurrentHashMap> hmHandByPlayerNumber, int jokerValue, int suitCode, boolean includeMoons, boolean includeStars) { - String[] playerHand = hmHandByPlayerNumber.get(0); - String[] opponentOneHand = hmHandByPlayerNumber.get(1); - String[] opponentTwoHand = hmHandByPlayerNumber.get(2); - String[] opponentThreeHand = hmHandByPlayerNumber.get(3); + List playerHand = hmHandByPlayerNumber.get(0); + List opponentOneHand = hmHandByPlayerNumber.get(1); + List opponentTwoHand = hmHandByPlayerNumber.get(2); + List opponentThreeHand = hmHandByPlayerNumber.get(3); return getResult(playerHand, opponentOneHand, opponentTwoHand, opponentThreeHand, jokerValue, suitCode, includeMoons, includeStars); } - public static String getResult(String[] playerHand, String[] opponentOneHand, String[] opponentTwoHand, String[] opponentThreeHand, + public static String getResult(List playerHand, List opponentOneHand, List opponentTwoHand, List opponentThreeHand, int jokerValue, int suitCode, boolean includeMoons, boolean includeStars) { Preferences prefs = Registry.prefs; @@ -137,8 +138,8 @@ public static String getReadableString(double[] diffVector) return ret; } - public static double[] getDifferenceVector(VectropyBid bid, String[] hand, int jokerValue, - boolean includeMoons, boolean includeStars) + public static double[] getDifferenceVector(VectropyBid bid, List hand, int jokerValue, + boolean includeMoons, boolean includeStars) { int clubsCount = CardsUtil.countClubs(hand, jokerValue) - bid.getClubs(); int diamondsCount = CardsUtil.countDiamonds(hand, jokerValue) - bid.getDiamonds(); diff --git a/server/src/main/java/object/HandDetails.java b/server/src/main/java/object/HandDetails.java index 27914d8..d0a0ae0 100644 --- a/server/src/main/java/object/HandDetails.java +++ b/server/src/main/java/object/HandDetails.java @@ -1,10 +1,11 @@ package object; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class HandDetails { - private ConcurrentHashMap hmHandByPlayerNumber = new ConcurrentHashMap<>(); + private ConcurrentHashMap> hmHandByPlayerNumber = new ConcurrentHashMap<>(); private ExtendedConcurrentHashMap hmHandSizeByPlayerNumber = new ExtendedConcurrentHashMap<>(); public ExtendedConcurrentHashMap getHandSizes() @@ -15,16 +16,16 @@ public void setHandSizes(ExtendedConcurrentHashMap hmHandSizeB { this.hmHandSizeByPlayerNumber = hmHandSizeByPlayerNumber; } - public ConcurrentHashMap getHands() + public ConcurrentHashMap> getHands() { return hmHandByPlayerNumber; } - public void setHands(ConcurrentHashMap hmHandByPlayerNumber) + public void setHands(ConcurrentHashMap> hmHandByPlayerNumber) { this.hmHandByPlayerNumber = hmHandByPlayerNumber; } - public String[] getHand(int playerNumber) + public List getHand(int playerNumber) { return hmHandByPlayerNumber.get(playerNumber); } @@ -34,7 +35,7 @@ public String getHandsForLogging() String handsStr = "Hands: "; for (int i=0; i<4; i++) { - String[] hand = hmHandByPlayerNumber.get(i); + List hand = hmHandByPlayerNumber.get(i); if (hand == null) { continue; @@ -52,10 +53,10 @@ public String getHandsForLogging() return handsStr; } - private String prettyPrintHand(String[] hand) + private String prettyPrintHand(List hand) { String ret = "["; - int handSize = hand.length; + int handSize = hand.size(); for (int i=0; i 0) @@ -63,7 +64,7 @@ private String prettyPrintHand(String[] hand) ret += ", "; } - ret += hand[i]; + ret += hand.get(i); } ret += "]"; diff --git a/server/src/main/java/object/Room.java b/server/src/main/java/object/Room.java deleted file mode 100644 index f782776..0000000 --- a/server/src/main/java/object/Room.java +++ /dev/null @@ -1,561 +0,0 @@ -package object; - -import auth.UserConnection; -import game.GameSettings; -import org.w3c.dom.Document; -import server.EntropyServer; -import util.*; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Server-side version of a Room - */ -public final class Room -{ - private boolean isCopy = false; - - private ExtendedConcurrentHashMap hmPlayerByPlayerNumber = new ExtendedConcurrentHashMap<>(); - private ConcurrentHashMap hmFormerPlayerByPlayerNumber = new ConcurrentHashMap<>(); - private ArrayList chatHistory = new ArrayList<>(); - private List currentPlayers = new ArrayList<>(); - private List observers = new ArrayList<>(); - private GameWrapper previousGame = null; - private GameWrapper currentGame = null; - - private String name; - private GameSettings settings; - private int capacity; - private EntropyServer server; - - public Room(String name, GameSettings settings, int capacity, EntropyServer server) - { - this.name = name; - this.settings = settings; - this.capacity = capacity; - this.server = server; - } - - public boolean isFull() - { - return currentPlayers.size() == capacity; - } - public boolean isEmpty() - { - return currentPlayers.isEmpty() - && observers.isEmpty(); - } - - public void clearChatIfEmpty() - { - synchronized (this) - { - if (!isEmpty()) - { - return; - } - - chatHistory.clear(); - } - } - - public int addToCurrentPlayers(String username, int playerNumber) - { - synchronized (this) - { - String existingUsername = hmPlayerByPlayerNumber.get(playerNumber); - if (existingUsername != null) - { - Debug.append(username + " tried to join room " + name + " as player " + playerNumber - + " but the space was taken by " + existingUsername); - return -1; - } - - if (hmPlayerByPlayerNumber.containsValue(username)) - { - Debug.append(username + " tried to join room " + name + " twice!"); - return -1; - } - - currentPlayers.add(username); - hmPlayerByPlayerNumber.put(playerNumber, username); - observers.remove(username); - - notifyAllPlayersOfPlayerChange(username, false); - ServerGlobals.lobbyService.lobbyChanged(); - return playerNumber; - } - } - - public void removePlayer(String username, boolean fireLobbyChanged) - { - for (int playerNumber=0; playerNumber hmHandSizeByPlayerNumber = details.getHandSizes(); - hmHandSizeByPlayerNumber.put(playerNumber, 0); - } - - int playerSize = hmPlayerByPlayerNumber.size(); - if (playerSize == 1) - { - int remainingPlayerNumber = hmPlayerByPlayerNumber.getOnlyKey(); - finishCurrentGame(remainingPlayerNumber); - } - else if (playerSize == 0) - { - resetCurrentPlayers(fireLobbyChanged); - clearChatIfEmpty(); - } - } - } - } - - private void notifyAllPlayersOfPlayerChange(String userToExclude, boolean blocking) - { - String notification = XmlBuilderServer.getPlayerNotification(this); - notifyAllUsersViaGameSocket(notification, userToExclude, blocking); - } - - private void notifyAllUsersViaGameSocket(String notification, String userToExclude, boolean blocking) - { - HashSet usersToNotify = getAllUsersInRoom(); - if (userToExclude != null) - { - usersToNotify.remove(userToExclude); - } - - List uscs = ServerGlobals.INSTANCE.getUscStore().getAllForNames(usersToNotify); - server.sendViaNotificationSocket(uscs, notification, XmlConstants.SOCKET_NAME_GAME, blocking); - } - - private void finishCurrentGame(int winningPlayer) - { - int roundNumber = currentGame.getRoundNumber(); - String winningUsername = hmPlayerByPlayerNumber.get(winningPlayer); - resetCurrentPlayers(); - currentGame.setWinningPlayer(winningPlayer); - - if (roundNumber > 1) - { - OnlineMessage om = new OnlineMessage("black", winningUsername + " won!", "Game"); - addToChatHistoryAndNotifyUsers(om); - } - - initialiseGame(); - - //Notify everyone that the game is over now we've finished setting up - String notification = XmlBuilderServer.factoryGameOverNotification(this, winningPlayer); - notifyAllUsersViaGameSocket(notification, null, false); - } - - public void resetCurrentPlayers() - { - resetCurrentPlayers(true); - } - public void resetCurrentPlayers(boolean fireLobbyChanged) - { - currentPlayers.clear(); - for (int i=0; i hmHandSizeByPlayerNumber = new ExtendedConcurrentHashMap<>(); - for (int i=0; i hmHandByPlayerNumber = dealHandsHashMap(hmHandSizeByPlayerNumber); - - details.setHands(hmHandByPlayerNumber); - details.setHandSizes(hmHandSizeByPlayerNumber); - currentGame.setDetailsForRound(1, details); - - Random rand = new Random(); - int personToStart = rand.nextInt(capacity); - - BidHistory history = new BidHistory(); - history.setPersonToStart(personToStart); - currentGame.setBidHistoryForRound(1, history); - } - catch (Throwable t) - { - Debug.stackTrace(t); - } - } - - public void handleChallenge(String gameId, int roundNumber, int playerNumber, int challengedNumber, Bid bid) - { - GameWrapper game = getGameForId(gameId); - HandDetails details = game.getDetailsForRound(roundNumber); - ConcurrentHashMap hmHandByPlayerNumber = details.getHands(); - if (bid.isOverbid(hmHandByPlayerNumber, settings.getJokerValue())) - { - //bidder loses - setUpNextRound(challengedNumber); - } - else - { - //challenger loses - setUpNextRound(playerNumber); - } - } - - public void handleIllegal(String gameId, int roundNumber, int playerNumber, int bidderNumber, Bid bid) - { - GameWrapper game = getGameForId(gameId); - HandDetails details = game.getDetailsForRound(roundNumber); - ConcurrentHashMap hmHandByPlayerNumber = details.getHands(); - if (bid.isPerfect(hmHandByPlayerNumber, settings.getJokerValue(), settings.getIncludeMoons(), settings.getIncludeStars())) - { - setUpNextRound(bidderNumber); - } - else - { - setUpNextRound(playerNumber); - } - } - - public void setUpNextRound(int losingPlayerNumber) - { - int currentRoundNumber = currentGame.getRoundNumber(); - - HandDetails nextRoundDetails = currentGame.getDetailsForRound(currentRoundNumber + 1); - if (nextRoundDetails != null) - { - Debug.stackTrace("Trying to set up next round but it's not null. Room " + name); - return; - } - - HandDetails currentRoundDetails = currentGame.getDetailsForRound(currentRoundNumber); - ExtendedConcurrentHashMap hmHandSizeByPlayerNumber = currentRoundDetails.getHandSizes(); - Integer handSize = hmHandSizeByPlayerNumber.get(losingPlayerNumber); - int newHandSize = Math.max(0, handSize.intValue() - 1); - - ExtendedConcurrentHashMap hmHandSizeByPlayerNumberForNextRound = hmHandSizeByPlayerNumber.factoryCopy(); - hmHandSizeByPlayerNumberForNextRound.put(losingPlayerNumber, Integer.valueOf(newHandSize)); - - int potentialWinner = getWinningPlayer(hmHandSizeByPlayerNumberForNextRound); - if (potentialWinner > -1) - { - finishCurrentGame(potentialWinner); - } - else - { - nextRoundDetails = new HandDetails(); - nextRoundDetails.setHandSizes(hmHandSizeByPlayerNumberForNextRound); - - ConcurrentHashMap hmHandByPlayerNumber = dealHandsHashMap(hmHandSizeByPlayerNumberForNextRound); - - nextRoundDetails.setHands(hmHandByPlayerNumber); - currentGame.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails); - - BidHistory history = new BidHistory(); - history.setPersonToStart(losingPlayerNumber); - currentGame.setBidHistoryForRound(currentRoundNumber + 1, history); - - currentGame.setRoundNumber(currentRoundNumber + 1); - - String newRoundNotification = XmlBuilderServer.factoryNewRoundNotification(this, nextRoundDetails, losingPlayerNumber); - notifyAllUsersViaGameSocket(newRoundNotification, null, false); - } - } - - private ConcurrentHashMap dealHandsHashMap(ExtendedConcurrentHashMap hmHandSizeByPlayerNumber) - { - ConcurrentHashMap hmHandByPlayerNumber = new ConcurrentHashMap<>(); - - long seed = server.generateSeed(); - List deck = CardsUtil.createAndShuffleDeck(true, settings.getJokerQuantity(), - settings.getIncludeMoons(), settings.getIncludeStars(), settings.getNegativeJacks(), seed); - for (int i=0; i hmHandSizeByPlayerNumber) - { - int activePlayers = 0; - int potentialWinner = 0; - - for (int i=0; i 0) - { - activePlayers++; - potentialWinner = i; - } - } - - if (activePlayers > 1) - { - return -1; - } - else - { - return potentialWinner; - } - } - - /** - * Returns a HashSet since it's possible for a player to be present as a player AND an observer. - * This occurs if they've left but the game is still going - we keep the reference as a player so - * others can't take the seat. They obviously then have the option to join as an observer. - */ - public HashSet getAllUsersInRoom() - { - ArrayList ret = getCurrentPlayers(); - ret.addAll(getObservers()); - - HashSet hs = new HashSet<>(ret); - return hs; - } - - /** - * Get/sets - */ - public String getName() { - return name; - } - public int getCapacity() { - return capacity; - } - public GameSettings getSettings() { - return settings; - } - public boolean isGameInProgress() - { - //This no longer works - //return currentGame != null; - //return currentPlayers.size() == capacity && (waitingForPlayerToSeeResult == null); - return currentPlayers.size() == capacity; - } - public boolean getIsCopy() - { - return isCopy; - } - public void setIsCopy(boolean isCopy) - { - this.isCopy = isCopy; - } - public ArrayList getCurrentPlayers() - { - return new ArrayList<>(currentPlayers); - } - public int getCurrentPlayerCount() - { - return currentPlayers.size(); - } - public ArrayList getObservers() - { - return new ArrayList<>(observers); - } - public int getObserverCount() - { - return observers.size(); - } - - /** - * HashMap gets/sets - */ - public String getPlayer(int playerNumber) - { - return hmPlayerByPlayerNumber.get(playerNumber); - } - public String getFormerPlayer(int playerNumber) - { - return hmFormerPlayerByPlayerNumber.get(playerNumber); - } - public GameWrapper getGameForId(String gameId) - { - String currentId = currentGame.getGameId(); - if (gameId.equals(currentId)) - { - return currentGame; - } - - String previousId = previousGame.getGameId(); - if (gameId.equals(previousId)) - { - return previousGame; - } - - throw new RuntimeException("Got a null game for room " + name + " and gameId " + gameId); - } - public GameWrapper getNextGameForId(String previousGameIdFromClient) - { - if (previousGameIdFromClient.isEmpty()) - { - return currentGame; - } - - if (previousGame == null) - { - Debug.append("Tried to get next game for gameId " + previousGameIdFromClient + " but previous game was null."); - Debug.appendWithoutDate("Current: " + currentGame.getGameId()); - return currentGame; - } - - String previousGameId = previousGame.getGameId(); - if (previousGameId.equals(previousGameIdFromClient)) - { - return currentGame; - } - - Debug.append("Tried to get next game for gameId " + previousGameIdFromClient + " but this didn't match my previous game."); - Debug.appendWithoutDate("Previous: " + previousGame.getGameId()); - Debug.appendWithoutDate("Current: " + currentGame.getGameId()); - return currentGame; - } - - - public Bid getLastBidForPlayer(int playerNumber, int roundNumber) - { - if (currentGame == null - || playerNumber == -1) - { - return null; - } - - BidHistory history = currentGame.getBidHistoryForRound(roundNumber); - return history.getLastBidForPlayer(playerNumber); - } - public boolean addBidForPlayer(String gameId, int playerNumber, int roundNumber, Bid newBid) - { - GameWrapper game = getGameForId(gameId); - - BidHistory history = game.getBidHistoryForRound(roundNumber); - boolean added = history.addBidForPlayer(playerNumber, newBid); - - if (added) - { - //Notify all other capacity - String bidNotification = XmlBuilderServer.getBidNotification(name, playerNumber, newBid); - notifyAllUsersViaGameSocket(bidNotification, null, false); - } - - return added; - } - - public void addToChatHistoryAndNotifyUsers(OnlineMessage message) - { - if (isEmpty()) - { - return; - } - - chatHistory.add(message); - - String chatMessage = XmlBuilderServer.getChatNotification(name, message); - HashSet users = getAllUsersInRoom(); - List uscs = ServerGlobals.INSTANCE.getUscStore().getAllForNames(users); - server.sendViaNotificationSocket(uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, false); - } - public ArrayList getChatHistory() - { - return chatHistory; - } - - @Override - public String toString() - { - return name; - } -} diff --git a/server/src/main/java/server/EntropyServer.java b/server/src/main/java/server/EntropyServer.java index aeddc02..1cd6bf1 100644 --- a/server/src/main/java/server/EntropyServer.java +++ b/server/src/main/java/server/EntropyServer.java @@ -5,9 +5,9 @@ import game.GameSettings; import logging.LoggerUncaughtExceptionHandler; import object.OnlineMessage; -import object.Room; import object.ServerRunnable; import object.ServerThread; +import room.Room; import util.*; import java.util.ArrayList; @@ -130,7 +130,7 @@ public void resetLobby() { int size = rooms.size(); for (int i = 0; i < size; i++) { Room room = rooms.get(i); - if (room.getIsCopy()) { + if (room.isCopy()) { String roomName = room.getName(); hmRoomByName.remove(roomName); countRemoved++; @@ -266,7 +266,7 @@ public Room registerCopy(Room room) { Room newRoom = registerNewRoom(newRoomName, capacity, settings); if (newRoom != null) { - newRoom.setIsCopy(true); + newRoom.setCopy(true); ServerGlobals.lobbyService.lobbyChanged(); } diff --git a/server/src/main/java/server/MessageHandlerRunnable.java b/server/src/main/java/server/MessageHandlerRunnable.java index da6e54d..6b3231a 100644 --- a/server/src/main/java/server/MessageHandlerRunnable.java +++ b/server/src/main/java/server/MessageHandlerRunnable.java @@ -15,12 +15,12 @@ import auth.UserConnection; import http.LegacyConstants; import object.NotificationSocket; -import object.Room; import object.ServerRunnable; import org.w3c.dom.Document; import org.w3c.dom.Element; +import room.Room; import util.*; import utils.CoreGlobals; diff --git a/server/src/main/java/util/XmlBuilderServer.java b/server/src/main/java/util/XmlBuilderServer.java index 3321c3e..7a501e5 100644 --- a/server/src/main/java/util/XmlBuilderServer.java +++ b/server/src/main/java/util/XmlBuilderServer.java @@ -3,6 +3,7 @@ import object.*; import org.w3c.dom.Document; import org.w3c.dom.Element; +import room.Room; import server.EntropyServer; import java.util.List; @@ -331,10 +332,10 @@ private static void addPlayerHands(Room room, Document response, Element rootEle Element handElement = response.createElement("Hand"); handElement.setAttribute("PlayerNumber", "" + i); - String[] hand = details.getHand(i); - for (int j=0; j hand = details.getHand(i); + for (int j=0; j hand = details.getHand(i); + if (hand.isEmpty()) { continue; } Element handElement = response.createElement("Hand"); handElement.setAttribute("PlayerNumber", "" + i); - for (int j=0; j() + private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() + val chatHistory: ArrayList = ArrayList() + private val currentPlayers: MutableList = ArrayList() + private val observers: MutableList = ArrayList() + private var previousGame: GameWrapper? = null + private var currentGame: GameWrapper? = null + + val isFull: Boolean + get() = currentPlayers.size == capacity + + val isEmpty: Boolean + get() = (currentPlayers.isEmpty() && observers.isEmpty()) + + fun clearChatIfEmpty() { + synchronized(this) { + if (!isEmpty) { + return + } + chatHistory.clear() + } + } + + fun addToCurrentPlayers(username: String, playerNumber: Int): Int { + synchronized(this) { + val existingUsername: String? = hmPlayerByPlayerNumber.get(playerNumber) + if (existingUsername != null) { + Debug.append( + (username + + " tried to join room " + + name + + " as player " + + playerNumber + + " but the space was taken by " + + existingUsername) + ) + return -1 + } + + if (hmPlayerByPlayerNumber.containsValue(username)) { + Debug.append(username + " tried to join room " + name + " twice!") + return -1 + } + + currentPlayers.add(username) + hmPlayerByPlayerNumber.put(playerNumber, username) + observers.remove(username) + + notifyAllPlayersOfPlayerChange(username, false) + ServerGlobals.lobbyService.lobbyChanged() + return playerNumber + } + } + + fun removePlayer(username: String, fireLobbyChanged: Boolean) { + for (playerNumber in 0 until capacity) { + val user: String? = hmPlayerByPlayerNumber.get(playerNumber) + if ((user != null && (username == user))) { + hmPlayerByPlayerNumber.remove(playerNumber) + hmFormerPlayerByPlayerNumber[playerNumber] = user + + // Notify everyone in the room that this player has left. Block on this. + notifyAllPlayersOfPlayerChange(username, true) + + // The game has not started + if (currentGame!!.gameStartMillis == -1L) { + // Unset the countdown if it's going, reset current capacity and get out of this + // madness + currentGame!!.countdownStartMillis = -1 + resetCurrentPlayers(fireLobbyChanged) + return + } + + // There is a game in progress + if (currentGame!!.gameEndMillis == -1L) { + val bid: LeftBid = LeftBid() + val player: Player = + Player(playerNumber, EntropyUtil.getColourForPlayerNumber(playerNumber)) + player.name = username + bid.player = player + + val history: BidHistory = currentGame!!.currentBidHistory + history.addBidForPlayer(playerNumber, bid) + + // Moved this into here as otherwise we set it to 0 incorrectly and a person + // ends up with no cards! + val details: HandDetails = currentGame!!.currentRoundDetails + val hmHandSizeByPlayerNumber: ConcurrentHashMap = details.handSizes + hmHandSizeByPlayerNumber[playerNumber] = 0 + } + + val playerSize: Int = hmPlayerByPlayerNumber.size + if (playerSize == 1) { + val remainingPlayerNumber: Int = hmPlayerByPlayerNumber.onlyKey + finishCurrentGame(remainingPlayerNumber) + } else if (playerSize == 0) { + resetCurrentPlayers(fireLobbyChanged) + clearChatIfEmpty() + } + } + } + } + + private fun notifyAllPlayersOfPlayerChange(userToExclude: String, blocking: Boolean) { + val notification: String = XmlBuilderServer.getPlayerNotification(this) + notifyAllUsersViaGameSocket(notification, userToExclude, blocking) + } + + private fun notifyAllUsersViaGameSocket( + notification: String, + userToExclude: String?, + blocking: Boolean + ) { + val usersToNotify: HashSet = allUsersInRoom + if (userToExclude != null) { + usersToNotify.remove(userToExclude) + } + + val uscs: List = uscStore.getAllForNames(usersToNotify) + server.sendViaNotificationSocket( + uscs, + notification, + XmlConstants.SOCKET_NAME_GAME, + blocking + ) + } + + private fun finishCurrentGame(winningPlayer: Int) { + val roundNumber: Int = currentGame!!.roundNumber + val winningUsername: String? = hmPlayerByPlayerNumber.get(winningPlayer) + resetCurrentPlayers() + currentGame!!.winningPlayer = winningPlayer + + if (roundNumber > 1) { + val om: OnlineMessage = OnlineMessage("black", "$winningUsername won!", "Game") + addToChatHistoryAndNotifyUsers(om) + } + + initialiseGame() + + // Notify everyone that the game is over now we've finished setting up + val notification: String = XmlBuilderServer.factoryGameOverNotification(this, winningPlayer) + notifyAllUsersViaGameSocket(notification, null, false) + } + + @JvmOverloads + fun resetCurrentPlayers(fireLobbyChanged: Boolean = true) { + currentPlayers.clear() + for (i in 0 until capacity) { + val username: String? = hmPlayerByPlayerNumber.get(i) + if (username != null) { + currentPlayers.add(username) + } + } + + hmFormerPlayerByPlayerNumber.clear() + if (fireLobbyChanged) { + ServerGlobals.lobbyService.lobbyChanged() + } + } + + fun addToObservers(username: String) { + synchronized(this) { + if (!observers.contains(username)) { + observers.add(username) + } + removePlayer(username, false) + ServerGlobals.lobbyService.lobbyChanged() + } + } + + fun removeFromObservers(username: String) { + val removed: Boolean = observers.remove(username) + if (removed) { + ServerGlobals.lobbyService.lobbyChanged() + } + + clearChatIfEmpty() + } + + fun initialiseGame() { + try { + val gameId: String = "G" + System.currentTimeMillis() + if (currentGame != null) { + previousGame = currentGame!!.factoryCopy() + } + + currentGame = GameWrapper(gameId) + + val details = HandDetails() + val hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap = + ExtendedConcurrentHashMap() + for (i in 0 until capacity) { + hmHandSizeByPlayerNumber[i] = 5 + } + + val hmHandByPlayerNumber: ConcurrentHashMap> = + dealHandsHashMap(hmHandSizeByPlayerNumber) + + details.hands = hmHandByPlayerNumber + details.handSizes = hmHandSizeByPlayerNumber + currentGame!!.setDetailsForRound(1, details) + + val personToStart: Int = Random().nextInt(capacity) + + val history = BidHistory() + history.personToStart = personToStart + currentGame!!.setBidHistoryForRound(1, history) + } catch (t: Throwable) { + Debug.stackTrace(t) + } + } + + fun handleChallenge( + gameId: String, + roundNumber: Int, + playerNumber: Int, + challengedNumber: Int, + bid: Bid + ) { + val game: GameWrapper? = getGameForId(gameId) + val details: HandDetails = game!!.getDetailsForRound(roundNumber) + val hmHandByPlayerNumber: ConcurrentHashMap> = details.hands + if (bid.isOverbid(hmHandByPlayerNumber, settings.jokerValue)) { + // bidder loses + setUpNextRound(challengedNumber) + } else { + // challenger loses + setUpNextRound(playerNumber) + } + } + + fun handleIllegal( + gameId: String, + roundNumber: Int, + playerNumber: Int, + bidderNumber: Int, + bid: Bid + ) { + val game: GameWrapper? = getGameForId(gameId) + val details: HandDetails = game!!.getDetailsForRound(roundNumber) + val hmHandByPlayerNumber: ConcurrentHashMap> = details.hands + if ( + bid.isPerfect( + hmHandByPlayerNumber, + settings.jokerValue, + settings.includeMoons, + settings.includeStars + ) + ) { + setUpNextRound(bidderNumber) + } else { + setUpNextRound(playerNumber) + } + } + + fun setUpNextRound(losingPlayerNumber: Int) { + val currentRoundNumber: Int = currentGame!!.roundNumber + + var nextRoundDetails: HandDetails? = + currentGame!!.getDetailsForRound(currentRoundNumber + 1) + if (nextRoundDetails != null) { + Debug.stackTrace("Trying to set up next round but it's not null. Room $name") + return + } + + val currentRoundDetails: HandDetails = currentGame!!.getDetailsForRound(currentRoundNumber) + val hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap = + currentRoundDetails.handSizes + val handSize: Int? = hmHandSizeByPlayerNumber.get(losingPlayerNumber) + val newHandSize: Int = max(0.0, (handSize!! - 1).toDouble()).toInt() + + val hmHandSizeByPlayerNumberForNextRound: ExtendedConcurrentHashMap = + hmHandSizeByPlayerNumber.factoryCopy() + hmHandSizeByPlayerNumberForNextRound[losingPlayerNumber] = newHandSize + + val potentialWinner: Int = getWinningPlayer(hmHandSizeByPlayerNumberForNextRound) + if (potentialWinner > -1) { + finishCurrentGame(potentialWinner) + } else { + nextRoundDetails = HandDetails() + nextRoundDetails.setHandSizes(hmHandSizeByPlayerNumberForNextRound) + + val hmHandByPlayerNumber: ConcurrentHashMap> = + dealHandsHashMap(hmHandSizeByPlayerNumberForNextRound) + + nextRoundDetails.setHands(hmHandByPlayerNumber) + currentGame!!.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails) + + val history: BidHistory = BidHistory() + history.personToStart = losingPlayerNumber + currentGame!!.setBidHistoryForRound(currentRoundNumber + 1, history) + + currentGame!!.roundNumber = currentRoundNumber + 1 + + val newRoundNotification: String = + XmlBuilderServer.factoryNewRoundNotification( + this, + nextRoundDetails, + losingPlayerNumber + ) + notifyAllUsersViaGameSocket(newRoundNotification, null, false) + } + } + + private fun dealHandsHashMap( + hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap + ): ConcurrentHashMap> { + val hmHandByPlayerNumber: ConcurrentHashMap> = ConcurrentHashMap() + + val seed: Long = server.generateSeed() + val deck = + CardsUtil.createAndShuffleDeck( + true, + settings.jokerQuantity, + settings.includeMoons, + settings.includeStars, + settings.negativeJacks, + seed + ) + + for (i in 0 ..< capacity) { + val size: Int = hmHandSizeByPlayerNumber.getValue(i) + val hand = (0 ..< size).map { deck.removeAt(0) } + hmHandByPlayerNumber[i] = hand + } + + return hmHandByPlayerNumber + } + + private fun getWinningPlayer(hmHandSizeByPlayerNumber: ConcurrentHashMap): Int { + var activePlayers: Int = 0 + var potentialWinner: Int = 0 + + for (i in 0 until capacity) { + val handSize: Int = (hmHandSizeByPlayerNumber.get(i))!! + if (handSize > 0) { + activePlayers++ + potentialWinner = i + } + } + + if (activePlayers > 1) { + return -1 + } else { + return potentialWinner + } + } + + val allUsersInRoom: HashSet + /** + * Returns a HashSet since it's possible for a player to be present as a player AND an + * observer. This occurs if they've left but the game is still going - we keep the reference + * as a player so others can't take the seat. They obviously then have the option to join as + * an observer. + */ + get() { + val ret: ArrayList = getCurrentPlayers() + ret.addAll(getObservers()) + + val hs: HashSet = HashSet(ret) + return hs + } + + val isGameInProgress: Boolean + get() { + // This no longer works + // return currentGame != null; + // return currentPlayers.size() == capacity && (waitingForPlayerToSeeResult == null); + return currentPlayers.size == capacity + } + + fun getCurrentPlayers(): ArrayList { + return ArrayList(currentPlayers) + } + + val currentPlayerCount: Int + get() { + return currentPlayers.size + } + + fun getObservers(): ArrayList { + return ArrayList(observers) + } + + val observerCount: Int + get() { + return observers.size + } + + /** HashMap gets/sets */ + fun getPlayer(playerNumber: Int): String? { + return hmPlayerByPlayerNumber.get(playerNumber) + } + + fun getFormerPlayer(playerNumber: Int): String? { + return hmFormerPlayerByPlayerNumber.get(playerNumber) + } + + fun getGameForId(gameId: String): GameWrapper? { + val currentId: String = currentGame!!.gameId + if ((gameId == currentId)) { + return currentGame + } + + val previousId: String = previousGame!!.gameId + if ((gameId == previousId)) { + return previousGame + } + + throw RuntimeException("Got a null game for room $name and gameId $gameId") + } + + fun getNextGameForId(previousGameIdFromClient: String): GameWrapper? { + if (previousGameIdFromClient.isEmpty()) { + return currentGame + } + + if (previousGame == null) { + Debug.append( + "Tried to get next game for gameId $previousGameIdFromClient but previous game was null." + ) + Debug.appendWithoutDate("Current: " + currentGame!!.gameId) + return currentGame + } + + val previousGameId: String = previousGame!!.gameId + if ((previousGameId == previousGameIdFromClient)) { + return currentGame + } + + Debug.append( + "Tried to get next game for gameId $previousGameIdFromClient but this didn't match my previous game." + ) + Debug.appendWithoutDate("Previous: " + previousGame!!.gameId) + Debug.appendWithoutDate("Current: " + currentGame!!.gameId) + return currentGame + } + + fun getLastBidForPlayer(playerNumber: Int, roundNumber: Int): Bid? { + if ((currentGame == null || playerNumber == -1)) { + return null + } + + val history: BidHistory = currentGame!!.getBidHistoryForRound(roundNumber) + return history.getLastBidForPlayer(playerNumber) + } + + fun addBidForPlayer( + gameId: String, + playerNumber: Int, + roundNumber: Int, + newBid: Bid? + ): Boolean { + val game: GameWrapper? = getGameForId(gameId) + + val history: BidHistory = game!!.getBidHistoryForRound(roundNumber) + val added: Boolean = history.addBidForPlayer(playerNumber, newBid) + + if (added) { + // Notify all other capacity + val bidNotification: String = + XmlBuilderServer.getBidNotification(name, playerNumber, newBid) + notifyAllUsersViaGameSocket(bidNotification, null, false) + } + + return added + } + + fun addToChatHistoryAndNotifyUsers(message: OnlineMessage) { + if (isEmpty) { + return + } + + chatHistory.add(message) + + val chatMessage: String = XmlBuilderServer.getChatNotification(name, message) + val users: HashSet = allUsersInRoom + val uscs: List = uscStore.getAllForNames(users) + server.sendViaNotificationSocket(uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, false) + } + + override fun toString(): String { + return name + } +} diff --git a/server/src/main/kotlin/routes/lobby/LobbyService.kt b/server/src/main/kotlin/routes/lobby/LobbyService.kt index 7cc0af8..6b87021 100644 --- a/server/src/main/kotlin/routes/lobby/LobbyService.kt +++ b/server/src/main/kotlin/routes/lobby/LobbyService.kt @@ -6,7 +6,7 @@ import http.dto.LobbyMessage import http.dto.OnlineUser import http.dto.RoomSummary import java.util.* -import `object`.Room +import room.Room import server.EntropyServer import store.Store import store.UserConnectionStore diff --git a/server/src/main/kotlin/routes/session/SessionService.kt b/server/src/main/kotlin/routes/session/SessionService.kt index 7118892..2118b44 100644 --- a/server/src/main/kotlin/routes/session/SessionService.kt +++ b/server/src/main/kotlin/routes/session/SessionService.kt @@ -10,7 +10,7 @@ import http.dto.BeginSessionRequest import http.dto.BeginSessionResponse import io.ktor.http.* import java.util.* -import `object`.Room +import room.Room import routes.ClientException import store.Store import util.OnlineConstants From cc69fd1fd257787ae87fcfa359211c38f2920883 Mon Sep 17 00:00:00 2001 From: Alyssa Date: Thu, 7 Nov 2024 20:36:21 +0000 Subject: [PATCH 2/9] some tidy up after porting Room to kotlin --- server/src/main/kotlin/room/Room.kt | 227 +++++++++++----------------- 1 file changed, 91 insertions(+), 136 deletions(-) diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index bc72886..cf7e4b9 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -15,12 +15,12 @@ import `object`.OnlineMessage import `object`.Player import server.EntropyServer import util.CardsUtil -import util.Debug import util.EntropyUtil import util.ServerGlobals import util.ServerGlobals.uscStore import util.XmlBuilderServer import util.XmlConstants +import utils.CoreGlobals.logger class Room( val name: String, @@ -32,19 +32,19 @@ class Room( private val hmPlayerByPlayerNumber = ExtendedConcurrentHashMap() private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() - val chatHistory: ArrayList = ArrayList() - private val currentPlayers: MutableList = ArrayList() - private val observers: MutableList = ArrayList() + val chatHistory: MutableList = mutableListOf() + private val currentPlayers: MutableList = mutableListOf() + private val observers: MutableList = mutableListOf() private var previousGame: GameWrapper? = null private var currentGame: GameWrapper? = null val isFull: Boolean get() = currentPlayers.size == capacity - val isEmpty: Boolean + private val isEmpty: Boolean get() = (currentPlayers.isEmpty() && observers.isEmpty()) - fun clearChatIfEmpty() { + private fun clearChatIfEmpty() { synchronized(this) { if (!isEmpty) { return @@ -55,27 +55,22 @@ class Room( fun addToCurrentPlayers(username: String, playerNumber: Int): Int { synchronized(this) { - val existingUsername: String? = hmPlayerByPlayerNumber.get(playerNumber) + val existingUsername: String? = hmPlayerByPlayerNumber[playerNumber] if (existingUsername != null) { - Debug.append( - (username + - " tried to join room " + - name + - " as player " + - playerNumber + - " but the space was taken by " + - existingUsername) + logger.info( + "seatTaken", + "$username tried to join $name as player $playerNumber but seat was taken by $existingUsername" ) return -1 } if (hmPlayerByPlayerNumber.containsValue(username)) { - Debug.append(username + " tried to join room " + name + " twice!") + logger.info("doubleJoin", "$username tried to join $name twice!") return -1 } currentPlayers.add(username) - hmPlayerByPlayerNumber.put(playerNumber, username) + hmPlayerByPlayerNumber[playerNumber] = username observers.remove(username) notifyAllPlayersOfPlayerChange(username, false) @@ -85,8 +80,8 @@ class Room( } fun removePlayer(username: String, fireLobbyChanged: Boolean) { - for (playerNumber in 0 until capacity) { - val user: String? = hmPlayerByPlayerNumber.get(playerNumber) + for (playerNumber in 0 ..< capacity) { + val user: String? = hmPlayerByPlayerNumber[playerNumber] if ((user != null && (username == user))) { hmPlayerByPlayerNumber.remove(playerNumber) hmFormerPlayerByPlayerNumber[playerNumber] = user @@ -105,8 +100,8 @@ class Room( // There is a game in progress if (currentGame!!.gameEndMillis == -1L) { - val bid: LeftBid = LeftBid() - val player: Player = + val bid = LeftBid() + val player = Player(playerNumber, EntropyUtil.getColourForPlayerNumber(playerNumber)) player.name = username bid.player = player @@ -117,7 +112,7 @@ class Room( // Moved this into here as otherwise we set it to 0 incorrectly and a person // ends up with no cards! val details: HandDetails = currentGame!!.currentRoundDetails - val hmHandSizeByPlayerNumber: ConcurrentHashMap = details.handSizes + val hmHandSizeByPlayerNumber = details.handSizes hmHandSizeByPlayerNumber[playerNumber] = 0 } @@ -143,9 +138,9 @@ class Room( userToExclude: String?, blocking: Boolean ) { - val usersToNotify: HashSet = allUsersInRoom + var usersToNotify = allUsersInRoom if (userToExclude != null) { - usersToNotify.remove(userToExclude) + usersToNotify = usersToNotify.minus(userToExclude) } val uscs: List = uscStore.getAllForNames(usersToNotify) @@ -159,12 +154,12 @@ class Room( private fun finishCurrentGame(winningPlayer: Int) { val roundNumber: Int = currentGame!!.roundNumber - val winningUsername: String? = hmPlayerByPlayerNumber.get(winningPlayer) + val winningUsername: String? = hmPlayerByPlayerNumber[winningPlayer] resetCurrentPlayers() currentGame!!.winningPlayer = winningPlayer if (roundNumber > 1) { - val om: OnlineMessage = OnlineMessage("black", "$winningUsername won!", "Game") + val om = OnlineMessage("black", "$winningUsername won!", "Game") addToChatHistoryAndNotifyUsers(om) } @@ -178,8 +173,8 @@ class Room( @JvmOverloads fun resetCurrentPlayers(fireLobbyChanged: Boolean = true) { currentPlayers.clear() - for (i in 0 until capacity) { - val username: String? = hmPlayerByPlayerNumber.get(i) + for (i in 0 ..< capacity) { + val username: String? = hmPlayerByPlayerNumber[i] if (username != null) { currentPlayers.add(username) } @@ -211,36 +206,29 @@ class Room( } fun initialiseGame() { - try { - val gameId: String = "G" + System.currentTimeMillis() - if (currentGame != null) { - previousGame = currentGame!!.factoryCopy() - } - - currentGame = GameWrapper(gameId) + val gameId: String = "G" + System.currentTimeMillis() + if (currentGame != null) { + previousGame = currentGame!!.factoryCopy() + } - val details = HandDetails() - val hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap = - ExtendedConcurrentHashMap() - for (i in 0 until capacity) { - hmHandSizeByPlayerNumber[i] = 5 - } + currentGame = GameWrapper(gameId) - val hmHandByPlayerNumber: ConcurrentHashMap> = - dealHandsHashMap(hmHandSizeByPlayerNumber) + val details = HandDetails() + val hmHandSizeByPlayerNumber = ExtendedConcurrentHashMap() + for (i in 0 ..< capacity) { + hmHandSizeByPlayerNumber[i] = 5 + } - details.hands = hmHandByPlayerNumber - details.handSizes = hmHandSizeByPlayerNumber - currentGame!!.setDetailsForRound(1, details) + val hmHandByPlayerNumber = dealHandsHashMap(hmHandSizeByPlayerNumber) + details.hands = hmHandByPlayerNumber + details.handSizes = hmHandSizeByPlayerNumber + currentGame!!.setDetailsForRound(1, details) - val personToStart: Int = Random().nextInt(capacity) + val personToStart: Int = Random().nextInt(capacity) - val history = BidHistory() - history.personToStart = personToStart - currentGame!!.setBidHistoryForRound(1, history) - } catch (t: Throwable) { - Debug.stackTrace(t) - } + val history = BidHistory() + history.personToStart = personToStart + currentGame!!.setBidHistoryForRound(1, history) } fun handleChallenge( @@ -250,8 +238,8 @@ class Room( challengedNumber: Int, bid: Bid ) { - val game: GameWrapper? = getGameForId(gameId) - val details: HandDetails = game!!.getDetailsForRound(roundNumber) + val game = getGameForId(gameId) + val details: HandDetails = game.getDetailsForRound(roundNumber) val hmHandByPlayerNumber: ConcurrentHashMap> = details.hands if (bid.isOverbid(hmHandByPlayerNumber, settings.jokerValue)) { // bidder loses @@ -269,8 +257,8 @@ class Room( bidderNumber: Int, bid: Bid ) { - val game: GameWrapper? = getGameForId(gameId) - val details: HandDetails = game!!.getDetailsForRound(roundNumber) + val game = getGameForId(gameId) + val details: HandDetails = game.getDetailsForRound(roundNumber) val hmHandByPlayerNumber: ConcurrentHashMap> = details.hands if ( bid.isPerfect( @@ -286,24 +274,22 @@ class Room( } } - fun setUpNextRound(losingPlayerNumber: Int) { + private fun setUpNextRound(losingPlayerNumber: Int) { val currentRoundNumber: Int = currentGame!!.roundNumber var nextRoundDetails: HandDetails? = currentGame!!.getDetailsForRound(currentRoundNumber + 1) if (nextRoundDetails != null) { - Debug.stackTrace("Trying to set up next round but it's not null. Room $name") + logger.error("doubleRound", "Trying to set up next round but it's not null. Room $name") return } val currentRoundDetails: HandDetails = currentGame!!.getDetailsForRound(currentRoundNumber) - val hmHandSizeByPlayerNumber: ExtendedConcurrentHashMap = - currentRoundDetails.handSizes - val handSize: Int? = hmHandSizeByPlayerNumber.get(losingPlayerNumber) - val newHandSize: Int = max(0.0, (handSize!! - 1).toDouble()).toInt() + val hmHandSizeByPlayerNumber = currentRoundDetails.handSizes + val handSize = hmHandSizeByPlayerNumber.getValue(losingPlayerNumber) + val newHandSize: Int = max(0, handSize - 1) - val hmHandSizeByPlayerNumberForNextRound: ExtendedConcurrentHashMap = - hmHandSizeByPlayerNumber.factoryCopy() + val hmHandSizeByPlayerNumberForNextRound = hmHandSizeByPlayerNumber.factoryCopy() hmHandSizeByPlayerNumberForNextRound[losingPlayerNumber] = newHandSize val potentialWinner: Int = getWinningPlayer(hmHandSizeByPlayerNumberForNextRound) @@ -311,15 +297,15 @@ class Room( finishCurrentGame(potentialWinner) } else { nextRoundDetails = HandDetails() - nextRoundDetails.setHandSizes(hmHandSizeByPlayerNumberForNextRound) + nextRoundDetails.handSizes = hmHandSizeByPlayerNumberForNextRound val hmHandByPlayerNumber: ConcurrentHashMap> = dealHandsHashMap(hmHandSizeByPlayerNumberForNextRound) - nextRoundDetails.setHands(hmHandByPlayerNumber) + nextRoundDetails.hands = hmHandByPlayerNumber currentGame!!.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails) - val history: BidHistory = BidHistory() + val history = BidHistory() history.personToStart = losingPlayerNumber currentGame!!.setBidHistoryForRound(currentRoundNumber + 1, history) @@ -361,83 +347,56 @@ class Room( } private fun getWinningPlayer(hmHandSizeByPlayerNumber: ConcurrentHashMap): Int { - var activePlayers: Int = 0 - var potentialWinner: Int = 0 + var activePlayers = 0 + var potentialWinner = 0 - for (i in 0 until capacity) { - val handSize: Int = (hmHandSizeByPlayerNumber.get(i))!! + for (i in 0 ..< capacity) { + val handSize: Int = hmHandSizeByPlayerNumber.getValue(i) if (handSize > 0) { activePlayers++ potentialWinner = i } } - if (activePlayers > 1) { - return -1 + return if (activePlayers > 1) { + -1 } else { - return potentialWinner + potentialWinner } } - val allUsersInRoom: HashSet - /** - * Returns a HashSet since it's possible for a player to be present as a player AND an - * observer. This occurs if they've left but the game is still going - we keep the reference - * as a player so others can't take the seat. They obviously then have the option to join as - * an observer. - */ - get() { - val ret: ArrayList = getCurrentPlayers() - ret.addAll(getObservers()) - - val hs: HashSet = HashSet(ret) - return hs - } + /** + * Returns a set since it's possible for a player to be present as a player AND an observer. + * This occurs if they've left but the game is still going - we keep the reference as a player + * so others can't take the seat. They obviously then have the option to join as an observer. + */ + private val allUsersInRoom: Set + get() = (currentPlayers + observers).toSet() val isGameInProgress: Boolean - get() { - // This no longer works - // return currentGame != null; - // return currentPlayers.size() == capacity && (waitingForPlayerToSeeResult == null); - return currentPlayers.size == capacity - } - - fun getCurrentPlayers(): ArrayList { - return ArrayList(currentPlayers) - } + get() = currentPlayers.size == capacity val currentPlayerCount: Int - get() { - return currentPlayers.size - } - - fun getObservers(): ArrayList { - return ArrayList(observers) - } + get() = currentPlayers.size val observerCount: Int - get() { - return observers.size - } + get() = observers.size - /** HashMap gets/sets */ fun getPlayer(playerNumber: Int): String? { - return hmPlayerByPlayerNumber.get(playerNumber) + return hmPlayerByPlayerNumber[playerNumber] } fun getFormerPlayer(playerNumber: Int): String? { - return hmFormerPlayerByPlayerNumber.get(playerNumber) + return hmFormerPlayerByPlayerNumber[playerNumber] } - fun getGameForId(gameId: String): GameWrapper? { - val currentId: String = currentGame!!.gameId - if ((gameId == currentId)) { - return currentGame + private fun getGameForId(gameId: String): GameWrapper { + if (gameId == currentGame?.gameId) { + return currentGame!! } - val previousId: String = previousGame!!.gameId - if ((gameId == previousId)) { - return previousGame + if ((gameId == previousGame?.gameId)) { + return previousGame!! } throw RuntimeException("Got a null game for room $name and gameId $gameId") @@ -449,10 +408,10 @@ class Room( } if (previousGame == null) { - Debug.append( - "Tried to get next game for gameId $previousGameIdFromClient but previous game was null." + logger.warn( + "staleGameId", + "Tried to get next game for gameId $previousGameIdFromClient but previous game was null. Current: ${currentGame?.gameId}" ) - Debug.appendWithoutDate("Current: " + currentGame!!.gameId) return currentGame } @@ -461,11 +420,11 @@ class Room( return currentGame } - Debug.append( - "Tried to get next game for gameId $previousGameIdFromClient but this didn't match my previous game." + logger.warn( + "staleGameId", + "Tried to get next game for gameId $previousGameIdFromClient but this did not match. Previous [${previousGame?.gameId}], Current [${currentGame?.gameId}]" ) - Debug.appendWithoutDate("Previous: " + previousGame!!.gameId) - Debug.appendWithoutDate("Current: " + currentGame!!.gameId) + return currentGame } @@ -484,15 +443,14 @@ class Room( roundNumber: Int, newBid: Bid? ): Boolean { - val game: GameWrapper? = getGameForId(gameId) + val game = getGameForId(gameId) - val history: BidHistory = game!!.getBidHistoryForRound(roundNumber) + val history: BidHistory = game.getBidHistoryForRound(roundNumber) val added: Boolean = history.addBidForPlayer(playerNumber, newBid) if (added) { // Notify all other capacity - val bidNotification: String = - XmlBuilderServer.getBidNotification(name, playerNumber, newBid) + val bidNotification = XmlBuilderServer.getBidNotification(name, playerNumber, newBid) notifyAllUsersViaGameSocket(bidNotification, null, false) } @@ -506,13 +464,10 @@ class Room( chatHistory.add(message) - val chatMessage: String = XmlBuilderServer.getChatNotification(name, message) - val users: HashSet = allUsersInRoom - val uscs: List = uscStore.getAllForNames(users) + val chatMessage = XmlBuilderServer.getChatNotification(name, message) + val uscs = uscStore.getAllForNames(allUsersInRoom) server.sendViaNotificationSocket(uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, false) } - override fun toString(): String { - return name - } + override fun toString() = name } From 0a4a1615a9b86b2e60ee3d32ae813363fdbcac04 Mon Sep 17 00:00:00 2001 From: Alyssa Date: Thu, 7 Nov 2024 20:59:51 +0000 Subject: [PATCH 3/9] room copying --- .../src/main/java/server/EntropyServer.java | 52 +++---------------- .../src/main/java/util/XmlBuilderServer.java | 3 +- server/src/main/kotlin/room/Room.kt | 31 ++++++----- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/server/src/main/java/server/EntropyServer.java b/server/src/main/java/server/EntropyServer.java index 1cd6bf1..9c2dc41 100644 --- a/server/src/main/java/server/EntropyServer.java +++ b/server/src/main/java/server/EntropyServer.java @@ -216,61 +216,25 @@ public List getChatHistory(String id) { return room.getChatHistory(); } - public Room registerNewRoom(String roomName, int capacity, GameSettings settings) { - return registerNewRoom(roomName, settings.getMode(), capacity, settings.getJokerQuantity(), settings.getJokerValue(), - settings.getIncludeMoons(), settings.getIncludeStars(), settings.getIllegalAllowed(), settings.getNegativeJacks(), - settings.getCardReveal()); - } - - private void registerNewRoom(String roomName, GameMode mode, int players, int jokerQuantity, int jokerValue) { - registerNewRoom(roomName, mode, players, jokerQuantity, jokerValue, false, false, false, false, false); - } - - private Room registerNewRoom(String roomName, GameMode mode, int players, int jokerQuantity, int jokerValue, - boolean includeMoons, boolean includeStars, boolean illegalAllowed, boolean negativeJacks, - boolean cardReveal) { + public Room registerNewRoom(Room room) { Iterator it = hmRoomByName.keySet().iterator(); for (; it.hasNext(); ) { String id = it.next(); - Room room = hmRoomByName.get(id); - - String nameToCheck = room.getName(); - if (nameToCheck.equals(roomName)) { - Debug.append("Not creating room " + nameToCheck + " as a room with that name already exists."); + Room existingRoom = hmRoomByName.get(id); + String nameToCheck = existingRoom.getName(); + if (nameToCheck.equals(room.getName())) { + logger.warn("duplicateRoom", "Not creating room " + nameToCheck + " as a room with that name already exists."); return null; } } - GameSettings settings = new GameSettings(mode, jokerQuantity, jokerValue, includeMoons, includeStars, - negativeJacks, cardReveal, illegalAllowed); + hmRoomByName.put(room.getName(), room); - Room room = new Room(roomName, settings, players, this); - room.initialiseGame(); - hmRoomByName.put(roomName, room); - - Debug.append("Room created: " + roomName); - return room; - } - - public Room registerCopy(Room room) { - String roomName = room.getName(); - int capacity = room.getCapacity(); - GameSettings settings = room.getSettings(); - - int index = roomName.indexOf(' ') + 1; - String roomNumberStr = roomName.substring(index); - int roomNumber = Integer.parseInt(roomNumberStr); - - String newRoomName = roomName.substring(0, index) + (roomNumber + 1); - - Room newRoom = registerNewRoom(newRoomName, capacity, settings); - - if (newRoom != null) { - newRoom.setCopy(true); + if (room.isCopy()) { ServerGlobals.lobbyService.lobbyChanged(); } - return newRoom; + return room; } public ArrayList getRooms() { diff --git a/server/src/main/java/util/XmlBuilderServer.java b/server/src/main/java/util/XmlBuilderServer.java index 7a501e5..f32022f 100644 --- a/server/src/main/java/util/XmlBuilderServer.java +++ b/server/src/main/java/util/XmlBuilderServer.java @@ -211,7 +211,8 @@ public static Document getRoomJoinResponse(Room room, String username, String ob if (room.isFull()) { - server.registerCopy(room); + var copy = room.makeCopy(); + server.registerNewRoom(copy); } } else diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index cf7e4b9..940f8b7 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -26,17 +26,16 @@ class Room( val name: String, val settings: GameSettings, val capacity: Int, + val isCopy: Boolean, private val server: EntropyServer ) { - var isCopy: Boolean = false - private val hmPlayerByPlayerNumber = ExtendedConcurrentHashMap() private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() val chatHistory: MutableList = mutableListOf() private val currentPlayers: MutableList = mutableListOf() private val observers: MutableList = mutableListOf() private var previousGame: GameWrapper? = null - private var currentGame: GameWrapper? = null + private var currentGame: GameWrapper = initialiseGame() val isFull: Boolean get() = currentPlayers.size == capacity @@ -153,17 +152,18 @@ class Room( } private fun finishCurrentGame(winningPlayer: Int) { - val roundNumber: Int = currentGame!!.roundNumber + val roundNumber: Int = currentGame.roundNumber val winningUsername: String? = hmPlayerByPlayerNumber[winningPlayer] resetCurrentPlayers() - currentGame!!.winningPlayer = winningPlayer + currentGame.winningPlayer = winningPlayer if (roundNumber > 1) { val om = OnlineMessage("black", "$winningUsername won!", "Game") addToChatHistoryAndNotifyUsers(om) } - initialiseGame() + previousGame = currentGame.factoryCopy() + currentGame = initialiseGame() // Notify everyone that the game is over now we've finished setting up val notification: String = XmlBuilderServer.factoryGameOverNotification(this, winningPlayer) @@ -205,13 +205,10 @@ class Room( clearChatIfEmpty() } - fun initialiseGame() { + private fun initialiseGame(): GameWrapper { val gameId: String = "G" + System.currentTimeMillis() - if (currentGame != null) { - previousGame = currentGame!!.factoryCopy() - } - currentGame = GameWrapper(gameId) + val newGame = GameWrapper(gameId) val details = HandDetails() val hmHandSizeByPlayerNumber = ExtendedConcurrentHashMap() @@ -222,13 +219,15 @@ class Room( val hmHandByPlayerNumber = dealHandsHashMap(hmHandSizeByPlayerNumber) details.hands = hmHandByPlayerNumber details.handSizes = hmHandSizeByPlayerNumber - currentGame!!.setDetailsForRound(1, details) + newGame.setDetailsForRound(1, details) - val personToStart: Int = Random().nextInt(capacity) + val personToStart = Random().nextInt(capacity) val history = BidHistory() history.personToStart = personToStart - currentGame!!.setBidHistoryForRound(1, history) + newGame.setBidHistoryForRound(1, history) + + return newGame } fun handleChallenge( @@ -469,5 +468,9 @@ class Room( server.sendViaNotificationSocket(uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, false) } + fun makeCopy(): Room { + return this + } + override fun toString() = name } From 265dd4998ed5d6e3a34683471cc8e9ef61240f09 Mon Sep 17 00:00:00 2001 From: Alyssa Date: Sun, 10 Nov 2024 11:00:39 +0000 Subject: [PATCH 4/9] extract out RoomFactory --- core/src/main/kotlin/game/GameSettings.kt | 14 +-- .../src/main/java/server/EntropyServer.java | 67 +--------- server/src/main/kotlin/room/Room.kt | 66 +++++----- server/src/main/kotlin/room/RoomFactory.kt | 57 +++++++++ server/src/main/resources/elements.txt | 118 ++++++++++++++++++ .../kotlin/routes/lobby/LobbyServiceTest.kt | 13 +- 6 files changed, 229 insertions(+), 106 deletions(-) create mode 100644 server/src/main/kotlin/room/RoomFactory.kt create mode 100644 server/src/main/resources/elements.txt diff --git a/core/src/main/kotlin/game/GameSettings.kt b/core/src/main/kotlin/game/GameSettings.kt index 409c0cf..138aab3 100644 --- a/core/src/main/kotlin/game/GameSettings.kt +++ b/core/src/main/kotlin/game/GameSettings.kt @@ -2,11 +2,11 @@ package game data class GameSettings( val mode: GameMode, - val jokerQuantity: Int, - val jokerValue: Int, - val includeMoons: Boolean, - val includeStars: Boolean, - val negativeJacks: Boolean, - val cardReveal: Boolean, - val illegalAllowed: Boolean + val jokerQuantity: Int = 0, + val jokerValue: Int = 0, + val includeMoons: Boolean = false, + val includeStars: Boolean = false, + val negativeJacks: Boolean = false, + val cardReveal: Boolean = false, + val illegalAllowed: Boolean = false ) diff --git a/server/src/main/java/server/EntropyServer.java b/server/src/main/java/server/EntropyServer.java index 9c2dc41..c2a5582 100644 --- a/server/src/main/java/server/EntropyServer.java +++ b/server/src/main/java/server/EntropyServer.java @@ -1,13 +1,12 @@ package server; import auth.UserConnection; -import game.GameMode; -import game.GameSettings; import logging.LoggerUncaughtExceptionHandler; import object.OnlineMessage; import object.ServerRunnable; import object.ServerThread; import room.Room; +import room.RoomFactory; import util.*; import java.util.ArrayList; @@ -38,7 +37,7 @@ public static void main() { private void onStart() { Debug.appendBanner("Start-Up"); - registerDefaultRooms(); + RoomFactory.INSTANCE.registerStarterRooms(); Debug.append("Starting permanent threads"); @@ -47,68 +46,6 @@ private void onStart() { Debug.appendBanner("Server is ready - accepting connections"); } - private void registerDefaultRooms() { - Debug.append("Creating rooms..."); - - //Entropy - //2 player - registerNewRoom("Potassium 1", GameMode.Entropy, 2, 0, 2); - registerNewRoom("Zinc 1", GameMode.Entropy, 2, 2, 2); - registerNewRoom("Helium 1", GameMode.Entropy, 2, 4, 3); - registerNewRoom("Magnesium 1", GameMode.Entropy, 2, 2, 2, true, true, true, false, false); - registerNewRoom("Cobalt 1", GameMode.Entropy, 2, 0, 2, true, true, false, false, false); - registerNewRoom("Chlorine 1", GameMode.Entropy, 2, 0, 2, true, true, false, true, false); - registerNewRoom("Gold 1", GameMode.Entropy, 2, 0, 2, false, false, true, false, false); - registerNewRoom("Lithium 1", GameMode.Entropy, 2, 2, 2, false, false, false, true, true); - registerNewRoom("Beryllium 1", GameMode.Entropy, 2, 0, 2, true, true, false, false, true); - - //3 player - registerNewRoom("Bromine 1", GameMode.Entropy, 3, 0, 2); - registerNewRoom("Argon 1", GameMode.Entropy, 3, 2, 2); - registerNewRoom("Hydrogen 1", GameMode.Entropy, 3, 4, 3); - registerNewRoom("Zirconium 1", GameMode.Entropy, 3, 2, 2, true, true, true, false, false); - registerNewRoom("Calcium 1", GameMode.Entropy, 3, 0, 2, true, true, false, false, false); - registerNewRoom("Iron 1", GameMode.Entropy, 3, 2, 2, true, false, false, true, false); - registerNewRoom("Palladium 1", GameMode.Entropy, 3, 3, 3, true, true, true, false, true); - - //4 player - registerNewRoom("Nickel 1", GameMode.Entropy, 4, 0, 2); - registerNewRoom("Sodium 1", GameMode.Entropy, 4, 2, 2); - registerNewRoom("Phosphorus 1", GameMode.Entropy, 4, 4, 3); - registerNewRoom("Titanium 1", GameMode.Entropy, 4, 0, 2, true, true, false, true, false); - registerNewRoom("Gallium 1", GameMode.Entropy, 4, 2, 2, true, true, false, false, true); - - //Vectropy - //2 player - registerNewRoom("Oxygen 1", GameMode.Vectropy, 2, 0, 2); - registerNewRoom("Neon 1", GameMode.Vectropy, 2, 2, 2); - registerNewRoom("Copper 1", GameMode.Vectropy, 2, 4, 3); - registerNewRoom("Manganese 1", GameMode.Vectropy, 2, 2, 2, true, true, true, false, false); - registerNewRoom("Selenium 1", GameMode.Vectropy, 2, 0, 2, true, true, false, false, false); - registerNewRoom("Chromium 1", GameMode.Vectropy, 2, 0, 2, true, true, false, true, false); - registerNewRoom("Silver 1", GameMode.Vectropy, 2, 0, 2, false, false, true, false, false); - registerNewRoom("Antimony 1", GameMode.Vectropy, 2, 2, 2, false, false, false, true, true); - registerNewRoom("Tungsten 1", GameMode.Vectropy, 2, 0, 2, true, true, false, false, true); - - //3 player - registerNewRoom("Carbon 1", GameMode.Vectropy, 3, 0, 2); - registerNewRoom("Silicon 1", GameMode.Vectropy, 3, 2, 2); - registerNewRoom("Nitrogen 1", GameMode.Vectropy, 3, 4, 3); - registerNewRoom("Sulphur 1", GameMode.Vectropy, 3, 2, 2, true, true, true, false, false); - registerNewRoom("Fluorine 1", GameMode.Vectropy, 3, 0, 2, true, true, false, false, false); - registerNewRoom("Tin 1", GameMode.Vectropy, 3, 2, 2, true, false, false, true, false); - registerNewRoom("Indium 1", GameMode.Vectropy, 3, 3, 3, true, true, true, false, true); - - //4 player - registerNewRoom("Iodine 1", GameMode.Vectropy, 4, 0, 2); - registerNewRoom("Lead 1", GameMode.Vectropy, 4, 2, 2); - registerNewRoom("Uranium 1", GameMode.Vectropy, 4, 4, 3); - registerNewRoom("Vanadium 1", GameMode.Vectropy, 4, 0, 2, true, true, false, true, false); - registerNewRoom("Xenon 1", GameMode.Vectropy, 4, 2, 2, true, true, false, false, true); - - Debug.append("Finished creating " + hmRoomByName.size() + " rooms"); - } - private void startListenerThreads() { try { ServerThread listenerThread = new ServerThread(new MessageListener(this, SERVER_PORT_NUMBER)); diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index 940f8b7..3f88c6d 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -13,7 +13,6 @@ import `object`.HandDetails import `object`.LeftBid import `object`.OnlineMessage import `object`.Player -import server.EntropyServer import util.CardsUtil import util.EntropyUtil import util.ServerGlobals @@ -23,11 +22,10 @@ import util.XmlConstants import utils.CoreGlobals.logger class Room( - val name: String, + private val baseName: String, val settings: GameSettings, val capacity: Int, - val isCopy: Boolean, - private val server: EntropyServer + private val index: Int = 1, ) { private val hmPlayerByPlayerNumber = ExtendedConcurrentHashMap() private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() @@ -37,6 +35,12 @@ class Room( private var previousGame: GameWrapper? = null private var currentGame: GameWrapper = initialiseGame() + val name: String + get() = "$baseName $index" + + val isCopy: Boolean + get() = index > 1 + val isFull: Boolean get() = currentPlayers.size == capacity @@ -89,28 +93,28 @@ class Room( notifyAllPlayersOfPlayerChange(username, true) // The game has not started - if (currentGame!!.gameStartMillis == -1L) { + if (currentGame.gameStartMillis == -1L) { // Unset the countdown if it's going, reset current capacity and get out of this // madness - currentGame!!.countdownStartMillis = -1 + currentGame.countdownStartMillis = -1 resetCurrentPlayers(fireLobbyChanged) return } // There is a game in progress - if (currentGame!!.gameEndMillis == -1L) { + if (currentGame.gameEndMillis == -1L) { val bid = LeftBid() val player = Player(playerNumber, EntropyUtil.getColourForPlayerNumber(playerNumber)) player.name = username bid.player = player - val history: BidHistory = currentGame!!.currentBidHistory + val history: BidHistory = currentGame.currentBidHistory history.addBidForPlayer(playerNumber, bid) // Moved this into here as otherwise we set it to 0 incorrectly and a person // ends up with no cards! - val details: HandDetails = currentGame!!.currentRoundDetails + val details: HandDetails = currentGame.currentRoundDetails val hmHandSizeByPlayerNumber = details.handSizes hmHandSizeByPlayerNumber[playerNumber] = 0 } @@ -143,7 +147,7 @@ class Room( } val uscs: List = uscStore.getAllForNames(usersToNotify) - server.sendViaNotificationSocket( + ServerGlobals.server.sendViaNotificationSocket( uscs, notification, XmlConstants.SOCKET_NAME_GAME, @@ -274,16 +278,15 @@ class Room( } private fun setUpNextRound(losingPlayerNumber: Int) { - val currentRoundNumber: Int = currentGame!!.roundNumber + val currentRoundNumber: Int = currentGame.roundNumber - var nextRoundDetails: HandDetails? = - currentGame!!.getDetailsForRound(currentRoundNumber + 1) + var nextRoundDetails: HandDetails? = currentGame.getDetailsForRound(currentRoundNumber + 1) if (nextRoundDetails != null) { logger.error("doubleRound", "Trying to set up next round but it's not null. Room $name") return } - val currentRoundDetails: HandDetails = currentGame!!.getDetailsForRound(currentRoundNumber) + val currentRoundDetails: HandDetails = currentGame.getDetailsForRound(currentRoundNumber) val hmHandSizeByPlayerNumber = currentRoundDetails.handSizes val handSize = hmHandSizeByPlayerNumber.getValue(losingPlayerNumber) val newHandSize: Int = max(0, handSize - 1) @@ -302,13 +305,13 @@ class Room( dealHandsHashMap(hmHandSizeByPlayerNumberForNextRound) nextRoundDetails.hands = hmHandByPlayerNumber - currentGame!!.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails) + currentGame.setDetailsForRound(currentRoundNumber + 1, nextRoundDetails) val history = BidHistory() history.personToStart = losingPlayerNumber - currentGame!!.setBidHistoryForRound(currentRoundNumber + 1, history) + currentGame.setBidHistoryForRound(currentRoundNumber + 1, history) - currentGame!!.roundNumber = currentRoundNumber + 1 + currentGame.roundNumber = currentRoundNumber + 1 val newRoundNotification: String = XmlBuilderServer.factoryNewRoundNotification( @@ -325,7 +328,7 @@ class Room( ): ConcurrentHashMap> { val hmHandByPlayerNumber: ConcurrentHashMap> = ConcurrentHashMap() - val seed: Long = server.generateSeed() + val seed: Long = ServerGlobals.server.generateSeed() val deck = CardsUtil.createAndShuffleDeck( true, @@ -390,8 +393,8 @@ class Room( } private fun getGameForId(gameId: String): GameWrapper { - if (gameId == currentGame?.gameId) { - return currentGame!! + if (gameId == currentGame.gameId) { + return currentGame } if ((gameId == previousGame?.gameId)) { @@ -401,7 +404,7 @@ class Room( throw RuntimeException("Got a null game for room $name and gameId $gameId") } - fun getNextGameForId(previousGameIdFromClient: String): GameWrapper? { + fun getNextGameForId(previousGameIdFromClient: String): GameWrapper { if (previousGameIdFromClient.isEmpty()) { return currentGame } @@ -409,30 +412,30 @@ class Room( if (previousGame == null) { logger.warn( "staleGameId", - "Tried to get next game for gameId $previousGameIdFromClient but previous game was null. Current: ${currentGame?.gameId}" + "Tried to get next game for gameId $previousGameIdFromClient but previous game was null. Current: ${currentGame.gameId}" ) return currentGame } val previousGameId: String = previousGame!!.gameId - if ((previousGameId == previousGameIdFromClient)) { + if (previousGameId == previousGameIdFromClient) { return currentGame } logger.warn( "staleGameId", - "Tried to get next game for gameId $previousGameIdFromClient but this did not match. Previous [${previousGame?.gameId}], Current [${currentGame?.gameId}]" + "Tried to get next game for gameId $previousGameIdFromClient but this did not match. Previous [${previousGame?.gameId}], Current [${currentGame.gameId}]" ) return currentGame } fun getLastBidForPlayer(playerNumber: Int, roundNumber: Int): Bid? { - if ((currentGame == null || playerNumber == -1)) { + if (playerNumber == -1) { return null } - val history: BidHistory = currentGame!!.getBidHistoryForRound(roundNumber) + val history: BidHistory = currentGame.getBidHistoryForRound(roundNumber) return history.getLastBidForPlayer(playerNumber) } @@ -465,12 +468,15 @@ class Room( val chatMessage = XmlBuilderServer.getChatNotification(name, message) val uscs = uscStore.getAllForNames(allUsersInRoom) - server.sendViaNotificationSocket(uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, false) + ServerGlobals.server.sendViaNotificationSocket( + uscs, + chatMessage, + XmlConstants.SOCKET_NAME_CHAT, + false + ) } - fun makeCopy(): Room { - return this - } + fun makeCopy() = Room(baseName, settings, capacity, index + 1) override fun toString() = name } diff --git a/server/src/main/kotlin/room/RoomFactory.kt b/server/src/main/kotlin/room/RoomFactory.kt new file mode 100644 index 0000000..1fb3827 --- /dev/null +++ b/server/src/main/kotlin/room/RoomFactory.kt @@ -0,0 +1,57 @@ +package room + +import game.GameMode +import game.GameSettings +import util.ServerGlobals + +object RoomFactory { + fun registerStarterRooms() { + makeStarterRooms().forEach(ServerGlobals.server::registerNewRoom) + } + + private fun makeStarterRooms(): List { + val elementGenerator = getElementNames().iterator() + + return GameMode.entries.flatMap { gameMode -> + val configs = getConfigs(gameMode) + val capacities = listOf(2, 3, 4) + + capacities.flatMap { capacity -> + configs.map { Room(elementGenerator.next(), it, capacity) } + } + } + } + + private fun getElementNames(): List { + val inputStream = + javaClass.getResourceAsStream("/elements.txt") + ?: throw RuntimeException("Unable to read elements list") + + return inputStream.bufferedReader().readLines() + } + + private fun getConfigs(mode: GameMode): List = + listOf( + GameSettings(mode), + GameSettings(mode, illegalAllowed = true), + GameSettings(mode, jokerQuantity = 2, jokerValue = 2), + GameSettings(mode, includeMoons = true, includeStars = true), + GameSettings(mode, includeMoons = true, includeStars = true, cardReveal = true), + GameSettings( + mode, + jokerQuantity = 2, + jokerValue = 2, + negativeJacks = true, + cardReveal = true + ), + GameSettings(mode, includeMoons = true, includeStars = true, negativeJacks = true), + GameSettings( + mode, + jokerQuantity = 2, + jokerValue = 2, + includeMoons = true, + includeStars = true, + illegalAllowed = true + ) + ) +} diff --git a/server/src/main/resources/elements.txt b/server/src/main/resources/elements.txt new file mode 100644 index 0000000..79c1edb --- /dev/null +++ b/server/src/main/resources/elements.txt @@ -0,0 +1,118 @@ +Hydrogen +Helium +Lithium +Beryllium +Boron +Carbon +Nitrogen +Oxygen +Fluorine +Neon +Sodium +Magnesium +Aluminum +Silicon +Phosphorus +Sulfur +Chlorine +Argon +Potassium +Calcium +Scandium +Titanium +Vanadium +Chromium +Manganese +Iron +Cobalt +Nickel +Copper +Zinc +Gallium +Germanium +Arsenic +Selenium +Bromine +Krypton +Rubidium +Strontium +Yttrium +Zirconium +Niobium +Molybdenum +Technetium +Ruthenium +Rhodium +Palladium +Silver +Cadmium +Indium +Tin +Antimony +Tellurium +Iodine +Xenon +Cesium +Barium +Lanthanum +Cerium +Praseodymium +Neodymium +Promethium +Samarium +Europium +Gadolinium +Terbium +Dysprosium +Holmium +Erbium +Thulium +Ytterbium +Lutetium +Hafnium +Tantalum +Tungsten +Rhenium +Osmium +Iridium +Platinum +Gold +Mercury +Thallium +Lead +Bismuth +Polonium +Astatine +Radon +Francium +Radium +Actinium +Thorium +Protactinium +Uranium +Neptunium +Plutonium +Americium +Curium +Berkelium +Californium +Einsteinium +Fermium +Mendelevium +Nobelium +Lawrencium +Rutherfordium +Dubnium +Seaborgium +Bohrium +Hassium +Meitnerium +Darmstadtium +Roentgenium +Copernicium +Nihonium +Flerovium +Moscovium +Livermorium +Tennessine +Oganesson diff --git a/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt b/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt index b1be2b2..d5d2d0d 100644 --- a/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt +++ b/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt @@ -10,6 +10,7 @@ import io.mockk.mockk import io.mockk.verify import java.util.* import org.junit.jupiter.api.Test +import room.Room import server.EntropyServer import store.MemoryUserConnectionStore import store.SessionStore @@ -31,10 +32,14 @@ class LobbyServiceTest : AbstractTest() { sessionStore.putAll(sessionA, sessionB) val settings = makeGameSettings(includeMoons = true) - val room = server.registerNewRoom("Carbon 1", 4, settings) - room.addToCurrentPlayers("Alyssa", 0) - val copy = server.registerCopy(room) - copy.addToObservers("Bob") + val roomOne = Room("Carbon", settings, 4) + val roomTwo = roomOne.makeCopy() + + server.registerNewRoom(roomOne) + server.registerNewRoom(roomTwo) + + roomOne.addToCurrentPlayers("Alyssa", 0) + roomTwo.addToObservers("Bob") val lobby = service.getLobby() lobby.users.shouldContainExactlyInAnyOrder(OnlineUser("Alyssa", 4), OnlineUser("Bob", 7)) From bd435417ca50239287da55be65260738af539c1a Mon Sep 17 00:00:00 2001 From: Alyssa Date: Sun, 10 Nov 2024 15:08:09 +0000 Subject: [PATCH 5/9] extract out room store --- .../java/online/util/ResponseHandler.java | 27 ------- core/src/main/java/util/XmlConstants.java | 1 - .../src/main/java/server/EntropyServer.java | 77 +++---------------- .../java/server/MessageHandlerRunnable.java | 13 +--- .../src/main/java/util/XmlBuilderServer.java | 23 +----- server/src/main/kotlin/room/Room.kt | 6 +- server/src/main/kotlin/room/RoomFactory.kt | 5 +- .../main/kotlin/routes/lobby/LobbyService.kt | 4 +- .../kotlin/routes/session/SessionService.kt | 2 +- server/src/main/kotlin/store/RoomStore.kt | 31 ++++++++ server/src/main/kotlin/util/ServerGlobals.kt | 5 +- .../kotlin/routes/lobby/LobbyServiceTest.kt | 19 +++-- .../src/test/kotlin/util/ApplicationTest.kt | 6 +- 13 files changed, 80 insertions(+), 139 deletions(-) create mode 100644 server/src/main/kotlin/store/RoomStore.kt diff --git a/client/src/main/java/online/util/ResponseHandler.java b/client/src/main/java/online/util/ResponseHandler.java index 65c90c7..0f3ced3 100644 --- a/client/src/main/java/online/util/ResponseHandler.java +++ b/client/src/main/java/online/util/ResponseHandler.java @@ -33,12 +33,6 @@ public static void handleDecryptedResponse(String messageStr, String responseStr Element root = response.getDocumentElement(); String responseName = root.getNodeName(); EntropyLobby lobby = ScreenCache.get(EntropyLobby.class); - - if (responseName.equals(RESPONSE_TAG_KICK_OFF)) - { - handleKickOff(root); - return; - } if (responseName.equals(RESPONSE_TAG_ACKNOWLEDGEMENT)) { @@ -105,27 +99,6 @@ else if (responseName.equals(RESPONSE_TAG_SOCKET_TIME_OUT)) } } - private static void handleKickOff(Element root) - { - ConnectingDialog dialog = ScreenCache.getConnectingDialog(); - if (dialog.isVisible()) - { - dialog.dismissDialog(); - DialogUtil.showErrorLater("Unable to connect to the Entropy server."); - return; - } - - EntropyLobby lobby = ScreenCache.get(EntropyLobby.class); - if (lobby.isVisible()) - { - String removalReason = root.getAttribute("RemovalReason"); - String message = removalReason + "\nEntropyOnline will now exit."; - DialogUtil.showErrorLater(message); - - lobby.exit(true); - } - } - private static void handleChatResponse(Element root, EntropyLobby lobby) { String name = root.getAttribute("RoomName"); diff --git a/core/src/main/java/util/XmlConstants.java b/core/src/main/java/util/XmlConstants.java index 9a882ca..98a8778 100644 --- a/core/src/main/java/util/XmlConstants.java +++ b/core/src/main/java/util/XmlConstants.java @@ -30,7 +30,6 @@ public interface XmlConstants public static final String RESPONSE_TAG_STATISTICS_NOTIFICATION = "StatisticsNotification"; //Server responses - public static final String RESPONSE_TAG_KICK_OFF = "KickOff"; public static final String RESPONSE_TAG_LEADERBOARD = "LeaderboardResponse"; public static final String RESPONSE_TAG_JOIN_ROOM_RESPONSE = "JoinRoomAck"; public static final String RESPONSE_TAG_CLOSE_ROOM_RESPONSE = "CloseRoomAck"; diff --git a/server/src/main/java/server/EntropyServer.java b/server/src/main/java/server/EntropyServer.java index c2a5582..59438a0 100644 --- a/server/src/main/java/server/EntropyServer.java +++ b/server/src/main/java/server/EntropyServer.java @@ -10,16 +10,13 @@ import util.*; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import static utils.CoreGlobals.logger; public final class EntropyServer implements OnlineConstants { //Caches - private ConcurrentHashMap hmRoomByName = new ConcurrentHashMap<>(); private ArrayList lobbyMessages = new ArrayList<>(); //Seed @@ -61,26 +58,10 @@ public void executeInWorkerPool(ServerRunnable runnable) { } public void resetLobby() { - int countRemoved = 0; + ServerGlobals.INSTANCE.getRoomStore().reset(); - List rooms = getRooms(); - int size = rooms.size(); - for (int i = 0; i < size; i++) { - Room room = rooms.get(i); - if (room.isCopy()) { - String roomName = room.getName(); - hmRoomByName.remove(roomName); - countRemoved++; - } - } - - //Log out if we've actually removed some rooms - if (countRemoved > 0) { - logger.info("roomsRemoved", "Removed " + countRemoved + " excess rooms"); - } - - logger.info("clearedMessages", "Cleared lobby messages"); lobbyMessages.clear(); + logger.info("clearedMessages", "Cleared lobby messages"); } public void addToChatHistory(String id, String message, String colour, String username) { @@ -90,10 +71,10 @@ public void addToChatHistory(String id, String message, String colour, String us public void addAdminMessage(String message) { OnlineMessage messageObj = new OnlineMessage("black", message, "Admin"); - Iterator it = hmRoomByName.keySet().iterator(); - for (; it.hasNext(); ) { - String name = it.next(); - addToChatHistory(name, messageObj); + + var rooms = ServerGlobals.INSTANCE.getRoomStore().getAll(); + for (Room room : rooms) { + room.addToChatHistoryAndNotifyUsers(messageObj); } addToChatHistory(LOBBY_ID, messageObj); @@ -107,8 +88,10 @@ private void addToChatHistory(String name, OnlineMessage message) { String chatMessage = XmlBuilderServer.getChatNotification(name, message); sendViaNotificationSocket(usersToNotify, chatMessage, XmlConstants.SOCKET_NAME_CHAT); } else { - Room room = hmRoomByName.get(name); - room.addToChatHistoryAndNotifyUsers(message); + Room room = ServerGlobals.INSTANCE.getRoomStore().findForName(name); + if (room != null) { + room.addToChatHistoryAndNotifyUsers(message); + } } } @@ -149,48 +132,10 @@ public List getChatHistory(String id) { return lobbyMessages; } - Room room = hmRoomByName.get(id); + Room room = ServerGlobals.INSTANCE.getRoomStore().findForName(id); return room.getChatHistory(); } - public Room registerNewRoom(Room room) { - Iterator it = hmRoomByName.keySet().iterator(); - for (; it.hasNext(); ) { - String id = it.next(); - Room existingRoom = hmRoomByName.get(id); - String nameToCheck = existingRoom.getName(); - if (nameToCheck.equals(room.getName())) { - logger.warn("duplicateRoom", "Not creating room " + nameToCheck + " as a room with that name already exists."); - return null; - } - } - - hmRoomByName.put(room.getName(), room); - - if (room.isCopy()) { - ServerGlobals.lobbyService.lobbyChanged(); - } - - return room; - } - - public ArrayList getRooms() { - ArrayList list = new ArrayList<>(); - Iterator it = hmRoomByName.keySet().iterator(); - - for (; it.hasNext(); ) { - String id = it.next(); - Room room = hmRoomByName.get(id); - list.add(room); - } - - return list; - } - - public Room getRoomForName(String name) { - return hmRoomByName.get(name); - } - /** * Generate a secure seed based on: * - Current time in nanos diff --git a/server/src/main/java/server/MessageHandlerRunnable.java b/server/src/main/java/server/MessageHandlerRunnable.java index efcbe31..f6c3e3f 100644 --- a/server/src/main/java/server/MessageHandlerRunnable.java +++ b/server/src/main/java/server/MessageHandlerRunnable.java @@ -39,8 +39,7 @@ public MessageHandlerRunnable(EntropyServer server, Socket clientSocket) this.server = server; this.clientSocket = clientSocket; } - - @SuppressWarnings("resource") + @Override public void run() { @@ -189,6 +188,7 @@ private Document getResponseForMessage() throws Throwable String id = root.getAttribute("RoomId"); String username = root.getAttribute("Username"); String name = root.getNodeName(); + Room room = ServerGlobals.INSTANCE.getRoomStore().findForName(id); usc.setLastActiveNow(); @@ -211,7 +211,6 @@ private Document getResponseForMessage() throws Throwable } else if (name.equals(ROOT_TAG_ROOM_JOIN_REQUEST)) { - Room room = server.getRoomForName(id); String observerStr = root.getAttribute("Observer"); int playerNumber = XmlUtil.getAttributeInt(root, "PlayerNumber"); @@ -219,26 +218,20 @@ else if (name.equals(ROOT_TAG_ROOM_JOIN_REQUEST)) } else if (name.equals(ROOT_TAG_CLOSE_ROOM_REQUEST)) { - Room room = server.getRoomForName(id); - return XmlBuilderServer.getCloseRoomResponse(room, username); } else if (name.equals(ROOT_TAG_OBSERVER_REQUEST)) { - Room room = server.getRoomForName(id); - return XmlBuilderServer.getObserverResponse(room); } else if (name.equals(ROOT_TAG_NEW_GAME_REQUEST)) { - Room room = server.getRoomForName(id); String currentGameId = root.getAttribute("CurrentGameId"); return XmlBuilderServer.getNewGameResponse(room, currentGameId); } else if (name.equals(ROOT_TAG_BID)) { - Room room = server.getRoomForName(id); String gameId = root.getAttribute("GameId"); int roundNumber = XmlUtil.getAttributeInt(root, "RoundNumber"); String bidStr = root.getAttribute("Bid"); @@ -248,7 +241,7 @@ else if (name.equals(ROOT_TAG_BID)) } else if (name.equals(ROOT_TAG_LEADERBOARD_REQUEST)) { - List rooms = server.getRooms(); + List rooms = ServerGlobals.INSTANCE.getRoomStore().getAll(); return XmlBuilderServer.getLeaderboardResponse(rooms); } else if (name.endsWith(SOCKET_NAME_SUFFIX)) diff --git a/server/src/main/java/util/XmlBuilderServer.java b/server/src/main/java/util/XmlBuilderServer.java index f32022f..8cd5766 100644 --- a/server/src/main/java/util/XmlBuilderServer.java +++ b/server/src/main/java/util/XmlBuilderServer.java @@ -13,26 +13,6 @@ public class XmlBuilderServer implements XmlConstants //Cached acknowledgements //AJH 28 Feb 2015 - Made these final - they don't change private static final Document ACKNOWLEDGEMENT = XmlUtil.factorySimpleMessage(RESPONSE_TAG_ACKNOWLEDGEMENT); - - private static final int ACHIEVMENTS_TOTAL = 80; - - public static Document getKickOffResponse(String username, String reason) - { - Document response = XmlUtil.factoryNewDocument(); - - Element rootElement = response.createElement(RESPONSE_TAG_KICK_OFF); - rootElement.setAttribute("RemovalReason", reason); - - if (username != null - && !username.isEmpty()) - { - rootElement.setAttribute("Username", username); - Debug.appendBanner("Kicking off " + username + ". Reason: " + reason); - } - - response.appendChild(rootElement); - return response; - } public static Document getAcknowledgement() { @@ -211,8 +191,7 @@ public static Document getRoomJoinResponse(Room room, String username, String ob if (room.isFull()) { - var copy = room.makeCopy(); - server.registerNewRoom(copy); + ServerGlobals.INSTANCE.getRoomStore().addCopy(room); } } else diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index 3f88c6d..6a96de3 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -13,6 +13,7 @@ import `object`.HandDetails import `object`.LeftBid import `object`.OnlineMessage import `object`.Player +import store.IHasId import util.CardsUtil import util.EntropyUtil import util.ServerGlobals @@ -22,11 +23,12 @@ import util.XmlConstants import utils.CoreGlobals.logger class Room( + override val id: UUID, private val baseName: String, val settings: GameSettings, val capacity: Int, private val index: Int = 1, -) { +) : IHasId { private val hmPlayerByPlayerNumber = ExtendedConcurrentHashMap() private val hmFormerPlayerByPlayerNumber: ConcurrentHashMap = ConcurrentHashMap() val chatHistory: MutableList = mutableListOf() @@ -476,7 +478,7 @@ class Room( ) } - fun makeCopy() = Room(baseName, settings, capacity, index + 1) + fun makeCopy() = Room(UUID.randomUUID(), baseName, settings, capacity, index + 1) override fun toString() = name } diff --git a/server/src/main/kotlin/room/RoomFactory.kt b/server/src/main/kotlin/room/RoomFactory.kt index 1fb3827..86f5dac 100644 --- a/server/src/main/kotlin/room/RoomFactory.kt +++ b/server/src/main/kotlin/room/RoomFactory.kt @@ -2,11 +2,12 @@ package room import game.GameMode import game.GameSettings +import java.util.* import util.ServerGlobals object RoomFactory { fun registerStarterRooms() { - makeStarterRooms().forEach(ServerGlobals.server::registerNewRoom) + makeStarterRooms().forEach(ServerGlobals.roomStore::put) } private fun makeStarterRooms(): List { @@ -17,7 +18,7 @@ object RoomFactory { val capacities = listOf(2, 3, 4) capacities.flatMap { capacity -> - configs.map { Room(elementGenerator.next(), it, capacity) } + configs.map { Room(UUID.randomUUID(), elementGenerator.next(), it, capacity) } } } } diff --git a/server/src/main/kotlin/routes/lobby/LobbyService.kt b/server/src/main/kotlin/routes/lobby/LobbyService.kt index 6b87021..01e357b 100644 --- a/server/src/main/kotlin/routes/lobby/LobbyService.kt +++ b/server/src/main/kotlin/routes/lobby/LobbyService.kt @@ -8,6 +8,7 @@ import http.dto.RoomSummary import java.util.* import room.Room import server.EntropyServer +import store.RoomStore import store.Store import store.UserConnectionStore import util.XmlConstants @@ -17,9 +18,10 @@ class LobbyService( private val server: EntropyServer, private val sessionStore: Store, private val uscStore: UserConnectionStore, + private val roomStore: RoomStore, ) { fun getLobby(): LobbyMessage { - val rooms = server.rooms.map { it.toSummary() } + val rooms = roomStore.getAll().map { it.toSummary() } val users = sessionStore.getAll().map { OnlineUser(it.name, it.achievementCount) } return LobbyMessage(rooms, users) } diff --git a/server/src/main/kotlin/routes/session/SessionService.kt b/server/src/main/kotlin/routes/session/SessionService.kt index 0fe9d0c..4d52be7 100644 --- a/server/src/main/kotlin/routes/session/SessionService.kt +++ b/server/src/main/kotlin/routes/session/SessionService.kt @@ -78,7 +78,7 @@ class SessionService( val usc = uscStore.get(session.name) usc.destroyNotificationSockets() - val rooms: List = ServerGlobals.server.getRooms() + val rooms: List = ServerGlobals.roomStore.getAll() rooms.forEach { room -> room.removeFromObservers(usc.name) room.removePlayer(usc.name, false) diff --git a/server/src/main/kotlin/store/RoomStore.kt b/server/src/main/kotlin/store/RoomStore.kt new file mode 100644 index 0000000..bb8d78b --- /dev/null +++ b/server/src/main/kotlin/store/RoomStore.kt @@ -0,0 +1,31 @@ +package store + +import java.util.* +import room.Room +import util.ServerGlobals +import utils.CoreGlobals.logger + +class RoomStore : MemoryStore() { + override val name = "rooms" + + fun findForName(name: String) = getAll().find { it.name == name } + + fun reset() { + val copies = getAll().filter { it.isCopy } + copies.forEach { remove(it.id) } + + logger.info("roomsRemoved", "Removed ${copies.size} room copies") + } + + fun addCopy(room: Room) { + val copy = room.makeCopy() + + if (findForName(copy.name) == null) { + logger.info("newRoom", "New room generated: ${copy.name}") + put(copy) + ServerGlobals.lobbyService.lobbyChanged() + } else { + logger.info("roomExists", "Room ${copy.name} already exists") + } + } +} diff --git a/server/src/main/kotlin/util/ServerGlobals.kt b/server/src/main/kotlin/util/ServerGlobals.kt index 2925181..ff95940 100644 --- a/server/src/main/kotlin/util/ServerGlobals.kt +++ b/server/src/main/kotlin/util/ServerGlobals.kt @@ -8,6 +8,7 @@ import routes.lobby.LobbyService import routes.session.SessionService import server.EntropyServer import store.MemoryUserConnectionStore +import store.RoomStore import store.SessionStore import store.Store import store.UserConnectionStore @@ -20,6 +21,7 @@ private const val KEEP_ALIVE_TIME = 20 object ServerGlobals { var uscStore: UserConnectionStore = MemoryUserConnectionStore() var sessionStore: Store = SessionStore() + var roomStore: RoomStore = RoomStore() @JvmField val workerPool = @@ -33,6 +35,7 @@ object ServerGlobals { val server: EntropyServer = EntropyServer() - @JvmField var lobbyService: LobbyService = LobbyService(server, sessionStore, uscStore) + @JvmField + var lobbyService: LobbyService = LobbyService(server, sessionStore, uscStore, roomStore) @JvmField var sessionService = SessionService(sessionStore, uscStore) } diff --git a/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt b/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt index d5d2d0d..41c42e0 100644 --- a/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt +++ b/server/src/test/kotlin/routes/lobby/LobbyServiceTest.kt @@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test import room.Room import server.EntropyServer import store.MemoryUserConnectionStore +import store.RoomStore import store.SessionStore import store.Store import store.UserConnectionStore @@ -25,18 +26,18 @@ import util.makeSession class LobbyServiceTest : AbstractTest() { @Test fun `getLobby should return rooms and online users`() { - val (service, server, sessionStore) = makeService() + val (service, _, sessionStore, _, roomStore) = makeService() val sessionA = makeSession(name = "Alyssa", achievementCount = 4) val sessionB = makeSession(name = "Bob", achievementCount = 7) sessionStore.putAll(sessionA, sessionB) val settings = makeGameSettings(includeMoons = true) - val roomOne = Room("Carbon", settings, 4) + val roomOne = Room(UUID.randomUUID(), "Carbon", settings, 4) val roomTwo = roomOne.makeCopy() - server.registerNewRoom(roomOne) - server.registerNewRoom(roomTwo) + roomStore.put(roomOne) + roomStore.put(roomTwo) roomOne.addToCurrentPlayers("Alyssa", 0) roomTwo.addToObservers("Bob") @@ -103,11 +104,19 @@ class LobbyServiceTest : AbstractTest() { val server: EntropyServer, val sessionStore: Store, val uscStore: UserConnectionStore, + val roomStore: RoomStore, ) private fun makeService(server: EntropyServer = EntropyServer()): SetupParams { val store = SessionStore() val uscStore = MemoryUserConnectionStore() - return SetupParams(LobbyService(server, store, uscStore), server, store, uscStore) + val roomStore = RoomStore() + return SetupParams( + LobbyService(server, store, uscStore, roomStore), + server, + store, + uscStore, + roomStore + ) } } diff --git a/server/src/test/kotlin/util/ApplicationTest.kt b/server/src/test/kotlin/util/ApplicationTest.kt index dc812ed..abd5568 100644 --- a/server/src/test/kotlin/util/ApplicationTest.kt +++ b/server/src/test/kotlin/util/ApplicationTest.kt @@ -4,8 +4,10 @@ import org.junit.jupiter.api.BeforeEach import routes.lobby.LobbyService import routes.session.SessionService import store.MemoryUserConnectionStore +import store.RoomStore import store.SessionStore import testCore.AbstractTest +import util.ServerGlobals.roomStore import util.ServerGlobals.sessionStore import util.ServerGlobals.uscStore @@ -15,7 +17,9 @@ abstract class ApplicationTest : AbstractTest() { fun beforeEach() { sessionStore = SessionStore() uscStore = MemoryUserConnectionStore() - ServerGlobals.lobbyService = LobbyService(ServerGlobals.server, sessionStore, uscStore) + roomStore = RoomStore() + ServerGlobals.lobbyService = + LobbyService(ServerGlobals.server, sessionStore, uscStore, roomStore) ServerGlobals.sessionService = SessionService(sessionStore, uscStore) } } From 9a3926f8f047a8ff8a884b52e10ae6c3497bc56d Mon Sep 17 00:00:00 2001 From: Alyssa Date: Sun, 10 Nov 2024 15:28:30 +0000 Subject: [PATCH 6/9] fix a couple of broken things --- client/src/main/java/online/screen/GameRoom.java | 1 + client/src/main/java/screen/ReplayDialog.java | 2 +- client/src/main/kotlin/http/SessionApi.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index 539d5a0..45e3786 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -1462,6 +1462,7 @@ public void illegalCalled() MessageUtil.sendMessage(illegal, 0); } + public String getRoomName() { return roomName; } public int getJokerValue() { return settings.getJokerValue(); diff --git a/client/src/main/java/screen/ReplayDialog.java b/client/src/main/java/screen/ReplayDialog.java index 686952e..1aa405a 100644 --- a/client/src/main/java/screen/ReplayDialog.java +++ b/client/src/main/java/screen/ReplayDialog.java @@ -338,7 +338,7 @@ public void initForRoomReplay(GameRoom room) } String username = room.getUsername(); - String roomName = room.getName(); + String roomName = room.getRoomName(); Debug.append("Opened online replay for room " + roomName, true); replay = Preferences.userRoot().node(NODE_ONLINE_REPLAY + roomName + username); diff --git a/client/src/main/kotlin/http/SessionApi.kt b/client/src/main/kotlin/http/SessionApi.kt index 22b7051..882af4c 100644 --- a/client/src/main/kotlin/http/SessionApi.kt +++ b/client/src/main/kotlin/http/SessionApi.kt @@ -34,7 +34,7 @@ class SessionApi(private val httpClient: HttpClient) { } fun updateAchievementCount(achievementCount: Int) { - httpClient.doCall( + httpClient.doCall( HttpMethod.POST, Routes.ACHIEVEMENT_COUNT, UpdateAchievementCountRequest(achievementCount), From 08b8d56d07bb2b7e9c5a31baa8bb97e0efa524a1 Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 12 Nov 2024 08:44:29 +0000 Subject: [PATCH 7/9] add tests and fix broken placeholder hands --- .../src/main/java/online/screen/GameRoom.java | 4 +- client/src/main/java/screen/HandPanelMk2.java | 34 ++++++--- client/src/test/kotlin/http/SessionApiTest.kt | 2 +- server/src/main/kotlin/room/Room.kt | 38 +++++----- server/src/test/kotlin/room/RoomTest.kt | 36 +++++++++ .../test/kotlin/store/AbstractStoreTest.kt | 3 +- server/src/test/kotlin/store/RoomStoreTest.kt | 76 +++++++++++++++++++ server/src/test/kotlin/util/TestFactory.kt | 9 +++ 8 files changed, 167 insertions(+), 35 deletions(-) create mode 100644 server/src/test/kotlin/room/RoomTest.kt create mode 100644 server/src/test/kotlin/store/RoomStoreTest.kt diff --git a/client/src/main/java/online/screen/GameRoom.java b/client/src/main/java/online/screen/GameRoom.java index 45e3786..f137a8f 100644 --- a/client/src/main/java/online/screen/GameRoom.java +++ b/client/src/main/java/online/screen/GameRoom.java @@ -495,7 +495,7 @@ public void addOrUpdatePlayer(int playerNumber, String username) newPlayer.setEnabled(true); hmPlayerByAdjustedPlayerNumber.put(adjustedNo, newPlayer); - handPanel.initialisePlayer(adjustedNo, username, colour); + handPanel.initialisePlayer(adjustedNo, username, colour, 5); handPanel.activateEmptySeats(); } else @@ -893,7 +893,7 @@ private void processEndOfGame(int winningPlayerAdjusted) return; } - handPanel.resetPlayers(); + handPanel.resetPlayers(5); clearScreenAfterGameEnd(); int currentSize = hmPlayerByAdjustedPlayerNumber.size(); diff --git a/client/src/main/java/screen/HandPanelMk2.java b/client/src/main/java/screen/HandPanelMk2.java index ba4a36b..4bbc708 100644 --- a/client/src/main/java/screen/HandPanelMk2.java +++ b/client/src/main/java/screen/HandPanelMk2.java @@ -309,6 +309,16 @@ private void initialiseCardLabelsForPlayer(List hand, CardLabel[] labels refreshIcons(labels); } + + private void makePlaceholderHand(CardLabel[] labels, int handSize) { + for (int i=0; i(), playerCards, false); + makePlaceholderHand(playerCards, startingNumberOfCards); break; case 1: opponentOneName = name; - initialiseCardLabelsForPlayer(new ArrayList<>(), opponentOneCards, false); + makePlaceholderHand(opponentOneCards, startingNumberOfCards); break; case 2: opponentTwoName = name; - initialiseCardLabelsForPlayer(new ArrayList<>(), opponentTwoCards, false); + makePlaceholderHand(opponentTwoCards, startingNumberOfCards); break; case 3: opponentThreeName = name; - initialiseCardLabelsForPlayer(new ArrayList<>(), opponentThreeCards, false); + makePlaceholderHand(opponentThreeCards, startingNumberOfCards); break; default: Debug.stackTrace("Unexpected playerNumber [" + playerNumber + "]"); @@ -677,23 +687,23 @@ private void deselectOtherPlayers(int playerNumber) } } - public void resetPlayers() + public void resetPlayers(int startingNumberOfCards) { resetPlayerCardPositions(); - resetPlayer(0, playerName); - resetPlayer(1, opponentOneName); - resetPlayer(2, opponentTwoName); - resetPlayer(3, opponentThreeName); + resetPlayer(0, playerName, startingNumberOfCards); + resetPlayer(1, opponentOneName, startingNumberOfCards); + resetPlayer(2, opponentTwoName, startingNumberOfCards); + resetPlayer(3, opponentThreeName, startingNumberOfCards); } - private void resetPlayer(int playerNumber, String name) + private void resetPlayer(int playerNumber, String name, int startingNumberOfCards) { PlayerLabel label = getPlayerLabelForPlayerNumber(playerNumber); label.setText(name); if (label.isVisible()) { - initialisePlayer(playerNumber, name, label.getColour()); + initialisePlayer(playerNumber, name, label.getColour(), startingNumberOfCards); } } diff --git a/client/src/test/kotlin/http/SessionApiTest.kt b/client/src/test/kotlin/http/SessionApiTest.kt index f0d25e9..d1c91c2 100644 --- a/client/src/test/kotlin/http/SessionApiTest.kt +++ b/client/src/test/kotlin/http/SessionApiTest.kt @@ -134,7 +134,7 @@ class SessionApiTest : AbstractClientTest() { SessionApi(httpClient).updateAchievementCount(8) verify { - httpClient.doCall( + httpClient.doCall( HttpMethod.POST, Routes.ACHIEVEMENT_COUNT, UpdateAchievementCountRequest(8), diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index 6a96de3..be70533 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -22,7 +22,7 @@ import util.XmlBuilderServer import util.XmlConstants import utils.CoreGlobals.logger -class Room( +data class Room( override val id: UUID, private val baseName: String, val settings: GameSettings, @@ -64,7 +64,7 @@ class Room( if (existingUsername != null) { logger.info( "seatTaken", - "$username tried to join $name as player $playerNumber but seat was taken by $existingUsername" + "$username tried to join $name as player $playerNumber but seat was taken by $existingUsername", ) return -1 } @@ -85,7 +85,7 @@ class Room( } fun removePlayer(username: String, fireLobbyChanged: Boolean) { - for (playerNumber in 0 ..< capacity) { + for (playerNumber in 0..() - for (i in 0 ..< capacity) { + for (i in 0.. 0) { activePlayers++ @@ -414,7 +414,7 @@ class Room( if (previousGame == null) { logger.warn( "staleGameId", - "Tried to get next game for gameId $previousGameIdFromClient but previous game was null. Current: ${currentGame.gameId}" + "Tried to get next game for gameId $previousGameIdFromClient but previous game was null. Current: ${currentGame.gameId}", ) return currentGame } @@ -426,7 +426,7 @@ class Room( logger.warn( "staleGameId", - "Tried to get next game for gameId $previousGameIdFromClient but this did not match. Previous [${previousGame?.gameId}], Current [${currentGame.gameId}]" + "Tried to get next game for gameId $previousGameIdFromClient but this did not match. Previous [${previousGame?.gameId}], Current [${currentGame.gameId}]", ) return currentGame @@ -445,7 +445,7 @@ class Room( gameId: String, playerNumber: Int, roundNumber: Int, - newBid: Bid? + newBid: Bid?, ): Boolean { val game = getGameForId(gameId) @@ -474,7 +474,7 @@ class Room( uscs, chatMessage, XmlConstants.SOCKET_NAME_CHAT, - false + false, ) } diff --git a/server/src/test/kotlin/room/RoomTest.kt b/server/src/test/kotlin/room/RoomTest.kt new file mode 100644 index 0000000..2bb3937 --- /dev/null +++ b/server/src/test/kotlin/room/RoomTest.kt @@ -0,0 +1,36 @@ +package room + +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import testCore.AbstractTest +import util.makeGameSettings +import util.makeRoom + +class RoomTest : AbstractTest() { + @Test + fun `should report correct name`() { + makeRoom(baseName = "Mercury", index = 1).name shouldBe "Mercury 1" + makeRoom(baseName = "Carbon", index = 5).name shouldBe "Carbon 5" + } + + @Test + fun `should report if room is a copy`() { + makeRoom(baseName = "Mercury", index = 1).isCopy shouldBe false + makeRoom(baseName = "Carbon", index = 2).isCopy shouldBe true + makeRoom(baseName = "Oxygen", index = 5).isCopy shouldBe true + } + + @Test + fun `should make a copy of a room`() { + val settings = makeGameSettings(includeMoons = true, jokerQuantity = 2) + val room = makeRoom(baseName = "Yttrium", index = 1, settings = settings, capacity = 3) + + val copy = room.makeCopy() + copy.id shouldNotBe room.id + copy.name shouldBe "Yttrium 2" + copy.settings shouldBe settings + copy.capacity shouldBe room.capacity + } +} diff --git a/server/src/test/kotlin/store/AbstractStoreTest.kt b/server/src/test/kotlin/store/AbstractStoreTest.kt index 572e0de..8597e79 100644 --- a/server/src/test/kotlin/store/AbstractStoreTest.kt +++ b/server/src/test/kotlin/store/AbstractStoreTest.kt @@ -5,8 +5,9 @@ import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import testCore.AbstractTest -abstract class AbstractStoreTest> { +abstract class AbstractStoreTest> : AbstractTest() { abstract fun makeStore(): Store abstract fun makeIdA(): K diff --git a/server/src/test/kotlin/store/RoomStoreTest.kt b/server/src/test/kotlin/store/RoomStoreTest.kt new file mode 100644 index 0000000..893f361 --- /dev/null +++ b/server/src/test/kotlin/store/RoomStoreTest.kt @@ -0,0 +1,76 @@ +package store + +import game.GameMode +import game.GameSettings +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import java.util.UUID +import org.junit.jupiter.api.Test +import room.Room + +class RoomStoreTest : AbstractStoreTest() { + override fun makeStore() = RoomStore() + + override fun makeIdA(): UUID = UUID.fromString("e115f1ce-0021-4653-9888-ea330b07f3a8") + + override fun makeIdB(): UUID = UUID.fromString("7a10f6a0-03bc-4e95-a4cb-44b92582f016") + + override fun makeItemA(id: UUID) = Room(id, "Mercury", GameSettings(GameMode.Entropy), 2) + + override fun makeItemB(id: UUID) = Room(id, "Oxygen", GameSettings(GameMode.Vectropy), 3) + + @Test + fun `Should be able to find a room for name`() { + val store = makeStore() + val roomA = makeItemA(UUID.randomUUID()) + val roomB = makeItemB(UUID.randomUUID()) + store.putAll(roomA, roomB) + + store.findForName("Mercury 1") shouldBe roomA + store.findForName("Oxygen 1") shouldBe roomB + store.findForName("Mercury 2") shouldBe null + } + + @Test + fun `Reset should remove all room copies`() { + val store = makeStore() + + val room = makeItemA(UUID.randomUUID()) + val roomCopy = room.makeCopy() + val roomCopy2 = roomCopy.makeCopy() + + store.putAll(room, roomCopy, roomCopy2) + store.reset() + + store.getAll().shouldContainExactly(room) + } + + @Test + fun `Should add a copy`() { + val store = makeStore() + + val room = makeItemA(UUID.randomUUID()) + store.put(room) + store.addCopy(room) + + val rooms = store.getAll() + rooms.shouldHaveSize(2) + rooms.map { it.name }.shouldContainExactlyInAnyOrder("Mercury 1", "Mercury 2") + } + + @Test + fun `Should not add a copy if it would be redundant`() { + val store = makeStore() + + val room = makeItemA(UUID.randomUUID()) + store.put(room) + store.addCopy(room) + store.getAll().shouldHaveSize(2) + + store.addCopy(room) + store.getAll().shouldHaveSize(2) + verifyLog("roomExists") + } +} diff --git a/server/src/test/kotlin/util/TestFactory.kt b/server/src/test/kotlin/util/TestFactory.kt index 8a357e8..a5248a8 100644 --- a/server/src/test/kotlin/util/TestFactory.kt +++ b/server/src/test/kotlin/util/TestFactory.kt @@ -5,6 +5,7 @@ import auth.UserConnection import game.GameMode import game.GameSettings import java.util.* +import room.Room fun makeSession( id: UUID = UUID.randomUUID(), @@ -36,3 +37,11 @@ fun makeGameSettings( cardReveal, illegalAllowed, ) + +fun makeRoom( + id: UUID = UUID.randomUUID(), + baseName: String = "Sodium", + settings: GameSettings = GameSettings(GameMode.Entropy), + capacity: Int = 2, + index: Int = 1, +) = Room(id, baseName, settings, capacity, index) From 90138763e8a5d122301ab0a57b19f2707c227f16 Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 12 Nov 2024 08:47:21 +0000 Subject: [PATCH 8/9] setVisible --- client/src/main/java/screen/HandPanelMk2.java | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/main/java/screen/HandPanelMk2.java b/client/src/main/java/screen/HandPanelMk2.java index 4bbc708..15f0044 100644 --- a/client/src/main/java/screen/HandPanelMk2.java +++ b/client/src/main/java/screen/HandPanelMk2.java @@ -315,6 +315,7 @@ private void makePlaceholderHand(CardLabel[] labels, int handSize) { labels[i].setFaceUp(false); labels[i].setFaded(false); labels[i].setCard(null); + labels[i].setVisible(true); } refreshIcons(labels); From 51edabff0add93b7b2407cc1427d9774b49afe4d Mon Sep 17 00:00:00 2001 From: alyssa Date: Tue, 12 Nov 2024 08:50:35 +0000 Subject: [PATCH 9/9] ktfmt --- server/src/main/kotlin/room/Room.kt | 12 ++++++------ server/src/test/kotlin/room/RoomTest.kt | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/server/src/main/kotlin/room/Room.kt b/server/src/main/kotlin/room/Room.kt index be70533..e2a01ca 100644 --- a/server/src/main/kotlin/room/Room.kt +++ b/server/src/main/kotlin/room/Room.kt @@ -85,7 +85,7 @@ data class Room( } fun removePlayer(username: String, fireLobbyChanged: Boolean) { - for (playerNumber in 0..() - for (i in 0.. 0) { activePlayers++ diff --git a/server/src/test/kotlin/room/RoomTest.kt b/server/src/test/kotlin/room/RoomTest.kt index 2bb3937..266c536 100644 --- a/server/src/test/kotlin/room/RoomTest.kt +++ b/server/src/test/kotlin/room/RoomTest.kt @@ -2,7 +2,6 @@ package room import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import testCore.AbstractTest import util.makeGameSettings