From 9f9c82474e18a416fb979a9e2af32d303ffeb626 Mon Sep 17 00:00:00 2001 From: alterdekim Date: Sat, 3 Aug 2024 15:58:55 +0300 Subject: [PATCH] Mulligan coding --- .../hearthhack/component/GameConnection.java | 2 +- .../hearthhack/component/GameRoom.java | 165 +++++++++++++++--- .../component/interfaces/IGameRoom.java | 2 - .../alterdekim/hearthhack/game/GameState.java | 1 - .../hearthhack/game/PegasusPacketType.java | 24 +++ .../hearthhack/game/RunningState.java | 7 + .../alterdekim/hearthhack/util/GameDeck.java | 28 ++- 7 files changed, 183 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/alterdekim/hearthhack/game/PegasusPacketType.java create mode 100644 src/main/java/com/alterdekim/hearthhack/game/RunningState.java diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java index 18fbe7b..7f45a33 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java +++ b/src/main/java/com/alterdekim/hearthhack/component/GameConnection.java @@ -63,7 +63,7 @@ public class GameConnection extends Thread { this.playerId = (long) handshake.getClientHandle(); return; } - if( packet.getType() == 115 ) { + if( packet.getType() == PegasusPacketType.SendPing.getValue() ) { pong(); return; } diff --git a/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java b/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java index 9931a08..4778362 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java +++ b/src/main/java/com/alterdekim/hearthhack/component/GameRoom.java @@ -11,15 +11,15 @@ import com.alterdekim.hearthhack.entity.Deck; import com.alterdekim.hearthhack.game.*; import com.alterdekim.hearthhack.service.UserService; import com.alterdekim.hearthhack.util.PegasusPacket; +import com.google.protobuf.InvalidProtocolBufferException; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; +import java.util.stream.IntStream; @Slf4j @@ -46,10 +46,11 @@ public class GameRoom extends Thread implements IGameRoom { private final String password; private GameState globalState; + private RunningState runningState; @Getter - private ConcurrentLinkedQueue incomeQueue; - private ConcurrentLinkedQueue outcomeQueue; + private final ConcurrentLinkedQueue incomeQueue; + private final ConcurrentLinkedQueue outcomeQueue; // TODO: abolish that later. It's a temporary solution private int entity_id = 4; @@ -63,6 +64,7 @@ public class GameRoom extends Thread implements IGameRoom { this.decks = new ConcurrentHashMap<>(); this.callbacks = new ConcurrentHashMap<>(); this.globalState = GameState.CreateGame; + this.runningState = RunningState.INITIAL_DEAL; this.objectConfig = objectConfig; log.info("GameRoom players: {}", this.players); this.start(); // TODO: debug mode should stop that @@ -86,14 +88,137 @@ public class GameRoom extends Thread implements IGameRoom { } private void processIncomePacket(IncomeGamePacket packet) { - log.info("processIncomePacket: {}", packet.getPacket()); - /* if( packet.getPacket().getType() != 1) return; - List l = this.outcomeQueue.stream() - .filter(p -> p.getPlayerId().longValue() == packet.getPlayerId().longValue()) + try { + switch (PegasusPacketType.fromInt(packet.getPacket().getType())) { + case SendChoices -> processChoices(packet.getPlayerId(), PegasusGame.ChooseEntities.parseFrom((byte[]) packet.getPacket().getBody())); + case SendEmoteOrUI -> processEmoteOrUI(packet.getPlayerId(), PegasusGame.UserUI.parseFrom((byte[]) packet.getPacket().getBody())); + default -> log.warn("processIncomePacket: {}", packet.getPacket()); + } + } catch (InvalidProtocolBufferException e) { + log.error(e.getMessage()); + } + } + + private void processChoices(final Long id, PegasusGame.ChooseEntities packet) { + if(this.globalState != GameState.Running || this.runningState != RunningState.INITIAL_DEAL || this.findPlayerById(id).isEmpty()) return; + // initial_deal state: + List redeal = this.decks.get(id).getHandCards() + .stream() + .map(PegasusGame.PowerHistoryEntity.Builder::getEntity) + .filter(entity -> !packet.getEntitiesList().contains(entity)) .collect(Collectors.toList()); - GamePacketCallback callback = this.callbacks.get(packet.getPlayerId()); - if (callback == null) return; - l.forEach(p -> callback.onMessage(p.getPacket()));*/ + + PegasusGame.PowerHistory.Builder pw = PegasusGame.PowerHistory.newBuilder(); + + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(this.findPlayerById(id).get().getController()+1) + .setTag(GameTag.MULLIGAN_STATE.getValue()) + .setValue(TagMulligan.DEALING.ordinal()) + ) + ); + + Queue handPoses = new ArrayDeque<>(); + + IntStream.range(0, this.decks.get(id).getSize()) + .boxed() + .filter(c -> redeal.contains(this.decks.get(id).getCard(c).getEntity())) + .forEach(i -> { + PegasusGame.PowerHistoryEntity.Builder b = this.decks.get(id).getCard(i) + .addTags(PegasusGame.Tag.newBuilder() + .setName(GameTag.ZONE.getValue()) + .setValue(TagZone.DECK.ordinal()) + ); + handPoses.add( + b.getTagsList() + .stream() + .filter(t -> t.getName() == GameTag.ZONE_POSITION.getValue()) + .map(PegasusGame.Tag::getValue) + .findFirst() + .orElse(0) + ); + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(this.decks.get(id).getCard(i).getEntity()) + .setTag(GameTag.ZONE.getValue()) + .setValue(TagZone.DECK.ordinal()) + ) + ); + this.decks.get(id).updateCard(b, i); + }); + new Random() + .ints(handPoses.size(), 0, this.decks.get(id).getSize()) + .boxed() + .forEach(i -> { + var l = this.decks.get(id).getCard(i); + IntStream.range(0, l.getTagsCount()) + .boxed() + .filter(h -> l.getTags(h).getName() == GameTag.ZONE_POSITION.getValue()) + .forEach(l::removeTags); + PegasusGame.Tag t = PegasusGame.Tag.newBuilder() + .setName(GameTag.ZONE_POSITION.getValue()) + .setValue(handPoses.poll()) + .build(); + l.addTags(t); + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(l.getEntity()) + .setTag(GameTag.ZONE_POSITION.getValue()) + .setValue(t.getValue()) + ) + ); + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(l.getEntity()) + .setTag(GameTag.ZONE.getValue()) + .setValue(TagZone.HAND.ordinal()) + ) + ); + IntStream.range(0, l.getTagsCount()) + .boxed() + .filter(h -> l.getTags(h).getName() == GameTag.ZONE.getValue()) + .forEach(h -> l.setTags(h, l.getTags(h).toBuilder().setValue(TagZone.HAND.ordinal()))); + this.decks.get(id).updateCard(l, i); + }); + + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(this.findPlayerById(id).get().getController()+1) + .setTag(GameTag.MULLIGAN_STATE.getValue()) + .setValue(TagMulligan.WAITING.ordinal()) + ) + ); + + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(this.findPlayerById(id).get().getController()+1) + .setTag(GameTag.MULLIGAN_STATE.getValue()) + .setValue(TagMulligan.DONE.ordinal()) + ) + ); + + pw.addList(PegasusGame.PowerHistoryData.newBuilder() + .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder() + .setEntity(1) + .setTag(GameTag.NEXT_STEP.getValue()) + .setValue(TagStep.MAIN_READY.ordinal()) + ) + ); + + this.broadcast(new PegasusPacket(19, 0, pw.build().toByteArray())); + // log.info("Initial_deal choices: player_id={}; packet={}", id, packet); + } + + private void processEmoteOrUI(final Long id, PegasusGame.UserUI packet) { + this.players.stream() + .filter(p -> p.getUserId().longValue() != id.longValue()) + .forEach(p -> this.outcomeQueue.add( + new OutcomeGamePacket( + p.getUserId(), + new PegasusPacket( 15, 0, packet.toBuilder().setPlayerId(id.intValue()).build().toByteArray()) + ) + ) + ); } private void updateGameState() { @@ -102,7 +227,6 @@ public class GameRoom extends Thread implements IGameRoom { case CreateGame -> createGameEntity(); case CreateDecks -> createDeckEntities(); case CreateHeroes -> createPlayerHeroEntity(); - case CreateHeroPowers -> createPlayerHeroPowerEntity(); } } @@ -387,7 +511,7 @@ public class GameRoom extends Thread implements IGameRoom { ) ) ); - this.decks.get(player.getUserId()).updateCard(b); + this.decks.get(player.getUserId()).updateCard(b, di); } for (int i = 1; i <= 3; i++, this.entity_id++, di++) { @@ -414,7 +538,7 @@ public class GameRoom extends Thread implements IGameRoom { ) ) ); - this.decks.get(player.getUserId()).updateCard(b); + this.decks.get(player.getUserId()).updateCard(b, di); } // there was a place for hero & hero_power @@ -449,11 +573,6 @@ public class GameRoom extends Thread implements IGameRoom { this.nextGlobalState(); } - @Override - public void createCardEntity() { - - } - @Override public void createPlayerHeroEntity() { log.info("Player hero entity"); @@ -562,12 +681,6 @@ public class GameRoom extends Thread implements IGameRoom { this.nextGlobalState(); } - @Override - public void createPlayerHeroPowerEntity() { - log.info("hero power entity"); - this.nextGlobalState(); - } - private Optional findPlayerById(Long id) { return players.stream().filter(p -> p.getUserId().longValue() == id.longValue()).findFirst(); } diff --git a/src/main/java/com/alterdekim/hearthhack/component/interfaces/IGameRoom.java b/src/main/java/com/alterdekim/hearthhack/component/interfaces/IGameRoom.java index c576c07..b91b03d 100644 --- a/src/main/java/com/alterdekim/hearthhack/component/interfaces/IGameRoom.java +++ b/src/main/java/com/alterdekim/hearthhack/component/interfaces/IGameRoom.java @@ -3,7 +3,5 @@ package com.alterdekim.hearthhack.component.interfaces; public interface IGameRoom { void createGameEntity(); void createDeckEntities(); - void createCardEntity(); void createPlayerHeroEntity(); - void createPlayerHeroPowerEntity(); } diff --git a/src/main/java/com/alterdekim/hearthhack/game/GameState.java b/src/main/java/com/alterdekim/hearthhack/game/GameState.java index 2bb8366..02a907b 100644 --- a/src/main/java/com/alterdekim/hearthhack/game/GameState.java +++ b/src/main/java/com/alterdekim/hearthhack/game/GameState.java @@ -4,7 +4,6 @@ public enum GameState { CreateGame, CreateDecks, CreateHeroes, - CreateHeroPowers, Running; public static GameState next(GameState state) { diff --git a/src/main/java/com/alterdekim/hearthhack/game/PegasusPacketType.java b/src/main/java/com/alterdekim/hearthhack/game/PegasusPacketType.java new file mode 100644 index 0000000..a4c288a --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/game/PegasusPacketType.java @@ -0,0 +1,24 @@ +package com.alterdekim.hearthhack.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum PegasusPacketType { + GetGameState(1), + Concede(11), + SendPing(115), + SendChoices(3), + SendOption(2), + SendEmoteOrUI(15); + + private final Integer value; + + public static PegasusPacketType fromInt(int i) { + for(PegasusPacketType t : values()) { + if( t.getValue() == i ) return t; + } + return SendPing; + } +} diff --git a/src/main/java/com/alterdekim/hearthhack/game/RunningState.java b/src/main/java/com/alterdekim/hearthhack/game/RunningState.java new file mode 100644 index 0000000..b665a13 --- /dev/null +++ b/src/main/java/com/alterdekim/hearthhack/game/RunningState.java @@ -0,0 +1,7 @@ +package com.alterdekim.hearthhack.game; + +public enum RunningState { + INITIAL_DEAL, + DRAW, + TURN +} diff --git a/src/main/java/com/alterdekim/hearthhack/util/GameDeck.java b/src/main/java/com/alterdekim/hearthhack/util/GameDeck.java index f654514..de8c572 100644 --- a/src/main/java/com/alterdekim/hearthhack/util/GameDeck.java +++ b/src/main/java/com/alterdekim/hearthhack/util/GameDeck.java @@ -7,33 +7,33 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class GameDeck { - private final Map deck; + private final List deck; public GameDeck() { - this.deck = new HashMap<>(); + this.deck = new ArrayList<>(); } public PegasusGame.PowerHistoryEntity.Builder getCard(Integer i) { - return this.deck.get(getKeys().get(i)); + return this.deck.get(i); } + public Optional getByEntityId(Integer i) { return this.deck.stream().filter(c -> c.getEntity() == i).findFirst(); } + public Integer getSize() { - return this.deck.values().size(); + return this.deck.size(); } public void addCard(PegasusGame.PowerHistoryEntity.Builder b) { - this.deck.put(b.getName(), b); + this.deck.add(b); } - public void updateCard(PegasusGame.PowerHistoryEntity.Builder b) { - deck.put(b.getName(), b); + public void updateCard(PegasusGame.PowerHistoryEntity.Builder b, Integer i) { + deck.set(i, b); } public List getHandCards() { @@ -42,7 +42,6 @@ public class GameDeck { public List getCardsByTag(GameTag tag, Integer val) { return deck - .values() .stream() .filter(c -> c.getTagsList() .stream() @@ -51,10 +50,7 @@ public class GameDeck { .collect(Collectors.toList()); } - private List getKeys() { - return this.deck.keySet() - .stream() - .sorted() - .collect(Collectors.toList()); + public void shuffleDeck() { + Collections.shuffle(deck); } }