From 8146be16ed21e35b180699503810f88c6a614427 Mon Sep 17 00:00:00 2001 From: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:28:45 +0100 Subject: [PATCH 1/5] Demo 5 * fast server connection * Fixed bugs and oversights * Renamed asset folder to resource, made resourceLoader more robust. Completed some TODO's, formatting * AppSettings now also get loaded into the assetmanager * start to reversi logic * legal moves now get highlighted in red * Moves flip dots. all tests pass. can play reversi local. * Working state. Split AudioManager into 3 different branches for easier testing and srp * Reworked to now use better defined generics and easier to use API. Added AudioResource to be used in changing volume * Made all of the updated classes more generic for better flexibility in unittesting * Added more flexible dependency injection to MusicManager for unittesting. Moved to event driven design for less complex code and lower runtime complexity. * Split SoundEffectManager from AudioManager. (#171) Clips no longer create a new clip instance each time they are played. A singular clip is made for each resource and is opened/closed when loaded/unloaded. When a clip is played that is already playing it'll stop playback and start again. Clip volume handling isn't done very well. * Unit tests for MusicManager.java * Hotfix for loading clip volume issue (#174) * Fixed AudioVolumemanager, all volumes calculations are now made in VolumeTypes enum * Added ability to remove a manager from VolumeTypes * Removed file no longer in use * Fixed grammer and spelling mistakes * Renamed VOLUME to MASTERVOLUME for better naming * Minor changes in API design * Renamed VolumeTypes to VolumeControl. Made it thread safe. Added docs to VolumeControl and co. removed .updateAllVolumes() in favor of auto updating inside enum instead * Added ErrorProne for potential bugs. Fixed potential bugs. * Small fixes. * Removed no more needed code. * Finished todo's * Moved restrictedAPI to future release * Finished todo's * Moved restrictedAPI to future release * Changed pom to be correct. Fixed SnowflakeGenerator not making unique ids. Changed naming for event implementation. Automated id getter for events. Added Error-Prone to all modules. Added parents to all modules. Added processors module. * SoundEffectManager now generic * Removed ResourceManager from AudioManagers * Added linelistener to SoundEffectAsset * commit * commit ofzo * Tests for SoundEffectManager * getLegalMoves logic seems fixed //todo write better tests * Tests toegevoegd * punk toegevoegd * Added shuffling on user request * Reworked NetworkingClientManager into SRP model. * Forgot to remove * Improved API for dependency injection * Some better docs. * gui refactor * fixed merge conflicts * Added exceptions. Added reconnect attempts and changeable address * Fixed event bug * add: reversi game * add: server chat box * visual update * Refactor to make Events easier to work with. * Quick fix for closing connection. * Documentation * Correct client creation and user polling * begin van audio display * Polling music event, fires every 1 second * Updated test. * Updated timings * Nuke everything on close. * Basis Audio Display toegevoegd + standaard CSS toegevoegd Kan nu zien hoe lang de song duurt, hoe lang ie al bezig is met draaien en de titel (-.mp3) * Clips now also return positional information * Skip Button * Skip Button * Fixes for garbage code by Omar * Tiny fix when natural skip * Small event fix * Faster event schedule for PlayingMusic event * test fix * added method for sorting the flipped pieces by distance to Move.position * new reversi test (both players no legal moves) * connect4 with minimax AI * Toegevoegd: -Play Button + CSS + Events -Previous Button + CSS + Events -Changed interface for AudioResource to include a pause button which works really well with mediaplayer, however now SoundEffectAsset has an unnessescary pause * Made it so that it indicates with the play/pause button if its paused or played * add simple flip animations and fixed(?) server somewhat * fixed tests * can start game from playerlist screen * tourney ready * spam minder * fixed setgamelabels * spam minder v2 * canvas changes * moved score out of game * can now go to last using previous and being at the first song * mainview false for sendchallengeview * moved score out of game * kleine ui fix * updated music ma,es * started working on the widget system * iets met timing verkeerd temporary fix * Fixes for garbage code by Omar * Added replace to reduce boiler plate code * Manually fallback to the fallback locale when a ResourceBundle is missing a resource key. Fallsback to "MISSING RESOUREC" if it's not present in the fallback. * Removed unused import and unused parameter * cool onhover effect for reversi * Made the GameState enum it's own file and fixed imports * Removed unused import * Turned abstract methods into an interface * Moved the Move record into it's own file, seperated from Game * Removed unused imports * Renamed Interface Playtable to IPlayable * Turned Abstract Method for AI into interface * Refactored Game to follow encapsulation principle * Removed unused imports * Applied encapsulation principle to TurnBasedBame.java * Privated methods that didn't have to be public * Reversi: made method private * Changed checkForEarlyDraw so it doesn't need a game as input. * Fixed warning "Warning:(27, 12) Copy constructor does not copy field 'mostRecentlyFlippedPieces'", removed unused field * Made connect4 public method private * half done with the widget system * added some comments and made some methods a bit more readable * widget system almost complete * Functional code, is now object orientated * Removed no more needed comments * started a basis for the tutorials, tic tac toe is almost done with some general stuff still to do. * rest van de tutorials toegevoegd * resizable true * fixed turn skip bug fixed end score bug now only shows legal and highlight moves when human * Squashed commit of the following: commit a517f2f302baa89f8ef59946a31c7bb59c56770f Author: Stef Date: Thu Nov 27 15:43:43 2025 +0100 Make it so the game shows "Waiting on ... to make their move". Styling isn't done but it is easier to see who's turn it is. There is a lot of structuring to do in the previous code... * Fixed compilation errors * Changed the way turns are being stored in TurnBasedGame. * Removed views * Added function input for enabling/disabling localization p/text * Fix eventbus problems (#265) * Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler * getAllListeners * Removed nulls * Fixed stress tests * Added docs, no more list creation when adding events to the bus. * Fixed unsubscribe not working. * Moved away from deprecated functions * moved from wildcard to typed * Moved away from deprecated function * Debugs for EventBus and fixed unsubscribe all (#266) * Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler * getAllListeners * Removed nulls * Fixed stress tests * Added docs, no more list creation when adding events to the bus. * Fixed unsubscribe not working. * Moved away from deprecated functions * moved from wildcard to typed * Moved away from deprecated function * Added debugging to GlobalEventBus * Fixed cleaning flow * Fixed unsubscribe all * Fixed unsubscribe all * Removed unused import * Fix music display not working (#267) * Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler * getAllListeners * Removed nulls * Fixed stress tests * Added docs, no more list creation when adding events to the bus. * Fixed unsubscribe not working. * Moved away from deprecated functions * moved from wildcard to typed * Moved away from deprecated function * Added debugging to GlobalEventBus * Fixed cleaning flow * Fixed unsubscribe all * Fixed unsubscribe all * Removed unused import * Added LoadingWidget.java for server feedback * Replace deprecated with correct function * Removed loading widget from Server.java * Fixed old new EventFlow().listen() missing false as third param * Tutorials to Dev (#264) * Fixed garbage code * added a pop button * Tutorial images now use ImageAsset.java * Added button to continue and start game. Refactors * Refactored nextScreen runnable * Removed unused imports * Refactored switch statement * Added documentation * Removed space * Added translations * Added function input for enabling/disabling localization p/text --------- Co-authored-by: ramollia <> * Merge new framework into development (#269) * Created a somewhat generic TurnBasedGame thread. Temporary UI that only works for TicTacToe rn. Added a LocalPlayer with the intent to add more players * (RANDOM COMMIT) Hope it works * Changes by bas * Fixed dependency issues * Fixed major issue in game deepcopy * Merge conflict fix * Removed unused import * Update GTBGT branch from dev branch (#263) * started a basis for the tutorials, tic tac toe is almost done with some general stuff still to do. * rest van de tutorials toegevoegd * Removed views * Merge conflict fix * Removed unused import --------- Co-authored-by: michiel301b Co-authored-by: ramollia <> Co-authored-by: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com> * Revert "Update GTBGT branch from dev branch (#263)" This reverts commit 9134d7e3432a9e35cf6ae9a49c9d7940581677cd. * Fixed frontend not using GameController because of spaghetti code. * Removed unused imports * GameCanvas not implements a DrawPlayerMove that can be overridden for specific implementations * Created an event that will request the controller to refresh the UI. * ADDED DEPENDENCY. Renamed GameControllers to GameManagers, gameThread is not game controller. * Attempt at adding an online player. I think it doesn't work because of unsubscriben after success not working * Multiplayer is functional through OnlineThreadBehaviour. Empty slots are currently represented by -1 in the GUI. * Removed sout spam, added logger than I can't get to work. * Idek what these changes are * Te lang geen commit, sorry * Multiplayer seems to work pretty well now, hopefully I can add the other games soon. * Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler * getAllListeners * Removed nulls * Inbetween commit of adding Reversi. This is a lot of spaghetti. * Fixed stress tests * Fixed typo in NetworkingGameClientHandler that prevented losses from being received * Missed 2nd typo. Fixed * Added docs, no more list creation when adding events to the bus. * Fixed unsubscribe not working. * Moved away from deprecated functions * moved from wildcard to typed * Moved away from deprecated function * Added debugging to GlobalEventBus * Fixed cleaning flow * Fixed unsubscribe all * Fixed unsubscribe all * Removed unused import * Works now with updated EventFlow(). Unsubscribing works. ReversiAIR has an issue where a forced move returns -1 and local play back button doesn't work properly. To be fixed * Fixed ReversiR issue that caused skip turn desync * Fixed color mismatch with server and online main player is now correct. * Added a bunch of java doc and small changes * Small changes * Added a new Thread Behaviour to test framework. * Fixed human error I made in TicTacToeR logic... * Fixed broken event and wrong player being presented as winner. * Idk changes * Fixed PR conflicts --------- Co-authored-by: michiel301b Co-authored-by: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com> * added back button sounds because SOMEONE fucked it up..... * 231 connecting to server feedback (#275) * Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler * getAllListeners * Removed nulls * Fixed stress tests * Added docs, no more list creation when adding events to the bus. * Fixed unsubscribe not working. * Moved away from deprecated functions * moved from wildcard to typed * Moved away from deprecated function * Added debugging to GlobalEventBus * Fixed cleaning flow * Fixed unsubscribe all * Fixed unsubscribe all * Removed unused import * Added LoadingWidget.java for server feedback * Imports * fixed loadingwidget * Workable LoadingWidget and trying to connect to server * Removed output * Small bug temp fix --------- Co-authored-by: ramollia <> * Double loading call fix, LoadingWidget docs * Main menu loader (#277) * LoadingWidget main menu * fixed garbage code * Fixed garbage code 2 * LoadWidget fix, added loading to starting the game. Removed unnecessary console output --------- Co-authored-by: ramollia <> * Fixed systems starting, before assets being loaded (I am retarded) * Added infinite boolean, fixed loading behaviour at startup * 272 remake game framework interfaces to properly represent vmc (#278) * Cleaned up a lot of old files and renamed/remade interfaces to better suit the framework * Broken commit * Fixed online play * Better file structure and closer to MVC * Best fix for white screen at start * Making threads verbose regarding exceptions * Loading circle, better loading colors. * Event bus now testable, improved UI (#284) * turn updates * smalle fixes aan turn updates * better human/ai selector with bot selection and depth on TicTacToeAIR * depth + thinktime back to AIs, along with a a specific TicTacToeAIRSleep * fixed overlapping back and disconnect buttons * Changed to debug instead of info * changed the transitionNextCustom to be easier to use * added getAllWidgets to WidgetContainer * Correct back view * added replacePrevious in ViewWidget * added removeIndexFromPreviousChain * fixed incorrect index counting * Fixt wrong view order * Removed todo * Challenge popups "Fixed" * Popups now remove themselves * localize the ChallengePopup text * made the game text a header instead * fixed getAllWidgets * Escape popup * fixed redundant container * Escape remove popup * Working escape menu * Added find functionality * Tutorials moved to escape menu * Escape can't be opened in mainview now * Can now test the event bus, created testable interfaces * Logging errors * Made events and handlers more generic * Suppress * Managers now have changeable eventbus * Tutorials fixed * Removed import * Single threaded eventbus * Fixed wrong eventbus * Removed get * Removed old code * Renaming * Optimization * Removed useless comment * Removed unnecessary imports * Rename * Renaming, refactor and type safety * Rename * Removed import --------- Co-authored-by: michiel301b Co-authored-by: ramollia <> * initSystems now uses latch instead of timer. Moved single threads to Executor * Safety * Deleted unnecessary imports * Code cleanup * changed "fullscreen exit key combination" from esc to F11 * shitty fix for player selector spacing issue * shitty fix for player selector spacing issue v2 * fixed reversi colors being switched, causing multiple issues * Merge bitboards into development (#285) * added new classes for the games that use bitboards instead. also combined game with turnbasedgame * (DOES NOT COMPILE) In-between commit * turn updates * smalle fixes aan turn updates * Bitboard implemented with scuffed TicTacToe translation done by game. This should be done by the view. * Almost done with implementing bitboards. Reversi is broken and artifical players don't work yet. * better human/ai selector with bot selection and depth on TicTacToeAIR * fixed getLegalMoves * depth + thinktime back to AIs, along with a a specific TicTacToeAIRSleep * fixed overlapping back and disconnect buttons * Changed to debug instead of info * changed the transitionNextCustom to be easier to use * added getAllWidgets to WidgetContainer * Correct back view * added replacePrevious in ViewWidget * added removeIndexFromPreviousChain * fixed incorrect index counting * Fixt wrong view order * fixed? getLegalMoves * Everything is broken * Removed todo * fixed getLegalMoves & getFlips * Challenge popups "Fixed" * Fixed local and online play for both games * Popups now remove themselves * Removed souts for debugging * localize the ChallengePopup text * made the game text a header instead * made more classes deepClonable. * fixed getAllWidgets * Added comment * Escape popup * fixed redundant container * Made all network events async again * Escape remove popup * Working escape menu * Removed old AI and old files. Added a new generic random AI. game no longer deals with translation. * Drawing of board on canvas is now done from bitboards rather than translating. * Added a method getWinner() to game interface.Controller now tells gameThreads how to deal with drawing UI and sending a move to server. * Added find functionality * Added a ChatGPT generated MiniMaxAI based on the old MiniMaxAI but with alpha-beta pruning and heuristics for Reversi * Removed System-Outs to clean up console * Update BitGameCanvas.java * Merge fixes * Removed unused imports --------- Co-authored-by: ramollia <> Co-authored-by: michiel301b Co-authored-by: lieght <49651652+BAFGdeJong@users.noreply.github.com> * Better limits to generic acceptance * Will fix tests etc later --------- Co-authored-by: ramollia <@> Co-authored-by: Ticho Hidding Co-authored-by: Stef <48526421+StefBuwalda@users.noreply.github.com> Co-authored-by: Stef Co-authored-by: michiel Co-authored-by: ramollia <> Co-authored-by: tichohidding <58555714+tichohidding@users.noreply.github.com> --- .github/workflows/checks.yaml | 80 +-- .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dictionaries/project.xml | 3 + .idea/encodings.xml | 4 + .idea/inspectionProfiles/Project_Default.xml | 3 +- .idea/resourceBundles.xml | 41 -- app/pom.xml | 103 +++- app/src/main/java/org/toop/Main.java | 14 +- app/src/main/java/org/toop/app/App.java | 366 ++++++++----- .../java/org/toop/app/GameInformation.java | 60 ++- app/src/main/java/org/toop/app/Server.java | 354 ++++++++++++ .../org/toop/app/canvas/BitGameCanvas.java | 247 +++++++++ .../org/toop/app/canvas/DrawPlayerHover.java | 7 + .../org/toop/app/canvas/DrawPlayerMove.java | 5 + .../java/org/toop/app/canvas/GameCanvas.java | 128 +---- .../java/org/toop/app/canvas/GameDrawer.java | 7 + .../org/toop/app/canvas/ReversiBitCanvas.java | 42 ++ ...ToeCanvas.java => TicTacToeBitCanvas.java} | 34 +- .../GenericGameController.java | 140 +++++ .../gameControllers/ReversiBitController.java | 22 + .../TicTacToeBitController.java | 23 + .../java/org/toop/app/layer/Container.java | 12 - .../main/java/org/toop/app/layer/Layer.java | 86 --- .../java/org/toop/app/layer/NodeBuilder.java | 140 ----- .../main/java/org/toop/app/layer/Popup.java | 20 - .../layer/containers/HorizontalContainer.java | 59 -- .../layer/containers/VerticalContainer.java | 59 -- .../toop/app/layer/layers/ConnectedLayer.java | 235 -------- .../toop/app/layer/layers/CreditsPopup.java | 73 --- .../org/toop/app/layer/layers/MainLayer.java | 65 --- .../app/layer/layers/MultiplayerLayer.java | 241 --------- .../toop/app/layer/layers/OptionsPopup.java | 222 -------- .../org/toop/app/layer/layers/QuitPopup.java | 47 -- .../layer/layers/game/GameFinishedPopup.java | 55 -- .../app/layer/layers/game/TicTacToeLayer.java | 329 ------------ .../java/org/toop/app/widget/Primitive.java | 208 +++++++ .../java/org/toop/app/widget/Updatable.java | 5 + .../main/java/org/toop/app/widget/Widget.java | 29 + .../org/toop/app/widget/WidgetContainer.java | 148 +++++ .../app/widget/complex/ConfirmWidget.java | 38 ++ .../widget/complex/LabeledChoiceWidget.java | 42 ++ .../widget/complex/LabeledInputWidget.java | 34 ++ .../widget/complex/LabeledSliderWidget.java | 49 ++ .../app/widget/complex/LoadingWidget.java | 162 ++++++ .../app/widget/complex/PlayerInfoWidget.java | 112 ++++ .../toop/app/widget/complex/PopupWidget.java | 21 + .../toop/app/widget/complex/StackWidget.java | 44 ++ .../toop/app/widget/complex/ToggleWidget.java | 65 +++ .../org/toop/app/widget/complex/Update.java | 5 + .../toop/app/widget/complex/ViewWidget.java | 104 ++++ .../toop/app/widget/display/SongDisplay.java | 117 ++++ .../toop/app/widget/popup/ChallengePopup.java | 64 +++ .../org/toop/app/widget/popup/ErrorPopup.java | 16 + .../toop/app/widget/popup/EscapePopup.java | 48 ++ .../toop/app/widget/popup/GameOverPopup.java | 23 + .../org/toop/app/widget/popup/QuitPopup.java | 27 + .../app/widget/popup/SendChallengePopup.java | 90 ++++ .../widget/tutorial/BaseTutorialWidget.java | 112 ++++ .../tutorial/Connect4TutorialWidget.java | 15 + .../tutorial/ReversiTutorialWidget.java | 17 + .../tutorial/ShowEnableTutorialWidget.java | 22 + .../tutorial/TicTacToeTutorialWidget.java | 16 + .../org/toop/app/widget/view/CreditsView.java | 81 +++ .../org/toop/app/widget/view/GameView.java | 179 +++++++ .../app/widget/view/LocalMultiplayerView.java | 150 ++++++ .../org/toop/app/widget/view/LocalView.java | 24 + .../org/toop/app/widget/view/MainView.java | 39 ++ .../org/toop/app/widget/view/OnlineView.java | 38 ++ .../org/toop/app/widget/view/OptionsView.java | 161 ++++++ .../org/toop/app/widget/view/ServerView.java | 57 ++ .../main/java/org/toop/local/AppContext.java | 67 ++- .../main/java/org/toop/local/AppSettings.java | 148 +++-- .../audio/fx/medium-button-click-backup.wav | Bin 0 -> 58022 bytes ... DOKURO - Guardian of the Former Seas.mp3} | Bin .../DM DOKURO - Roar of the Jungle Dragon.mp3 | Bin 0 -> 20172907 bytes .../audio/music/Daft Punk - Get Lucky.mp3 | Bin 0 -> 3967313 bytes ...mp3 => Hans Zimmer - Extraction Point.mp3} | Bin ...damned.mp3 => Kevin Sherwood - Damned.mp3} | Bin ...u.mp3 => MW2 (2009) Multiplayer Theme.mp3} | Bin .../audio/music/Ramonas - Blitzkrieg.mp3 | Bin 0 -> 3189374 bytes ...Tomisawa - Godfrey, First Elden Lord .mp3} | Bin .../{gladius.mp3 => bignic - Gladius.mp3} | Bin app/src/main/resources/assets/images/cat.jpg | Bin 0 -> 610323 bytes .../resources/assets/images/connect41.png | Bin 0 -> 18049 bytes .../resources/assets/images/connect42.png | Bin 0 -> 30647 bytes .../main/resources/assets/images/reversi1.png | Bin 0 -> 15539 bytes .../main/resources/assets/images/reversi2.png | Bin 0 -> 33563 bytes .../resources/assets/images/tictactoe1.png | Bin 0 -> 9922 bytes .../resources/assets/images/tictactoe2.png | Bin 0 -> 28992 bytes .../localization/localization_ar.properties | 63 --- .../localization/localization_de.properties | 63 --- .../localization/localization_en.properties | 67 --- .../localization/localization_es.properties | 63 --- .../localization/localization_fr.properties | 63 --- .../localization/localization_hi.properties | 63 --- .../localization/localization_it.properties | 63 --- .../localization/localization_ja.properties | 63 --- .../localization/localization_ko.properties | 63 --- .../localization/localization_nl.properties | 63 --- .../localization/localization_ru.properties | 63 --- .../localization/localization_zh.properties | 63 --- .../main/resources/assets/style/dark-hc.css | 215 -------- app/src/main/resources/assets/style/dark.css | 215 -------- app/src/main/resources/assets/style/large.css | 11 - .../main/resources/assets/style/light-hc.css | 215 -------- app/src/main/resources/assets/style/light.css | 215 -------- .../main/resources/assets/style/medium.css | 11 - app/src/main/resources/assets/style/small.css | 11 - .../localization/localization_ar.properties | 106 ++++ .../localization/localization_de.properties | 107 ++++ .../localization/localization_en.properties | 109 ++++ .../localization/localization_es.properties | 106 ++++ .../localization/localization_fr.properties | 106 ++++ .../localization/localization_hi.properties | 106 ++++ .../localization/localization_it.properties | 105 ++++ .../localization/localization_ja.properties | 105 ++++ .../localization/localization_ko.properties | 105 ++++ .../localization/localization_nl.properties | 105 ++++ .../localization/localization_ru.properties | 105 ++++ .../localization/localization_zh.properties | 105 ++++ app/src/main/resources/style/dark.css | 210 ++++++++ app/src/main/resources/style/general.css | 42 ++ .../main/resources/style/high-contrast.css | 208 +++++++ app/src/main/resources/style/large.css | 61 +++ app/src/main/resources/style/light.css | 210 ++++++++ app/src/main/resources/style/medium.css | 61 +++ app/src/main/resources/style/small.css | 61 +++ framework/pom.xml | 95 +++- .../main/java/org/toop/framework/Logging.java | 329 ++++++------ .../toop/framework/SnowflakeGenerator.java | 104 +--- .../asset/events/AssetLoaderEvents.java | 8 - .../framework/asset/resources/MusicAsset.java | 39 -- .../asset/resources/SoundEffectAsset.java | 80 --- .../framework/audio/AudioEventListener.java | 84 +++ .../framework/audio/AudioVolumeManager.java | 162 +++--- .../toop/framework/audio/MusicManager.java | 189 +++++++ .../framework/audio/SoundEffectManager.java | 57 ++ .../toop/framework/audio/SoundManager.java | 197 ------- .../toop/framework/audio/VolumeControl.java | 162 ++++++ .../framework/audio/events/AudioEvents.java | 106 +--- .../audio/interfaces/AudioManager.java | 7 + .../audio/interfaces/MusicManager.java | 11 + .../audio/interfaces/SoundEffectManager.java | 8 + .../audio/interfaces/VolumeManager.java | 53 ++ .../framework/dispatch/JavaFXDispatcher.java | 11 + .../dispatch/interfaces/Dispatcher.java | 5 + .../toop/framework/eventbus/EventFlow.java | 507 +++++++++++++----- .../framework/eventbus/GlobalEventBus.java | 191 +------ .../framework/eventbus/ListenerHandler.java | 25 - .../eventbus/bus/DefaultEventBus.java | 53 ++ .../eventbus/bus/DisruptorEventBus.java | 117 ++++ .../toop/framework/eventbus/bus/EventBus.java | 12 + .../eventbus/events/EventWithSnowflake.java | 8 - .../events/EventWithoutSnowflake.java | 3 - .../framework/eventbus/events/EventsBase.java | 67 +-- .../eventbus/events/GenericEvent.java | 3 + .../events/ResponseToUniqueEvent.java | 31 ++ .../eventbus/events/UniqueEvent.java | 23 + .../eventbus/store/AsyncSubscriberStore.java | 47 ++ .../store/DefaultSubscriberStore.java | 73 +++ .../eventbus/store/SubscriberStore.java | 11 + .../eventbus/store/SyncSubscriberStore.java | 37 ++ .../subscriber/DefaultNamedSubscriber.java | 8 + .../subscriber/DefaultSubscriber.java | 8 + .../framework/eventbus/subscriber/HasId.java | 5 + .../eventbus/subscriber/IdSubscriber.java | 5 + .../eventbus/subscriber/LongIdSubscriber.java | 8 + .../eventbus/subscriber/NamedSubscriber.java | 5 + .../eventbus/subscriber/Subscriber.java | 10 + .../framework/gameFramework/GameState.java | 18 + .../gameFramework/LongPairConsumer.java | 6 + .../controller/GameController.java | 18 + .../controller/UpdatesGameUI.java | 10 + .../model/game/BoardProvider.java | 5 + .../model/game/DeepCopyable.java | 5 + .../gameFramework/model/game/PlayResult.java | 12 + .../gameFramework/model/game/Playable.java | 24 + .../model/game/PlayerProvider.java | 7 + .../model/game/SupportsOnlinePlay.java | 20 + .../model/game/TurnBasedGame.java | 7 + .../AbstractThreadBehaviour.java | 61 +++ .../game/threadBehaviour/Controllable.java | 7 + .../game/threadBehaviour/ThreadBehaviour.java | 18 + .../gameFramework/model/player/AI.java | 7 + .../model/player/AbstractAI.java | 17 + .../model/player/AbstractPlayer.java | 50 ++ .../model/player/MoveProvider.java | 7 + .../model/player/NameProvider.java | 5 + .../gameFramework/model/player/Player.java | 7 + .../gameFramework/view/GUIEvents.java | 26 + .../NetworkingClientEventListener.java | 152 ++++++ .../networking/NetworkingClientManager.java | 297 ++++------ .../TournamentNetworkingClient.java} | 91 ++-- .../networking/events/NetworkEvents.java | 313 +++++------ .../exceptions/ClientNotFoundException.java | 25 + .../exceptions/CouldNotConnectException.java | 21 + .../NetworkingInitializationException.java | 2 +- .../NetworkingGameClientHandler.java | 77 +-- .../NetworkingTicTacToeClientHandler.java | 12 - .../interfaces/NetworkingClient.java | 13 + .../interfaces/NetworkingClientManager.java | 17 + .../networking/types/NetworkingConnector.java | 5 + .../networking/types/ServerCommand.java | 3 + .../networking/types/ServerMessage.java | 3 + .../{asset => resource}/ResourceLoader.java | 39 +- .../{asset => resource}/ResourceManager.java | 67 +-- .../{asset => resource}/ResourceMeta.java | 6 +- .../resource/events/AssetLoaderEvents.java | 8 + ...ouldNotCreateResourceFactoryException.java | 12 + .../exceptions/IsNotAResourceException.java | 7 + .../exceptions/ResourceNotFoundException.java | 7 + .../resources/BaseResource.java | 2 +- .../resources/CssAsset.java | 4 +- .../resources/FontAsset.java | 6 +- .../resources/ImageAsset.java | 11 +- .../resources/JsonAsset.java | 11 +- .../resources/LocalizationAsset.java | 46 +- .../resource/resources/MusicAsset.java | 106 ++++ .../resources/SettingsAsset.java | 38 +- .../resource/resources/SoundEffectAsset.java | 166 ++++++ .../resources/TextAsset.java | 6 +- .../resource/types/AudioResource.java | 13 + .../types/BundledResource.java | 9 +- .../types/FileExtension.java | 6 +- .../types/LoadableResource.java | 4 +- .../types/PreloadResource.java | 4 +- .../org/toop/framework/settings/Settings.java | 5 + .../framework/SnowflakeGeneratorTest.java | 157 +++--- .../framework/audio/MusicManagerTest.java | 204 +++++++ .../audio/SoundEffectManagerTest.java | 132 +++++ .../eventbus/EventFlowStressTest.java | 456 ++++++++-------- .../framework/eventbus/EventFlowTest.java | 185 +++---- .../eventbus/GlobalEventBusTest.java | 319 +++++------ .../NetworkingClientManagerTest.java | 247 ++++----- .../networking/events/NetworkEventsTest.java | 325 +++++------ game/pom.xml | 108 ++-- game/src/main/java/org/toop/game/AI.java | 5 - .../main/java/org/toop/game/BitboardGame.java | 86 +++ game/src/main/java/org/toop/game/Game.java | 39 -- game/src/main/java/org/toop/game/Move.java | 3 + .../java/org/toop/game/TurnBasedGame.java | 27 - .../LocalFixedRateThreadBehaviour.java | 88 +++ .../gameThreads/LocalThreadBehaviour.java | 76 +++ .../gameThreads/OnlineThreadBehaviour.java | 84 +++ .../OnlineWithSleepThreadBehaviour.java | 40 ++ .../game/games/reversi/BitboardReversi.java | 170 ++++++ .../games/tictactoe/BitboardTicTacToe.java | 103 ++++ .../java/org/toop/game/othello/Othello.java | 19 - .../java/org/toop/game/othello/OthelloAI.java | 11 - .../toop/game/players/ArtificialPlayer.java | 55 ++ .../org/toop/game/players/LocalPlayer.java | 86 +++ .../java/org/toop/game/players/MiniMaxAI.java | 165 ++++++ .../org/toop/game/players/OnlinePlayer.java | 36 ++ .../java/org/toop/game/players/RandomAI.java | 38 ++ .../org/toop/game/tictactoe/TicTacToe.java | 100 ---- .../org/toop/game/tictactoe/TicTacToeAI.java | 68 --- .../toop/game/tictactoe/TicTacToeAITest.java | 81 --- pom.xml | 65 ++- processors/pom.xml | 45 ++ .../toop/annotations/AutoResponseResult.java | 7 + .../java/org/toop/annotations/TestsOnly.java | 7 + .../AutoResponseResultProcessor.java | 20 + 262 files changed, 11587 insertions(+), 6669 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/resourceBundles.xml create mode 100644 app/src/main/java/org/toop/app/Server.java create mode 100644 app/src/main/java/org/toop/app/canvas/BitGameCanvas.java create mode 100644 app/src/main/java/org/toop/app/canvas/DrawPlayerHover.java create mode 100644 app/src/main/java/org/toop/app/canvas/DrawPlayerMove.java create mode 100644 app/src/main/java/org/toop/app/canvas/GameDrawer.java create mode 100644 app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java rename app/src/main/java/org/toop/app/canvas/{TicTacToeCanvas.java => TicTacToeBitCanvas.java} (55%) create mode 100644 app/src/main/java/org/toop/app/gameControllers/GenericGameController.java create mode 100644 app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java create mode 100644 app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java delete mode 100644 app/src/main/java/org/toop/app/layer/Container.java delete mode 100644 app/src/main/java/org/toop/app/layer/Layer.java delete mode 100644 app/src/main/java/org/toop/app/layer/NodeBuilder.java delete mode 100644 app/src/main/java/org/toop/app/layer/Popup.java delete mode 100644 app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java delete mode 100644 app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/CreditsPopup.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/MainLayer.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/MultiplayerLayer.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/OptionsPopup.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/QuitPopup.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/game/GameFinishedPopup.java delete mode 100644 app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java create mode 100644 app/src/main/java/org/toop/app/widget/Primitive.java create mode 100644 app/src/main/java/org/toop/app/widget/Updatable.java create mode 100644 app/src/main/java/org/toop/app/widget/Widget.java create mode 100644 app/src/main/java/org/toop/app/widget/WidgetContainer.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/ConfirmWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/LabeledChoiceWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/LabeledInputWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/LabeledSliderWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/LoadingWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/PopupWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/StackWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/ToggleWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/Update.java create mode 100644 app/src/main/java/org/toop/app/widget/complex/ViewWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/display/SongDisplay.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/ErrorPopup.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/EscapePopup.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/GameOverPopup.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/QuitPopup.java create mode 100644 app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java create mode 100644 app/src/main/java/org/toop/app/widget/tutorial/BaseTutorialWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/tutorial/Connect4TutorialWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/tutorial/ReversiTutorialWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/tutorial/ShowEnableTutorialWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/tutorial/TicTacToeTutorialWidget.java create mode 100644 app/src/main/java/org/toop/app/widget/view/CreditsView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/GameView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/LocalView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/MainView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/OnlineView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/OptionsView.java create mode 100644 app/src/main/java/org/toop/app/widget/view/ServerView.java create mode 100644 app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav rename app/src/main/resources/assets/audio/music/{formerseas.mp3 => DM DOKURO - Guardian of the Former Seas.mp3} (100%) create mode 100644 app/src/main/resources/assets/audio/music/DM DOKURO - Roar of the Jungle Dragon.mp3 create mode 100644 app/src/main/resources/assets/audio/music/Daft Punk - Get Lucky.mp3 rename app/src/main/resources/assets/audio/music/{extraction-point.mp3 => Hans Zimmer - Extraction Point.mp3} (100%) rename app/src/main/resources/assets/audio/music/{damned.mp3 => Kevin Sherwood - Damned.mp3} (100%) rename app/src/main/resources/assets/audio/music/{mw2-main-menu.mp3 => MW2 (2009) Multiplayer Theme.mp3} (100%) create mode 100644 app/src/main/resources/assets/audio/music/Ramonas - Blitzkrieg.mp3 rename app/src/main/resources/assets/audio/music/{godfrey.mp3 => Tai Tomisawa - Godfrey, First Elden Lord .mp3} (100%) rename app/src/main/resources/assets/audio/music/{gladius.mp3 => bignic - Gladius.mp3} (100%) create mode 100644 app/src/main/resources/assets/images/cat.jpg create mode 100644 app/src/main/resources/assets/images/connect41.png create mode 100644 app/src/main/resources/assets/images/connect42.png create mode 100644 app/src/main/resources/assets/images/reversi1.png create mode 100644 app/src/main/resources/assets/images/reversi2.png create mode 100644 app/src/main/resources/assets/images/tictactoe1.png create mode 100644 app/src/main/resources/assets/images/tictactoe2.png delete mode 100644 app/src/main/resources/assets/localization/localization_ar.properties delete mode 100644 app/src/main/resources/assets/localization/localization_de.properties delete mode 100644 app/src/main/resources/assets/localization/localization_en.properties delete mode 100644 app/src/main/resources/assets/localization/localization_es.properties delete mode 100644 app/src/main/resources/assets/localization/localization_fr.properties delete mode 100644 app/src/main/resources/assets/localization/localization_hi.properties delete mode 100644 app/src/main/resources/assets/localization/localization_it.properties delete mode 100644 app/src/main/resources/assets/localization/localization_ja.properties delete mode 100644 app/src/main/resources/assets/localization/localization_ko.properties delete mode 100644 app/src/main/resources/assets/localization/localization_nl.properties delete mode 100644 app/src/main/resources/assets/localization/localization_ru.properties delete mode 100644 app/src/main/resources/assets/localization/localization_zh.properties delete mode 100644 app/src/main/resources/assets/style/dark-hc.css delete mode 100644 app/src/main/resources/assets/style/dark.css delete mode 100644 app/src/main/resources/assets/style/large.css delete mode 100644 app/src/main/resources/assets/style/light-hc.css delete mode 100644 app/src/main/resources/assets/style/light.css delete mode 100644 app/src/main/resources/assets/style/medium.css delete mode 100644 app/src/main/resources/assets/style/small.css create mode 100644 app/src/main/resources/localization/localization_ar.properties create mode 100644 app/src/main/resources/localization/localization_de.properties create mode 100644 app/src/main/resources/localization/localization_en.properties create mode 100644 app/src/main/resources/localization/localization_es.properties create mode 100644 app/src/main/resources/localization/localization_fr.properties create mode 100644 app/src/main/resources/localization/localization_hi.properties create mode 100644 app/src/main/resources/localization/localization_it.properties create mode 100644 app/src/main/resources/localization/localization_ja.properties create mode 100644 app/src/main/resources/localization/localization_ko.properties create mode 100644 app/src/main/resources/localization/localization_nl.properties create mode 100644 app/src/main/resources/localization/localization_ru.properties create mode 100644 app/src/main/resources/localization/localization_zh.properties create mode 100644 app/src/main/resources/style/dark.css create mode 100644 app/src/main/resources/style/general.css create mode 100644 app/src/main/resources/style/high-contrast.css create mode 100644 app/src/main/resources/style/large.css create mode 100644 app/src/main/resources/style/light.css create mode 100644 app/src/main/resources/style/medium.css create mode 100644 app/src/main/resources/style/small.css delete mode 100644 framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java delete mode 100644 framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java delete mode 100644 framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java create mode 100644 framework/src/main/java/org/toop/framework/audio/AudioEventListener.java create mode 100644 framework/src/main/java/org/toop/framework/audio/MusicManager.java create mode 100644 framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java delete mode 100644 framework/src/main/java/org/toop/framework/audio/SoundManager.java create mode 100644 framework/src/main/java/org/toop/framework/audio/VolumeControl.java create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/AudioManager.java create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java create mode 100644 framework/src/main/java/org/toop/framework/dispatch/JavaFXDispatcher.java create mode 100644 framework/src/main/java/org/toop/framework/dispatch/interfaces/Dispatcher.java delete mode 100644 framework/src/main/java/org/toop/framework/eventbus/ListenerHandler.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/bus/DefaultEventBus.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/bus/DisruptorEventBus.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/bus/EventBus.java delete mode 100644 framework/src/main/java/org/toop/framework/eventbus/events/EventWithSnowflake.java delete mode 100644 framework/src/main/java/org/toop/framework/eventbus/events/EventWithoutSnowflake.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/events/GenericEvent.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/events/ResponseToUniqueEvent.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/events/UniqueEvent.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/store/AsyncSubscriberStore.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/store/DefaultSubscriberStore.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/store/SubscriberStore.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/store/SyncSubscriberStore.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/DefaultNamedSubscriber.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/DefaultSubscriber.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/HasId.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/IdSubscriber.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/LongIdSubscriber.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/NamedSubscriber.java create mode 100644 framework/src/main/java/org/toop/framework/eventbus/subscriber/Subscriber.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/GameState.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/LongPairConsumer.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/controller/GameController.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/controller/UpdatesGameUI.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/BoardProvider.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/DeepCopyable.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayResult.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/Playable.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/PlayerProvider.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/SupportsOnlinePlay.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/AbstractThreadBehaviour.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/Controllable.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/threadBehaviour/ThreadBehaviour.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/AI.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractAI.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/AbstractPlayer.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/MoveProvider.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/NameProvider.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/player/Player.java create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/view/GUIEvents.java create mode 100644 framework/src/main/java/org/toop/framework/networking/NetworkingClientEventListener.java rename framework/src/main/java/org/toop/framework/networking/{NetworkingClient.java => clients/TournamentNetworkingClient.java} (57%) create mode 100644 framework/src/main/java/org/toop/framework/networking/exceptions/ClientNotFoundException.java create mode 100644 framework/src/main/java/org/toop/framework/networking/exceptions/CouldNotConnectException.java rename framework/src/main/java/org/toop/framework/networking/{ => exceptions}/NetworkingInitializationException.java (79%) rename framework/src/main/java/org/toop/framework/networking/{ => handlers}/NetworkingGameClientHandler.java (71%) delete mode 100644 framework/src/main/java/org/toop/framework/networking/handlers/NetworkingTicTacToeClientHandler.java create mode 100644 framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClient.java create mode 100644 framework/src/main/java/org/toop/framework/networking/interfaces/NetworkingClientManager.java create mode 100644 framework/src/main/java/org/toop/framework/networking/types/NetworkingConnector.java create mode 100644 framework/src/main/java/org/toop/framework/networking/types/ServerCommand.java create mode 100644 framework/src/main/java/org/toop/framework/networking/types/ServerMessage.java rename framework/src/main/java/org/toop/framework/{asset => resource}/ResourceLoader.java (86%) rename framework/src/main/java/org/toop/framework/{asset => resource}/ResourceManager.java (70%) rename framework/src/main/java/org/toop/framework/{asset => resource}/ResourceMeta.java (77%) create mode 100644 framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java create mode 100644 framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java create mode 100644 framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java create mode 100644 framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/BaseResource.java (84%) rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/CssAsset.java (73%) rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/FontAsset.java (91%) rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/ImageAsset.java (80%) rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/JsonAsset.java (85%) rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/LocalizationAsset.java (79%) create mode 100644 framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/SettingsAsset.java (63%) create mode 100644 framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java rename framework/src/main/java/org/toop/framework/{asset => resource}/resources/TextAsset.java (85%) create mode 100644 framework/src/main/java/org/toop/framework/resource/types/AudioResource.java rename framework/src/main/java/org/toop/framework/{asset => resource}/types/BundledResource.java (92%) rename framework/src/main/java/org/toop/framework/{asset => resource}/types/FileExtension.java (89%) rename framework/src/main/java/org/toop/framework/{asset => resource}/types/LoadableResource.java (95%) rename framework/src/main/java/org/toop/framework/{asset => resource}/types/PreloadResource.java (92%) create mode 100644 framework/src/test/java/org/toop/framework/audio/MusicManagerTest.java create mode 100644 framework/src/test/java/org/toop/framework/audio/SoundEffectManagerTest.java delete mode 100644 game/src/main/java/org/toop/game/AI.java create mode 100644 game/src/main/java/org/toop/game/BitboardGame.java delete mode 100644 game/src/main/java/org/toop/game/Game.java create mode 100644 game/src/main/java/org/toop/game/Move.java delete mode 100644 game/src/main/java/org/toop/game/TurnBasedGame.java create mode 100644 game/src/main/java/org/toop/game/gameThreads/LocalFixedRateThreadBehaviour.java create mode 100644 game/src/main/java/org/toop/game/gameThreads/LocalThreadBehaviour.java create mode 100644 game/src/main/java/org/toop/game/gameThreads/OnlineThreadBehaviour.java create mode 100644 game/src/main/java/org/toop/game/gameThreads/OnlineWithSleepThreadBehaviour.java create mode 100644 game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java create mode 100644 game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java delete mode 100644 game/src/main/java/org/toop/game/othello/Othello.java delete mode 100644 game/src/main/java/org/toop/game/othello/OthelloAI.java create mode 100644 game/src/main/java/org/toop/game/players/ArtificialPlayer.java create mode 100644 game/src/main/java/org/toop/game/players/LocalPlayer.java create mode 100644 game/src/main/java/org/toop/game/players/MiniMaxAI.java create mode 100644 game/src/main/java/org/toop/game/players/OnlinePlayer.java create mode 100644 game/src/main/java/org/toop/game/players/RandomAI.java delete mode 100644 game/src/main/java/org/toop/game/tictactoe/TicTacToe.java delete mode 100644 game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java delete mode 100644 game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java create mode 100644 processors/pom.xml create mode 100644 processors/src/main/java/org/toop/annotations/AutoResponseResult.java create mode 100644 processors/src/main/java/org/toop/annotations/TestsOnly.java create mode 100644 processors/src/main/java/org/toop/processors/AutoResponseResultProcessor.java diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 028e10f..35b9cdb 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -1,42 +1,42 @@ -name: Checks +#name: Checks -on: - push: - branches: - - 'main' - pull_request: - branches: - - 'main' +#on: +# push: +# branches: +# - 'main' +# pull_request: +# branches: +# - 'main' +# +#jobs: +# formatting-check: +# name: Follow Google Formatting Guidelines +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v5 +# with: +# fetch-depth: 0 # Fix for incremental formatting +# - uses: actions/setup-java@v5 +# with: +# java-version: '25' +# distribution: 'temurin' +# cache: maven +# - name: Run Format Check +# run: mvn spotless:check -jobs: - formatting-check: - name: Follow Google Formatting Guidelines - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - with: - fetch-depth: 0 # Fix for incremental formatting - - uses: actions/setup-java@v5 - with: - java-version: '25' - distribution: 'temurin' - cache: maven - - name: Run Format Check - run: mvn spotless:check - - tests: - name: Unittests - runs-on: ${{ matrix.os }} - needs: formatting-check - strategy: - matrix: - os: [ubuntu-latest] #windows-latest, macos-latest - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 - with: - java-version: '25' - distribution: 'temurin' - cache: maven - - name: Run Unittests - run: mvn -B test +# tests: +# name: Unittests +# runs-on: ${{ matrix.os }} +# needs: formatting-check +# strategy: +# matrix: +# os: [ubuntu-latest] #windows-latest, macos-latest +# steps: +# - uses: actions/checkout@v5 +# - uses: actions/setup-java@v5 +# with: +# java-version: '25' +# distribution: 'temurin' +# cache: maven +# - name: Run Unittests +# run: mvn -B test diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml index 46f4d3b..e6d7540 100644 --- a/.idea/dictionaries/project.xml +++ b/.idea/dictionaries/project.xml @@ -5,14 +5,17 @@ clid dcompile errorprone + espaƱol flushnl gaaf gamelist + pism playerlist tictactoe toop vmoptions xplugin + yourturn \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 895ac80..3b3a142 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,12 +1,16 @@ + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index c168b80..655cfae 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,7 +2,8 @@ \ No newline at end of file diff --git a/.idea/resourceBundles.xml b/.idea/resourceBundles.xml deleted file mode 100644 index af8f6fc..0000000 --- a/.idea/resourceBundles.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - localization - - - - - - - - - - - - - - - localization - - - \ No newline at end of file diff --git a/app/pom.xml b/app/pom.xml index eb93b3b..a4da233 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -1,8 +1,13 @@ 4.0.0 - org.toop - pism_app + + org.toop + pism + 0.1 + + + app 0.1 @@ -24,24 +29,36 @@ gson 2.10.1 - - org.toop - pism_framework - 0.1 - compile - - - org.toop - pism_game - 0.1 - compile - org.openjfx javafx-controls 25 + + + com.google.errorprone + error_prone_core + 2.42.0 + + + com.google.errorprone + error_prone_annotations + 2.42.0 + + + org.toop + framework + 0.1 + compile + + + org.toop + game + 0.1 + compile + + @@ -112,14 +129,56 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 25 - 25 - - + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + true + true + ${java.home}/bin/javac + 25 + 25 + 25 + UTF-8 + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + -Xplugin:ErrorProne \ + + + + + + + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + + + + com.google.errorprone + error_prone_core + 2.42.0 + + + + + + com.google.errorprone + error_prone_core + 2.42.0 + + + \ No newline at end of file diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java index 8456819..3b4fef3 100644 --- a/app/src/main/java/org/toop/Main.java +++ b/app/src/main/java/org/toop/Main.java @@ -1,21 +1,9 @@ package org.toop; import org.toop.app.App; -import org.toop.framework.asset.ResourceLoader; -import org.toop.framework.asset.ResourceManager; -import org.toop.framework.audio.SoundManager; -import org.toop.framework.networking.NetworkingClientManager; -import org.toop.framework.networking.NetworkingInitializationException; public final class Main { - public static void main(String[] args) { - initSystems(); + static void main(String[] args) { App.run(args); } - - private static void initSystems() throws NetworkingInitializationException { - ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets")); - new Thread(NetworkingClientManager::new).start(); - new Thread(SoundManager::new).start(); - } } diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index c4c9251..5d99acd 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -1,167 +1,279 @@ package org.toop.app; -import java.util.Stack; -import javafx.application.Application; import javafx.application.Platform; -import javafx.scene.Scene; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; -import org.toop.app.layer.Layer; -import org.toop.app.layer.layers.MainLayer; -import org.toop.app.layer.layers.QuitPopup; -import org.toop.framework.asset.ResourceManager; -import org.toop.framework.asset.resources.CssAsset; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyEvent; + +import org.toop.app.widget.Primitive; +import org.toop.app.widget.WidgetContainer; +import org.toop.app.widget.complex.LoadingWidget; +import org.toop.app.widget.display.SongDisplay; +import org.toop.app.widget.popup.EscapePopup; +import org.toop.app.widget.popup.QuitPopup; +import org.toop.app.widget.view.MainView; +import org.toop.framework.audio.*; import org.toop.framework.audio.events.AudioEvents; import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.eventbus.GlobalEventBus; +import org.toop.framework.networking.NetworkingClientEventListener; +import org.toop.framework.networking.NetworkingClientManager; +import org.toop.framework.resource.ResourceLoader; +import org.toop.framework.resource.ResourceManager; +import org.toop.framework.resource.events.AssetLoaderEvents; +import org.toop.framework.resource.resources.CssAsset; +import org.toop.framework.resource.resources.MusicAsset; +import org.toop.framework.resource.resources.SoundEffectAsset; import org.toop.local.AppContext; import org.toop.local.AppSettings; -public final class App extends Application { - private static Stage stage; - private static Scene scene; - private static StackPane root; +import javafx.application.Application; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public final class App extends Application { + private static Stage stage; + private static Scene scene; - private static Stack stack; private static int height; private static int width; - private static boolean isQuitting; + public static void run(String[] args) { + launch(args); + } - public static void run(String[] args) { - launch(args); - } + @Override + public void start(Stage stage) { + // Start loading localization + ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/localization")); + ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/style")); - @Override - public void start(Stage stage) throws Exception { - final StackPane root = new StackPane(); - final Scene scene = new Scene(root); + final StackPane root = WidgetContainer.setup(); + final Scene scene = new Scene(root); - stage.setTitle(AppContext.getString("appTitle")); - stage.setWidth(1080); - stage.setHeight(720); + stage.setOpacity(0.0); - stage.setOnCloseRequest( - event -> { - event.consume(); + stage.setTitle(AppContext.getString("app-title")); + stage.titleProperty().bind(AppContext.bindToKey("app-title")); - if (!isQuitting) { - quitPopup(); - } - }); + stage.setWidth(0); + stage.setHeight(0); - stage.setScene(scene); - stage.setResizable(false); + scene.getRoot(); - stage.show(); + stage.setMinWidth(1200); + stage.setMinHeight(800); + stage.setOnCloseRequest(event -> { + event.consume(); + quit(); + }); - App.stage = stage; - App.scene = scene; - App.root = root; + stage.setScene(scene); + stage.setResizable(true); - App.stack = new Stack<>(); + App.stage = stage; + App.scene = scene; - App.width = (int) stage.getWidth(); - App.height = (int) stage.getHeight(); + App.width = (int)stage.getWidth(); + App.height = (int)stage.getHeight(); - App.isQuitting = false; + AppSettings.applySettings(); - final AppSettings settings = new AppSettings(); - settings.applySettings(); + setKeybinds(root); - new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent(); - activate(new MainLayer()); - } + LoadingWidget loading = new LoadingWidget(Primitive.text( + "Loading...", false), 0, 0, Integer.MAX_VALUE, false, false // Just set a high default + ); - public static void activate(Layer layer) { - Platform.runLater( - () -> { - popAll(); - push(layer); - }); - } + WidgetContainer.setCurrentView(loading); - public static void push(Layer layer) { - Platform.runLater( - () -> { - root.getChildren().addLast(layer.getLayer()); - stack.push(layer); - }); - } + setOnLoadingSuccess(loading); - public static void pop() { - Platform.runLater( - () -> { - root.getChildren().removeLast(); - stack.pop(); + EventFlow loadingFlow = new EventFlow(); - isQuitting = false; - }); - } + final boolean[] hasRun = {false}; + loadingFlow + .listen(AssetLoaderEvents.LoadingProgressUpdate.class, e -> { + if (!hasRun[0]) { + hasRun[0] = true; + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + Platform.runLater(() -> stage.setOpacity(1.0)); + } - public static void popAll() { - Platform.runLater( - () -> { - final int childrenCount = root.getChildren().size(); - - for (int i = 0; i < childrenCount; i++) { - try { - root.getChildren().removeLast(); - } catch (Exception e) { - IO.println(e); + Platform.runLater(() -> { + loading.setMaxAmount(e.isLoadingAmount()); + try { + loading.setAmount(e.hasLoadedAmount()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + if (e.hasLoadedAmount() >= e.isLoadingAmount()-1) { + Platform.runLater(loading::triggerSuccess); + loadingFlow.unsubscribe("init_loading"); } - } + }); - stack.removeAllElements(); - }); + + }, false, "init_loading"); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + executor.submit( + () -> ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets") + )); + } finally { + executor.shutdown(); + } + + stage.show(); + + } + + private void setKeybinds(StackPane root) { + root.addEventHandler(KeyEvent.KEY_PRESSED,event -> { + if (event.getCode() == KeyCode.ESCAPE) { + escapePopup(); + } + }); + stage.setFullScreenExitKeyCombination( + new KeyCodeCombination( + KeyCode.F11 + ) + ); + } + + public void escapePopup() { + + if ( WidgetContainer.getCurrentView() == null + || WidgetContainer.getCurrentView() instanceof MainView) { + return; + } + + if (!Objects.requireNonNull( + WidgetContainer.find(widget -> widget instanceof QuitPopup || widget instanceof EscapePopup) + ).isEmpty()) { + WidgetContainer.removeFirst(QuitPopup.class); + WidgetContainer.removeFirst(EscapePopup.class); + return; + } + + EscapePopup escPopup = new EscapePopup(); + escPopup.show(Pos.CENTER); + } + + private void setOnLoadingSuccess(LoadingWidget loading) { + loading.setOnSuccess(() -> { + + try { + initSystems(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + AppSettings.applyMusicVolumeSettings(); + new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).postEvent(); + loading.hide(); + WidgetContainer.add(Pos.CENTER, new MainView()); + WidgetContainer.add(Pos.BOTTOM_RIGHT, new SongDisplay()); + stage.setOnCloseRequest(event -> { + event.consume(); + + if (WidgetContainer.getAllWidgets().stream().anyMatch(e -> e instanceof QuitPopup)) return; + + QuitPopup a = new QuitPopup(); + a.show(Pos.CENTER); + + }); + }); + } + + private void initSystems() throws InterruptedException { // TODO Move to better place + + final int THREAD_COUNT = 2; + CountDownLatch latch = new CountDownLatch(THREAD_COUNT); + + @SuppressWarnings("resource") + ExecutorService threads = Executors.newFixedThreadPool(THREAD_COUNT); + + try { + + threads.submit(() -> { + new NetworkingClientEventListener( + GlobalEventBus.get(), + new NetworkingClientManager(GlobalEventBus.get())); + + latch.countDown(); + }); + + threads.submit(() -> { + MusicManager musicManager = + new MusicManager<>( + GlobalEventBus.get(), + ResourceManager.getAllOfTypeAndRemoveWrapper(MusicAsset.class), + true + ); + + SoundEffectManager soundEffectManager = + new SoundEffectManager<>(ResourceManager.getAllOfType(SoundEffectAsset.class)); + + AudioVolumeManager audioVolumeManager = new AudioVolumeManager() + .registerManager(VolumeControl.MASTERVOLUME, musicManager) + .registerManager(VolumeControl.MASTERVOLUME, soundEffectManager) + .registerManager(VolumeControl.FX, soundEffectManager) + .registerManager(VolumeControl.MUSIC, musicManager); + + new AudioEventListener<>( + GlobalEventBus.get(), + musicManager, + soundEffectManager, + audioVolumeManager + ).initListeners("medium-button-click.wav"); + + latch.countDown(); + }); + + } finally { + latch.await(); + threads.shutdown(); + } } - public static void quitPopup() { - Platform.runLater( - () -> { - push(new QuitPopup()); - isQuitting = true; - }); - } + public static void quit() { + stage.close(); + System.exit(0); // TODO: This is like dropping a nuke + } - public static void quit() { - stage.close(); - } + public static void setFullscreen(boolean fullscreen) { + stage.setFullScreen(fullscreen); - public static void reloadAll() { - stage.setTitle(AppContext.getString("appTitle")); + width = (int)stage.getWidth(); + height = (int)stage.getHeight(); + } - for (final Layer layer : stack) { - layer.reload(); - } - } + public static void setStyle(String theme, String layoutSize) { + scene.getStylesheets().clear(); - public static void setFullscreen(boolean fullscreen) { - stage.setFullScreen(fullscreen); + scene.getStylesheets().add(ResourceManager.get("general.css").getUrl()); + scene.getStylesheets().add(ResourceManager.get(theme + ".css").getUrl()); + scene.getStylesheets().add(ResourceManager.get(layoutSize + ".css").getUrl()); + } - width = (int) stage.getWidth(); - height = (int) stage.getHeight(); + public static int getWidth() { + return width; + } - reloadAll(); - } - - public static void setStyle(String theme, String layoutSize) { - final int stylesCount = scene.getStylesheets().size(); - - for (int i = 0; i < stylesCount; i++) { - scene.getStylesheets().removeLast(); - } - - scene.getStylesheets().add(ResourceManager.get(theme + ".css").getUrl()); - scene.getStylesheets().add(ResourceManager.get(layoutSize + ".css").getUrl()); - - reloadAll(); - } - - public static int getWidth() { - return width; - } - - public static int getHeight() { - return height; - } -} + public static int getHeight() { + return height; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/GameInformation.java b/app/src/main/java/org/toop/app/GameInformation.java index 2a3e56f..08e3f46 100644 --- a/app/src/main/java/org/toop/app/GameInformation.java +++ b/app/src/main/java/org/toop/app/GameInformation.java @@ -1,10 +1,54 @@ package org.toop.app; -public record GameInformation( - String[] playerName, - boolean[] isPlayerHuman, - int[] computerDifficulty, - int[] computerThinkTime, - boolean isConnectionLocal, - String serverIP, - String serverPort) {} +public class GameInformation { + public enum Type { + TICTACTOE(2, 5), + REVERSI(2, 10); + + private final int playerCount; + private final int maxDepth; + + Type(int playerCount, int maxDepth) { + this.playerCount = playerCount; + this.maxDepth = maxDepth; + } + + public int getPlayerCount() { + return playerCount; + } + + public int getMaxDepth() { + return maxDepth; + } + + public String getTypeToString() { + String name = this.name(); + return switch (name) { + case "TICTACTOE" -> "TicTacToe"; + case "REVERSI" -> "Reversi"; + case "CONNECT4" -> "Connect4"; + case "BATTLESHIP" -> "Battleship"; + default -> name; + }; + } + } + + public static class Player { + public String name = ""; + public boolean isHuman = true; + public int computerDifficulty = 1; + public int computerThinkTime = 1; + } + + public final Type type; + public final Player[] players; + + public GameInformation(Type type) { + this.type = type; + players = new Player[type.getPlayerCount()]; + + for (int i = 0; i < players.length; i++) { + players[i] = new Player(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java new file mode 100644 index 0000000..ef691b4 --- /dev/null +++ b/app/src/main/java/org/toop/app/Server.java @@ -0,0 +1,354 @@ +package org.toop.app; + +import javafx.application.Platform; +import javafx.geometry.Pos; +import org.toop.app.gameControllers.*; +import org.toop.app.widget.Primitive; +import org.toop.app.widget.WidgetContainer; +import org.toop.app.widget.complex.LoadingWidget; +import org.toop.app.widget.popup.ChallengePopup; +import org.toop.app.widget.popup.ErrorPopup; +import org.toop.app.widget.popup.SendChallengePopup; +import org.toop.app.widget.view.ServerView; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.gameFramework.controller.GameController; +import org.toop.framework.eventbus.GlobalEventBus; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.framework.networking.clients.TournamentNetworkingClient; +import org.toop.framework.networking.events.NetworkEvents; +import org.toop.framework.networking.types.NetworkingConnector; +import org.toop.game.games.reversi.BitboardReversi; +import org.toop.game.games.tictactoe.BitboardTicTacToe; +import org.toop.game.players.ArtificialPlayer; +import org.toop.game.players.OnlinePlayer; +import org.toop.game.players.RandomAI; +import org.toop.local.AppContext; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public final class Server { + // TODO: Keep track of listeners. Remove them on Server connection close so reference is deleted. + private String user = ""; + private long clientId = -1; + + private final List onlinePlayers = new CopyOnWriteArrayList<>(); + private final List gameList = new CopyOnWriteArrayList<>(); + + private ServerView primary; + private boolean isPolling = true; + + private GameController gameController; + + private final AtomicBoolean isSingleGame = new AtomicBoolean(false); + + private ScheduledExecutorService scheduler; + + private EventFlow connectFlow; + + public static GameInformation.Type gameToType(String game) { + if (game.equalsIgnoreCase("tic-tac-toe")) { + return GameInformation.Type.TICTACTOE; + } else if (game.equalsIgnoreCase("reversi")) { + return GameInformation.Type.REVERSI; + } + + return null; + } + + + // Server has to deal with ALL network related listen events. This "server" can then interact with the manager to make stuff happen. + // This prevents data races where events get sent to the game manager but the manager isn't ready yet. + public Server(String ip, String port, String user) { + if (ip.split("\\.").length < 4) { + new ErrorPopup("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address")); + return; + } + + int parsedPort; + + try { + parsedPort = Integer.parseInt(port); + } catch (NumberFormatException _) { + new ErrorPopup("\"" + port + "\" " + AppContext.getString("is-not-a-valid-port")); + return; + } + + if (user.isEmpty() || user.matches("^[0-9].*")) { + new ErrorPopup(AppContext.getString("invalid-username")); + return; + } + + final int reconnectAttempts = 10; + + LoadingWidget loading = new LoadingWidget( + Primitive.text("connecting"), 0, 0, reconnectAttempts, true, true + ); + + WidgetContainer.getCurrentView().transitionNextCustom(loading, "disconnect", this::disconnect); + + var a = new EventFlow() + .addPostEvent(NetworkEvents.StartClient.class, + new TournamentNetworkingClient(GlobalEventBus.get()), + new NetworkingConnector(ip, parsedPort, reconnectAttempts, 1, TimeUnit.SECONDS) + ); + + loading.setOnFailure(() -> { + if (WidgetContainer.getCurrentView() == loading) WidgetContainer.getCurrentView().transitionPrevious(); + a.unsubscribeAll(); + WidgetContainer.add( + Pos.CENTER, + new ErrorPopup(AppContext.getString("connecting-failed") + " " + ip + ":" + port) + ); + }); + + a.onResponse(NetworkEvents.CreatedIdForClient.class, e -> clientId = e.clientId(), true); + + a.onResponse(NetworkEvents.StartClientResponse.class, e -> { + if (!e.successful()) { + return; + } + + primary = new ServerView(user, this::sendChallenge); + WidgetContainer.getCurrentView().transitionNextCustom(primary, "disconnect", this::disconnect); + + a.unsubscribe("connecting"); + a.unsubscribe("startclient"); + + this.user = user; + + new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, user)).postEvent(); + + startPopulateScheduler(); + populateGameList(); + + primary.removeViewFromPreviousChain(loading); + + }, false, "startclient") + .listen( + NetworkEvents.ConnectTry.class, + e -> { + if (clientId != e.clientId()) return; + Platform.runLater( + () -> { + try { + loading.setAmount(e.amount()); + if (e.amount() >= loading.getMaxAmount()) { + loading.triggerFailure(); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + ); + }, + false, "connecting" + ) + .postEvent(); + + a.listen(NetworkEvents.ChallengeResponse.class, this::handleReceivedChallenge, false, "challenge") + .listen(NetworkEvents.GameMatchResponse.class, this::handleMatchResponse, false, "match-response") + .listen(NetworkEvents.GameResultResponse.class, this::handleGameResult, false, "game-result") + .listen(NetworkEvents.GameMoveResponse.class, this::handleReceivedMove, false, "game-move") + .listen(NetworkEvents.YourTurnResponse.class, this::handleYourTurn, false, "your-turn"); + + connectFlow = a; + } + + private void sendChallenge(String opponent) { + if (!isPolling) return; + + var a = new SendChallengePopup(this, opponent, (playerInformation, gameType) -> { + new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)).postEvent(); + isSingleGame.set(true); + }); + + a.show(Pos.CENTER); + } + + private void handleMatchResponse(NetworkEvents.GameMatchResponse response) { + // TODO: Redo all of this mess + if (gameController != null) { + gameController.stop(); + } + + gameController = null; + + //if (!isPolling) return; + + String gameType = extractQuotedValue(response.gameType()); + if (response.clientId() == clientId) { + isPolling = false; + onlinePlayers.clear(); + + final GameInformation.Type type = gameToType(gameType); + if (type == null) { + new ErrorPopup("Unsupported game type: " + gameType); + return; + } + + final int myTurn = response.playerToMove().equalsIgnoreCase(response.opponent()) ? 1 : 0; + + final GameInformation information = new GameInformation(type); + //information.players[0] = playerInformation; + information.players[0].name = user; + information.players[0].isHuman = false; + information.players[0].computerDifficulty = 5; + information.players[0].computerThinkTime = 1; + information.players[1].name = response.opponent(); + + /*switch (type){ + case TICTACTOE ->{ + players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(9), user); + } + case REVERSI ->{ + players[myTurn] = new ArtificialPlayer<>(new ReversiAIR(), user); + } + }*/ + + + + switch (type) { + case TICTACTOE ->{ + Player[] players = new Player[2]; + players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); + players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); + gameController = new TicTacToeBitController(players); + } + case REVERSI -> { + Player[] players = new Player[2]; + players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); + players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); + gameController = new ReversiBitController(players);} + default -> new ErrorPopup("Unsupported game type."); + + } + + if (gameController != null){ + gameController.start(); + } + } + } + + private void handleYourTurn(NetworkEvents.YourTurnResponse response) { + if (gameController == null) { + return; + } + gameController.onYourTurn(response); + + } + + private void handleGameResult(NetworkEvents.GameResultResponse response) { + if (gameController == null) { + return; + } + gameController.gameFinished(response); + } + + private void handleReceivedMove(NetworkEvents.GameMoveResponse response) { + if (gameController == null) { + return; + } + gameController.onMoveReceived(response); + } + + private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) { + if (!isPolling) return; + + String challengerName = extractQuotedValue(response.challengerName()); + String gameType = extractQuotedValue(response.gameType()); + final String finalGameType = gameType; + var a = new ChallengePopup(challengerName, gameType, (playerInformation) -> { + final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); + new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); + isSingleGame.set(true); + }); + + a.show(Pos.CENTER); + } + + private void sendMessage(String message) { + new EventFlow().addPostEvent(new NetworkEvents.SendMessage(clientId, message)).postEvent(); + } + + private void disconnect() { + new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent(); + isPolling = false; + stopScheduler(); + connectFlow.unsubscribeAll(); + + WidgetContainer.getCurrentView().transitionPrevious(); + } + + private void forfeitGame() { + new EventFlow().addPostEvent(new NetworkEvents.SendForfeit(clientId)).postEvent(); + } + + private void exitGame() { + forfeitGame(); + startPopulateScheduler(); + } + + private void gameOver(){ + startPopulateScheduler(); + } + + private void startPopulateScheduler() { + isPolling = true; + isSingleGame.set(false); + stopScheduler(); + + new EventFlow() + .listen(NetworkEvents.PlayerlistResponse.class, e -> { + if (e.clientId() == clientId) { + onlinePlayers.clear(); + onlinePlayers.addAll(List.of(e.playerlist())); + onlinePlayers.removeIf(name -> name.equalsIgnoreCase(user)); + primary.update(onlinePlayers); + } + }, false); + + scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduler.scheduleAtFixedRate(() -> { + if (isPolling) { + new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(clientId)).postEvent(); + } else { + stopScheduler(); + } + }, 0, 1, TimeUnit.SECONDS); + } + + private void stopScheduler() { + if (scheduler != null && !scheduler.isShutdown()) { + scheduler.shutdownNow(); + } + } + + private void gamesListFromServerHandler(NetworkEvents.GamelistResponse event) { + gameList.clear(); + gameList.addAll(List.of(event.gamelist())); + } + + public void populateGameList() { + new EventFlow().addPostEvent(new NetworkEvents.SendGetGamelist(clientId)) + .listen(NetworkEvents.GamelistResponse.class, this::gamesListFromServerHandler, true) + .postEvent(); + } + + public List getGameList() { + return gameList; + } + + private String extractQuotedValue(String s) { + int first = s.indexOf('"'); + int last = s.lastIndexOf('"'); + if (first >= 0 && last > first) { + return s.substring(first + 1, last); + } + return s; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java new file mode 100644 index 0000000..85c6f7d --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java @@ -0,0 +1,247 @@ +package org.toop.app.canvas; + +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.input.MouseButton; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.util.Duration; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.view.GUIEvents; + +import java.util.function.Consumer; + +public abstract class BitGameCanvas> implements GameCanvas { + protected record Cell(float x, float y, float width, float height) { + public boolean isInside(double x, double y) { + return x >= this.x && x <= this.x + width && + y >= this.y && y <= this.y + height; + } + } + + protected final Canvas canvas; + protected final GraphicsContext graphics; + + protected final Color color; + protected final Color backgroundColor; + + protected final int width; + protected final int height; + + protected final int rowSize; + protected final int columnSize; + + protected final int gapSize; + protected final boolean edges; + + protected final Cell[] cells; + + private Consumer onCellCLicked; + + public void setOnCellClicked(Consumer onClick) { + this.onCellCLicked = onClick; + } + + protected BitGameCanvas(Color color, Color backgroundColor, int width, int height, int rowSize, int columnSize, int gapSize, boolean edges) { + canvas = new Canvas(width, height); + graphics = canvas.getGraphicsContext2D(); + + this.onCellCLicked = (c) -> new EventFlow().addPostEvent(GUIEvents.PlayerAttemptedMove.class, c).postEvent(); + + this.color = color; + this.backgroundColor = backgroundColor; + + this.width = width; + this.height = height; + + this.rowSize = rowSize; + this.columnSize = columnSize; + + this.gapSize = gapSize; + this.edges = edges; + + cells = new Cell[rowSize * columnSize]; + + final float cellWidth = ((float) width - gapSize * rowSize - gapSize) / rowSize; + final float cellHeight = ((float) height - gapSize * columnSize - gapSize) / columnSize; + + for (int y = 0; y < columnSize; y++) { + final float startY = y * cellHeight + y * gapSize + gapSize; + + for (int x = 0; x < rowSize; x++) { + final float startX = x * cellWidth + x * gapSize + gapSize; + cells[x + y * rowSize] = new Cell(startX, startY, cellWidth, cellHeight); + } + } + + canvas.setOnMouseClicked(event -> { + if (event.getButton() != MouseButton.PRIMARY) { + return; + } + + final int column = (int) ((event.getX() / this.width) * rowSize); + final int row = (int) ((event.getY() / this.height) * columnSize); + + final Cell cell = cells[column + row * rowSize]; + + if (cell.isInside(event.getX(), event.getY())) { + event.consume(); + this.onCellCLicked.accept(1L << (column + row * rowSize)); + } + }); + + + + + render(); + } + + public void loopOverBoard(long bb, Consumer onCell){ + while (bb != 0) { + int idx = Long.numberOfTrailingZeros(bb); // index of least-significant 1-bit + onCell.accept(idx); + + bb &= bb - 1; // clear LSB 1-bit + } + } + + private void render() { + graphics.setFill(backgroundColor); + graphics.fillRect(0, 0, width, height); + + graphics.setFill(color); + + for (int x = 0; x < rowSize - 1; x++) { + final float start = cells[x].x + cells[x].width; + graphics.fillRect(start, gapSize, gapSize, height - gapSize * 2); + } + + for (int y = 0; y < columnSize - 1; y++) { + final float start = cells[y * rowSize].y + cells[y * rowSize].height; + graphics.fillRect(gapSize, start, width - gapSize * 2, gapSize); + } + + if (edges) { + graphics.fillRect(0, 0, width, gapSize); + graphics.fillRect(0, 0, gapSize, height); + + graphics.fillRect(width - gapSize, 0, gapSize, height); + graphics.fillRect(0, height - gapSize, width, gapSize); + } + } + + public void fill(Color color, int cell) { + final float x = cells[cell].x(); + final float y = cells[cell].y(); + + final float width = cells[cell].width(); + final float height = cells[cell].height(); + + graphics.setFill(color); + graphics.fillRect(x, y, width, height); + } + + public void clear(int cell) { + final float x = cells[cell].x(); + final float y = cells[cell].y(); + + final float width = cells[cell].width(); + final float height = cells[cell].height(); + + graphics.clearRect(x, y, width, height); + + graphics.setFill(backgroundColor); + graphics.fillRect(x, y, width, height); + } + + public void clearAll() { + for (int i = 0; i < cells.length; i++) { + clear(i); + } + } + + public void drawPlayerMove(int player, int move) { + final float x = cells[move].x() + gapSize; + final float y = cells[move].y() + gapSize; + + final float width = cells[move].width() - gapSize * 2; + final float height = cells[move].height() - gapSize * 2; + + graphics.setFill(color); + graphics.setFont(Font.font("Arial", 40)); // TODO different font and size + graphics.fillText(String.valueOf(player), x + width, y + height); + } + + public void drawDot(Color color, int cell) { + final float x = cells[cell].x() + gapSize; + final float y = cells[cell].y() + gapSize; + + final float width = cells[cell].width() - gapSize * 2; + final float height = cells[cell].height() - gapSize * 2; + + graphics.setFill(color); + graphics.fillOval(x, y, width, height); + } + + public void drawInnerDot(Color color, int cell, boolean slightlyBigger) { + final float x = cells[cell].x() + gapSize; + final float y = cells[cell].y() + gapSize; + + float multiplier = slightlyBigger?1.4f:1.5f; + + final float width = (cells[cell].width() - gapSize * 2)/multiplier; + final float height = (cells[cell].height() - gapSize * 2)/multiplier; + + float offset = slightlyBigger?5f:4f; + + graphics.setFill(color); + graphics.fillOval(x + width/offset, y + height/offset, width, height); + } + + private void drawDotScaled(Color color, int cell, double scale) { + final float cx = cells[cell].x() + gapSize; + final float cy = cells[cell].y() + gapSize; + + final float fullWidth = cells[cell].width() - gapSize * 2; + final float height = cells[cell].height() - gapSize * 2; + + final float scaledWidth = (float)(fullWidth * scale); + final float offsetX = (fullWidth - scaledWidth) / 2; + + graphics.setFill(color); + graphics.fillOval(cx + offsetX, cy, scaledWidth, height); + } + + public Timeline flipDot(Color fromColor, Color toColor, int cell) { + final int steps = 60; + final long duration = 250; + final double interval = duration / (double) steps; + + final Timeline timeline = new Timeline(); + + for (int i = 0; i <= steps; i++) { + final double t = i / (double) steps; + final KeyFrame keyFrame = new KeyFrame(Duration.millis(i * interval), + _ -> { + clear(cell); + + final double scale = t <= 0.5 ? 1 - 2 * t : 2 * t - 1; + final Color currentColor = t < 0.5 ? fromColor : toColor; + + drawDotScaled(currentColor, cell, scale); + } + ); + + timeline.getKeyFrames().add(keyFrame); + } + + return timeline; + } + + public Canvas getCanvas() { + return canvas; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/canvas/DrawPlayerHover.java b/app/src/main/java/org/toop/app/canvas/DrawPlayerHover.java new file mode 100644 index 0000000..abf9ac1 --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/DrawPlayerHover.java @@ -0,0 +1,7 @@ +package org.toop.app.canvas; + +import org.toop.framework.gameFramework.model.game.TurnBasedGame; + +public interface DrawPlayerHover { + void drawPlayerHover(int player, int move, TurnBasedGame game); +} diff --git a/app/src/main/java/org/toop/app/canvas/DrawPlayerMove.java b/app/src/main/java/org/toop/app/canvas/DrawPlayerMove.java new file mode 100644 index 0000000..fca2b46 --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/DrawPlayerMove.java @@ -0,0 +1,5 @@ +package org.toop.app.canvas; + +public interface DrawPlayerMove { + void drawPlayerMove(int player, int move); +} diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java index 5ed5775..d1361c5 100644 --- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -1,130 +1,8 @@ package org.toop.app.canvas; -import java.util.function.Consumer; import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.input.MouseButton; -import javafx.scene.paint.Color; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public abstract class GameCanvas { - protected record Cell(float x, float y, float width, float height) {} - - protected final Canvas canvas; - protected final GraphicsContext graphics; - - protected final Color color; - - protected int width; - protected int height; - - protected final int rows; - protected final int columns; - - protected final int gapSize; - protected final boolean edges; - - protected final Cell[] cells; - - protected GameCanvas( - Color color, - int width, - int height, - int rows, - int columns, - int gapSize, - boolean edges, - Consumer onCellClicked) { - canvas = new Canvas(width, height); - graphics = canvas.getGraphicsContext2D(); - - this.color = color; - - this.width = width; - this.height = height; - - this.rows = rows; - this.columns = columns; - - this.gapSize = gapSize; - this.edges = edges; - - cells = new Cell[rows * columns]; - - final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows; - final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns; - - for (int y = 0; y < columns; y++) { - final float startY = y * cellHeight + y * gapSize; - - for (int x = 0; x < rows; x++) { - final float startX = x * cellWidth + x * gapSize; - cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight); - } - } - - canvas.setOnMouseClicked( - event -> { - if (event.getButton() != MouseButton.PRIMARY) { - return; - } - - final int column = (int) ((event.getX() / width) * rows); - final int row = (int) ((event.getY() / height) * columns); - - event.consume(); - onCellClicked.accept(row * rows + column); - }); - - render(); - } - - public void clear() { - graphics.clearRect(0, 0, width, height); - } - - public void render() { - graphics.setFill(color); - - for (int x = 1; x < rows; x++) { - graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height); - } - - for (int y = 1; y < columns; y++) { - graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize); - } - - if (edges) { - graphics.fillRect(-gapSize, 0, gapSize, height); - graphics.fillRect(0, -gapSize, width, gapSize); - - graphics.fillRect(width - gapSize, 0, gapSize, height); - graphics.fillRect(0, height - gapSize, width, gapSize); - } - } - - public void draw(Color color, int cell) { - final float x = cells[cell].x() + gapSize; - final float y = cells[cell].y() + gapSize; - - final float width = cells[cell].width() - gapSize * 2; - final float height = cells[cell].height() - gapSize * 2; - - graphics.setFill(color); - graphics.fillRect(x, y, width, height); - } - - public void resize(int width, int height) { - canvas.setWidth(width); - canvas.setHeight(height); - - this.width = width; - this.height = height; - - clear(); - render(); - } - - public Canvas getCanvas() { - return canvas; - } +public interface GameCanvas> extends GameDrawer{ + Canvas getCanvas(); } diff --git a/app/src/main/java/org/toop/app/canvas/GameDrawer.java b/app/src/main/java/org/toop/app/canvas/GameDrawer.java new file mode 100644 index 0000000..261334c --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/GameDrawer.java @@ -0,0 +1,7 @@ +package org.toop.app.canvas; + +import org.toop.framework.gameFramework.model.game.TurnBasedGame; + +public interface GameDrawer> { + void redraw(T gameCopy); +} diff --git a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java new file mode 100644 index 0000000..7c2bde0 --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java @@ -0,0 +1,42 @@ +package org.toop.app.canvas; + +import javafx.scene.paint.Color; +import org.toop.app.App; +import org.toop.game.games.reversi.BitboardReversi; + +import java.util.Arrays; +import java.util.function.Consumer; + +public class ReversiBitCanvas extends BitGameCanvas { + public ReversiBitCanvas() { + super(Color.GRAY, new Color(0f, 0.4f, 0.2f, 1f), (App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3, 8, 8, 5, true); + canvas.setOnMouseMoved(event -> { + double mouseX = event.getX(); + double mouseY = event.getY(); + int cellId = -1; + + BitGameCanvas.Cell hovered = null; + for (BitGameCanvas.Cell cell : cells) { + if (cell.isInside(mouseX, mouseY)) { + hovered = cell; + cellId = turnCoordsIntoCellId(mouseX, mouseY); + break; + } + } + }); + } + + private int turnCoordsIntoCellId(double x, double y) { + final int column = (int) ((x / this.width) * rowSize); + final int row = (int) ((y / this.height) * columnSize); + return column + row * rowSize; + } + + @Override + public void redraw(BitboardReversi gameCopy) { + clearAll(); + long[] board = gameCopy.getBoard(); + loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, i)); + loopOverBoard(board[1], (i) -> drawDot(Color.BLACK, i)); + } +} diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java similarity index 55% rename from app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java rename to app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java index 1838335..443adbd 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java @@ -1,13 +1,39 @@ package org.toop.app.canvas; -import java.util.function.Consumer; import javafx.scene.paint.Color; +import org.toop.app.App; +import org.toop.game.games.tictactoe.BitboardTicTacToe; -public class TicTacToeCanvas extends GameCanvas { - public TicTacToeCanvas(Color color, int width, int height, Consumer onCellClicked) { - super(color, width, height, 3, 3, 10, false, onCellClicked); +import java.util.Arrays; +import java.util.function.Consumer; + +public class TicTacToeBitCanvas extends BitGameCanvas{ + public TicTacToeBitCanvas() { + super( + Color.GRAY, + Color.TRANSPARENT, + (App.getHeight() / 4) * 3, + (App.getHeight() / 4) * 3, + 3, + 3, + 30, + false + ); } + @Override + public void redraw(BitboardTicTacToe gameCopy) { + clearAll(); + drawMoves(gameCopy.getBoard()); + } + + private void drawMoves(long[] gameBoard){ + loopOverBoard(gameBoard[0], (i) -> drawX(Color.RED, i)); + loopOverBoard(gameBoard[1], (i) -> drawO(Color.BLUE, i)); + + } + + public void drawX(Color color, int cell) { graphics.setStroke(color); graphics.setLineWidth(gapSize); diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java new file mode 100644 index 0000000..2c3ad49 --- /dev/null +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -0,0 +1,140 @@ +package org.toop.app.gameControllers; + +import javafx.application.Platform; +import javafx.geometry.Pos; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.toop.app.canvas.GameCanvas; +import org.toop.app.widget.WidgetContainer; +import org.toop.app.widget.view.GameView; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.eventbus.GlobalEventBus; +import org.toop.framework.gameFramework.controller.GameController; +import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.framework.gameFramework.view.GUIEvents; +import org.toop.framework.networking.events.NetworkEvents; +import org.toop.game.players.LocalPlayer; + +public class GenericGameController> implements GameController { + protected final EventFlow eventFlow = new EventFlow(); + + // Logger for logging + protected final Logger logger = LogManager.getLogger(this.getClass()); + + // Reference to gameView view + protected final GameView gameView; + + // Reference to game canvas + protected final GameCanvas canvas; + + protected final TurnBasedGame game; // Reference to game instance + private final ThreadBehaviour gameThreadBehaviour; + + // TODO: Change gameType to automatically happen with either dependency injection or something else. + public GenericGameController(GameCanvas canvas, T game, ThreadBehaviour gameThreadBehaviour, String gameType) { + logger.info("Creating: " + this.getClass()); + + this.canvas = canvas; + this.game = game; + this.gameThreadBehaviour = gameThreadBehaviour; + + // Tell thread how to send moves + this.gameThreadBehaviour.setOnSendMove((id, m) -> GlobalEventBus.get().post(new NetworkEvents.SendMove(id, (short)translateMove(m)))); + + // Tell thread how to update UI + this.gameThreadBehaviour.setOnUpdateUI(() -> Platform.runLater(this::updateUI)); + + // Change scene to game view + gameView = new GameView(null, null, null, gameType); + gameView.add(Pos.CENTER, canvas.getCanvas()); + WidgetContainer.getCurrentView().transitionNext(gameView, true); + + // Listen to updates + eventFlow + .listen(GUIEvents.GameEnded.class, this::onGameFinish, false) + .listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());}}, false); + } + + public void start(){ + logger.info("Starting GameManager"); + updateUI(); + gameThreadBehaviour.start(); + } + + public void stop(){ + logger.info("Stopping GameManager"); + removeListeners(); + gameThreadBehaviour.stop(); + } + + public Player getCurrentPlayer(){ + return game.getPlayer(getCurrentPlayerIndex()); + } + + public int getCurrentPlayerIndex(){ + return game.getCurrentTurn(); + } + + protected long translateMove(int move){ + return 1L << move; + } + + protected int translateMove(long move){ + return Long.numberOfTrailingZeros(move); + } + + private void removeListeners(){ + eventFlow.unsubscribeAll(); + } + + private void onGameFinish(GUIEvents.GameEnded event){ + logger.info("Game Finished"); + String name = event.winner() == -1 ? null : getPlayer(event.winner()).getName(); + gameView.gameOver(event.winOrTie(), name); + stop(); + } + + public Player getPlayer(int player){ + if (player < 0 || player >= 2){ // TODO: Make game turn player count + logger.error("Invalid player index"); + throw new IllegalArgumentException("player out of range"); + } + return game.getPlayer(player); + } + + private boolean isOnline(){ + return this.gameThreadBehaviour instanceof SupportsOnlinePlay; + } + + public void onYourTurn(NetworkEvents.YourTurnResponse event){ + if (isOnline()){ + ((SupportsOnlinePlay) this.gameThreadBehaviour).onYourTurn(event.clientId()); + } + } + + public void onMoveReceived(NetworkEvents.GameMoveResponse event){ + if (isOnline()){ + ((SupportsOnlinePlay) this.gameThreadBehaviour).onMoveReceived( + translateMove(Integer.parseInt(event.move()))); + } + } + + public void gameFinished(NetworkEvents.GameResultResponse event){ + if (isOnline()){ + ((SupportsOnlinePlay) this.gameThreadBehaviour).gameFinished(event.condition()); + } + } + + @Override + public void sendMove(long clientId, long move) { + new EventFlow().addPostEvent(NetworkEvents.SendMove.class, clientId, (short) Long.numberOfTrailingZeros(move)).asyncPostEvent(); + } + + @Override + public void updateUI() { + canvas.redraw(game.deepCopy()); + } +} diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java new file mode 100644 index 0000000..40784b0 --- /dev/null +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java @@ -0,0 +1,22 @@ +package org.toop.app.gameControllers; + +import org.toop.app.canvas.ReversiBitCanvas; +import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.game.gameThreads.LocalThreadBehaviour; +import org.toop.game.gameThreads.OnlineThreadBehaviour; +import org.toop.game.games.reversi.BitboardReversi; +import org.toop.game.players.OnlinePlayer; + +public class ReversiBitController extends GenericGameController { + public ReversiBitController(Player[] players) { + BitboardReversi game = new BitboardReversi(players); + ThreadBehaviour thread = new LocalThreadBehaviour<>(game); + for (Player player : players) { + if (player instanceof OnlinePlayer){ + thread = new OnlineThreadBehaviour<>(game); + } + } + super(new ReversiBitCanvas(), game, thread, "Reversi"); + } +} diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java new file mode 100644 index 0000000..6307894 --- /dev/null +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java @@ -0,0 +1,23 @@ +package org.toop.app.gameControllers; + +import org.toop.app.canvas.TicTacToeBitCanvas; +import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; +import org.toop.framework.gameFramework.model.player.Player; +import org.toop.game.gameThreads.LocalFixedRateThreadBehaviour; +import org.toop.game.gameThreads.LocalThreadBehaviour; +import org.toop.game.gameThreads.OnlineThreadBehaviour; +import org.toop.game.games.tictactoe.BitboardTicTacToe; +import org.toop.game.players.OnlinePlayer; + +public class TicTacToeBitController extends GenericGameController { + public TicTacToeBitController(Player[] players) { + BitboardTicTacToe game = new BitboardTicTacToe(players); + ThreadBehaviour thread = new LocalThreadBehaviour<>(game); + for (Player player : players) { + if (player instanceof OnlinePlayer){ + thread = new OnlineThreadBehaviour<>(game); + } + } + super(new TicTacToeBitCanvas(), game, thread , "TicTacToe"); + } +} diff --git a/app/src/main/java/org/toop/app/layer/Container.java b/app/src/main/java/org/toop/app/layer/Container.java deleted file mode 100644 index 89e6436..0000000 --- a/app/src/main/java/org/toop/app/layer/Container.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.toop.app.layer; - -import javafx.scene.Node; -import javafx.scene.layout.Region; - -public abstract class Container { - public abstract Region getContainer(); - - public abstract void addNodes(Node... nodes); - - public abstract void addContainer(Container container, boolean fill); -} diff --git a/app/src/main/java/org/toop/app/layer/Layer.java b/app/src/main/java/org/toop/app/layer/Layer.java deleted file mode 100644 index d357200..0000000 --- a/app/src/main/java/org/toop/app/layer/Layer.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.toop.app.layer; - -import javafx.geometry.Pos; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import org.toop.app.App; -import org.toop.app.canvas.GameCanvas; - -public abstract class Layer { - protected StackPane layer; - protected Region background; - - protected Layer(String... backgroundStyles) { - layer = new StackPane(); - - background = new Region(); - background.getStyleClass().addAll(backgroundStyles); - background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE); - - layer.getChildren().addLast(background); - } - - protected void addContainer( - Container container, - Pos position, - int xOffset, - int yOffset, - int widthPercent, - int heightPercent) { - StackPane.setAlignment(container.getContainer(), position); - - final double widthUnit = App.getWidth() / 100.0; - final double heightUnit = App.getHeight() / 100.0; - - if (widthPercent > 0) { - container.getContainer().setMaxWidth(widthPercent * widthUnit); - } else { - container.getContainer().setMaxWidth(Region.USE_PREF_SIZE); - } - - if (heightPercent > 0) { - container.getContainer().setMaxHeight(heightPercent * heightUnit); - } else { - container.getContainer().setMaxHeight(Region.USE_PREF_SIZE); - } - - container.getContainer().setTranslateX(xOffset * widthUnit); - container.getContainer().setTranslateY(yOffset * heightUnit); - - layer.getChildren().addLast(container.getContainer()); - } - - protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) { - StackPane.setAlignment(canvas.getCanvas(), position); - - final double widthUnit = App.getWidth() / 100.0; - final double heightUnit = App.getHeight() / 100.0; - - canvas.getCanvas().setTranslateX(xOffset * widthUnit); - canvas.getCanvas().setTranslateY(yOffset * heightUnit); - - layer.getChildren().addLast(canvas.getCanvas()); - } - - protected void pop() { - if (layer.getChildren().size() <= 1) { - return; - } - - layer.getChildren().removeLast(); - } - - protected void popAll() { - final int containers = layer.getChildren().size(); - - for (int i = 1; i < containers; i++) { - layer.getChildren().removeLast(); - } - } - - public StackPane getLayer() { - return layer; - } - - public abstract void reload(); -} diff --git a/app/src/main/java/org/toop/app/layer/NodeBuilder.java b/app/src/main/java/org/toop/app/layer/NodeBuilder.java deleted file mode 100644 index b55a70d..0000000 --- a/app/src/main/java/org/toop/app/layer/NodeBuilder.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.toop.app.layer; - -import java.util.function.Consumer; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.geometry.Orientation; -import javafx.scene.Node; -import javafx.scene.control.*; -import javafx.scene.text.Text; -import org.toop.framework.audio.events.AudioEvents; -import org.toop.framework.eventbus.EventFlow; - -public final class NodeBuilder { - public static void addCss(Node node, String... cssClasses) { - node.getStyleClass().addAll(cssClasses); - } - - public static void setCss(Node node, String... cssClasses) { - node.getStyleClass().removeAll(); - node.getStyleClass().addAll(cssClasses); - } - - public static Text header(String x) { - final Text element = new Text(x); - setCss(element, "text-primary", "text-header"); - - return element; - } - - public static Text text(String x) { - final Text element = new Text(x); - setCss(element, "text-secondary", "text-normal"); - - return element; - } - - public static Label button(String x, Runnable runnable) { - final Label element = new Label(x); - setCss(element, "button", "text-normal"); - - element.setOnMouseClicked( - _ -> { - new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent(); - runnable.run(); - }); - - return element; - } - - public static Label toggle(String x1, String x2, boolean toggled, Consumer consumer) { - final Label element = new Label(toggled ? x2 : x1); - setCss(element, "toggle", "text-normal"); - - final BooleanProperty checked = new SimpleBooleanProperty(toggled); - - element.setOnMouseClicked( - _ -> { - new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent(); - checked.set(!checked.get()); - - if (checked.get()) { - element.setText(x1); - } else { - element.setText(x2); - } - - consumer.accept(checked.get()); - }); - - return element; - } - - public static Slider slider(int max, int initial, Consumer consumer) { - final Slider element = new Slider(0, max, initial); - setCss(element, "bg-slider-track"); - - element.setMinorTickCount(0); - element.setMajorTickUnit(1); - element.setBlockIncrement(1); - - element.setSnapToTicks(true); - element.setShowTickLabels(true); - - element.setOnMouseClicked( - _ -> { - new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent(); - }); - - element.valueProperty() - .addListener( - (_, _, newValue) -> { - consumer.accept(newValue.intValue()); - }); - - return element; - } - - public static TextField input(String x, Consumer consumer) { - final TextField element = new TextField(x); - setCss(element, "input", "text-normal"); - - element.setOnMouseClicked( - _ -> { - new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent(); - }); - - element.textProperty() - .addListener( - (_, _, newValue) -> { - consumer.accept(newValue); - }); - - return element; - } - - public static ChoiceBox choiceBox(Consumer consumer) { - final ChoiceBox element = new ChoiceBox<>(); - setCss(element, "choice-box", "text-normal"); - - element.setOnMouseClicked( - _ -> { - new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent(); - }); - - element.valueProperty() - .addListener( - (_, _, newValue) -> { - consumer.accept(newValue); - }); - - return element; - } - - public static Separator separator() { - final Separator element = new Separator(Orientation.HORIZONTAL); - setCss(element, "separator"); - - return element; - } -} diff --git a/app/src/main/java/org/toop/app/layer/Popup.java b/app/src/main/java/org/toop/app/layer/Popup.java deleted file mode 100644 index 7e498df..0000000 --- a/app/src/main/java/org/toop/app/layer/Popup.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.toop.app.layer; - -import org.toop.app.App; - -public abstract class Popup extends Layer { - protected Popup(boolean popOnBackground, String... backgroundStyles) { - super(backgroundStyles); - - if (popOnBackground) { - background.setOnMouseClicked( - _ -> { - App.pop(); - }); - } - } - - protected Popup(boolean popOnBackground) { - this(popOnBackground, "bg-popup"); - } -} diff --git a/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java b/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java deleted file mode 100644 index 2350216..0000000 --- a/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.toop.app.layer.containers; - -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; -import org.toop.app.layer.Container; - -public final class HorizontalContainer extends Container { - private final HBox container; - - public HorizontalContainer(int spacing, String... cssClasses) { - container = new HBox(spacing); - container.getStyleClass().addAll(cssClasses); - } - - public HorizontalContainer(int spacing) { - this(spacing, "container"); - } - - @Override - public Region getContainer() { - return container; - } - - @Override - public void addNodes(Node... nodes) { - container.getChildren().addAll(nodes); - } - - @Override - public void addContainer(Container container, boolean fill) { - if (fill) { - container.getContainer().setMinSize(0, 0); - container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); - HBox.setHgrow(container.getContainer(), Priority.ALWAYS); - } else { - container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); - } - - this.container.getChildren().add(container.getContainer()); - - if (fill) { - balanceChildWidths(); - } - } - - private void balanceChildWidths() { - final ObservableList children = container.getChildren(); - final double widthPerChild = container.getWidth() / children.size(); - - for (final Node child : children) { - if (child instanceof Region) { - ((Region) child).setPrefWidth(widthPerChild); - } - } - } -} diff --git a/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java b/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java deleted file mode 100644 index 56d610c..0000000 --- a/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.toop.app.layer.containers; - -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; -import javafx.scene.layout.VBox; -import org.toop.app.layer.Container; - -public final class VerticalContainer extends Container { - private final VBox container; - - public VerticalContainer(int spacing, String... cssClasses) { - container = new VBox(spacing); - container.getStyleClass().addAll(cssClasses); - } - - public VerticalContainer(int spacing) { - this(spacing, "container"); - } - - @Override - public Region getContainer() { - return container; - } - - @Override - public void addNodes(Node... nodes) { - container.getChildren().addAll(nodes); - } - - @Override - public void addContainer(Container container, boolean fill) { - if (fill) { - container.getContainer().setMinSize(0, 0); - container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); - VBox.setVgrow(container.getContainer(), Priority.ALWAYS); - } else { - container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); - } - - this.container.getChildren().add(container.getContainer()); - - if (fill) { - balanceChildHeights(); - } - } - - private void balanceChildHeights() { - final ObservableList children = container.getChildren(); - final double heightPerChild = container.getHeight() / children.size(); - - for (final Node child : children) { - if (child instanceof Region) { - ((Region) child).setPrefHeight(heightPerChild); - } - } - } -} 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 deleted file mode 100644 index b255c3d..0000000 --- a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java +++ /dev/null @@ -1,235 +0,0 @@ -package org.toop.app.layer.layers; - -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import javafx.application.Platform; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import org.toop.app.App; -import org.toop.app.GameInformation; -import org.toop.app.layer.Container; -import org.toop.app.layer.Layer; -import org.toop.app.layer.NodeBuilder; -import org.toop.app.layer.Popup; -import org.toop.app.layer.containers.HorizontalContainer; -import org.toop.app.layer.containers.VerticalContainer; -import org.toop.app.layer.layers.game.TicTacToeLayer; -import org.toop.framework.eventbus.EventFlow; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.local.AppContext; - -public final class ConnectedLayer extends Layer { - private static Timer pollTimer = new Timer(); - - private static class ChallengePopup extends Popup { - private final GameInformation information; - - private final String challenger; - private final String game; - - private final long clientID; - private final int challengeID; - - public ChallengePopup( - GameInformation information, - String challenger, - String game, - long clientID, - String challengeID) { - super(false, "bg-popup"); - - this.information = information; - - this.challenger = challenger; - this.game = game; - - this.clientID = clientID; - this.challengeID = - Integer.parseInt(challengeID.substring(18, challengeID.length() - 2)); - - reload(); - } - - @Override - public void reload() { - popAll(); - - final var challengeText = NodeBuilder.header(AppContext.getString("challengeText")); - final var challengerNameText = NodeBuilder.header(challenger); - - final var gameText = NodeBuilder.text(AppContext.getString("gameIsText")); - final var gameNameText = NodeBuilder.text(game); - - final var acceptButton = - NodeBuilder.button( - AppContext.getString("accept"), - () -> { - pollTimer.cancel(); - - new EventFlow() - .addPostEvent( - new NetworkEvents.SendAcceptChallenge( - clientID, challengeID)) - .postEvent(); - App.activate(new TicTacToeLayer(information, clientID)); - }); - - final var denyButton = - NodeBuilder.button( - AppContext.getString("deny"), - () -> { - App.pop(); - }); - - final Container controlContainer = new HorizontalContainer(30); - controlContainer.addNodes(acceptButton, denyButton); - - final Container mainContainer = new VerticalContainer(30); - mainContainer.addNodes(challengeText, challengerNameText); - mainContainer.addNodes(gameText, gameNameText); - - mainContainer.addContainer(controlContainer, false); - - addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30); - } - } - - GameInformation information; - long clientId; - String user; - List onlinePlayers = new CopyOnWriteArrayList<>(); - - public ConnectedLayer(GameInformation information) { - super("bg-primary"); - - this.information = information; - - new EventFlow() - .addPostEvent( - NetworkEvents.StartClient.class, - information.serverIP(), - Integer.parseInt(information.serverPort())) - .onResponse( - NetworkEvents.StartClientResponse.class, - e -> { - clientId = e.clientId(); - user = information.playerName()[0].replaceAll("\\s+", ""); - - new EventFlow() - .addPostEvent( - new NetworkEvents.SendLogin(this.clientId, this.user)) - .postEvent(); - - Thread popThread = new Thread(this::populatePlayerList); - popThread.setDaemon(false); - popThread.start(); - }) - .postEvent(); - - new EventFlow().listen(this::handleReceivedChallenge); - - reload(); - } - - 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(); - Platform.runLater(() -> reload()); - } - }; - - pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later - } - - private void sendChallenge(String oppUsername, String gameType) { - final AtomicInteger challengeId = new AtomicInteger(-1); - - if (onlinePlayers.contains(oppUsername)) { - new EventFlow() - .addPostEvent( - new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType)) - .listen( - NetworkEvents.ChallengeResponse.class, - e -> { - challengeId.set( - Integer.parseInt( - e.challengeId() - .substring( - 18, e.challengeId().length() - 2))); - }) - .listen( - NetworkEvents.GameMatchResponse.class, - e -> { - if (e.clientId() == this.clientId) { - pollTimer.cancel(); - App.activate(new TicTacToeLayer(information, this.clientId)); - } - }, - false) - .postEvent(); - // ^ - // | - // | - // | - } - } - - private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) { - App.push( - new ChallengePopup( - information, - response.challengerName(), - response.gameType(), - clientId, - response.challengeId())); - } - - @Override - public void reload() { - popAll(); - - ListView