diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 9b3545a..9523452 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -93,7 +93,11 @@ public final class App extends Application { final int childrenCount = root.getChildren().size(); for (int i = 0; i < childrenCount; i++) { - root.getChildren().removeLast(); + try { + root.getChildren().removeLast(); + } catch (Exception e) { + IO.println(e); + } } stack.removeAllElements(); diff --git a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java new file mode 100644 index 0000000..20fd7c5 --- /dev/null +++ b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java @@ -0,0 +1,68 @@ +package org.toop.app.layer.layers; + +import org.toop.app.layer.Layer; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.networking.events.NetworkEvents; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; + +public final class ConnectedLayer extends Layer { + long clientId; + String user; + List onlinePlayers = new CopyOnWriteArrayList<>(); + + + public ConnectedLayer(long clientId, String user) { + super("multiplayer.css"); + this.clientId = clientId; + this.user = user; + reload(); + + new EventFlow().addPostEvent(new NetworkEvents.SendLogin(this.clientId, this.user)).postEvent(); + new EventFlow().listen(this::handleReceivedChallenge); + + new Thread(this::populatePlayerList).start(); + } + + private void populatePlayerList() { + EventFlow sendGetPlayerList = new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId)); + new EventFlow().listen(NetworkEvents.PlayerlistResponse.class, e -> { + if (e.clientId() == this.clientId) { + List playerList = new java.util.ArrayList<>(List.of(e.playerlist())); // TODO: Garbage, but works + playerList.removeIf(name -> name.equalsIgnoreCase(user)); + if (this.onlinePlayers != playerList) { + this.onlinePlayers.clear(); + this.onlinePlayers.addAll(playerList); + } + } + }); + + TimerTask task = new TimerTask() { + public void run() { + sendGetPlayerList.postEvent(); + } + }; + + Timer pollTimer = new Timer(); + pollTimer.schedule(task, 0L, 5000L); + } + + private void sendChallenge(String oppUsername, String gameType) { + if (onlinePlayers.contains(oppUsername)) { + new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType)) + .postEvent(); + } + } + + private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) { + // TODO: Popup? Idk what this actually sends back. + } + + @Override + public void reload() { + popAll(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java b/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java index ed0fbf7..93e2f9f 100644 --- a/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java +++ b/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java @@ -1,13 +1,10 @@ package org.toop.app.layer.layers.game; -import javafx.geometry.Pos; -import javafx.scene.paint.Color; import org.toop.app.App; import org.toop.app.GameInformation; import org.toop.app.canvas.TicTacToeCanvas; import org.toop.app.layer.Container; import org.toop.app.layer.Layer; -import org.toop.app.layer.NodeBuilder; import org.toop.app.layer.containers.VerticalContainer; import org.toop.app.layer.layers.MainLayer; import org.toop.framework.eventbus.EventFlow; @@ -17,9 +14,13 @@ import org.toop.game.tictactoe.TicTacToe; import org.toop.game.tictactoe.TicTacToeAI; import org.toop.local.AppContext; +import javafx.geometry.Pos; +import javafx.scene.paint.Color; + import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Pattern; public final class TicTacToeLayer extends Layer { private TicTacToeCanvas canvas; @@ -36,9 +37,9 @@ public final class TicTacToeLayer extends Layer { private String player2Name = ""; public TicTacToeLayer(GameInformation information) { - super("bg-primary"); + super("game.css"); - canvas = new TicTacToeCanvas(Color.LIME, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> { + canvas = new TicTacToeCanvas(Color.WHITE, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> { try { if (information.isConnectionLocal()) { if (ticTacToe.getCurrentTurn() == 0) { @@ -47,11 +48,9 @@ public final class TicTacToeLayer extends Layer { playerMoveQueue.put(new Game.Move(cell, 'O')); } } else { - if (ticTacToe.getCurrentTurn() == 0) { - if (information.isPlayerHuman()[0] && currentPlayerMove != Game.EMPTY) { - playerMoveQueue.put(new Game.Move(cell, currentPlayerMove)); - } - } + if (information.isPlayerHuman()[0] && currentPlayerMove != Game.EMPTY) { + playerMoveQueue.put(new Game.Move(cell, currentPlayerMove)); + } } } catch (InterruptedException e) { return; @@ -94,13 +93,11 @@ public final class TicTacToeLayer extends Layer { } } - final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> { + final Container controlContainer = new VerticalContainer(5); + controlContainer.addButton(AppContext.getString("back"), () -> { App.activate(new MainLayer()); }); - final Container controlContainer = new VerticalContainer(5); - controlContainer.addNodes(backButton); - addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0); addGameCanvas(canvas, Pos.CENTER, 0, 0); } @@ -162,12 +159,10 @@ public final class TicTacToeLayer extends Layer { class OnlineGameState { public long clientId = -1; public long receivedMove = -1; - public boolean firstPlayerIsMe = true; + public boolean firstPlayerIsMe = true; } - - AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true); - AtomicBoolean gameHasStarted = new AtomicBoolean(false); - + AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true); + AtomicBoolean gameHasStarted = new AtomicBoolean(false); private void serverGameThread(NetworkEvents.StartClientResponse event) { boolean running = true; final long clientId = event.clientId(); @@ -177,108 +172,105 @@ public final class TicTacToeLayer extends Layer { //new EventFlow() // .listen(NetworkEvents.GameMoveResponse.class,respEvent -> onMoveResponse(onlineGameState, respEvent)); - new EventFlow() - .listen(this::yourTurnResponse) - .listen(this::handleChallengeResponse) - .listen(this::handleServerGameStart) - .listen(this::handleReceivedMessage) - .listen(this::onMoveResponse); - - new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, information.playerName()[0])) - .postEvent(); - - new EventFlow().addPostEvent(new NetworkEvents.SendSubscribe(clientId, "tic-tac-toe")) - .postEvent(); + new EventFlow() + .listen(this::yourTurnResponse) + .listen(this::handleChallengeResponse) + .listen(this::handleServerGameStart) + .listen(this::handleReceivedMessage) + .listen(this::onMoveResponse); while (running) { - try { - Thread.sleep(250); - } catch (InterruptedException exception) { - } - boolean hasStarted = gameHasStarted.get(); - if (hasStarted) { - onlineGameState.firstPlayerIsMe = firstPlayerIsMe.get(); - if (onlineGameState.firstPlayerIsMe) { - currentPlayerMove = 'X'; - } else { - currentPlayerMove = 'O'; - } - if (!information.isPlayerHuman()[0]) { - boolean myTurn = (onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 0) - || (!onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 1); - if (myTurn) { - Game.Move move; - move = ticTacToeAI.findBestMove(ticTacToe, compurterDifficultyToDepth(10, 10)); - new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) move.position())) - .postEvent(); - } - } else { - try { - final Game.Move wants = playerMoveQueue.take(); - final Game.Move[] legalMoves = ticTacToe.getLegalMoves(); - for (final Game.Move legalMove : legalMoves) { - if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) { - new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) wants.position())) - .postEvent(); - break; - } - } - } catch (InterruptedException exception) { - return; - } - } - } + try { + Thread.sleep(250); + } catch (InterruptedException exception) {} + boolean hasStarted = gameHasStarted.get(); + if (hasStarted) { + onlineGameState.firstPlayerIsMe = firstPlayerIsMe.get(); + if (onlineGameState.firstPlayerIsMe) { + currentPlayerMove = 'X'; + } + else { + currentPlayerMove = 'O'; + } + if(!information.isPlayerHuman()[0]){ + boolean myTurn = (onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 0) + || (!onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 1); + if (myTurn) { + Game.Move move; + move = ticTacToeAI.findBestMove(ticTacToe, compurterDifficultyToDepth(10, 10)); + new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) move.position())) + .postEvent(); + } + } + else { + try { + final Game.Move wants = playerMoveQueue.take(); + final Game.Move[] legalMoves = ticTacToe.getLegalMoves(); + for (final Game.Move legalMove : legalMoves) { + if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) { + new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) wants.position())) + .postEvent(); + break; + } + } + } catch (InterruptedException exception) { + System.out.println(exception.getMessage()); + return; + } + } + } } } - private void drawSymbol(Game.Move move) { - if (move.value() == 'X') { - canvas.drawX(Color.RED, move.position()); - } else if (move.value() == 'O') { - canvas.drawO(Color.BLUE, move.position()); - } - } + private void drawSymbol(Game.Move move) { + if (move.value() == 'X') { + canvas.drawX(Color.RED, move.position()); + } else if (move.value() == 'O') { + canvas.drawO(Color.BLUE, move.position()); + } + } - private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) { - if (resp.playerToMove().equals(resp.opponent())) { - firstPlayerIsMe.set(false); - } else { - firstPlayerIsMe.set(true); - } - gameHasStarted.set(true); - } + private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) { + if(resp.playerToMove().equals(resp.opponent())){ + firstPlayerIsMe.set(false); + } + else{ + firstPlayerIsMe.set(true); + } + gameHasStarted.set(true); + } private void onMoveResponse(NetworkEvents.GameMoveResponse resp) { - char playerChar; - if (resp.player().equals(information.playerName()[0]) && firstPlayerIsMe.get() - || !resp.player().equals(information.playerName()[0]) && !firstPlayerIsMe.get()) { - playerChar = 'X'; - } else { - playerChar = 'O'; - } - Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar); - Game.State state = ticTacToe.play(move); - if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw - gameHasStarted.set(false); - } - drawSymbol(move); + char playerChar; + if (resp.player().equals(information.playerName()[0]) && firstPlayerIsMe.get() + || !resp.player().equals(information.playerName()[0]) && !firstPlayerIsMe.get()) { + playerChar = 'X'; + } + else { + playerChar = 'O'; + } + Game.Move move =new Game.Move(Integer.parseInt(resp.move()),playerChar); + Game.State state = ticTacToe.play(move); + if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw + gameHasStarted.set(false); + } + drawSymbol(move); } - private void handleChallengeResponse(NetworkEvents.ChallengeResponse resp) { - new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(resp.clientId(), Integer.parseInt(resp.challengeId()))) - .postEvent(); - } + private void handleChallengeResponse(NetworkEvents.ChallengeResponse resp) { + new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(resp.clientId(),Integer.parseInt(resp.challengeId()))) + .postEvent(); + } - private void yourTurnResponse(NetworkEvents.YourTurnResponse response) { + private void yourTurnResponse(NetworkEvents.YourTurnResponse response) { - //new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(),"CHALLENGE banaan tic-tac-toe")).postEvent(); - //new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(),(short)2)) - // .postEvent(); - } - - private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) { - System.out.println("Received Message: " + msg.message()); //todo add chat window - } + //new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(),"CHALLENGE banaan tic-tac-toe")).postEvent(); + //new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(),(short)2)) + // .postEvent(); + } + private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) { + System.out.println("Received Message: " + msg.message()); //todo add chat window + } private void serverGameThreadResponseHandler(OnlineGameState ogs, NetworkEvents.ChallengeResponse msg) { if (msg.clientId() != ogs.clientId) return; diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java index 3ca6386..d207077 100644 --- a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java +++ b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java @@ -5,9 +5,11 @@ import org.toop.framework.asset.types.LoadableResource; import javax.sound.sampled.*; import java.io.*; +import java.nio.file.Files; @FileExtension({"wav"}) public class SoundEffectAsset extends BaseResource implements LoadableResource { + private byte[] rawData; public SoundEffectAsset(final File audioFile) { super(audioFile); @@ -15,10 +17,6 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { // Gets a new clip to play public Clip getNewClip() throws LineUnavailableException, UnsupportedAudioFileException, IOException { - if(!this.isLoaded()){ - this.load(); - } - // Get a new clip from audio system Clip clip = AudioSystem.getClip(); @@ -32,7 +30,13 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { // Generates a new audio stream from byte array private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException { - return AudioSystem.getAudioInputStream(this.file); + // Check if raw data is loaded into memory + if(!this.isLoaded()){ + this.load(); + } + + // Turn rawData into an input stream and turn that into an audio input stream; + return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData)); } private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) { @@ -52,16 +56,17 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { @Override public void load() { try { - this.getAudioStream(); + this.rawData = Files.readAllBytes(file.toPath()); this.isLoaded = true; - } catch (UnsupportedAudioFileException | IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } } @Override public void unload() { - this.isLoaded = false; // TODO? + this.rawData = null; + this.isLoaded = false; } @Override diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java index 6614236..ff9da6b 100644 --- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java +++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java @@ -75,6 +75,20 @@ public class SoundManager { for (MediaPlayer mediaPlayer : this.activeMusic) { mediaPlayer.setVolume(this.volume); } + for (Clip clip : this.activeSoundEffects.values()){ + updateClipVolume(clip); + } + } + + private void updateClipVolume(Clip clip){ + if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)){ + FloatControl volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); + float min = volumeControl.getMinimum(); + float max = volumeControl.getMaximum(); + float dB = (float) (Math.log10(Math.max(volume, 0.0001)) * 20.0); // convert linear to dB + dB = Math.max(min, Math.min(max, dB)); + volumeControl.setValue(dB); + } } private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) { @@ -144,6 +158,9 @@ public class SoundManager { // Get a new clip from resource Clip clip = asset.getNewClip(); + // Set volume of clip + updateClipVolume(clip); + // If supposed to loop make it loop, else just start it once if (loop) { clip.loop(Clip.LOOP_CONTINUOUSLY);