From 53559adc735438e9fcecb3c8f29ca43a904fa2f5 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:50:22 +0200 Subject: [PATCH 1/2] fix: stop free poker calls and mid-hand buy-ins A call below the current bet capped the seat as all in, whatever was still in the player's pockets. Saying "call" with nothing on the felt kept the player in the hand for free. A short call now goes through only when the pockets hold nothing stakeable; otherwise the player is told how much more to put down. Anyone could put chips on a live Hold'em or Five-Draw table and be seated in the hand already dealt. Games now decide through allowStake who may stake. Poker and draw refuse anyone outside the live hand, and a seat that has folded, so it cannot feed a pot it has given up. Chips past the current bet followed by "check" or "call" never became the bet, so nobody had to answer them. They now count as a raise. Applies to Five-Draw as well, which shares the betting code. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../net/tfminecraft/games/game/DrawGame.java | 21 +++++++ .../java/net/tfminecraft/games/game/Game.java | 9 +++ .../net/tfminecraft/games/game/PokerGame.java | 21 +++++++ .../tfminecraft/games/table/TableManager.java | 16 +++++ .../tfminecraft/games/wager/WagerEngine.java | 8 +++ src/main/resources/messages.yml | 3 + .../tfminecraft/games/game/DrawGameTest.java | 57 ++++++++++++++++++ .../tfminecraft/games/game/PokerGameTest.java | 60 +++++++++++++++++++ .../table/TableManagerChipPlaceTest.java | 38 ++++++++++++ .../games/table/TableManagerFixture.java | 1 + .../games/table/TableManagerLootTest.java | 13 ++++ .../games/wager/WagerEngineTest.java | 9 +++ 12 files changed, 256 insertions(+) diff --git a/src/main/java/net/tfminecraft/games/game/DrawGame.java b/src/main/java/net/tfminecraft/games/game/DrawGame.java index 427e3aa..66b34ee 100644 --- a/src/main/java/net/tfminecraft/games/game/DrawGame.java +++ b/src/main/java/net/tfminecraft/games/game/DrawGame.java @@ -137,6 +137,10 @@ public void onPlayWord(Table table, Player player, String word) { return; } int contrib = streetContrib(table, id); + if (contrib > street.currentBet && ("check".equals(word) || "call".equals(word))) { + // Chips past the bet are a bet whatever it is called, so the others have to answer it. + word = "raise"; + } switch (word) { case "check" -> { if (contrib < street.currentBet) { @@ -147,6 +151,12 @@ public void onPlayWord(Table table, Player player, String word) { } case "call" -> { if (contrib < street.currentBet) { + // Calling short is only an all in: chips still in pockets have to go down first. + if (!WagerEngine.get().allIn(table, player)) { + player.sendMessage(Messages.get("draw.need_call", + "n", String.valueOf(street.currentBet - contrib))); + return; + } street.capped.add(id); } else { street.capped.remove(id); @@ -195,6 +205,17 @@ public void onReturnedSelected(Table table, Player player, int count) { }); } + /** A live hand takes money only from seats still in it; anyone else waits for the next one. */ + @Override + public boolean allowStake(Table table, Player player) { + if (!table.live()) { + return true; + } + UUID id = player.getUniqueId(); + Street street = streets.get(table.getId()); + return table.actives().contains(id) && (street == null || !street.folded.contains(id)); + } + @Override public void onFeltPilesChanged(Table table) { TableManager.get().refreshLabel(table); diff --git a/src/main/java/net/tfminecraft/games/game/Game.java b/src/main/java/net/tfminecraft/games/game/Game.java index 83c1925..6def226 100644 --- a/src/main/java/net/tfminecraft/games/game/Game.java +++ b/src/main/java/net/tfminecraft/games/game/Game.java @@ -99,6 +99,15 @@ default void onDealerGone(Table table) { onTableReady(table); } + /** + * Whether this player may put money on the felt right now. Games that deal a hand to a fixed + * set of seats say no to anyone not in the live hand, so a late stake cannot buy into a hand + * already dealt and a folded seat cannot feed a pot it has given up. + */ + default boolean allowStake(Table table, Player player) { + return true; + } + /** Every game reacts to chips arriving or a seat leaving, if only to refresh its label. */ void onChipIn(Table table, Player player); diff --git a/src/main/java/net/tfminecraft/games/game/PokerGame.java b/src/main/java/net/tfminecraft/games/game/PokerGame.java index 1f60ac6..e5be72d 100644 --- a/src/main/java/net/tfminecraft/games/game/PokerGame.java +++ b/src/main/java/net/tfminecraft/games/game/PokerGame.java @@ -123,6 +123,10 @@ public void onPlayWord(Table table, Player player, String word) { Street street = streets.get(table.getId()); UUID id = player.getUniqueId(); int contrib = streetContrib(table, id); + if (contrib > street.currentBet && ("check".equals(word) || "call".equals(word))) { + // Chips past the bet are a bet whatever it is called, so the others have to answer it. + word = "raise"; + } switch (word) { case "check" -> { if (contrib < street.currentBet) { @@ -133,6 +137,12 @@ public void onPlayWord(Table table, Player player, String word) { } case "call" -> { if (contrib < street.currentBet) { + // Calling short is only an all in: chips still in pockets have to go down first. + if (!WagerEngine.get().allIn(table, player)) { + player.sendMessage(Messages.get("poker.need_call", + "n", String.valueOf(street.currentBet - contrib))); + return; + } street.capped.add(id); } else { street.capped.remove(id); @@ -162,6 +172,17 @@ public void onPlayWord(Table table, Player player, String word) { finishOrAdvance(table, new ArrayList<>(table.actives()), true); } + /** A live hand takes money only from seats still in it; anyone else waits for the next one. */ + @Override + public boolean allowStake(Table table, Player player) { + if (!table.live()) { + return true; + } + UUID id = player.getUniqueId(); + Street street = streets.get(table.getId()); + return table.actives().contains(id) && (street == null || !street.folded.contains(id)); + } + @Override public void onFeltPilesChanged(Table table) { TableManager.get().refreshLabel(table); diff --git a/src/main/java/net/tfminecraft/games/table/TableManager.java b/src/main/java/net/tfminecraft/games/table/TableManager.java index 51f0e7e..7b5650d 100644 --- a/src/main/java/net/tfminecraft/games/table/TableManager.java +++ b/src/main/java/net/tfminecraft/games/table/TableManager.java @@ -2519,6 +2519,9 @@ private boolean dumpLoot(Player player, Table table, ItemStack snapshot, int den if (dealerTray && refuseTrayStock(table, player)) { return false; } + if (!dealerTray && refuseStake(table, player)) { + return false; + } int placeDenars = denars * need; ItemStack one = snapshot.clone(); one.setAmount(1); @@ -2626,6 +2629,9 @@ private boolean tryPlaceChip(Player player, Location click) { player.sendMessage(Messages.get("bet.closed")); return true; } + if (!dealerTray && refuseStake(table, player)) { + return true; + } if (table.isPaying()) { return true; } @@ -2679,6 +2685,16 @@ private boolean tryPlaceChip(Player player, Location click) { return true; } + /** True when the game will not take this player's money now, such as mid-hand from a stranger. */ + private static boolean refuseStake(Table table, Player player) { + Game game = gameOf(table); + if (game == null || game.allowStake(table, player)) { + return false; + } + player.sendMessage(Messages.get("wager.not_in_hand")); + return true; + } + /** * True when this dealer must not stock the tray by hand. On a backed table the house money is * the bank's, so personal coins going in would be money the guild then banks as its own. diff --git a/src/main/java/net/tfminecraft/games/wager/WagerEngine.java b/src/main/java/net/tfminecraft/games/wager/WagerEngine.java index 78e832b..8ac29ae 100644 --- a/src/main/java/net/tfminecraft/games/wager/WagerEngine.java +++ b/src/main/java/net/tfminecraft/games/wager/WagerEngine.java @@ -64,6 +64,14 @@ public int owned(Table table, UUID owner, int street) { return table.ledger().total(owner, street); } + /** + * True when this player's pockets hold nothing a table would take as a bet, so a call short + * of the current bet is an all in rather than a free ride. + */ + public boolean allIn(Table table, Player player) { + return Accounts.pockets(table, player).available() < 1; + } + public int tray(Table table) { return table.ledger().total(table.getId()); } diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index 8a865ce..cab1fef 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -105,6 +105,7 @@ hand: poker: need_players: "#ff5555Need two seated players to deal." cannot_check: "#ff5555You cannot check. Call, raise, or fold." + need_call: "#ff5555Put {n} more on this street to call, or fold." need_chips: "#ff5555Put chips on this street first." checked: "#aaaaaaChecked." called: "#aaaaaaCalled." @@ -122,6 +123,7 @@ poker: draw: need_players: "#ff5555Need two seated players to deal." cannot_check: "#ff5555You cannot check. Call, raise, or fold." + need_call: "#ff5555Put {n} more on this street to call, or fold." need_chips: "#ff5555Put chips on this street first." checked: "#aaaaaaChecked." called: "#aaaaaaCalled." @@ -148,6 +150,7 @@ wager: need_item: "#ff5555Hold the item you want to wager." use_click: "#ff5555Place coins and valued chips by right-clicking the table." coins_only: "#ff5555This table only takes coins." + not_in_hand: "#ff5555You are not in this hand. Wait for the next one." need_amount: "#ff5555Enter a whole denar amount of 1 or more." no_vote: "#ff5555There is no wager to vote on." not_eligible: "#ff5555You cannot vote on this wager." diff --git a/src/test/java/net/tfminecraft/games/game/DrawGameTest.java b/src/test/java/net/tfminecraft/games/game/DrawGameTest.java index 0ba0682..4659ee7 100644 --- a/src/test/java/net/tfminecraft/games/game/DrawGameTest.java +++ b/src/test/java/net/tfminecraft/games/game/DrawGameTest.java @@ -8,6 +8,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.junit.jupiter.api.BeforeEach; +import org.mockbukkit.mockbukkit.MockBukkit; import org.mockbukkit.mockbukkit.entity.PlayerMock; import org.junit.jupiter.api.Test; @@ -263,6 +264,7 @@ void bettingRejectsEmptyRaiseAndUnmatchedCheckButAllowsShortAllInCall() { act("check"); assertEquals(carol.getUniqueId(), table.actor()); contribute(carol, 3); + broke.add(carol.getUniqueId()); act("call"); contribute(alice, 20); act("raise"); @@ -692,12 +694,67 @@ void removingTheTableDuringTheInitialDealStopsDealing() { assertNull(table.actor()); } + @Test + void shortCallIsRefusedWhileChipsRemainInPocketAndCapsOnlyOnceAllIn() { + start(alice, bob, carol); + contribute(bob, 10); + act("raise"); + assertEquals(carol.getUniqueId(), table.actor()); + act("call"); + assertEquals(carol.getUniqueId(), table.actor(), "a free call must not pass the turn"); + assertTrue(((PlayerMock) carol).nextMessage().startsWith("draw.need_call")); + assertTrue(game.extraLabel(table).contains("n, 10")); + contribute(carol, 3); + act("call"); + assertEquals(carol.getUniqueId(), table.actor()); + assertTrue(((PlayerMock) carol).nextMessage().contains("n, 7")); + broke.add(carol.getUniqueId()); + act("call"); + assertEquals(alice.getUniqueId(), table.actor()); + assertEquals("draw.called", ((PlayerMock) carol).nextMessage()); + } + + @Test + void onlySeatsStillInALiveHandMayStake() { + Player stranger = MockBukkit.getMock().addPlayer(); + assertTrue(game.allowStake(table, stranger), "an idle table takes anyone's chips"); + seat(alice, bob, carol); + table.startSession(); + game.onSessionStart(table); + assertTrue(game.allowStake(table, alice), "seats may stake while holes are dealt"); + assertFalse(game.allowStake(table, stranger), "a stranger cannot buy into a dealt hand"); + drain(); + assertEquals(bob.getUniqueId(), table.actor()); + assertTrue(game.allowStake(table, bob)); + act("fold"); + assertTrue(table.live()); + assertFalse(game.allowStake(table, bob), "a folded seat cannot feed the pot"); + assertTrue(game.allowStake(table, carol)); + assertFalse(game.allowStake(table, stranger)); + } + + @Test + void chipsPastTheBetAreARaiseEvenWhenCalledACheckOrCall() { + start(alice, bob, carol); + contribute(bob, 10); + act("check"); + assertEquals("draw.raised[draw.raised, n, 10]", ((PlayerMock) bob).nextMessage()); + assertEquals(carol.getUniqueId(), table.actor()); + contribute(carol, 25); + act("call"); + assertEquals("draw.raised[draw.raised, n, 25]", ((PlayerMock) carol).nextMessage()); + contribute(alice, 25); + act("call"); + assertEquals(bob.getUniqueId(), table.actor(), "the first bettor has to answer the raise"); + } + @Test void allInSeatIsSkippedWhileTheOthersKeepRaising() { start(alice, bob, carol); contribute(bob, 10); act("raise"); contribute(carol, 3); + broke.add(carol.getUniqueId()); act("call"); contribute(alice, 20); act("raise"); diff --git a/src/test/java/net/tfminecraft/games/game/PokerGameTest.java b/src/test/java/net/tfminecraft/games/game/PokerGameTest.java index c70c773..746b501 100644 --- a/src/test/java/net/tfminecraft/games/game/PokerGameTest.java +++ b/src/test/java/net/tfminecraft/games/game/PokerGameTest.java @@ -104,6 +104,7 @@ void raiseRequiresChipsAndReopensActionWhileShortCallCapsPlayer() { act("check"); assertEquals(carol.getUniqueId(), table.actor()); contribute(carol, 5); + broke.add(carol.getUniqueId()); act("call"); assertEquals(alice.getUniqueId(), table.actor()); contribute(alice, 30); @@ -478,12 +479,67 @@ void endingTheSessionDuringTheHoleDealStopsDealing() { assertFalse(table.live()); } + @Test + void shortCallIsRefusedWhileChipsRemainInPocketAndCapsOnlyOnceAllIn() { + start(alice, bob, carol); + contribute(bob, 10); + act("raise"); + assertEquals(carol.getUniqueId(), table.actor()); + act("call"); + assertEquals(carol.getUniqueId(), table.actor(), "a free call must not pass the turn"); + assertTrue(((PlayerMock) carol).nextMessage().startsWith("poker.need_call")); + assertTrue(game.extraLabel(table).contains("n, 10")); + contribute(carol, 3); + act("call"); + assertEquals(carol.getUniqueId(), table.actor()); + assertTrue(((PlayerMock) carol).nextMessage().contains("n, 7")); + broke.add(carol.getUniqueId()); + act("call"); + assertEquals(alice.getUniqueId(), table.actor()); + assertEquals("poker.called", ((PlayerMock) carol).nextMessage()); + } + + @Test + void onlySeatsStillInALiveHandMayStake() { + Player stranger = MockBukkit.getMock().addPlayer(); + assertTrue(game.allowStake(table, stranger), "an idle table takes anyone's chips"); + seat(alice, bob, carol); + table.startSession(); + game.onSessionStart(table); + assertTrue(game.allowStake(table, alice), "seats may stake while holes are dealt"); + assertFalse(game.allowStake(table, stranger), "a stranger cannot buy into a dealt hand"); + drain(); + assertEquals(bob.getUniqueId(), table.actor()); + assertTrue(game.allowStake(table, bob)); + act("fold"); + assertTrue(table.live()); + assertFalse(game.allowStake(table, bob), "a folded seat cannot feed the pot"); + assertTrue(game.allowStake(table, carol)); + assertFalse(game.allowStake(table, stranger)); + } + + @Test + void chipsPastTheBetAreARaiseEvenWhenCalledACheckOrCall() { + start(alice, bob, carol); + contribute(bob, 10); + act("check"); + assertEquals("poker.raised[poker.raised, n, 10]", ((PlayerMock) bob).nextMessage()); + assertEquals(carol.getUniqueId(), table.actor()); + contribute(carol, 25); + act("call"); + assertEquals("poker.raised[poker.raised, n, 25]", ((PlayerMock) carol).nextMessage()); + contribute(alice, 25); + act("call"); + assertEquals(bob.getUniqueId(), table.actor(), "the first bettor has to answer the raise"); + } + @Test void allInSeatIsSkippedWhileTheOthersKeepRaising() { start(alice, bob, carol); contribute(bob, 10); act("raise"); contribute(carol, 3); + broke.add(carol.getUniqueId()); act("call"); assertEquals(alice.getUniqueId(), table.actor()); contribute(alice, 20); @@ -680,6 +736,8 @@ abstract class GameScenarioFixture { final Map invested = new HashMap<>(); final Map> contributions = new HashMap<>(); final List dealt = new ArrayList<>(); + /** Seats with nothing left in their pockets to bet, so a short call is an all in. */ + final java.util.Set broke = new java.util.HashSet<>(); private MockedStatic managers; private MockedStatic wagers; private MockedStatic messages; @@ -704,6 +762,8 @@ void setUpScenario() { carol = MockBukkit.getMock().addPlayer(); when(manager.table(table.getId())).thenReturn(table); when(wager.totalsExcept(table, table.getId())).thenAnswer(call -> new HashMap<>(invested)); + when(wager.allIn(eq(table), any(Player.class))).thenAnswer(call -> + broke.contains(call.getArgument(1, Player.class).getUniqueId())); when(wager.owned(eq(table), any(UUID.class), anyInt())).thenAnswer(call -> contributions.getOrDefault(call.getArgument(2), Map.of()).getOrDefault(call.getArgument(1), 0)); doAnswer(call -> { diff --git a/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java b/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java index 313bb9e..110d6d8 100644 --- a/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java +++ b/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java @@ -67,6 +67,44 @@ class TableManagerChipPlaceTest extends TableManagerFixture { assertTrue(table.ledger().isEmpty()); } + @Test void aGameRefusingAStakeKeepsTheCoinsButStillTakesAnotherSeatsBet() { + Table table = placeQuietly(); + PlayerMock seated = opponent(); + stakeCoin(seated, table); + when(game.allowStake(any(), argThat(p -> p != null && p.getUniqueId().equals(player.getUniqueId())))) + .thenReturn(false); + table.startSession(); + player.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, 3)); + assertTrue(click(player, felt(table)).isCancelled()); + assertEquals("wager.not_in_hand", player.nextMessage()); + assertEquals(3, player.getInventory().getItemInMainHand().getAmount()); + assertFalse(table.actives().contains(player.getUniqueId())); + assertEquals(0, manager.ownedDenars(table, player.getUniqueId())); + seated.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, 2)); + assertTrue(click(seated, felt(table)).isCancelled()); + assertEquals(2, manager.ownedDenars(table, seated.getUniqueId())); + } + + @Test void freePlayStillSeatsAStrangerWhoStakesMidHand() { + Table table = placeQuietly(); + stakeCoin(opponent(), table); + table.startSession(); + player.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, 3)); + assertTrue(click(player, felt(table)).isCancelled()); + assertTrue(table.actives().contains(player.getUniqueId())); + assertEquals(1, manager.ownedDenars(table, player.getUniqueId())); + } + + @Test void aLiveTableWhoseGameIsNoLongerRegisteredStillTakesAStrangersCoins() { + Table table = placeQuietly(); + stakeCoin(opponent(), table); + table.startSession(); + games.when(() -> GamesRegistry.of("freeplay")).thenReturn(null); + player.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, 3)); + assertTrue(click(player, felt(table)).isCancelled()); + assertEquals(1, manager.ownedDenars(table, player.getUniqueId())); + } + @Test void coinsCannotBeStakedOnTheShoe() { Cache.tableLayouts.put("freeplay", new TableLayout(Cache.pokerCardSet, "Cards", "icon", 6, Map.of(), new TableLayout.FeltRing(0.3, 1.2), null, null, 0.5)); diff --git a/src/test/java/net/tfminecraft/games/table/TableManagerFixture.java b/src/test/java/net/tfminecraft/games/table/TableManagerFixture.java index d345561..4e2dd12 100644 --- a/src/test/java/net/tfminecraft/games/table/TableManagerFixture.java +++ b/src/test/java/net/tfminecraft/games/table/TableManagerFixture.java @@ -142,6 +142,7 @@ abstract class TableManagerFixture { anchors = track(mockStatic(WorldAnchors.class)); game = mock(Game.class, withSettings().extraInterfaces(net.tfminecraft.games.game.LiveCardReturns.class)); when(game.tablePileSlot(any(), anyString(), anyInt(), anyInt(), anyBoolean())).thenCallRealMethod(); + when(game.allowStake(any(), any())).thenCallRealMethod(); games = track(mockStatic(GamesRegistry.class)); games.when(() -> GamesRegistry.of("freeplay")).thenReturn(game); messages = track(mockStatic(Messages.class, call -> call.getArgument(0))); diff --git a/src/test/java/net/tfminecraft/games/table/TableManagerLootTest.java b/src/test/java/net/tfminecraft/games/table/TableManagerLootTest.java index 5648472..caf0111 100644 --- a/src/test/java/net/tfminecraft/games/table/TableManagerLootTest.java +++ b/src/test/java/net/tfminecraft/games/table/TableManagerLootTest.java @@ -93,6 +93,19 @@ class TableManagerLootTest extends TableManagerFixture { assertEquals("wager.busy", player.nextMessage(), "the refused placement keeps the arm"); } + @Test void lootTheGameRefusesStaysInHand() { + Table table = armedTable(7); + table.actives().remove(player.getUniqueId()); + when(game.allowStake(any(), argThat(p -> p != null && p.getUniqueId().equals(player.getUniqueId())))) + .thenReturn(false); + table.startSession(); + assertTrue(click(player, table.getOrigin().clone().add(0.75, 0, 0)).isCancelled()); + assertEquals("wager.not_in_hand", player.nextMessage()); + assertEquals(2, diamonds()); + assertEquals(1, table.ledger().total()); + assertFalse(table.actives().contains(player.getUniqueId())); + } + @Test void aDeclaredValueTooLargeToAddUpTakesNothing() { Table table = armedTable(2_000_000_000); assertTrue(click(player, table.getOrigin().clone().add(0.75, 0, 0)).isCancelled()); diff --git a/src/test/java/net/tfminecraft/games/wager/WagerEngineTest.java b/src/test/java/net/tfminecraft/games/wager/WagerEngineTest.java index 5b75973..01b77b8 100644 --- a/src/test/java/net/tfminecraft/games/wager/WagerEngineTest.java +++ b/src/test/java/net/tfminecraft/games/wager/WagerEngineTest.java @@ -81,6 +81,15 @@ private void bet(PlayerMock player, int count) { assertEquals(count, result.moved()); } + @Test + void allInOnlyOnceNothingStakeableIsLeftInPocket() { + assertTrue(engine.allIn(table, winner)); + winner.getInventory().addItem(new ItemStack(Material.STONE, 5)); + assertTrue(engine.allIn(table, winner), "items with no coin value are not chips"); + winner.getInventory().addItem(coin(1)); + assertFalse(engine.allIn(table, winner)); + } + @Test void restorePreservesStreetPlacementAndSeparatesTrayFromPot() { UUID owner = winner.getUniqueId(); From 188dbf6a5f352ac87355b888068d97d5a1bad3bd Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Wed, 30 Sep 2026 22:00:57 +0200 Subject: [PATCH 2/2] fix: only refuse a mid-hand stake once the item is a stake The stake check ran before the held item was classified, so a stranger or folded seat holding an ordinary item near a live table had the click cancelled and got a refusal. It now runs after the item is known to be a whole-denar stake, so other items pass through as before. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/main/java/net/tfminecraft/games/table/TableManager.java | 6 +++--- .../tfminecraft/games/table/TableManagerChipPlaceTest.java | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/tfminecraft/games/table/TableManager.java b/src/main/java/net/tfminecraft/games/table/TableManager.java index 7b5650d..b7cb3ce 100644 --- a/src/main/java/net/tfminecraft/games/table/TableManager.java +++ b/src/main/java/net/tfminecraft/games/table/TableManager.java @@ -2629,9 +2629,6 @@ private boolean tryPlaceChip(Player player, Location click) { player.sendMessage(Messages.get("bet.closed")); return true; } - if (!dealerTray && refuseStake(table, player)) { - return true; - } if (table.isPaying()) { return true; } @@ -2666,6 +2663,9 @@ private boolean tryPlaceChip(Player player, Location click) { if (!dealerTray && refuseBlackjackPlace(player, table, player.getUniqueId(), denars)) { return true; } + if (!dealerTray && refuseStake(table, player)) { + return true; + } UUID bucket = dealerTray ? table.getId() : player.getUniqueId(); // One coin of exactly this kind, picked out and staked as a single movement. The coin is in // the player's hand and worth exactly this much, so the movement always goes through. diff --git a/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java b/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java index 110d6d8..b85bc4a 100644 --- a/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java +++ b/src/test/java/net/tfminecraft/games/table/TableManagerChipPlaceTest.java @@ -80,6 +80,9 @@ class TableManagerChipPlaceTest extends TableManagerFixture { assertEquals(3, player.getInventory().getItemInMainHand().getAmount()); assertFalse(table.actives().contains(player.getUniqueId())); assertEquals(0, manager.ownedDenars(table, player.getUniqueId())); + player.getInventory().setItemInMainHand(new ItemStack(Material.STONE)); + assertFalse(click(player, felt(table)).isCancelled(), "ordinary items still pass through"); + assertNull(player.nextMessage()); seated.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, 2)); assertTrue(click(seated, felt(table)).isCancelled()); assertEquals(2, manager.ownedDenars(table, seated.getUniqueId()));