Compare commits

..

80 Commits

Author SHA1 Message Date
Calum 09c02d5c54 Finish screen not appearing. Start screen opened simultanously. 2017-09-28 12:35:47 +13:00
Calum 08c34a0b1e Finish arrow not spawning fix. 2017-09-28 09:54:03 +13:00
Michael Rausch 8b7407bf89 Various bug fixes
- Closed socket when discovery server crashes, so it can be restarted
- Flattened water on terrain mesh
- Added checks to ensure server is started before connecting
- Added a check to ensure client has started  before connecting to the server
- Fixed concurrency issue in server-> client thread list

Tags: #story[1281]
2017-09-28 01:58:49 +13:00
Calum 00ddf117b2 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	src/main/java/seng302/visualiser/controllers/RaceViewController.java
#	src/main/resources/images/wind-180.png
2017-09-28 00:26:09 +13:00
Calum a266779709 Added new Wind Waker mesh
#implement #story[1293]
2017-09-28 00:24:34 +13:00
Haoming Yin df24fe072a Added animation for wind turning.
- enlarged the wind image size to centre the compass
- added animation to smooth the turning
2017-09-28 00:13:45 +13:00
Calum a1a34b9bd7 Merge branch 'develop' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 into develop 2017-09-27 23:36:14 +13:00
Calum 42d490d6fd Added new Wind Waker mesh
#implement #story[1293]
2017-09-27 23:35:51 +13:00
Haoming Yin cd4a2f8da3 Merge remote-tracking branch 'origin/develop' into develop 2017-09-27 22:47:08 +13:00
Haoming Yin 287cfd77d0 Fix: smoothing wind rotation
- added animation for wind rotation
- enlarged wind image's background to centre the compass

#story[1273]
2017-09-27 22:45:56 +13:00
William Muir ff4c2cd5b6 Merge branch '1273_Skybox' into 'develop'
1273 skybox

Server Discovery:
 - Created server discovery server.
 - Implemented protocols to support matchmaking & room code connection
 - Improved error handling for server disconnections

Skybox:
 - Added a skybox
 - Added a terrain mesh

See merge request !79
2017-09-27 21:52:34 +13:00
Peter Galloway cd199767ae created new models for all pickups #story[1293] 2017-09-27 20:40:40 +13:00
Michael Rausch 5cc4898ab5 Fixed failing tests & other bug fixes
- Fixed server capacity in server list
- Fixed failing unit tests for chat

Tags: #story[1281] #pair[mra106, cir27]
2017-09-27 20:37:29 +13:00
Haoming Yin 22e1e57c24 Issue #73: If you change controls to 'Continuously turning' before moving into game, it is not applied
- always send turning mode packet when race starts to sync the steering mode

#story[1278]
2017-09-27 20:18:06 +13:00
Haoming Yin c5d2016733 Merge branch 'issue_#67_early_start' into 'develop'
added a check in the start logic to make sure a boat can't cross the start line early



See merge request !74
2017-09-27 19:42:05 +13:00
Kusal Ekanayake efc71f2003 Resolved issue #69 and added dock icon for splash screen. 2017-09-27 19:27:13 +13:00
Michael Rausch 2b3a972ed5 Various bug fixes
- Fixed bug where an invalid port number would crash the program
- Closed the stage before cleaning up resources. This speeds up closing the app.
- Added error handling for when the client looses connection to the server.

Tags: #story[1281]
2017-09-27 17:14:55 +13:00
Calum 2a523a5664 Added a duck boat mesh.
#implement #story[1274]
2017-09-27 17:03:02 +13:00
Michael Rausch 67f3124cfb Merge branch 'develop' into 1273_Skybox 2017-09-27 15:32:54 +13:00
Michael Rausch 7db387bdec Merged Host Customisation onto 1273_Skybox 2017-09-27 15:31:54 +13:00
Alistair McIntyre 2ceca2fd42 Merge branch 'loading_screens' into 'develop'
Loading screens

# Changes
- Fixed a bug with sound not being muted consistently (the music would play when you press play again even if the music was muted)
- Added a splash screen to show for ~2 seconds on launch, doesn't repeat again while playing the same instance
- Added a loading screen which disappears when the race view has finished being set up (boats in place etc)

# Testing
- Manual testing done, few irrelevant bugs have been found, will log in issue tracker

See merge request !78
2017-09-27 15:05:36 +13:00
Michael Rausch 00ff771fc3 Merge remote-tracking branch 'origin/story1275_host_customization' into 1273_Skybox
# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/gameServer/ServerToClientThread.java
#	src/main/java/seng302/visualiser/ClientToServerThread.java
#	src/main/java/seng302/visualiser/GameView.java
#	src/main/java/seng302/visualiser/GameView3D.java
#	src/main/java/seng302/visualiser/controllers/dialogs/ServerCreationController.java
2017-09-27 14:56:09 +13:00
Michael Rausch d963785679 Added files from merge 2017-09-27 14:47:32 +13:00
Michael Rausch 78f64557c3 Merged dev onto 1273_Skybox 2017-09-27 14:46:12 +13:00
Michael Rausch 982fac38a0 Merge remote-tracking branch 'origin/develop' into 1273_Skybox
# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/gameServer/MessageFactory.java
#	src/main/java/seng302/gameServer/ServerToClientThread.java
#	src/main/java/seng302/model/ClientYacht.java
#	src/main/java/seng302/model/mark/MarkOrder.java
#	src/main/java/seng302/visualiser/GameClient.java
#	src/main/java/seng302/visualiser/GameView3D.java
#	src/main/java/seng302/visualiser/controllers/ServerListController.java
#	src/main/java/seng302/visualiser/controllers/dialogs/ServerCreationController.java
#	src/main/resources/icons/bumperIcon.png
#	src/main/resources/icons/handlingIcon.png
#	src/main/resources/icons/velocity.png
#	src/main/resources/icons/windWalkerIcon.png
#	src/main/resources/views/RaceView.fxml
#	src/main/resources/views/dialogs/ServerCreationDialog.fxml
2017-09-27 14:23:38 +13:00
Kusal Ekanayake edbfb2f84f Modified timings for load screen. 2017-09-27 14:16:27 +13:00
Kusal Ekanayake c01111038f Merge branch 'develop' into loading_screens
# Conflicts:
#	src/main/resources/views/RaceView.fxml
2017-09-27 14:05:46 +13:00
William Muir fd53fd52a4 Forgot to git add the new icon.. again.. sorree
#story[1293]
2017-09-27 12:44:50 +13:00
William Muir 6f62efcc93 Updated the icons to be more uniform and transparent
#story[1293]
2017-09-27 12:44:22 +13:00
Michael Rausch daf3867433 Server discovery bug fixes & error handling improvements
- Fixed concurrency bug that prevented players from connecting to servers
- Discovery server can restart itself if it crashes
- Added nicer error handling for server discovery.
- Using AWS to get servers external IP address.

Tags: #story[1281]
2017-09-27 12:32:17 +13:00
William Muir e5af7bf666 Minor fixes. Only one person can have wind walker now. Tokens spawn at 30s rather than 15
#story[1293]
2017-09-27 12:06:16 +13:00
William Muir 85ca91db96 Minor fixes. Only one person can have wind walker now. Tokens spawn at 30s rather than 15
#story[1293]
2017-09-27 11:55:40 +13:00
William Muir 2eb7e603f1 Merge remote-tracking branch 'origin/develop' into 1293_PowerUps 2017-09-27 03:01:41 +13:00
William Muir 658a342118 Added the bad random to git sorree forgot to git add
#story[1293]
2017-09-27 03:01:23 +13:00
Calum Irwin ea52977aeb Merge branch '1293_PowerUps' into 'develop'
1293 power ups

# Changes 
* Token Objects added to represent pickups along with their corresponding mesh objects 
* Yachts now have a powerup attribute 
* Power up attribute is checked in Game State updates and updates are now done appropriately, dependant on this power up 
* RaceViewController now has a grid pane to show the different power up icons. These will display when a token is picked up and blink when they are about to dissapear
* Token generator created. This generates token locations randomly in the map in circles centred between the legs of the race 
* Added Yacht EventCodes for the different type of power / up / down status effect actions
 
# Refactor
* Moved Timer tasks such as spawn tokens and update wind into the GameState where they belong
* Moved the creation of some messages in odd places, server side, into the MessageFactory class 
 
# Testing 
* Manual test done
* Junits created for the random token generator

See merge request !77
2017-09-27 02:47:57 +13:00
William Muir 10fa51a105 Added some testing for the Random spawner
#story[1293]
2017-09-27 02:27:30 +13:00
William Muir c54a1e141d Merge remote-tracking branch 'origin/develop' into 1293_PowerUps
# Conflicts:
#	src/main/resources/views/RaceView.fxml
2017-09-27 01:49:18 +13:00
William Muir 6d51ea3574 Created a random place token generator.
Generates a location between any leg of the race in a random angle / distance of the radius of the centre point of the two gates to one of the gates

#story[1293]
2017-09-27 01:44:01 +13:00
Zhi You Tan d56468e4aa - Fixed snackbar not showing on race view
- Added close label on server creation dialog

#story[1273]
2017-09-27 00:38:42 +13:00
William Muir 330ccd272d The random token now has a 50% chance of causing your boat to have a speed penalty
#story[1293]
2017-09-26 21:13:35 +13:00
William Muir 4b7dfe38c4 Fixed Boats powering up and down correctly. Icons respond correctly
#story[1293]
2017-09-26 19:43:32 +13:00
William Muir ab07c7f298 Merge remote-tracking branch 'origin/develop' into 1293_PowerUps
# Conflicts:
#	src/main/java/seng302/visualiser/GameView3D.java
2017-09-26 19:01:53 +13:00
William Muir b5076bc976 Fixed wind walker and bumper
Added a hack to bumper so that the collision distance is larger than the regular collision distance
Wind walker now makes you go at you max speed rather than VMG speed

#story[1293]
2017-09-26 18:57:15 +13:00
Kusal Ekanayake 671efcaf08 Adding spinners to the loading screen. 2017-09-26 17:25:57 +13:00
William Muir 8ba44d7476 Minor commit for testing
#story[1293]
2017-09-26 17:19:45 +13:00
Kusal Ekanayake 98abe64f00 Added splash and loading screen 2017-09-26 17:07:02 +13:00
Peter Galloway 870d7a6e82 added a check in the start logic to make sure a boat can't cross the start line prematurely (they will have to go back and go through it again) #fix #refactor 2017-09-26 15:25:24 +13:00
William Muir 7a4cdbe0c9 Fixed WindWalker
#story[1293]
2017-09-26 11:12:29 +13:00
Calum 735699dc85 Changed default number of legs. 2017-09-26 01:53:33 +13:00
Calum 81e791bd1a Fixed issues with showing race view controller. Added missing icons. Added a smooth version of land.
#fix
2017-09-26 01:49:43 +13:00
Calum 06e5f4ae00 Merge remote-tracking branch 'origin/1273_Skybox' into 1273_Skybox 2017-09-26 01:35:07 +13:00
Calum cd2b4cb93c fixed anchor pane issues 2017-09-26 01:34:59 +13:00
Calum dde1c82dbb merge with host customization and dev. 2017-09-26 01:21:55 +13:00
Calum d9c832168b Merge branch 'story1275_host_customization' into 1273_Skybox
# Conflicts:
#	src/main/java/seng302/visualiser/GameView3D.java
#	src/main/java/seng302/visualiser/controllers/LobbyController.java
#	src/main/java/seng302/visualiser/controllers/ViewManager.java
#	src/main/resources/views/LobbyView.fxml
#	src/main/resources/views/RaceView.fxml
2017-09-26 01:19:47 +13:00
Michael Rausch 9cfb3b9e5d Added functionality to automatically select a server
- Added functionality on the DiscoveryServer to return a random server to the player
- Added elements to the UI to support auto-selecting a server
- Added client side code to request a random server

Tags: #story[1281]
2017-09-26 01:14:02 +13:00
Michael Rausch d0844e861d Changed water color to blue 2017-09-25 19:25:03 +13:00
Michael Rausch 5d32d76d9d Added local skybox texture, and changed water color
- Changed water color to a more sea-like blue
- Added an extra sun
- Moved skybox texture to the resources folder
2017-09-25 19:19:46 +13:00
Michael Rausch 9ca39d1a7c Merge branch '1273_Changing_Cameras' into 1273_Skybox 2017-09-25 18:40:15 +13:00
Michael Rausch 30dad8509e Added skybox & cleaned up server list UI
- Moved direct connect fields to a dialog as there was not enough room
- Moved room code to its own label
- Added a skybox texture to the game view
- Added the land mesh to the game view
2017-09-25 18:25:24 +13:00
William Muir 0211f2df38 Merged dev back on
#story[1293]
2017-09-25 17:38:09 +13:00
William Muir dba5a5680f Merge remote-tracking branch 'origin/develop' into 1293_PowerUps
# Conflicts:
#	src/main/java/seng302/model/ServerYacht.java
#	src/main/resources/views/RaceView.fxml
2017-09-25 17:20:25 +13:00
William Muir 29b97a194d Merged dev back on
#story[1293]
2017-09-25 11:26:44 +13:00
William Muir 8a0ad8d6a9 Merge remote-tracking branch 'origin/develop' into 1293_PowerUps
# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/model/ServerYacht.java
2017-09-25 11:19:10 +13:00
Michael Rausch ca320f7fb8 Merge remote-tracking branch 'origin/1273_Changing_Cameras' into 1281_Server_Discovery_Internet 2017-09-25 00:18:35 +13:00
William Muir 78259f8e33 Boat now changes color when it is bumped for a time
ClientYacht: Added ColorChangeListener from GameView3D to re paint the boat when the color attribute is changed

#story[1293]
2017-09-23 16:54:03 +12:00
William Muir c47e5b1450 Boat now changes color when it is bumped for a time
ClientYacht: Added ColorChangeListener from GameView3D to re paint the boat when the color attribute is changed

#story[1293]
2017-09-23 16:31:18 +12:00
William Muir 8c7f9a878d Created Boat bumper logic. Refactored logic for powering up / dpwn
YachtEventType: Added some new events, a generic power down event and a bumper_crash event for an affected boat
GameState: Implemented boat bumper logic
MessageFactory: Made new messages for powerdown and status effect
ClientYacht: Had to create another powerDown functional interface to inform the race view controller when to turn off the icon
RaceViewController/GameClient: Now waits for a message about powering down before turning off rather than waiting time client side

#story[1293]
2017-09-23 13:23:16 +12:00
William Muir ecb3d4ecbf Removed sendServerMessage to be replaced with notifyMessageListeners. Minor structure move arounds
#story[1293]
2017-09-23 11:49:56 +12:00
William Muir e61b6d50a1 Small refactor. Fixed tokens to spawn on the minute mark.
Moved updates of wind and token timers into gamestate from mainserver thread. Now triggered upon GameState change to start

#story[1293]
2017-09-23 11:33:01 +12:00
William Muir 061e49bab9 Implemented wind walker algorithm. Refactored some GameState updating logic to allow for better token logic integration
GameState: Moved all token logic into its own function startPoint so that it is dsijoint from other updating logic
GameState: Implemented wind walker algorithm.
GameState: Changed Generic 'speedMultiplier' to 'serverSpeedMultiplier' to make it obviously disjoint from a boats speed multiplier
MessageFactory: Moved some found message creation (Chatter Message)  server side into MessageFactory that wasnt already there
ServerYacht: Added a speed multiplier and a handling multiplier to the serveryacht class that is set and reset upon powerup / down

#story[1293]
2017-09-22 23:44:03 +12:00
William Muir a3c555d5fe Merge remote-tracking branch 'origin/develop' into 1293_PowerUps
# Conflicts:
#	src/main/java/seng302/model/ClientYacht.java
2017-09-22 21:18:58 +12:00
Michael Rausch 5e3ae40d03 Made discovery more reliable & added docs/tests
- Added unit tests
- Added documentation for discovery classes
- Improved error handling

Tags: #story[1281]
2017-09-22 00:01:13 +12:00
Michael Rausch 95ad7a4840 Finished implementing room codes.
- Fixed bug where room code wasn't parsed correctly
- Added room code selection to server list screen.
- Added room code to hosts lobby.
- Implemented communication protocols on the game client.

Tags: #story[1281]
2017-09-21 22:48:33 +12:00
Michael Rausch e17e9749d8 Implemented server to manage a list of available servers on the internet.
- Implemented a server manager that keeps track of servers & room codes, and removes old servers
- Implemented queries to find a server with a specific room code
- Implemented protocol to register servers

#story[1281]
2017-09-20 20:26:14 +12:00
William Muir 3be8cd264d Adding icon files to git
#story[1245]
2017-09-20 17:52:02 +12:00
William Muir 66d9a06f9e Adding icon files to git
#story[1245]
2017-09-20 17:19:14 +12:00
William Muir 034e4c252a Icons now display, blink when they are about to run off, and turn off after their time out
TokenType Enum now also has a timeout construction field
#story[1245]
2017-09-20 13:09:09 +12:00
William Muir 6cde016401 Fixed random token assigning and realisation
Token class now has two functions: assignRandomType and realiseRandomType
The former can be used to assign any random type to the token including the random type
The latter can be used to assign a concrete random type to the token (not the random type)

#story[1245]
2017-09-20 12:02:12 +12:00
William Muir 52d3cea592 Added preliminary icons for pickups
RaceView now has a grid pane to contain some icons to display power ups. These are just preliminary

ClientYacht now has a power up field that is set from recieveing messages in the Game Client, as well as observed by the RaceViewController to display the relevant icon when the powerup field is changed

#story[1245]
2017-09-19 23:04:17 +12:00
William Muir 78596ea111 Initial commit for Power Up story
Made new preliminary models for each power up. Currently just different colour balls
Added new YachtEventTypes in the enum for each pick up to be sent out to clients
Tokens now not only randomise location but also randomise which type of token will be sent out
Added new methods to the MessageFactory class - Make collision and Make pickup Message
Game Client now checks what type of Yacht Event code has come in to respond appropriately rather than just generic collision / token.. although this has not been implemented yet
Game View loads appropriate token models depending on what is in XML

#story[1245]
2017-09-19 17:47:05 +12:00
93 changed files with 4921 additions and 1089 deletions
+6
View File
@@ -109,6 +109,12 @@
<version>3.4.2</version> <version>3.4.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.fxyz3d</groupId>
<artifactId>fxyz3d</artifactId>
<version>0.1.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
+31 -2
View File
@@ -10,11 +10,13 @@ import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.discoveryServer.DiscoveryServer;
import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.controllers.ViewManager;
public class App extends Application { public class App extends Application {
private static Logger logger = LoggerFactory.getLogger(App.class); private static Logger logger = LoggerFactory.getLogger(App.class);
private static boolean isRunningAsCache = false;
public static void parseArgs(String[] args) throws ParseException { public static void parseArgs(String[] args) throws ParseException {
Options options = new Options(); Options options = new Options();
@@ -25,9 +27,21 @@ public class App extends Application {
.getLogger(Logger.ROOT_LOGGER_NAME); .getLogger(Logger.ROOT_LOGGER_NAME);
options.addOption("debugLevel", true, "Set the application debug level"); options.addOption("debugLevel", true, "Set the application debug level");
options.addOption("runAsDiscoveryServer", false, "Run as a discovery server");
options.addOption("discoveryDevMode", false, "Use a local discovery server");
cmd = parser.parse(options, args); cmd = parser.parse(options, args);
if (cmd.hasOption("runAsDiscoveryServer")){
isRunningAsCache = true;
rootLogger.setLevel(Level.ALL);
return;
}
if (cmd.hasOption("discoveryDevMode")) {
DiscoveryServer.DISCOVERY_SERVER = "localhost";
}
if (cmd.hasOption("debugLevel")) { if (cmd.hasOption("debugLevel")) {
switch (cmd.getOptionValue("debugLevel")) { switch (cmd.getOptionValue("debugLevel")) {
@@ -63,19 +77,34 @@ public class App extends Application {
@Override @Override
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
ViewManager.getInstance().initialStartView(primaryStage); ViewManager.getInstance().initialiseSplashScreen(primaryStage);
} }
private static void runDiscoveryServer() throws Exception {
while (true){
try {
new DiscoveryServer();
}
catch (Exception ignored){
;
}
}
}
public static void main(String[] args) { public static void main(String[] args) throws Exception {
try { try {
parseArgs(args); parseArgs(args);
} catch (ParseException e) { } catch (ParseException e) {
logger.error("Could not parse command line arguments"); logger.error("Could not parse command line arguments");
} }
if (!isRunningAsCache){
launch(args); launch(args);
} }
else{
runDiscoveryServer();
}
}
} }
@@ -0,0 +1,170 @@
package seng302.discoveryServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RoomCodeRequest;
import seng302.gameServer.messages.ServerRegistrationMessage;
import seng302.model.stream.packets.PacketType;
import seng302.discoveryServer.util.ServerListing;
import seng302.discoveryServer.util.ServerRepoStreamParser;
import seng302.discoveryServer.util.ServerTable;
import seng302.visualiser.ServerListener;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Timer;
public class DiscoveryServer {
public static final String ANSI_GREEN = "\u001B[32m";
public static final String ANSI_YELLOW = "\u001B[33m";
public static final String ANSI_BLUE = "\u001B[34m";
public static final String ANSI_RESET = "\u001B[0m";
private static final int MAX_SERVER_TRIES = 10;
public static String DISCOVERY_SERVER = "party.sydney.srv.michaelrausch.nz";
private ServerTable serverTable;
public static final Integer PORT_NUMBER = 9969;
private ServerSocket serverSocket;
private final Logger logger = LoggerFactory.getLogger(DiscoveryServer.class);
private void displayHeader(){
String selectedColor = Arrays.asList(ANSI_BLUE, ANSI_GREEN, ANSI_YELLOW).get(new Random().nextInt(2));
System.out.println(selectedColor);
System.out.println(" .ccccc. \n" +
" .cc;'coooxkl;. \n" +
" .:c:::c:,,,,,;c;;,.'. \n" +
" .clc,',:,..:xxocc;'..c; \n" +
" .c:,';:ox:..:c,,,,,,...cd, \n" +
" .c:'.,oxxxxl::l:.,loll;..;ol. \n" +
" ;Oc..:xxxxxxxxx:.,llll,....oc \n" +
" .,;,',:loxxxxxxxxx:.,llll;.,,.'ld, \n" +
" .lo;..:xxxxxxxxxxxx:.'cllc,.:l:'cO; \n" +
" .:;...'cxxxxxxxxxxxxoc;,::,..cdl;;l' \n" +
" .cl;':,'';oxxxxxxdxxxxxx:....,cooc,cO; \n" +
" .,,,::;,lxoc:,,:lxxxxxxxxxxxo:,,;lxxl;'oNc \n" +
" .cdxo;':lxxxxxxc'';cccccoxxxxxxxxxxxxo,.;lc. " + ANSI_YELLOW + "Party-Parrots-At-Sea Discovery Server v0.1 " + selectedColor +"\n" +
" .loc'.'lxxxxxxxxocc;''''';ccoxxxxxxxxx:..oc \n" +
"olc,..',:cccccccccccc:;;;;;;;;:ccccccccc,.'c, \n" +
"Ol;......................................;l' ");
System.out.println(ANSI_RESET);
}
public DiscoveryServer() throws Exception {
displayHeader();
serverTable = new ServerTable();
try{
serverSocket = new ServerSocket(PORT_NUMBER);
}
catch(java.net.BindException e){
logger.error("FATAL - Could not bind socket, are you sure there isn't already an instance running?");
System.exit(1);
return;
}
logger.info("Started successfully - Now accepting connections");
try{
while (true){
Socket clientSocket = serverSocket.accept();
parseRequest(clientSocket);
clientSocket.close();
}
}
catch (Exception e){
close();
}
}
private void parseRequest(Socket clientSocket) throws Exception {
ServerRepoStreamParser parser = new ServerRepoStreamParser(clientSocket.getInputStream());
if (clientSocket.isConnected() && !clientSocket.isClosed()){
PacketType parsePacketResult = parser.parse();
switch (parsePacketResult){
case SERVER_REGISTRATION:
ServerListing listing = parser.getServerListing();
if (!serverTable.getAllServers().contains(listing)){
listing.setRoomCode(serverTable.getNextRoomCode().toString());
}
serverTable.addServer(listing);
Message serverRegMessage = new RoomCodeRequest(listing.getRoomCode());
clientSocket.getOutputStream().write(serverRegMessage.getBuffer());
break;
case ROOM_CODE_REQUEST:
String desiredRoomCode = parser.getRoomCode();
ServerListing serverListing;
if (desiredRoomCode.equals("0000")){
serverListing = getRandomFreeServer();
}
else {
serverListing = serverTable.getServerByRoomCode(desiredRoomCode);
}
Message response;
if (serverListing != null){
response = new ServerRegistrationMessage(serverListing.getServerName(), serverListing.getMapName(), serverListing.getAddress(), serverListing.getPortNumber(), 0, 0, desiredRoomCode);
}
else{
response = ServerRegistrationMessage.getEmptyRegistration();
}
clientSocket.getOutputStream().write(response.getBuffer());
break;
}
}
}
public ServerListing getRandomFreeServer() {
ServerListing serverToJoin;
List<ServerListing> servers = serverTable.getAllServers();
if (servers.size() <= 0){
return null;
}
if (servers.size() == 1){
return servers.get(0);
}
serverToJoin = servers.get(new Random().nextInt(servers.size()));
int tries = 0;
while (serverToJoin != null && serverToJoin.isMaxPlayersReached() && tries < MAX_SERVER_TRIES){
serverToJoin = servers.get(new Random().nextInt(servers.size()));
tries++;
}
if (serverToJoin != null && serverToJoin.isMaxPlayersReached()){
return null;
}
return serverToJoin;
}
public void close(){
try {
serverSocket.close();
} catch (IOException ignored) {
;
}
}
}
@@ -0,0 +1,195 @@
package seng302.discoveryServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.discoveryServer.util.ServerListing;
import seng302.discoveryServer.util.ServerRepoStreamParser;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RoomCodeRequest;
import seng302.gameServer.messages.ServerRegistrationMessage;
import seng302.model.stream.packets.PacketType;
import seng302.visualiser.controllers.ViewManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
public class DiscoveryServerClient {
private final Integer UPDATE_INTERVAL_MS = 5000;
private static String roomCode = null;
private Timer serverListingUpdateTimer;
private Logger logger = LoggerFactory.getLogger(DiscoveryServerClient.class);
private String ip = "";
private Boolean isInInvalidState = false;
public DiscoveryServerClient() {
try {
ip = getInetIpAddr();
} catch (Exception e) {
failError();
}
}
public String getInetIp(){
return ip;
}
private void failError() {
isInInvalidState = true;
ViewManager.getInstance().showErrorSnackBar("You do not appear to be able to connect to the internet. Matchmaking will be unavailable.");
}
public boolean didFail(){
return isInInvalidState;
}
/**
* Register the server with the discovery server
* @param serverListing The listing to register
*/
public void register(ServerListing serverListing){
if (isInInvalidState) return;
if (serverListingUpdateTimer != null){
serverListingUpdateTimer.cancel();
serverListingUpdateTimer = null;
}
serverListingUpdateTimer = new Timer();
serverListingUpdateTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
sendRegistrationUpdate(serverListing);
} catch (Exception e) {
logger.debug("Could not update server listing");
}
}
}, 0, UPDATE_INTERVAL_MS);
}
/**
* Stop updating the server registration updates
*/
public void unregister(){
if (serverListingUpdateTimer != null)
serverListingUpdateTimer.cancel();
}
/**
* Gets the connection information for a server given a room code
*
* @param roomCode The room code to search for
* @return The ServerListing, or null if there was an error
* @throws Exception .
*/
public ServerListing getServerForRoomCode(String roomCode) throws Exception {
Socket socket = new Socket(DiscoveryServer.DISCOVERY_SERVER, DiscoveryServer.PORT_NUMBER);
ServerRepoStreamParser parser = new ServerRepoStreamParser(socket.getInputStream());
Message request = new RoomCodeRequest(roomCode); //roomCode);
socket.getOutputStream().write(request.getBuffer());
PacketType packetType = parser.parse();
if (packetType != PacketType.SERVER_REGISTRATION){
logger.debug("Wrong packet received in response to a room code request");
return null;
}
socket.close();
return parser.getServerListing();
}
public ServerListing getRandomServer() throws Exception {
Socket socket = new Socket(DiscoveryServer.DISCOVERY_SERVER, DiscoveryServer.PORT_NUMBER);
ServerRepoStreamParser parser = new ServerRepoStreamParser(socket.getInputStream());
Message request = new RoomCodeRequest("0000");
socket.getOutputStream().write(request.getBuffer());
PacketType packetType = parser.parse();
if (packetType != PacketType.SERVER_REGISTRATION){
logger.error("Incorrect packet type received");
return null;
}
socket.close();
ServerListing serverListing = parser.getServerListing();
if (serverListing == null || serverListing.equals(ServerRegistrationMessage.getEmptyRegistration())){
return null;
}
return serverListing;
}
/**
* Sends a registration update to the discovery server.
*
* @param serverListing The server listing to send
* @throws Exception IF there was an error sending the update
*/
private void sendRegistrationUpdate(ServerListing serverListing) throws Exception {
Socket socket = new Socket(DiscoveryServer.DISCOVERY_SERVER, DiscoveryServer.PORT_NUMBER);
ServerRepoStreamParser parser = new ServerRepoStreamParser(socket.getInputStream());
Message req = new ServerRegistrationMessage(serverListing);
socket.getOutputStream().write(req.getBuffer());
PacketType packetType = parser.parse();
if (packetType != PacketType.ROOM_CODE_REQUEST){
socket.close();
return;
}
String roomCode = parser.getRoomCode();
if (roomCode.length() != 0){
DiscoveryServerClient.roomCode = roomCode;
}
socket.close();
}
/**
* @return The last room code received by the client
*/
public static String getRoomCode(){
return roomCode;
}
public static String getInetIpAddr() throws Exception {
URL myIp = new URL("http://checkip.amazonaws.com");
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(
myIp.openStream()));
String ip = in.readLine();
return ip;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@@ -0,0 +1,50 @@
package seng302.discoveryServer.util;
import java.io.InputStream;
public class ReadableByteInputStream {
private InputStream is;
public ReadableByteInputStream(InputStream is){
this.is = is;
}
/**
* Get n bytes from the input stream
* @param n number of bytes
* @return the bytes read
* @throws Exception .
*/
public byte[] getBytes(int n) throws Exception {
byte[] bytes = new byte[n];
for (int i = 0; i < n; i++) {
bytes[i] = (byte) readByte();
}
return bytes;
}
/**
* Skip n bytes
* @param n number of bytes to skip
* @throws Exception
*/
public void skipBytes(long n) throws Exception {
for (int i = 0; i < n; i++) {
readByte();
}
}
/**
* Read the next byte from the stream
* @return The byte that was read
* @throws Exception .
*/
public int readByte() throws Exception {
int currentByte = is.read();
if (currentByte == -1) {
throw new Exception();
}
return currentByte;
}
}
@@ -0,0 +1,117 @@
package seng302.discoveryServer.util;
public class ServerListing {
public final static int SERVER_TTL_DEFAULT = 10;
private String serverName = "";
private String mapName = "";
private String address = "";
private int portNumber = 0;
private int capacity = 0;
private int players = 0;
private String roomCode = "";
private int ttl = SERVER_TTL_DEFAULT;
public ServerListing(String serverName, String mapName, String address, int portNumber, int capacity){
this.serverName = serverName;
this.mapName = mapName;
this.address = address;
this.portNumber = portNumber;
this.capacity = capacity;
}
public ServerListing setNumberOfPlayers(int players){
this.players = players;
return this;
}
public ServerListing setRoomCode(String roomCode){
this.roomCode = roomCode;
return this;
}
public void refreshTtl(){
ttl = SERVER_TTL_DEFAULT;
}
public void decrementTtl(){
ttl--;
}
public boolean hasTtlExpired(){
return ttl < 0;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!ServerListing.class.isAssignableFrom(obj.getClass())) {
return false;
}
final ServerListing other = (ServerListing) obj;
if (this.getPortNumber() != other.getPortNumber()){
return false;
}
if (!this.getMapName().equals(other.getMapName())){
return false;
}
if (!this.getServerName().equals(other.getServerName())){
return false;
}
if (this.getCapacity() != other.getCapacity()){
return false;
}
if (!this.getAddress().equals(other.getAddress())){
return false;
}
return true;
}
@Override
public int hashCode() {
return this.getServerName().hashCode() +
this.getAddress().hashCode() + this.getMapName().hashCode();
}
public String getRoomCode() {
return roomCode;
}
public int getPortNumber() {
return portNumber;
}
public String getMapName() {
return mapName;
}
public String getServerName() {
return serverName;
}
public int getCapacity() {
return capacity;
}
public String getAddress() {
return address;
}
public void setTtl(Integer ttl){
this.ttl = ttl;
}
public boolean isMaxPlayersReached() {
return players >= capacity;
}
}
@@ -0,0 +1,109 @@
package seng302.discoveryServer.util;
import seng302.gameServer.messages.Message;
import seng302.model.stream.packets.PacketType;
import java.io.InputStream;
import java.util.Arrays;
public class ServerRepoStreamParser {
private ReadableByteInputStream inputStream;
private String roomCode;
private String mapName;
private ServerListing serverListing;
public ServerRepoStreamParser(InputStream is){
inputStream = new ReadableByteInputStream(is);
}
public PacketType parse() throws Exception {
int sync1 = inputStream.readByte();
int sync2 = inputStream.readByte();
PacketType packetType = null;
if (sync1 == 0x47 && sync2 == 0x83) {
int type = inputStream.readByte();
inputStream.skipBytes(10);
long payloadLength = Message.bytesToLong(inputStream.getBytes(2));
byte[] payload = inputStream.getBytes((int) payloadLength);
inputStream.skipBytes(4);
packetType = PacketType.assignPacketType(type, payload);
switch (packetType) {
case ROOM_CODE_REQUEST:
roomCode = parseRoomCodeRequest(payload);
break;
case LOBBY_REQUEST:
mapName = parseLobbyRequest(payload);
case SERVER_REGISTRATION:
serverListing = parseServerRegistration(payload);
break;
}
}
return packetType;
}
private String parseLobbyRequest(byte[] payload) {
int mapNameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0 ,4));
return new String(Arrays.copyOfRange(payload, 4, 4+mapNameLength));
}
private String parseRoomCodeRequest(byte[] payload) {
int roomCodeLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0 ,6));
return new String(Arrays.copyOfRange(payload, 6, 6+roomCodeLength));
}
public static ServerListing parseServerRegistration(byte[] payload) {
int nameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 0, 6));
int mapNameLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 6, 12));
int addressLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 12, 18));
int roomCodeLength = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 18, 24));
int portNumber = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 24, 28));
int players = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 28, 32));
int capacity = (int) Message.bytesToLong(Arrays.copyOfRange(payload, 32, 36));
int currentPos = 36;
int nextPos = currentPos + nameLength;
String serverName = new String(Arrays.copyOfRange(payload, currentPos, nextPos));
currentPos = nextPos;
nextPos = currentPos + mapNameLength;
String mapName = new String(Arrays.copyOfRange(payload, currentPos, nextPos));
currentPos = nextPos;
nextPos = currentPos + addressLength;
String address = new String(Arrays.copyOfRange(payload, currentPos, nextPos));
currentPos = nextPos;
nextPos = currentPos + roomCodeLength;
String roomCode = new String(Arrays.copyOfRange(payload, currentPos, nextPos));
ServerListing serverListing = new ServerListing(serverName, mapName, address, portNumber, capacity);
serverListing.setNumberOfPlayers(players);
serverListing.setRoomCode(roomCode);
return serverListing;
}
public String getRoomCode() {
return roomCode;
}
public String getMapName() {
return mapName;
}
public ServerListing getServerListing() {
return serverListing;
}
}
@@ -0,0 +1,97 @@
package seng302.discoveryServer.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class ServerTable {
private List<ServerListing> servers;
private int lastRoomCode = 4020;
private Logger logger = LoggerFactory.getLogger(ServerTable.class);
public ServerTable(){
servers = new ArrayList<>();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
updateServers();
}
}, 0, 1000);
}
/**
* Update the servers TTL values, and then remove expired servers
*/
private void updateServers() {
List<ServerListing> serversToRemove = new ArrayList<>();
for (ServerListing server : servers){
server.decrementTtl();
if (server.hasTtlExpired()){
logger.debug("Removed expired server - " + server.getServerName());
serversToRemove.add(server);
}
}
servers.removeAll(serversToRemove);
}
/**
* Add a server to the table
* @param server The server to add
*/
public void addServer(ServerListing server){
if (servers.contains(server)){
updateTtlForServer(server);
return;
}
logger.debug("Added new server - " + server.getServerName() + " at address: " + server.getAddress() + ":" + server.getPortNumber());
servers.add(server);
}
/**
* Update the TTL for a given server to the default TTL value
* @param server The server to update
*/
private void updateTtlForServer(ServerListing server) {
for (ServerListing serverListing : servers){
if (server.equals(serverListing)){
serverListing.refreshTtl();
}
}
}
/**
* @return All the servers in the table
*/
public List<ServerListing> getAllServers(){
return Collections.unmodifiableList(servers);
}
/**
* Get a server from the table given its room code
* @param roomCode The room code to search for
* @return The ServerListing of the found server, or null
* the server wasn't found
*/
public ServerListing getServerByRoomCode(String roomCode){
for (ServerListing serverListing : servers){
if (serverListing.getRoomCode().equals(roomCode)){
return serverListing;
}
}
return null;
}
/**
* @return The next available room code
*/
public Integer getNextRoomCode(){
lastRoomCode += 1;
return lastRoomCode;
}
}
+302 -114
View File
@@ -1,7 +1,7 @@
package seng302.gameServer; package seng302.gameServer;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,8 +22,6 @@ import seng302.gameServer.messages.MarkRoundingMessage;
import seng302.gameServer.messages.MarkType; import seng302.gameServer.messages.MarkType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RoundingBoatStatus; import seng302.gameServer.messages.RoundingBoatStatus;
import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.gameServer.messages.YachtEventType;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
import seng302.model.Limit; import seng302.model.Limit;
import seng302.model.Player; import seng302.model.Player;
@@ -34,6 +34,7 @@ import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.Token; import seng302.model.token.Token;
import seng302.model.token.TokenType; import seng302.model.token.TokenType;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.utilities.RandomSpawn;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
/** /**
@@ -51,63 +52,75 @@ public class GameState implements Runnable {
private static Logger logger = LoggerFactory.getLogger(GameState.class); private static Logger logger = LoggerFactory.getLogger(GameState.class);
private static final Integer STATE_UPDATES_PER_SECOND = 60;
//Scheduling constants
static final int WARNING_TIME = 10 * -1000; static final int WARNING_TIME = 10 * -1000;
static final int PREPATORY_TIME = 5 * -1000; static final int PREPATORY_TIME = 5 * -1000;
private static final int TIME_TILL_START = 10 * 1000; private static final int TIME_TILL_START = 10 * 1000;
private static final Long POWERUP_TIMEOUT_MS = 10_000L; //Wind Constants
private static final int MAX_WIND_SPEED = 12000;
private static final int MIN_WIND_SPEED = 8000;
private static final Integer STATE_UPDATES_PER_SECOND = 60; //Rounding Constants
private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further private static final Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
//Collision constants
private static final Double MARK_COLLISION_DISTANCE = 15d; private static final Double MARK_COLLISION_DISTANCE = 15d;
public static final Double YACHT_COLLISION_DISTANCE = 25.0; public static final Double YACHT_COLLISION_DISTANCE = 25.0;
private static final Double BOUNCE_DISTANCE_MARK = 20.0; private static final Double BOUNCE_DISTANCE_MARK = 20.0;
public static final Double BOUNCE_DISTANCE_YACHT = 30.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
private static final Double COLLISION_VELOCITY_PENALTY = 0.3; private static final Double COLLISION_VELOCITY_PENALTY = 0.3;
//Powerup Constants
public static final Double VELOCITY_BOOST_MULTIPLIER = 2d;
public static final Integer HANDLING_BOOST_MULTIPLIER = 2;
private static final Double BAD_RANDOM_SPEED_PENALTY = 0.3;
public static final Long BUMPER_DISABLE_TIME = 5_000L;
private static final Long TOKEN_SPAWN_TIME = 30_000L;
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
private static Double windSpeed; private static Double windSpeed;
private static Double speedMultiplier = 1d; private static Double serverSpeedMultiplier;
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat. private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
private static Boolean playerHasLeftFlag; private static Boolean playerHasLeftFlag;
private static String hostIpAddress;
private static List<Player> players; private static List<Player> players;
private static Map<Integer, ServerYacht> yachts; private static Map<Integer, ServerYacht> yachts;
private static Boolean isRaceStarted;
private static GameStages currentStage; private static GameStages currentStage;
private static MarkOrder markOrder; private static MarkOrder markOrder;
private static long startTime; private static long startTime;
private static Set<Mark> marks; private static Set<Mark> marks = new HashSet<>();
private static List<Limit> courseLimit; private static List<Limit> courseLimit = new ArrayList<>();
private static Integer maxPlayers = 8; private static Integer maxPlayers = 8;
private static List<Token> allTokens;
private static List<Token> tokensInPlay; private static List<Token> tokensInPlay;
private static RandomSpawn randomSpawn;
private static List<NewMessageListener> newMessageListeners; private static List<NewMessageListener> newMessageListeners;
private static Map<Player, String> playerStringMap = new HashMap<>(); private static Map<Player, String> playerStringMap = new HashMap<>();
private static boolean tokensEnabled = false;
public GameState() { public GameState() {
windDirection = 180d; windDirection = 180d;
windSpeed = 10000d; windSpeed = 10000d;
yachts = new HashMap<>(); yachts = new HashMap<>();
tokensInPlay = new ArrayList<>(); tokensInPlay = new ArrayList<>();
marks = new HashSet<>();
players = new ArrayList<>(); players = new ArrayList<>();
customizationFlag = false; customizationFlag = false;
playerHasLeftFlag = false; playerHasLeftFlag = false;
speedMultiplier = 1.0; serverSpeedMultiplier = 1.0;
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false;
//set this when game stage changes to prerace
previousUpdateTime = System.currentTimeMillis(); previousUpdateTime = System.currentTimeMillis();
newMessageListeners = new ArrayList<>(); newMessageListeners = new ArrayList<>();
allTokens = makeTokens();
resetStartTime(); resetStartTime();
//setCourseLimit("/server_config/race.xml");
new Thread(this, "GameState").start(); //Run the auto updates on the game state new Thread(this, "GameState").start(); //Run the auto updates on the game state
} }
@@ -116,27 +129,10 @@ public class GameState implements Runnable {
for (CompoundMark compoundMark : raceXMLData.getCompoundMarks().values()){ for (CompoundMark compoundMark : raceXMLData.getCompoundMarks().values()){
marks.addAll(compoundMark.getMarks()); marks.addAll(compoundMark.getMarks());
} }
randomSpawn = new RandomSpawn(markOrder.getOrderedUniqueCompoundMarks());
courseLimit = raceXMLData.getCourseLimit(); courseLimit = raceXMLData.getCourseLimit();
} }
/**
* Make a pre defined set of tokensInPlay. //TODO wmu16 - Should read from some file for each
* race ideally
*
* @return A list of possible tokensInPlay for this race
*/
private ArrayList<Token> makeTokens() {
Token token1 = new Token(TokenType.BOOST, 57.66946, 11.83154);
Token token2 = new Token(TokenType.BOOST, 57.66877, 11.83382);
Token token3 = new Token(TokenType.BOOST, 57.66914, 11.83965);
Token token4 = new Token(TokenType.BOOST, 57.66684, 11.83214);
return new ArrayList<>(Arrays.asList(token1, token2, token3, token4));
}
public static Set<Mark> getMarks() {
return Collections.unmodifiableSet(marks);
}
public static List<Player> getPlayers() { public static List<Player> getPlayers() {
return players; return players;
} }
@@ -145,6 +141,10 @@ public class GameState implements Runnable {
return tokensInPlay; return tokensInPlay;
} }
public static Set<Mark> getMarks() {
return Collections.unmodifiableSet(marks);
}
public static void addPlayer(Player player) { public static void addPlayer(Player player) {
players.add(player); players.add(player);
String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName() String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName()
@@ -165,10 +165,6 @@ public class GameState implements Runnable {
yachts.remove(yachtId); yachts.remove(yachtId);
} }
public static Boolean getIsRaceStarted() {
return isRaceStarted;
}
public static GameStages getCurrentStage() { public static GameStages getCurrentStage() {
return currentStage; return currentStage;
} }
@@ -237,12 +233,77 @@ public class GameState implements Runnable {
} catch (InterruptedException e) { } catch (InterruptedException e) {
System.out.println("[GameState] interrupted exception"); System.out.println("[GameState] interrupted exception");
} }
if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) { if (currentStage == GameStages.PRE_RACE) {
update();
if (System.currentTimeMillis() > startTime) {
startSpawningTokens();
startUpdatingWind();
GameState.setCurrentStage(GameStages.RACING);
}
}
if (currentStage == GameStages.RACING) {
update(); update();
} }
} }
} }
/**
* Start spawning coins every 60s after the first minute
*/
private void startSpawningTokens() {
Timer timer = new Timer("Token Spawning Timer");
timer.schedule(new TimerTask() {
@Override
public void run() {
if (tokensEnabled) {
spawnNewToken();
notifyMessageListeners(MessageFactory.getRaceXML());
}
}
}, 0, TOKEN_SPAWN_TIME);
}
// TODO: 29/08/17 wmu16 - This sort of update should be in game state
private static void startUpdatingWind() {
Timer timer = new Timer("Wind Updating Timer");
timer.schedule(new TimerTask() {
@Override
public void run() {
updateWind();
}
}, 0, 500);
}
private static void updateWind() {
Integer direction = GameState.getWindDirection().intValue();
Integer windSpeed = GameState.getWindSpeedMMS().intValue();
Random random = new Random();
if (Math.floorMod(random.nextInt(), 2) == 0) {
direction += random.nextInt(4);
windSpeed += random.nextInt(20) + 459;
} else {
direction -= random.nextInt(4);
windSpeed -= random.nextInt(20) + 459;
}
direction = Math.floorMod(direction, 360);
if (windSpeed > MAX_WIND_SPEED) {
windSpeed -= random.nextInt(500);
}
if (windSpeed <= MIN_WIND_SPEED) {
windSpeed += random.nextInt(500);
}
GameState.setWindSpeed(Double.valueOf(windSpeed));
GameState.setWindDirection(direction.doubleValue());
}
public static void updateBoat(Integer sourceId, BoatAction actionType) { public static void updateBoat(Integer sourceId, BoatAction actionType) {
ServerYacht playerYacht = yachts.get(sourceId); ServerYacht playerYacht = yachts.get(sourceId);
switch (actionType) { switch (actionType) {
@@ -277,10 +338,13 @@ public class GameState implements Runnable {
* Randomly select a subset of tokensInPlay from a pre defined superset * Randomly select a subset of tokensInPlay from a pre defined superset
* Broadasts a new race status message to show this update * Broadasts a new race status message to show this update
*/ */
public static void spawnNewToken() { private void spawnNewToken() {
Random random = new Random();
tokensInPlay.clear(); tokensInPlay.clear();
tokensInPlay.add(allTokens.get(random.nextInt(allTokens.size()))); Token token = randomSpawn.getRandomToken();
// token.assignType(TokenType.WIND_WALKER);
logger.debug("Spawned token of type " + token.getTokenType());
tokensInPlay.add(token);
MessageFactory.updateTokens(tokensInPlay);
} }
/** /**
@@ -297,14 +361,12 @@ public class GameState implements Runnable {
Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0; Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0;
previousUpdateTime = System.currentTimeMillis(); previousUpdateTime = System.currentTimeMillis();
if (System.currentTimeMillis() > startTime) {
GameState.setCurrentStage(GameStages.RACING);
}
for (ServerYacht yacht : yachts.values()) { for (ServerYacht yacht : yachts.values()) {
updateVelocity(yacht); updateVelocity(yacht);
checkPowerUpTimeout(yacht);
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
preformTokenUpdates(yacht); //This update must be done before collision. Sorta hacky
checkCollision(yacht); checkCollision(yacht);
if (yacht.getBoatStatus() != BoatStatus.FINISHED) { if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
checkForLegProgression(yacht); checkForLegProgression(yacht);
@@ -318,17 +380,138 @@ public class GameState implements Runnable {
} }
/**
* All token functionality entry points is taken care of here. So can be disabled and enabled
* easily
*
* @param yacht The yacht to perform token checks on
*/
private void preformTokenUpdates(ServerYacht yacht) {
Token collidedToken = checkTokenPickUp(yacht);
if (collidedToken != null) {
tokensInPlay.remove(collidedToken);
powerUpYacht(yacht, collidedToken);
MessageFactory.updateTokens(tokensInPlay);
notifyMessageListeners(MessageFactory.getRaceXML());
}
checkPowerUpTimeout(yacht);
TokenType powerUp = yacht.getPowerUp();
if (powerUp != null) {
switch (powerUp) {
case WIND_WALKER:
windWalk(yacht);
break;
case BUMPER:
ServerYacht collidedYacht = checkYachtCollision(yacht, true);
if (collidedYacht != null) {
yacht.powerDown();
boatTempShutDown(collidedYacht);
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
notifyMessageListeners(
MessageFactory.makeStatusEffectMessage(collidedYacht, powerUp));
}
break;
case RANDOM:
yacht.setPowerUpSpeedMultiplier(BAD_RANDOM_SPEED_PENALTY);
}
}
}
/**
* Powers up a thisYacht with the given token type.
*
* @param thisYacht The yacht to be powered up
* @param collidedToken The token which this thisYacht collided with
*/
private void powerUpYacht(ServerYacht thisYacht, Token collidedToken) {
//The random token has a 50% chance of becoming another token else becoming a speed detriment!
if (collidedToken.getTokenType() == TokenType.RANDOM && new Random().nextBoolean()) {
collidedToken.realiseRandom();
}
//If another yacht has the wind walker token, They should be powered down. Only one allowed!
else if (collidedToken.getTokenType() == TokenType.WIND_WALKER) {
for (ServerYacht otherYacht : yachts.values()) {
if (otherYacht != thisYacht && otherYacht.getPowerUp() == TokenType.WIND_WALKER) {
powerDownYacht(otherYacht);
}
}
}
thisYacht.powerUp(collidedToken.getTokenType());
String logMessage =
thisYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType().getName()
+ " token";
notifyMessageListeners(
MessageFactory.makeChatterMessage(thisYacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(MessageFactory.makePickupMessage(thisYacht, collidedToken));
logger.debug(
"Yacht: " + thisYacht.getShortName() + " got powerup " + collidedToken.getTokenType());
}
private void powerDownYacht(ServerYacht yacht) {
String logMessage =
yacht.getBoatName() + "'s " + yacht.getPowerUp().getName() + " expired";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
yacht.powerDown();
}
// TODO: 23/09/17 wmu16 - This is a hacky way to have the boat power down. Need some sort of separation between token and status effect :/
/**
* Disables the given boat for BUMPER_DISABLE_TIME ms.
*
* @param yacht The yacht to disable
*/
private void boatTempShutDown(ServerYacht yacht) {
yacht.setPowerUpSpeedMultiplier(0d);
Timer shutDownTimer = new Timer("Shutdown Timer");
shutDownTimer.schedule(new TimerTask() {
@Override
public void run() {
yacht.powerDown(); //Note this actually resets the boat to normal.
}
}, BUMPER_DISABLE_TIME);
}
/**
* Checks how long a powerup has been active for. If it has exceeded its timeout, it powers the
* yacht down.
*
* @param yacht The yacht to check to power down
*/
private void checkPowerUpTimeout(ServerYacht yacht) { private void checkPowerUpTimeout(ServerYacht yacht) {
if (yacht.getPowerUp() != null) { if (yacht.getPowerUp() != null) {
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > POWERUP_TIMEOUT_MS) { if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp()
yacht.powerDown(); .getTimeout()) {
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired"); powerDownYacht(yacht);
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
} }
} }
} }
/**
* This function changes the wind to be at an angle that causes the yacht in question to be at
* its fastest velocity
*
* @param yacht The yacht to fix the wind for
*/
private void windWalk(ServerYacht yacht) {
Double optimalAngle = PolarTable.getOptimalAngle();
Double heading = yacht.getHeading();
windDirection = (double) Math.floorMod(Math.round(heading + optimalAngle), 360L);
}
/** /**
* Check if the yacht has crossed the course limit * Check if the yacht has crossed the course limit
* *
@@ -342,6 +525,7 @@ public class GameState implements Runnable {
return true; return true;
} }
} }
if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0), if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0),
yacht.getLastLocation(), yacht.getLocation()) != 0) { yacht.getLastLocation(), yacht.getLocation()) != 0) {
return true; return true;
@@ -350,13 +534,15 @@ public class GameState implements Runnable {
} }
/** /**
* Checks all tokensInPlay to see if a yacht has picked one up * Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up
* @return Token which was collided with * in the appropriate way
* @param serverYacht The yacht to check for collision with a token * @param yacht The yacht to check for collision with a token
*
* @return The token collided with
*/ */
private static Token checkTokenPickUp(ServerYacht serverYacht) { private Token checkTokenPickUp(ServerYacht yacht) {
for (Token token : tokensInPlay) { for (Token token : tokensInPlay) {
Double distance = GeoUtility.getDistance(token, serverYacht.getLocation()); Double distance = GeoUtility.getDistance(token, yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { if (distance < YACHT_COLLISION_DISTANCE) {
return token; return token;
} }
@@ -379,7 +565,7 @@ public class GameState implements Runnable {
*/ */
public static void checkCollision(ServerYacht serverYacht) { public static void checkCollision(ServerYacht serverYacht) {
//Yacht Collision //Yacht Collision
ServerYacht collidedYacht = checkYachtCollision(serverYacht); ServerYacht collidedYacht = checkYachtCollision(serverYacht, false);
Mark collidedMark = checkMarkCollision(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht);
if (collidedYacht != null) { if (collidedYacht != null) {
@@ -396,9 +582,7 @@ public class GameState implements Runnable {
collidedYacht.setCurrentVelocity( collidedYacht.setCurrentVelocity(
collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
); );
notifyMessageListeners( notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
);
} }
//Mark Collision //Mark Collision
@@ -410,9 +594,7 @@ public class GameState implements Runnable {
serverYacht.setCurrentVelocity( serverYacht.setCurrentVelocity(
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
); );
notifyMessageListeners( notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
);
} }
//Boundary Collision //Boundary Collision
@@ -425,23 +607,7 @@ public class GameState implements Runnable {
serverYacht.setCurrentVelocity( serverYacht.setCurrentVelocity(
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
); );
notifyMessageListeners( notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
);
}
//Token Collision
Token collidedToken = checkTokenPickUp(serverYacht);
if (collidedToken != null) {
sendServerMessage(serverYacht.getSourceId(), serverYacht.getBoatName() + " has picked speed-up token");
tokensInPlay.remove(collidedToken);
serverYacht.powerUp(collidedToken.getTokenType());
logger.debug("Yacht: " + serverYacht.getShortName() + " got powerup " + collidedToken
.getTokenType());
System.out.println("AGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.TOKEN));
} }
} }
@@ -449,29 +615,30 @@ public class GameState implements Runnable {
private void updateVelocity(ServerYacht yacht) { private void updateVelocity(ServerYacht yacht) {
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier * yacht.getMaxSpeedMultiplier(); Double maxBoatSpeed =
if (yacht.getPowerUp() != null) { GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht
if (yacht.getPowerUp().equals(TokenType.BOOST)) { .getPowerUpSpeedMultiplier() * yacht.getBoatTypeSpeedMultiplier();
// TODO: 11/09/17 wmu16 CHANGE THIS TO MAGIC NUMBER
maxBoatSpeed *= 2;
}
}
Double currentVelocity = yacht.getCurrentVelocity(); Double currentVelocity = yacht.getCurrentVelocity();
// TODO: 15/08/17 remove magic numbers from these equations. // TODO: 15/08/17 remove magic numbers from these equations.
if (yacht.getSailIn()) { if (yacht.getSailIn()) {
if (currentVelocity < maxBoatSpeed - 500) { if (currentVelocity < maxBoatSpeed - 500) {
yacht.changeVelocity((maxBoatSpeed / 100) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(maxBoatSpeed / 100) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity > maxBoatSpeed + 500) { } else if (currentVelocity > maxBoatSpeed + 500) {
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
} else { } else {
yacht.setCurrentVelocity((maxBoatSpeed) * yacht.getAccelerationMultiplier()); yacht
.setCurrentVelocity((maxBoatSpeed) * yacht.getBoatTypeAccelerationMultiplier());
} }
} else { } else {
if (currentVelocity > 3000) { if (currentVelocity > 3000) {
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity > 100) { } else if (currentVelocity > 100) {
yacht.changeVelocity((-currentVelocity / 50) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 50) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity <= 100) { } else if (currentVelocity <= 100) {
yacht.setCurrentVelocity(0d); yacht.setCurrentVelocity(0d);
} }
@@ -534,7 +701,10 @@ public class GameState implements Runnable {
if (hasProgressed) { if (hasProgressed) {
if (currentMarkSeqID != 0 && !markOrder.isLastMark(currentMarkSeqID)) { if (currentMarkSeqID != 0 && !markOrder.isLastMark(currentMarkSeqID)) {
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed leg " + yacht.getLegNumber());
String logMessage = yacht.getBoatName() + " passed leg " + yacht.getLegNumber();
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
} }
yacht.incrementLegNumber(); yacht.incrementLegNumber();
sendMarkRoundingMessage(yacht); sendMarkRoundingMessage(yacht);
@@ -555,6 +725,10 @@ public class GameState implements Runnable {
* @param yacht The current yacht to check for * @param yacht The current yacht to check for
*/ */
private Boolean checkStartLineCrossing(ServerYacht yacht) { private Boolean checkStartLineCrossing(ServerYacht yacht) {
long timeTillStart = System.currentTimeMillis() - this.getStartTime();
if (timeTillStart < 0){
return false;
}
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID(); Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID); CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
GeoPoint lastLocation = yacht.getLastLocation(); GeoPoint lastLocation = yacht.getLastLocation();
@@ -570,7 +744,9 @@ public class GameState implements Runnable {
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
yacht.setClosestCurrentMark(mark1); yacht.setClosestCurrentMark(mark1);
yacht.setBoatStatus(BoatStatus.RACING); yacht.setBoatStatus(BoatStatus.RACING);
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed start line"); String logMessage = yacht.getBoatName() + " passed start line";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
return true; return true;
} }
} }
@@ -674,7 +850,10 @@ public class GameState implements Runnable {
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
yacht.setClosestCurrentMark(mark1); yacht.setClosestCurrentMark(mark1);
yacht.setBoatStatus(BoatStatus.FINISHED); yacht.setBoatStatus(BoatStatus.FINISHED);
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed finish line");
String logMessage = yacht.getBoatName() + " passed finish line";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
return true; return true;
} }
} }
@@ -697,6 +876,7 @@ public class GameState implements Runnable {
String name = new String(customizeData); String name = new String(customizeData);
playerYacht.setBoatName(name); playerYacht.setBoatName(name);
} else if (requestType.equals(CustomizeRequestType.COLOR)) { } else if (requestType.equals(CustomizeRequestType.COLOR)) {
//This low level stuff shouldnt be here alistair! In fact no logic LIKE THIS should! - wmu16
int red = customizeData[0] & 0xFF; int red = customizeData[0] & 0xFF;
int green = customizeData[1] & 0xFF; int green = customizeData[1] & 0xFF;
int blue = customizeData[2] & 0xFF; int blue = customizeData[2] & 0xFF;
@@ -709,7 +889,7 @@ public class GameState implements Runnable {
} }
private static Mark checkMarkCollision(ServerYacht yacht) { private static Mark checkMarkCollision(ServerYacht yacht) {
Set<Mark> marksInRace = GameState.getMarks(); Set<Mark> marksInRace = new HashSet<>(marks);
for (Mark mark : marksInRace) { for (Mark mark : marksInRace) {
if (GeoUtility.getDistance(yacht.getLocation(), mark) if (GeoUtility.getDistance(yacht.getLocation(), mark)
<= MARK_COLLISION_DISTANCE) { <= MARK_COLLISION_DISTANCE) {
@@ -738,15 +918,22 @@ public class GameState implements Runnable {
* Collision detection which iterates through all the yachts and check if any yacht collided * Collision detection which iterates through all the yachts and check if any yacht collided
* with this yacht. Return collided yacht or null if no collision. * with this yacht. Return collided yacht or null if no collision.
* *
* UPDATE: HACK!!! wmu16 - forBumperCollision is (the goddamn dirtiest) dirty flag to fix a
* weird bug where the bumper collision would not be registerd but the knock back collision would.
* In other words, only set the 'forBumperCollision' flag true if used for the bumper power up.
*
* @return yacht to compare to all other yachts. * @return yacht to compare to all other yachts.
*/ */
private static ServerYacht checkYachtCollision(ServerYacht yacht) { private static ServerYacht checkYachtCollision(ServerYacht yacht, Boolean forBumperCollision) {
Double collisionDistance =
(forBumperCollision) ? YACHT_COLLISION_DISTANCE + 2.5 : YACHT_COLLISION_DISTANCE;
for (ServerYacht otherYacht : GameState.getYachts().values()) { for (ServerYacht otherYacht : GameState.getYachts().values()) {
if (otherYacht != yacht) { if (otherYacht != yacht) {
Double distance = GeoUtility Double distance = GeoUtility
.getDistance(otherYacht.getLocation(), yacht.getLocation()); .getDistance(otherYacht.getLocation(), yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { ;
if (distance < collisionDistance) {
return otherYacht; return otherYacht;
} }
} }
@@ -782,13 +969,6 @@ public class GameState implements Runnable {
roundingMark.getSourceID())); roundingMark.getSourceID()));
} }
public static void sendServerMessage(Integer messageType, String message) {
notifyMessageListeners(new ChatterMessage(
messageType, "SERVER: " + message
));
}
public static void processChatter(ChatterMessage chatterMessage, boolean isHost) { public static void processChatter(ChatterMessage chatterMessage, boolean isHost) {
String chatterText = chatterMessage.getMessage(); String chatterText = chatterMessage.getMessage();
String[] words = chatterText.split("\\s+"); String[] words = chatterText.split("\\s+");
@@ -796,17 +976,19 @@ public class GameState implements Runnable {
switch (words[2].trim()) { switch (words[2].trim()) {
case "/speed": case "/speed":
try { try {
setSpeedMultiplier(Double.valueOf(words[3])); serverSpeedMultiplier = Double.valueOf(words[3]);
sendServerMessage(chatterMessage.getMessage_type(), String logMessage = "Speed modifier set to x" + words[3];
"Speed modifier set to x" + words[3]); notifyMessageListeners(MessageFactory
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
} catch (Exception e) { } catch (Exception e) {
Logger logger = LoggerFactory.getLogger(GameState.class); Logger logger = LoggerFactory.getLogger(GameState.class);
logger.error("cannot parse >speed value"); logger.error("cannot parse >speed value");
} }
return; return;
case "/finish": case "/finish":
sendServerMessage(chatterMessage.getMessage_type(), String logMessage = "Game will now finish";
"Game will now finish"); notifyMessageListeners(MessageFactory
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
endRace(); endRace();
return; return;
} }
@@ -856,6 +1038,12 @@ public class GameState implements Runnable {
public static void setMaxPlayers(Integer newMax){ public static void setMaxPlayers(Integer newMax){
maxPlayers = newMax; maxPlayers = newMax;
try {
ServerAdvertiser.getInstance().setCapacity(newMax);
} catch (IOException e) {
logger.warn("Couldn't update max players");
}
} }
public static void endRace () { public static void endRace () {
@@ -863,11 +1051,11 @@ public class GameState implements Runnable {
currentStage = GameStages.FINISHED; currentStage = GameStages.FINISHED;
} }
public static void setSpeedMultiplier (double multiplier) { public static double getServerSpeedMultiplier() {
speedMultiplier = multiplier; return serverSpeedMultiplier;
} }
public static double getSpeedMultiplier () { public static void setTokensEnabled (boolean tokensEnabled) {
return speedMultiplier; GameState.tokensEnabled = tokensEnabled;
} }
} }
@@ -19,6 +19,13 @@ import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
/** /**
* A class describing the overall server, which creates and collects server threads for each client * A class describing the overall server, which creates and collects server threads for each client
* Created by wmu16 on 13/07/17. * Created by wmu16 on 13/07/17.
@@ -30,12 +37,10 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
private static final int PORT = 4942; private static final int PORT = 4942;
private static final Integer CLIENT_UPDATES_PER_SECOND = 60; private static final Integer CLIENT_UPDATES_PER_SECOND = 60;
private static final int MAX_WIND_SPEED = 12000;
private static final int MIN_WIND_SPEED = 8000;
private boolean terminated; private boolean terminated;
private Thread thread; private Thread thread;
private boolean hasStarted = false;
private ServerSocket serverSocket = null; private ServerSocket serverSocket = null;
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>(); private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();
@@ -59,7 +64,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
ServerAdvertiser.getInstance() ServerAdvertiser.getInstance()
.setMapName(regattaXMLData.getCourseName()) .setMapName(regattaXMLData.getCourseName())
.setCapacity(capacity) .setCapacity(capacity)
.setNumberOfPlayers(numPlayers) .setNumberOfPlayers(numPlayers - 1)
.registerGame(PORT, regattaXMLData.getRegattaName()); .registerGame(PORT, regattaXMLData.getRegattaName());
} catch (IOException e) { } catch (IOException e) {
logger.warn("Could not register server"); logger.warn("Could not register server");
@@ -80,16 +85,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
private void startServer() { private void startServer() {
PolarTable.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv"));
MessageFactory.updateXMLGenerator(raceXMLData, regattaXMLData); MessageFactory.updateXMLGenerator(raceXMLData, regattaXMLData);
GameState.setRace(raceXMLData); GameState.setRace(raceXMLData);
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values())); MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
startAdvertisingServer(); startAdvertisingServer();
PolarTable
.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv"));
GameState.addMessageEventListener(this::broadcastMessage); GameState.addMessageEventListener(this::broadcastMessage);
startUpdatingWind();
startSpawningTokens();
System.out.println("SAAAANNNNNNNNNDDDDDDDDDDDDDDDDDDDDDDDDDDdd");
sendSetupMessages(); sendSetupMessages();
} }
@@ -98,13 +99,14 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
new HeartbeatThread(this); new HeartbeatThread(this);
new ServerListenThread(serverSocket, this); new ServerListenThread(serverSocket, this);
hasStarted = true;
//You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app. //You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
while (!terminated) { while (!terminated) {
if (GameState.getPlayerHasLeftFlag()) { if (GameState.getPlayerHasLeftFlag()) {
for (ServerToClientThread stc : serverToClientThreads) { for (ServerToClientThread stc : serverToClientThreads) {
if (!stc.isSocketOpen()) { if (!stc.isSocketOpen()) {
GameState.getYachts().remove(stc.getSourceId()); GameState.getYachts().remove(stc.getSourceId());
System.out.println("AAAAAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
sendSetupMessages(); sendSetupMessages();
try { try {
stc.getSocket().close(); stc.getSocket().close();
@@ -122,7 +124,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState.getCustomizationFlag()) { if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState.getCustomizationFlag()) {
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values())); MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
System.out.println("gfdgfdgfdg");
sendSetupMessages(); sendSetupMessages();
GameState.resetCustomizationFlag(); GameState.resetCustomizationFlag();
} }
@@ -148,9 +149,11 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
} }
try { try {
synchronized (this){
for (ServerToClientThread serverToClientThread : serverToClientThreads) { for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.terminate(); serverToClientThread.terminate();
} }
}
serverSocket.close(); serverSocket.close();
} catch (IOException e) { } catch (IOException e) {
System.out.println("IO error in server thread handler upon closing socket"); System.out.println("IO error in server thread handler upon closing socket");
@@ -164,6 +167,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
private void sendSetupMessages() { private void sendSetupMessages() {
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
broadcastMessage(MessageFactory.getRaceXML()); broadcastMessage(MessageFactory.getRaceXML());
broadcastMessage(MessageFactory.getRegattaXML()); broadcastMessage(MessageFactory.getRegattaXML());
broadcastMessage(MessageFactory.getBoatXML()); broadcastMessage(MessageFactory.getBoatXML());
@@ -175,63 +179,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
} }
private static void updateWind(){
Integer direction = GameState.getWindDirection().intValue();
Integer windSpeed = GameState.getWindSpeedMMS().intValue();
Random random = new Random();
if (Math.floorMod(random.nextInt(), 2) == 0){
direction += random.nextInt(4);
windSpeed += random.nextInt(20) + 459;
}
else{
direction -= random.nextInt(4);
windSpeed -= random.nextInt(20) + 459;
}
direction = Math.floorMod(direction, 360);
if (windSpeed > MAX_WIND_SPEED){
windSpeed -= random.nextInt(500);
}
if (windSpeed <= MIN_WIND_SPEED){
windSpeed += random.nextInt(500);
}
GameState.setWindSpeed(Double.valueOf(windSpeed));
GameState.setWindDirection(direction.doubleValue());
}
// TODO: 29/08/17 wmu16 - This sort of update should be in game state
private static void startUpdatingWind(){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
updateWind();
}
}, 0, 500);
}
/**
* Start spawning coins every 60s after the first minute
*/
private void startSpawningTokens() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
GameState.spawnNewToken();
broadcastMessage(MessageFactory.getRaceXML());
}
}, 10000, 60000);
}
/** /**
* A client has tried to connect to the server * A client has tried to connect to the server
* *
@@ -259,22 +206,26 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
}); });
} else { } else {
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values())); //serverToClientThread.addConnectionListener(this::sendSetupMessages);
for (ServerYacht serverYacht : GameState.getYachts().values()) {
System.out.println("Connecterino" + serverYacht);
}
serverToClientThread.addConnectionListener(() -> {
System.out.println("LUSTENER");
sendSetupMessages();
});
} }
serverToClientThreads.add(serverToClientThread); serverToClientThreads.add(serverToClientThread);
serverToClientThread.addDisconnectListener(this::clientDisconnected);
try { try {
ServerAdvertiser.getInstance().setNumberOfPlayers(GameState.getNumberOfPlayers()); ServerAdvertiser.getInstance().setNumberOfPlayers(GameState.getNumberOfPlayers());
} catch (IOException e) { } catch (IOException e) {
logger.warn("Couldn't update advertisement"); logger.warn("Couldn't update advertisement");
} }
while (regattaXMLData == null && raceXMLData == null){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
serverToClientThread.addConnectionListener(this::sendSetupMessages);
serverToClientThread.addDisconnectListener(this::clientDisconnected);
} }
/** /**
@@ -295,6 +246,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
serverToClientThread.sendSetupMessages(); serverToClientThread.sendSetupMessages();
} }
} }
serverToClientThreads.remove(closedConnection); serverToClientThreads.remove(closedConnection);
try { try {
@@ -503,4 +455,8 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
); );
} }
} }
public boolean hasStarted() {
return hasStarted;
}
} }
@@ -1,9 +1,11 @@
package seng302.gameServer; package seng302.gameServer;
import seng302.gameServer.messages.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import seng302.gameServer.messages.BoatLocationMessage; import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.BoatSubMessage; import seng302.gameServer.messages.BoatSubMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.RaceStartNotificationType; import seng302.gameServer.messages.RaceStartNotificationType;
import seng302.gameServer.messages.RaceStartStatusMessage; import seng302.gameServer.messages.RaceStartStatusMessage;
import seng302.gameServer.messages.RaceStatus; import seng302.gameServer.messages.RaceStatus;
@@ -11,6 +13,8 @@ import seng302.gameServer.messages.RaceStatusMessage;
import seng302.gameServer.messages.RaceType; import seng302.gameServer.messages.RaceType;
import seng302.gameServer.messages.XMLMessage; import seng302.gameServer.messages.XMLMessage;
import seng302.gameServer.messages.XMLMessageSubType; import seng302.gameServer.messages.XMLMessageSubType;
import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.gameServer.messages.YachtEventType;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.ServerYacht; import seng302.model.ServerYacht;
import seng302.model.stream.xml.generator.RaceXMLTemplate; import seng302.model.stream.xml.generator.RaceXMLTemplate;
@@ -18,8 +22,12 @@ import seng302.model.stream.xml.generator.RegattaXMLTemplate;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.model.token.Token; import seng302.model.token.Token;
import seng302.model.token.TokenType;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
import java.util.ArrayList;
import java.util.List;
/** /**
* A Class for interfacing between the data we have in the GameState to the messages we need to send * A Class for interfacing between the data we have in the GameState to the messages we need to send
* through the MainServerThread. * through the MainServerThread.
@@ -69,9 +77,9 @@ public class MessageFactory {
} }
public static void updateBoats(List<ServerYacht> yachts) { public static void updateBoats(List<ServerYacht> yachts) {
for (ServerYacht serverYacht : yachts) { // for (ServerYacht serverYacht : yachts) {
System.out.println(serverYacht); // System.out.println(serverYacht);
} // }
xmlGenerator.getRace().setBoats(yachts); xmlGenerator.getRace().setBoats(yachts);
String xmlStr = xmlGenerator.getBoatsAsXml(); String xmlStr = xmlGenerator.getBoatsAsXml();
MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length()); MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length());
@@ -152,4 +160,73 @@ public class MessageFactory {
public static XMLMessage getBoatXML() { public static XMLMessage getBoatXML() {
return boats; return boats;
} }
public static YachtEventCodeMessage makeCollisionMessage(ServerYacht serverYacht) {
return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION);
}
/**
* Constructs a message to be sent out whenever a yacht picks up a boost
*
* @param serverYacht The yacht that has picked up a power up
* @param token The token which they picked up
* @return The corresponding YachtEventCodeMessage
*/
public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) {
YachtEventType yachtEventType = null;
switch (token.getTokenType()) {
case BOOST:
yachtEventType = YachtEventType.TOKEN_VELOCITY;
break;
case HANDLING:
yachtEventType = YachtEventType.TOKEN_HANDLING;
break;
case WIND_WALKER:
yachtEventType = YachtEventType.TOKEN_WIND_WALKER;
break;
case BUMPER:
yachtEventType = YachtEventType.TOKEN_BUMPER;
break;
case RANDOM:
yachtEventType = YachtEventType.TOKEN_RANDOM;
break;
}
return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType);
}
/**
* Constructs a message representing a certain buff / debuff for a given yacht. For now this is
* just for the bumper debuff so the affected boat is aware that it has been crashed. This could
* however be extended to render affects for all boats given a certain debuff.
*
* @param yacht The yacht affected by some status
* @param token The token indicating what status they have
* @return A YachtEventCodeMessage
*/
public static YachtEventCodeMessage makeStatusEffectMessage(ServerYacht yacht,
TokenType token) {
YachtEventType yachtEventType = null;
switch (token) {
case BUMPER:
yachtEventType = YachtEventType.BUMPER_CRASH;
break;
}
return new YachtEventCodeMessage(yacht.getSourceId(), yachtEventType);
}
/**
* Constructs a message to be sent out when a given yacht powers down (From a boost of any type)
*
* @param yacht The yacht that is powering down
* @return A YachtEventCodeMessage representing this action
*/
public static YachtEventCodeMessage makePowerDownMessage(ServerYacht yacht) {
return new YachtEventCodeMessage(yacht.getSourceId(), YachtEventType.POWER_DOWN);
}
public static ChatterMessage makeChatterMessage(Integer messageType, String message) {
return new ChatterMessage(messageType, "SERVER: " + message);
}
} }
@@ -1,5 +1,10 @@
package seng302.gameServer; package seng302.gameServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.discoveryServer.DiscoveryServerClient;
import seng302.discoveryServer.util.ServerListing;
import javax.jmdns.JmDNS; import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo; import javax.jmdns.ServiceInfo;
import java.io.IOException; import java.io.IOException;
@@ -32,12 +37,16 @@ public class ServerAdvertiser {
private static ServerAdvertiser instance = null; private static ServerAdvertiser instance = null;
private static JmDNS jmdnsInstance = null; private static JmDNS jmdnsInstance = null;
private ServiceInfo serviceInfo; // Note: Whenever this is changed, our service will be re-registered on the network. private ServiceInfo serviceInfo; // Note: Whenever this is changed, our service will be re-registered on the network.
private DiscoveryServerClient repositoryClient;
private Hashtable<String ,String> props; private Hashtable<String ,String> props;
private Logger logger = LoggerFactory.getLogger(ServerAdvertiser.class);
private ServerAdvertiser() throws IOException{ private ServerAdvertiser() throws IOException{
jmdnsInstance = JmDNS.create(InetAddress.getByName(getLocalHostIp())); jmdnsInstance = JmDNS.create(InetAddress.getByName(getLocalHostIp()));
repositoryClient = new DiscoveryServerClient();
props = new Hashtable<>(); props = new Hashtable<>();
props.put("map", ""); props.put("map", "");
props.put("spacesLeft", "0"); props.put("spacesLeft", "0");
@@ -122,10 +131,13 @@ public class ServerAdvertiser {
try { try {
jmdnsInstance.registerService(serviceInfo); jmdnsInstance.registerService(serviceInfo);
} catch (IOException e) { } catch (IOException e) {
System.out.println("Failed"); logger.warn("Failed to register service info");
} }
} }
}, 0); }, 0);
ServerListing serverListing = new ServerListing(serverName, props.get("map"), new DiscoveryServerClient().getInetIp(), portNo, Integer.parseInt(props.get("capacity")));
repositoryClient.register(serverListing);
} }
/** /**
@@ -7,6 +7,10 @@ public class ServerDescription {
private String serverName; private String serverName;
private String mapName; private String mapName;
private Integer numPlayers; private Integer numPlayers;
private Long lastUpdated;
private Long lastRefreshed;
private static Long EXPIRY_INTERVAL = 5000L;
public ServerDescription(String serverName, String mapName, Integer numPlayers, Integer capacity, String address, Integer portNum){ public ServerDescription(String serverName, String mapName, Integer numPlayers, Integer capacity, String address, Integer portNum){
this.serverName = serverName; this.serverName = serverName;
@@ -15,6 +19,7 @@ public class ServerDescription {
this.address = address; this.address = address;
this.portNum = portNum; this.portNum = portNum;
this.capacity = capacity; this.capacity = capacity;
lastUpdated = System.currentTimeMillis();
} }
@@ -80,4 +85,17 @@ public class ServerDescription {
return this.getName().hashCode() + this.getAddress().hashCode() + return this.getName().hashCode() + this.getAddress().hashCode() +
this.portNumber().hashCode() + this.getMapName().hashCode(); this.portNumber().hashCode() + this.getMapName().hashCode();
} }
public Boolean hasExpired(){
return System.currentTimeMillis() - lastUpdated > EXPIRY_INTERVAL;
}
public Boolean serverShouldBeRemoved() {
if (lastRefreshed == null) return false;
return System.currentTimeMillis() - lastRefreshed > EXPIRY_INTERVAL;
}
public void hasBeenRefreshed(){
lastRefreshed = System.currentTimeMillis();
}
} }
@@ -1,20 +1,10 @@
package seng302.gameServer; package seng302.gameServer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.*;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.ChatterMessage; import seng302.gameServer.messages.ChatterMessage;
@@ -31,8 +21,20 @@ import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.utilities.StreamParser; import seng302.utilities.StreamParser;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import seng302.utilities.XMLParser; import seng302.utilities.XMLParser;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/** /**
* A class describing a single connection to a Client for the purposes of sending and receiving on * A class describing a single connection to a Client for the purposes of sending and receiving on
@@ -77,7 +79,6 @@ public class ServerToClientThread implements Runnable {
private List<ConnectionListener> connectionListeners = new ArrayList<>(); private List<ConnectionListener> connectionListeners = new ArrayList<>();
private DisconnectListener disconnectListener; private DisconnectListener disconnectListener;
private ServerYacht yacht;
private Player player; private Player player;
private SimpleObjectProperty<RaceXMLData> raceXMLProperty = new SimpleObjectProperty<>(); private SimpleObjectProperty<RaceXMLData> raceXMLProperty = new SimpleObjectProperty<>();
@@ -103,16 +104,15 @@ public class ServerToClientThread implements Runnable {
} }
private void setUpPlayer(){ private void setUpPlayer(){
String fName = "Player" + GameState.getNumberOfPlayers().toString(); String shortName = "P" + sourceId;
String lName = ""; String longName = "Player " + sourceId;
ServerYacht yacht = new ServerYacht( ServerYacht yacht = new ServerYacht(
BoatMeshType.DINGHY, sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ" BoatMeshType.DINGHY, sourceId, sourceId.toString(), shortName, longName, "NZ");
);
System.out.println(yacht);
player = new Player(socket, yacht); player = new Player(socket, yacht);
GameState.addYacht(sourceId, yacht); GameState.addYacht(sourceId, yacht);
GameState.addPlayer(player); GameState.addPlayer(player);
System.out.println(GameState.getYachts().size());
} }
private void completeRegistration(ClientType clientType) throws IOException { private void completeRegistration(ClientType clientType) throws IOException {
@@ -136,9 +136,8 @@ public class ServerToClientThread implements Runnable {
this.sourceId = sourceId; this.sourceId = sourceId;
isRegistered = true; isRegistered = true;
os.write(responseMessage.getBuffer()); os.write(responseMessage.getBuffer());
System.out.println("MAKING A PLAYER");
setUpPlayer(); setUpPlayer();
System.out.println("DONE MAKING A PLAYER");
for (ConnectionListener listener : connectionListeners) { for (ConnectionListener listener : connectionListeners) {
listener.notifyConnection(); listener.notifyConnection();
@@ -204,6 +203,7 @@ public class ServerToClientThread implements Runnable {
XMLParser.parseRace(document) XMLParser.parseRace(document)
); );
GameState.setMaxPlayers(XMLParser.getMaxPlayers(document)); GameState.setMaxPlayers(XMLParser.getMaxPlayers(document));
GameState.setTokensEnabled(XMLParser.tokensEnabled(document));
break; break;
case REGATTA_XML: case REGATTA_XML:
regattaXMLProperty.set( regattaXMLProperty.set(
@@ -300,10 +300,6 @@ public class ServerToClientThread implements Runnable {
return socket; return socket;
} }
public ServerYacht getYacht() {
return yacht;
}
public void addConnectionListener(ConnectionListener listener) { public void addConnectionListener(ConnectionListener listener) {
connectionListeners.add(listener); connectionListeners.add(listener);
} }
@@ -40,7 +40,7 @@ public class ChatterMessage extends Message {
return message; return message;
} }
public int getMessage_type() { public int getMessageType() {
return message_type; return message_type;
} }
} }
@@ -21,7 +21,10 @@ public enum MessageType {
REGISTRATION_REQUEST(101), REGISTRATION_REQUEST(101),
REGISTRATION_RESPONSE(102), REGISTRATION_RESPONSE(102),
CUSTOMIZATION_REQUEST(103), CUSTOMIZATION_REQUEST(103),
CUSTOMIZATION_RESPONSE(104); CUSTOMIZATION_RESPONSE(104),
REPO_REGISTRATION_REQUEST(201),
ROOM_CODE_REQUEST(202),
LOBBY_REQUEST(203);
private int code; private int code;
@@ -0,0 +1,24 @@
package seng302.gameServer.messages;
public class RoomCodeRequest extends Message{
private int size = 0;
@Override
public int getSize() {
return size;
}
public RoomCodeRequest(String roomCode){
size = roomCode.length() + 6;
setHeader(new Header(MessageType.ROOM_CODE_REQUEST, 0x01, (short)getSize()));
allocateBuffer();
writeHeaderToBuffer();
putInt(roomCode.length(), 6);
putBytes(roomCode.getBytes());
writeCRC();
rewind();
}
}
@@ -0,0 +1,64 @@
package seng302.gameServer.messages;
import seng302.discoveryServer.util.ServerListing;
public class ServerRegistrationMessage extends Message {
private int size;
public ServerRegistrationMessage(ServerListing serverListing) {
String serverName = serverListing.getServerName();
String mapName = serverListing.getMapName();
String address = serverListing.getAddress();
int port = serverListing.getPortNumber();
int players = serverListing.getPortNumber();
int capacity = serverListing.getCapacity();
String roomCode = serverListing.getRoomCode();
createMessage(serverName, mapName, address, port, players, capacity, roomCode);
}
public static ServerRegistrationMessage getEmptyRegistration() {
return new ServerRegistrationMessage("","","",0,0,0,"");
}
@Override
public int getSize() {
return size;
}
public ServerRegistrationMessage(String serverName, String mapName, String address, int port, int players, int capacity, String roomCode){
createMessage(serverName, mapName, address, port, players, capacity, roomCode);
}
private void createMessage(String serverName, String mapName, String address, int port, int players, int capacity, String roomCode){
size = serverName.getBytes().length + mapName.length() + address.length() + roomCode.length() + 36;
setHeader(new Header(MessageType.REPO_REGISTRATION_REQUEST, 0x01, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
int nameLength = serverName.length();
int mapNameLength = mapName.length();
int addressLength = address.length();
int roomCodeLength = roomCode.length();
// Put fields here
putInt(nameLength, 6);
putInt(mapNameLength, 6);
putInt(addressLength, 6);
putInt(roomCodeLength, 6);
putInt(port, 4);
putInt(players, 4);
putInt(capacity, 4);
putBytes(serverName.getBytes());
putBytes(mapName.getBytes());
putBytes(address.getBytes());
putBytes(roomCode.getBytes());
writeCRC();
rewind();
}
}
@@ -1,11 +1,18 @@
package seng302.gameServer.messages; package seng302.gameServer.messages;
/** /**
* Created by wmu16 on 11/09/17. * Enum for different event types for the yacht
*/ */
public enum YachtEventType { public enum YachtEventType {
COLLISION(33), COLLISION(33),
TOKEN(34); TOKEN_VELOCITY(34),
TOKEN_BUMPER(35),
TOKEN_HANDLING(36),
TOKEN_WIND_WALKER(37),
TOKEN_RANDOM(38),
POWER_DOWN(39),
BUMPER_CRASH(40);
private int code; private int code;
@@ -13,10 +13,15 @@ import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper; import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongProperty;
import javafx.beans.property.ReadOnlyLongWrapper; import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.beans.value.ObservableObjectValue;
import javafx.collections.FXCollections;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import jdk.nashorn.internal.objects.annotations.Function;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.model.token.TokenType;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import seng302.model.token.TokenType;
import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.BoatObject;
/** /**
@@ -37,6 +42,26 @@ public class ClientYacht extends Observable {
void notifyRounding(ClientYacht yacht, int legNumber); void notifyRounding(ClientYacht yacht, int legNumber);
} }
@FunctionalInterface
public interface ColorChangeListener {
void notifyColorChange(ClientYacht yacht);
}
//This notifies RaceViewController so it can display icon - wmu16
@FunctionalInterface
public interface PowerUpListener {
void notifyPowerUp(ClientYacht yacht, TokenType tokenType);
}
//This notifies RaceViewController so it can remove token icon - wmu16
@FunctionalInterface
public interface PowerDownListener {
void notifyPowerDown(ClientYacht yacht);
}
private Logger logger = LoggerFactory.getLogger(ClientYacht.class); private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
@@ -47,6 +72,7 @@ public class ClientYacht extends Observable {
private String boatName; private String boatName;
private String country; private String country;
private Integer position; private Integer position;
private TokenType powerUp;
private Long estimateTimeAtFinish; private Long estimateTimeAtFinish;
private Boolean sailIn = true; private Boolean sailIn = true;
@@ -65,6 +91,10 @@ public class ClientYacht extends Observable {
private List<YachtLocationListener> locationListeners = new ArrayList<>(); private List<YachtLocationListener> locationListeners = new ArrayList<>();
private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>(); private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>();
private List<PowerUpListener> powerUpListeners = new ArrayList<>();
private List<PowerDownListener> powerDownListeners = new ArrayList<>();
private List<ColorChangeListener> colorChangeListeners = new ArrayList<>();
private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
@@ -208,6 +238,32 @@ public class ClientYacht extends Observable {
this.position = position; this.position = position;
} }
/**
* Powers down the boat and notifies the raceViewController to display
*/
public void powerDown() {
this.powerUp = null;
for (PowerDownListener listener : powerDownListeners) {
listener.notifyPowerDown(this);
}
}
/**
* powers up the boat and notifies the raceViewController to display
*
* @param tokenType The type of token that this boat is being powered up with
*/
public void setPowerUp(TokenType tokenType) {
this.powerUp = tokenType;
for (PowerUpListener listener : powerUpListeners) {
listener.notifyPowerUp(this, tokenType);
}
}
public TokenType getPowerUp() {
return powerUp;
}
public void toggleSail() { public void toggleSail() {
sailIn = !sailIn; sailIn = !sailIn;
} }
@@ -257,6 +313,9 @@ public class ClientYacht extends Observable {
public void setColour(Color colour) { public void setColour(Color colour) {
this.colour = colour; this.colour = colour;
for (ColorChangeListener listener : colorChangeListeners) {
listener.notifyColorChange(this);
}
} }
public void updateLocation(double lat, double lng, double heading, double velocity) { public void updateLocation(double lat, double lng, double heading, double velocity) {
@@ -282,6 +341,18 @@ public class ClientYacht extends Observable {
markRoundingListeners.add(listener); markRoundingListeners.add(listener);
} }
public void addPowerUpListener(PowerUpListener listener) {
powerUpListeners.add(listener);
}
public void addPowerDownListener(PowerDownListener listener) {
powerDownListeners.add(listener);
}
public void addColorChangeListener(ColorChangeListener listener) {
colorChangeListeners.add(listener);
}
public void removeMarkRoundingListener(MarkRoundingListener listener) { public void removeMarkRoundingListener(MarkRoundingListener listener) {
markRoundingListeners.remove(listener); markRoundingListeners.remove(listener);
} }
+37 -4
View File
@@ -4,7 +4,9 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
/** /**
* A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised * A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised
@@ -17,6 +19,7 @@ public final class PolarTable {
private static HashMap<Double, HashMap<Double, Double>> polarTable; private static HashMap<Double, HashMap<Double, Double>> polarTable;
private static HashMap<Double, HashMap<Double, Double>> upwindOptimal; private static HashMap<Double, HashMap<Double, Double>> upwindOptimal;
private static HashMap<Double, HashMap<Double, Double>> downwindOptimal; private static HashMap<Double, HashMap<Double, Double>> downwindOptimal;
private static Double optimalAngle;
private static int upTwaIndex; private static int upTwaIndex;
private static int dnTwaIndex; private static int dnTwaIndex;
@@ -33,11 +36,13 @@ public final class PolarTable {
upwindOptimal = new HashMap<>(); upwindOptimal = new HashMap<>();
downwindOptimal = new HashMap<>(); downwindOptimal = new HashMap<>();
String line; String line = null;
String check;
Boolean isHeaderLine = true; Boolean isHeaderLine = true;
try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) { try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) {
while ((line = br.readLine()) != null) { while ((check = br.readLine()) != null) {
line = check;
String[] thisLine = line.split(","); String[] thisLine = line.split(",");
//Initial line in file //Initial line in file
@@ -66,7 +71,10 @@ public final class PolarTable {
upwindOptimal.put(thisWindSpeed, thisUpWindPolar); upwindOptimal.put(thisWindSpeed, thisUpWindPolar);
downwindOptimal.put(thisWindSpeed, thisDnWindPolar); downwindOptimal.put(thisWindSpeed, thisDnWindPolar);
} }
} }
getMaxSpeedAngle(line);
} catch (IOException e) { } catch (IOException e) {
System.out.println("[PolarTable] IO exception"); System.out.println("[PolarTable] IO exception");
@@ -74,6 +82,27 @@ public final class PolarTable {
} }
/**
* Passes the final line of the polar table and iterates over the speeds for each
* angle, velocity pair to find the angle that produces the highest velocity
*
* @param line The last line of the polar file
*/
private static void getMaxSpeedAngle(String line) {
String[] theLastLine = line.split(",");
Double maxWindVal = Double.parseDouble(theLastLine[0]);
Double optimalAngle = Double.parseDouble(theLastLine[1]);
Double maxSpeed = Double.parseDouble(theLastLine[2]);
for (Map.Entry<Double, Double> entry : polarTable.get(maxWindVal).entrySet()) {
if (entry.getValue() > maxSpeed) {
maxSpeed = entry.getValue();
optimalAngle = entry.getKey();
}
}
PolarTable.optimalAngle = optimalAngle;
}
/** /**
* Parses the header line of a polar file * Parses the header line of a polar file
@@ -85,14 +114,18 @@ public final class PolarTable {
String thisItem = thisLine[i]; String thisItem = thisLine[i];
if (thisItem.toLowerCase().startsWith("uptwa")) { if (thisItem.toLowerCase().startsWith("uptwa")) {
upTwaIndex = i; upTwaIndex = i;
} } else if (thisItem.toLowerCase().startsWith("dntwa")) {
else if (thisItem.toLowerCase().startsWith("dntwa")) {
dnTwaIndex = i; dnTwaIndex = i;
} }
} }
} }
public static Double getOptimalAngle() {
return optimalAngle;
}
/** /**
* @return The entire polar table * @return The entire polar table
*/ */
+51 -13
View File
@@ -23,9 +23,9 @@ public class ServerYacht {
//Boat info //Boat info
private BoatMeshType boatType; private BoatMeshType boatType;
private Double turnStep = 5.0; private Double turnStep = 5.0;
private Double maxSpeedMultiplier = 1.0; private Double boatTypeSpeedMultiplier = 1.0;
private Double turnStepMultiplier = 1.0; private Double boatTypeTurnStepMultiplier = 1.0;
private Double accelerationMultiplier = 1.0; private Double boatTypeAccelerationMultiplier = 1.0;
private Integer sourceId; private Integer sourceId;
private String hullID; //matches HullNum in the XML spec. private String hullID; //matches HullNum in the XML spec.
private String shortName; private String shortName;
@@ -55,6 +55,8 @@ public class ServerYacht {
//PowerUp //PowerUp
private TokenType powerUp; private TokenType powerUp;
private Long powerUpStartTime; private Long powerUpStartTime;
private Double powerUpSpeedMultiplier;
private Integer powerUpHandlingMultiplier;
//turning mode //turning mode
private Boolean continuouslyTurning; private Boolean continuouslyTurning;
@@ -78,11 +80,11 @@ public class ServerYacht {
this.legNumber = 0; this.legNumber = 0;
this.boatColor = Colors.getColor(sourceId - 1); this.boatColor = Colors.getColor(sourceId - 1);
this.powerUp = null; this.powerUp = null;
this.powerUpSpeedMultiplier = 1d;
this.powerUpHandlingMultiplier = 1;
this.hasEnteredRoundingZone = false; this.hasEnteredRoundingZone = false;
this.hasPassedLine = false; this.hasPassedLine = false;
this.hasPassedThroughGate = false; this.hasPassedThroughGate = false;
this.continuouslyTurning = false; this.continuouslyTurning = false;
} }
@@ -110,13 +112,33 @@ public class ServerYacht {
location = geoPoint; location = geoPoint;
} }
/**
* Powers up a yacht with a given yacht, only after powering it down first to avoid double power
* ups
*
* @param powerUp The given power up
*/
public void powerUp(TokenType powerUp) { public void powerUp(TokenType powerUp) {
powerDown();
switch (powerUp) {
case BOOST:
powerUpSpeedMultiplier = GameState.VELOCITY_BOOST_MULTIPLIER;
break;
case HANDLING:
powerUpHandlingMultiplier = GameState.HANDLING_BOOST_MULTIPLIER;
break;
}
this.powerUp = powerUp; this.powerUp = powerUp;
powerUpStartTime = System.currentTimeMillis(); powerUpStartTime = System.currentTimeMillis();
} }
/**
* Powers down a yacht, returning its power multipliers back to 1
*/
public void powerDown() { public void powerDown() {
this.powerUp = null; this.powerUp = null;
this.powerUpSpeedMultiplier = 1d;
this.powerUpHandlingMultiplier = 1;
} }
public Long getPowerUpStartTime() { public Long getPowerUpStartTime() {
@@ -133,7 +155,7 @@ public class ServerYacht {
* @param amount the amount by which to adjust the boat heading. * @param amount the amount by which to adjust the boat heading.
*/ */
public void adjustHeading(Double amount) { public void adjustHeading(Double amount) {
Double newVal = heading + (amount * turnStepMultiplier); Double newVal = heading + amount * powerUpHandlingMultiplier * boatTypeTurnStepMultiplier;
lastHeading = heading; lastHeading = heading;
heading = (double) Math.floorMod(newVal.longValue(), 360L); heading = (double) Math.floorMod(newVal.longValue(), 360L);
} }
@@ -435,18 +457,18 @@ public class ServerYacht {
} }
public void setBoatType(BoatMeshType boatType) { public void setBoatType(BoatMeshType boatType) {
this.accelerationMultiplier = boatType.accelerationMultiplier; this.boatTypeAccelerationMultiplier = boatType.accelerationMultiplier;
this.maxSpeedMultiplier = boatType.maxSpeedMultiplier; this.boatTypeSpeedMultiplier = boatType.maxSpeedMultiplier;
this.turnStepMultiplier = boatType.turnStep; this.boatTypeTurnStepMultiplier = boatType.turnStep;
this.boatType = boatType; this.boatType = boatType;
} }
public Double getMaxSpeedMultiplier() { public Double getBoatTypeSpeedMultiplier() {
return maxSpeedMultiplier; return boatTypeSpeedMultiplier;
} }
public Double getAccelerationMultiplier(){ public Double getBoatTypeAccelerationMultiplier() {
return accelerationMultiplier; return boatTypeAccelerationMultiplier;
} }
@@ -457,4 +479,20 @@ public class ServerYacht {
public void setContinuouslyTurning(Boolean continuouslyTurning) { public void setContinuouslyTurning(Boolean continuouslyTurning) {
this.continuouslyTurning = continuouslyTurning; this.continuouslyTurning = continuouslyTurning;
} }
public Double getPowerUpSpeedMultiplier() {
return powerUpSpeedMultiplier;
}
public void setPowerUpSpeedMultiplier(Double powerUpSpeedMultiplier) {
this.powerUpSpeedMultiplier = powerUpSpeedMultiplier;
}
public Integer getPowerUpHandlingMultiplier() {
return powerUpHandlingMultiplier;
}
public void setPowerUpHandlingMultiplier(Integer powerUpHandlingMultiplier) {
this.powerUpHandlingMultiplier = powerUpHandlingMultiplier;
}
} }
@@ -13,7 +13,9 @@ import seng302.model.stream.xml.parser.RaceXMLData;
*/ */
public class MarkOrder { public class MarkOrder {
private List<CompoundMark> raceMarkOrder; private List<CompoundMark> raceMarkOrder;
private List<CompoundMark> orderedUniqueCompoundMarks;
private Logger logger = LoggerFactory.getLogger(MarkOrder.class); private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
private List<Mark> allMarks;
public MarkOrder(RaceXMLData raceXMLData){ public MarkOrder(RaceXMLData raceXMLData){
@@ -25,6 +27,7 @@ public class MarkOrder {
); );
raceMarkOrder.add(compoundMark); raceMarkOrder.add(compoundMark);
} }
orderedUniqueCompoundMarks = new ArrayList<>(raceXMLData.getCompoundMarks().values());
} }
/** /**
@@ -39,6 +42,10 @@ public class MarkOrder {
return Collections.unmodifiableList(raceMarkOrder); return Collections.unmodifiableList(raceMarkOrder);
} }
public List<CompoundMark> getOrderedUniqueCompoundMarks() {
return orderedUniqueCompoundMarks;
}
/** /**
* @param seqID The seqID of the current mark the boat is heading to * @param seqID The seqID of the current mark the boat is heading to
* @return A Boolean indicating if this coming mark is the last one (finish line) * @return A Boolean indicating if this coming mark is the last one (finish line)
@@ -20,7 +20,9 @@ public enum PacketType {
RACE_REGISTRATION_REQUEST, RACE_REGISTRATION_REQUEST,
RACE_REGISTRATION_RESPONSE, RACE_REGISTRATION_RESPONSE,
RACE_CUSTOMIZATION_REQUEST, RACE_CUSTOMIZATION_REQUEST,
RACE_CUSTOMIZATION_RESPONSE; RACE_CUSTOMIZATION_RESPONSE,
SERVER_REGISTRATION, ROOM_CODE_REQUEST, LOBBY_REQUEST;
public static PacketType assignPacketType(int packetType, byte[] payload){ public static PacketType assignPacketType(int packetType, byte[] payload){
switch(packetType){ switch(packetType){
@@ -65,6 +67,10 @@ public enum PacketType {
return RACE_CUSTOMIZATION_REQUEST; return RACE_CUSTOMIZATION_REQUEST;
case 104: case 104:
return RACE_CUSTOMIZATION_RESPONSE; return RACE_CUSTOMIZATION_RESPONSE;
case 201:
return SERVER_REGISTRATION;
case 202:
return ROOM_CODE_REQUEST;
default: default:
} }
return OTHER; return OTHER;
@@ -1,5 +1,9 @@
package seng302.model.token; package seng302.model.token;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
/** /**
@@ -9,13 +13,50 @@ import seng302.model.GeoPoint;
public class Token extends GeoPoint { public class Token extends GeoPoint {
private TokenType tokenType; private TokenType tokenType;
private Random random = new Random();
//Constructor for creating a specific type client side
public Token(TokenType tokenType, double lat, double lng) { public Token(TokenType tokenType, double lat, double lng) {
super(lat, lng); super(lat, lng);
this.tokenType = tokenType; this.tokenType = tokenType;
} }
//Making random type server side
public Token(double lat, double lng) {
super(lat, lng);
assignRandomType();
}
//Making random type server side
public Token(GeoPoint geoPoint) {
super(geoPoint.getLat(), geoPoint.getLng());
assignRandomType();
}
public TokenType getTokenType() { public TokenType getTokenType() {
return tokenType; return tokenType;
} }
/**
* Assigns a random type to the token (including the random type token)
*/
public void assignRandomType() {
tokenType = TokenType.values()[random.nextInt(TokenType.values().length)];
}
/**
* Assigns a random, concrete type to the token (cannot be the random type)
*/
public void realiseRandom() {
List<TokenType> tokenTypeList = new ArrayList<>(Arrays.asList(TokenType.values()));
tokenTypeList.remove(TokenType.RANDOM);
tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size()));
}
/**
* Exists for testing purposes only
*/
public void assignType(TokenType tokenType) {
this.tokenType = tokenType;
}
} }
@@ -5,27 +5,32 @@ package seng302.model.token;
* Created by wmu16 on 28/08/17. * Created by wmu16 on 28/08/17.
*/ */
public enum TokenType { public enum TokenType {
BOOST(0),
HANDLING(1); BOOST(0, "Boost", 10_000),
HANDLING(1, "Handling", 10_000),
BUMPER(2, "Bumper", 10_000),
WIND_WALKER(3, "Wind Walker", 10_000),
RANDOM(4, "Random", 10_000);
private int value; private int value;
private String name;
private int timeout;
TokenType(int value) { TokenType(int value, String name, int timeout) {
this.value = value; this.value = value;
this.name = name;
this.timeout = timeout;
} }
public int getValue() { public int getValue() {
return value; return value;
} }
public static TokenType getToken(int value) { public String getName() {
switch (value) { return name;
case 0: }
return BOOST;
case 1: public int getTimeout() {
return HANDLING; return timeout;
default:
return BOOST;
}
} }
} }
@@ -0,0 +1,64 @@
package seng302.utilities;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import seng302.model.GeoPoint;
import seng302.model.mark.CompoundMark;
import seng302.model.token.Token;
/**
* A class for generating and spawning tokens in random locations
* Created by wmu16 on 27/09/17.
*/
public class RandomSpawn {
private static final Integer DEGREES_IN_CIRCLE = 360;
private HashMap<GeoPoint, Double> spawnRadii;
private Object[] spawnCentres;
private Random random;
/**
* @param markOrder this must be the ORDERED list of marks. Better yet UNIQUE to avoid over
* computation
*/
public RandomSpawn(List<CompoundMark> markOrder) {
this.spawnRadii = new HashMap<>();
random = new Random();
spawnRadii = generateSpawnRadii(markOrder);
spawnCentres = spawnRadii.keySet().toArray();
}
private HashMap<GeoPoint, Double> generateSpawnRadii(List<CompoundMark> markOrder) {
HashMap<GeoPoint, Double> spawnRadii = new HashMap<>();
for (int i = 0; i < markOrder.size() - 1; i++) {
GeoPoint spawnCentre = GeoUtility.getDirtyMidPoint(
markOrder.get(i).getMidPoint(),
markOrder.get(i + 1).getMidPoint());
Double distance = GeoUtility.getDistance(spawnCentre, markOrder.get(i).getMidPoint());
spawnRadii.put(spawnCentre, distance);
}
return spawnRadii;
}
/**
* @return A random token type at a random location in a random radii of the set of possible
* radii
*/
public Token getRandomToken() {
GeoPoint randomSpawnCentre = (GeoPoint) spawnCentres[random.nextInt(spawnCentres.length)];
Double spawnRadius = spawnRadii.get(randomSpawnCentre);
Double randomDistance = spawnRadius * random.nextDouble();
Double randomAngle = random.nextDouble() * DEGREES_IN_CIRCLE;
GeoPoint randomLocation = GeoUtility
.getGeoCoordinate(randomSpawnCentre, randomAngle, randomDistance);
return new Token(randomLocation);
}
}
@@ -101,6 +101,10 @@ public class Sounds {
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE); musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
musicPlayer.setVolume(0.3); musicPlayer.setVolume(0.3);
musicPlayer.play(); musicPlayer.play();
musicPlayer.setMute(musicMuted);
if (soundEffect != null) {
soundEffect.stop();
}
} }
@@ -136,7 +136,6 @@ public class StreamParser {
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14)); long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
String xmlMessage = new String( String xmlMessage = new String(
(Arrays.copyOfRange(payload, 14, (int) (14 + messageLength)))).trim(); (Arrays.copyOfRange(payload, 14, (int) (14 + messageLength)))).trim();
System.out.println(xmlMessage);
//Create XML document Object //Create XML document Object
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db; DocumentBuilder db;
+10 -4
View File
@@ -1,6 +1,7 @@
package seng302.utilities; package seng302.utilities;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -201,11 +202,16 @@ public class XMLParser {
public static Boolean tokensEnabled(Document doc) { public static Boolean tokensEnabled(Document doc) {
Element docEle = doc.getDocumentElement(); Element docEle = doc.getDocumentElement();
try { try {
Node tokenNode = docEle.getAttributeNode("Tokens"); NamedNodeMap namedNodeMap = docEle.getElementsByTagName("Tokens").item(0).getAttributes();
return Boolean.valueOf(XMLParser.getNodeAttributeString(tokenNode, "Enabled")); Node node = namedNodeMap.getNamedItem("Enabled");
if (node != null) {
return Boolean.parseBoolean(node.getNodeValue());
}
} catch (NullPointerException npe) { } catch (NullPointerException npe) {
npe.printStackTrace();
return false; return false;
} }
return false;
} }
public static Integer getMaxPlayers(Document doc) { public static Integer getMaxPlayers(Document doc) {
@@ -375,7 +381,7 @@ public class XMLParser {
/** /**
* This ungodly combination of existing methods and code blocks parses a race definition file. * This ungodly combination of existing methods and code blocks parses a race definition file.
* Look upon it and despair. * Look upon it and despair.
* @param url the location of the race def file * @param url The input file path
* @param serverName the name of the server * @param serverName the name of the server
* @param repetitions the repetitions of a segment of the race def file. * @param repetitions the repetitions of a segment of the race def file.
* @param maxPlayers max number of players. uses the default race max if null or greater than the actual max. * @param maxPlayers max number of players. uses the default race max if null or greater than the actual max.
@@ -390,7 +396,7 @@ public class XMLParser {
Document doc = null; Document doc = null;
try { try {
db = dbf.newDocumentBuilder(); db = dbf.newDocumentBuilder();
doc = db.parse(url); doc = db.parse(XMLParser.class.getResourceAsStream(url));
} catch (ParserConfigurationException | IOException | SAXException e) { } catch (ParserConfigurationException | IOException | SAXException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -14,6 +14,7 @@ import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import javafx.application.Platform;
import javafx.util.Pair; import javafx.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -34,6 +35,7 @@ import seng302.model.stream.xml.generator.RaceXMLTemplate;
import seng302.model.stream.xml.generator.RegattaXMLTemplate; import seng302.model.stream.xml.generator.RegattaXMLTemplate;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser; import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.ViewManager;
/** /**
* A class describing a single connection to a Server for the purposes of sending and receiving on * A class describing a single connection to a Server for the purposes of sending and receiving on
@@ -41,6 +43,8 @@ import seng302.utilities.XMLParser;
*/ */
public class ClientToServerThread implements Runnable { public class ClientToServerThread implements Runnable {
private boolean isStarted = false;
/** /**
* Functional interface for receiving packets from client socket. * Functional interface for receiving packets from client socket.
*/ */
@@ -54,6 +58,11 @@ public class ClientToServerThread implements Runnable {
void notifyDisconnection(String message); void notifyDisconnection(String message);
} }
@FunctionalInterface
public interface ConnectionErrorListener {
void notifyConnectionError(String message);
}
private class ByteReadException extends Exception { private class ByteReadException extends Exception {
private ByteReadException(String message) { private ByteReadException(String message) {
super(message); super(message);
@@ -63,6 +72,7 @@ public class ClientToServerThread implements Runnable {
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>(); private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
private List<ClientSocketListener> listeners = new ArrayList<>(); private List<ClientSocketListener> listeners = new ArrayList<>();
private List<DisconnectedFromHostListener> disconnectionListeners = new ArrayList<>(); private List<DisconnectedFromHostListener> disconnectionListeners = new ArrayList<>();
private ConnectionErrorListener connectionErrorListener = null;
private Thread thread; private Thread thread;
private Socket socket; private Socket socket;
@@ -81,6 +91,7 @@ public class ClientToServerThread implements Runnable {
private ByteArrayOutputStream crcBuffer; private ByteArrayOutputStream crcBuffer;
private boolean socketOpen = true; private boolean socketOpen = true;
private boolean ignoreDC = false;
/** /**
* Constructor for ClientToServerThread which takes in ipAddress and portNumber and attempts to * Constructor for ClientToServerThread which takes in ipAddress and portNumber and attempts to
@@ -110,6 +121,8 @@ public class ClientToServerThread implements Runnable {
* variable is false. * variable is false.
*/ */
public void run() { public void run() {
isStarted = true;
int sync1; int sync1;
int sync2; int sync2;
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop // TODO: 14/07/17 wmu16 - Work out how to fix this while loop
@@ -140,10 +153,12 @@ public class ClientToServerThread implements Runnable {
else { else {
if (clientId == -1) continue; // Do not continue if not registered if (clientId == -1) continue; // Do not continue if not registered
streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload)); streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
synchronized (this) {
for (ClientSocketListener csl : listeners) for (ClientSocketListener csl : listeners)
csl.newPacket(); csl.newPacket();
} }
} }
}
} else { } else {
logger.warn("Packet has been dropped", 1); logger.warn("Packet has been dropped", 1);
} }
@@ -157,6 +172,13 @@ public class ClientToServerThread implements Runnable {
logger.warn("Closed connection to server", 1); logger.warn("Closed connection to server", 1);
notifyDisconnectListeners("Connection to server was terminated"); notifyDisconnectListeners("Connection to server was terminated");
closeSocket(); closeSocket();
Platform.runLater(() -> {
if (ignoreDC) {
ViewManager.getInstance().showErrorSnackBar("Server rejected connection.");
ViewManager.getInstance().goToStartView();
}
});
} }
public void sendCustomizationRequest(CustomizeRequestType reqType, byte[] payload) { public void sendCustomizationRequest(CustomizeRequestType reqType, byte[] payload) {
@@ -171,13 +193,19 @@ public class ClientToServerThread implements Runnable {
} }
private void notifyDisconnectListeners (String message) { private void notifyDisconnectListeners (String message) {
if (socketOpen) { if (socketOpen && !ignoreDC) {
for (DisconnectedFromHostListener listener : disconnectionListeners) { for (DisconnectedFromHostListener listener : disconnectionListeners) {
listener.notifyDisconnection(message); listener.notifyDisconnection(message);
} }
} }
} }
private void handleConnectionError(String message){
if (connectionErrorListener != null){
connectionErrorListener.notifyConnectionError(message);
}
}
/** /**
* Sends a request to the server asking for a source ID * Sends a request to the server asking for a source ID
*/ */
@@ -217,8 +245,10 @@ public class ClientToServerThread implements Runnable {
else{ else{
alertErrorText = "Could not connect to server"; alertErrorText = "Could not connect to server";
} }
handleConnectionError("Server no longer available.");
notifyDisconnectListeners(alertErrorText); notifyDisconnectListeners(alertErrorText);
closeSocket();
System.out.println();
} }
/** /**
@@ -325,20 +355,32 @@ public class ClientToServerThread implements Runnable {
} }
public void addStreamObserver (ClientSocketListener streamListener) { public void addStreamObserver (ClientSocketListener streamListener) {
synchronized (this){
listeners.add(streamListener); listeners.add(streamListener);
} }
}
public void removeStreamObserver (ClientSocketListener streamListener) { public void removeStreamObserver (ClientSocketListener streamListener) {
listeners.remove(streamListener); listeners.remove(streamListener);
} }
public void addDisconnectionListener (DisconnectedFromHostListener listener) { public void addDisconnectionListener (DisconnectedFromHostListener listener) {
synchronized (this){
disconnectionListeners.add(listener); disconnectionListeners.add(listener);
} }
}
public void removeDisconnectionListener (DisconnectedFromHostListener listener) { public void removeDisconnectionListener (DisconnectedFromHostListener listener) {
synchronized (this){
disconnectionListeners.remove(listener); disconnectionListeners.remove(listener);
} }
}
public void setConnectionErrorListener(ConnectionErrorListener listener){
synchronized (this){
connectionErrorListener = listener;
}
}
private int readByte() throws ByteReadException { private int readByte() throws ByteReadException {
int currentByte = -1; int currentByte = -1;
@@ -352,8 +394,9 @@ public class ClientToServerThread implements Runnable {
} }
if (currentByte == -1) { if (currentByte == -1) {
notifyDisconnectListeners("Cannot read from server."); notifyDisconnectListeners("Cannot read from server.");
closeSocket();
logger.warn("InputStream reach end of stream", 1); logger.warn("InputStream reach end of stream", 1);
handleConnectionError("Could not connect to server. Server is no longer available.");
closeSocket();
} }
return currentByte; return currentByte;
} }
@@ -396,4 +439,12 @@ public class ClientToServerThread implements Runnable {
).getBuffer() ).getBuffer()
); );
} }
public boolean hasStarted() {
return isStarted;
}
public void ignoreDC() {
ignoreDC = true;
}
} }
+107 -34
View File
@@ -10,13 +10,15 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.util.Pair; import javafx.util.Pair;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
@@ -37,6 +39,7 @@ import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.parser.YachtEventData; import seng302.model.stream.parser.YachtEventData;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.model.token.TokenType;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.utilities.StreamParser; import seng302.utilities.StreamParser;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
@@ -92,7 +95,6 @@ public class GameClient {
socketThread.addDisconnectionListener((cause) -> { socketThread.addDisconnectionListener((cause) -> {
showConnectionError(cause); showConnectionError(cause);
tearDownConnection(); tearDownConnection();
Platform.runLater(this::loadStartScreen);
}); });
socketThread.addStreamObserver(this::parsePackets); socketThread.addStreamObserver(this::parsePackets);
@@ -109,13 +111,23 @@ public class GameClient {
ViewManager.getInstance().setProperty("serverName", regattaData.getRegattaName()); ViewManager.getInstance().setProperty("serverName", regattaData.getRegattaName());
ViewManager.getInstance().setProperty("mapName", regattaData.getCourseName()); ViewManager.getInstance().setProperty("mapName", regattaData.getCourseName());
getServerThread().setConnectionErrorListener((eMessage) -> {
ViewManager.getInstance().showErrorSnackBar(eMessage);
//destroyClientToServerThread();
});
this.lobbyController = ViewManager.getInstance().goToLobby(true); this.lobbyController = ViewManager.getInstance().goToLobby(true);
} catch (IOException ioe) { } catch (IOException ioe) {
showConnectionError("Unable to find server"); ViewManager.getInstance().showErrorSnackBar("There are no servers currently available.");
} }
} }
private void destroyClientToServerThread() {
socketThread.closeSocket();
socketThread = null;
}
/** /**
* Connect to a game as the host at the given address and starts the visualiser. * Connect to a game as the host at the given address and starts the visualiser.
* @param ipAddress IP to connect to. * @param ipAddress IP to connect to.
@@ -129,18 +141,45 @@ public class GameClient {
server = new MainServerThread(); server = new MainServerThread();
while (!server.hasStarted()){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try { try {
startClientToServerThread(ipAddress, 4942); startClientToServerThread(ipAddress, 4942);
} catch (IOException e) { } catch (IOException e) {
showConnectionError("Cannot connect to server as host"); showConnectionError("Cannot connect to server as host");
} }
// Wait for C2S thread
while (!socketThread.hasStarted()){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
socketThread.sendXML(race, serverName, numLegs, maxPlayers, tokensEnabled); socketThread.sendXML(race, serverName, numLegs, maxPlayers, tokensEnabled);
while (regattaData == null){
int triesLeft = 15;
while (regattaData == null && triesLeft > 0){
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
triesLeft--;
}
if (triesLeft <= 0){
showConnectionError("Could not launch server");
return null;
} }
this.lobbyController = ViewManager.getInstance().goToLobby(false); this.lobbyController = ViewManager.getInstance().goToLobby(false);
@@ -157,16 +196,6 @@ public class GameClient {
} }
} }
private void loadStartScreen() {
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("/views/StartScreenView.fxml"));
try {
holderPane.getChildren().clear();
holderPane.getChildren().add(fxmlLoader.load());
} catch (IOException e) {
showConnectionError("JavaFX crashed. Please restart the app");
}
}
private void showConnectionError (String message) { private void showConnectionError (String message) {
Platform.runLater(() -> { Platform.runLater(() -> {
@@ -175,7 +204,11 @@ public class GameClient {
controller.setContent(message); controller.setContent(message);
controller.setOptionButtonText("GO HOME"); controller.setOptionButtonText("GO HOME");
controller controller
.setOptionButtonEventHandler(event -> ViewManager.getInstance().goToStartView()); .setOptionButtonEventHandler(event -> {
System.out.println("inShowConnectionError");
ViewManager.getInstance().goToStartView();
});
}); });
} }
@@ -214,7 +247,6 @@ public class GameClient {
break; break;
case RACE_XML: case RACE_XML:
System.out.println("HEY I GOT A RACE MANG AND I AM CLIENT " + ((Boolean) (server==null)).toString());
RaceXMLData raceXMLData = XMLParser.parseRace( RaceXMLData raceXMLData = XMLParser.parseRace(
StreamParser.extractXmlMessage(packet) StreamParser.extractXmlMessage(packet)
); );
@@ -254,12 +286,7 @@ public class GameClient {
break; break;
case YACHT_EVENT_CODE: case YACHT_EVENT_CODE:
YachtEventData yachtEventData = StreamParser.extractYachtEventCode(packet); processYachtEvent(StreamParser.extractYachtEventCode(packet));
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN.getCode()) {
showPickUp();
}
break; break;
case CHATTER_TEXT: case CHATTER_TEXT:
@@ -279,12 +306,13 @@ public class GameClient {
ClientYacht player = allBoatsMap.get(socketThread.getClientId()); ClientYacht player = allBoatsMap.get(socketThread.getClientId());
raceView.loadRace(allBoatsMap, courseData, raceState, player); raceView.loadRace(allBoatsMap, courseData, raceState, player);
raceView.showView();
raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> { raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> {
if (isPressed) { if (isPressed) {
formatAndSendChatMessage(raceView.readChatInput()); formatAndSendChatMessage(raceView.readChatInput());
} }
}); });
sendToggleTurningModePacket(); // notify the server about player's steering mode
} }
} }
@@ -418,21 +446,66 @@ public class GameClient {
return courseData; return courseData;
} }
/**
* Appropriately displays the event client side given the YachtEventCode (collision / token..)
*
* @param yachtEventData The YachtEvent data packet
*/
private void processYachtEvent(YachtEventData yachtEventData) {
ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue());
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
showCollisionAlert(thisYacht);
} else if (yachtEventData.getEventId() == YachtEventType.POWER_DOWN.getCode()) {
thisYacht.powerDown();
Sounds.playTokenPickupSound(); // TODO: 23/09/17 This should be power down sound
} else if (yachtEventData.getEventId() == YachtEventType.BUMPER_CRASH.getCode()) {
showDisableAlert(thisYacht);
} else { //Else all token pickup types
TokenType tokenType = null;
if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) {
tokenType = TokenType.BOOST;
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_BUMPER.getCode()) {
tokenType = TokenType.BUMPER;
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_HANDLING.getCode()) {
tokenType = TokenType.HANDLING;
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_RANDOM.getCode()) {
tokenType = TokenType.RANDOM;
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_WIND_WALKER.getCode()) {
tokenType = TokenType.WIND_WALKER;
}
Sounds.playTokenPickupSound();
thisYacht.setPowerUp(tokenType);
}
}
/**
* Turns a disabled boat black until the bumper affect wears off
*
* @param yacht The yacht to show as disabled
*/
private void showDisableAlert(ClientYacht yacht) {
Color originalColor = yacht.getColour();
yacht.setColour(Color.BLACK);
Timer disableTimer = new Timer("Disable Timer");
disableTimer.schedule(new TimerTask() {
@Override
public void run() {
yacht.setColour(originalColor);
}
}, GameState.BUMPER_DISABLE_TIME);
}
/** /**
* Tells race view to show a collision animation. * Tells race view to show a collision animation.
*/ */
private void showCollisionAlert(YachtEventData yachtEventData) { private void showCollisionAlert(ClientYacht yacht) {
Sounds.playCrashSound(); Sounds.playCrashSound();
raceState.storeCollision( raceState.storeCollision(yacht);
allBoatsMap.get(
yachtEventData.getSubjectId().intValue()
)
);
}
// TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user
private void showPickUp() {
Sounds.playTokenPickupSound();
} }
private void formatAndSendChatMessage(String rawChat) { private void formatAndSendChatMessage(String rawChat) {
@@ -10,7 +10,7 @@ import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner; import seng302.model.mark.Corner;
/** /**
* Common elements of game visualizing classes. * Abstract class for keeping functionality common between race visualisation.
*/ */
public abstract class GameView { public abstract class GameView {
@@ -15,11 +15,13 @@ import javafx.scene.Node;
import javafx.scene.PerspectiveCamera; import javafx.scene.PerspectiveCamera;
import javafx.scene.SceneAntialiasing; import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene; import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale; import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate; import javafx.scene.transform.Translate;
import org.fxyz3d.scene.Skybox;
import seng302.gameServer.messages.RoundingSide; import seng302.gameServer.messages.RoundingSide;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.GameKeyBind; import seng302.model.GameKeyBind;
@@ -40,20 +42,24 @@ import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.fxObjects.MarkArrowFactory; import seng302.visualiser.fxObjects.MarkArrowFactory;
import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.BoatObject;
import seng302.visualiser.fxObjects.assets_3D.Marker3D; import seng302.visualiser.fxObjects.assets_3D.Marker3D;
import seng302.visualiser.fxObjects.assets_3D.Model;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory; import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
import seng302.visualiser.fxObjects.assets_3D.ModelType; import seng302.visualiser.fxObjects.assets_3D.ModelType;
/** /**
* Collection of animated3D assets that displays a race. * Collection of animated3D assets that displays a race.
*/ */
public class GameView3D extends GameView{ public class GameView3D extends GameView{
private final double FOV = 60; private final double FOV = 60;
private final double DEFAULT_CAMERA_X = 0; private final double DEFAULT_CAMERA_X = 0;
private final double DEFAULT_CAMERA_Y = 100; private final double DEFAULT_CAMERA_Y = 160;
private Group root3D; private Group root3D;
private SubScene view; private SubScene view;
private Group gameObjects;
private Group raceBorder = new Group(); private Group raceBorder = new Group();
// Cameras // Cameras
private PerspectiveCamera isometricCam; private PerspectiveCamera isometricCam;
@@ -64,13 +70,13 @@ public class GameView3D extends GameView{
should be used as the limits of the map. */ should be used as the limits of the map. */
private Map<Mark, Marker3D> markerObjects; private Map<Mark, Marker3D> markerObjects;
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>(); private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
private BoatObject selectedBoat = null;
private Group wakesGroup = new Group(); private Group wakesGroup = new Group();
private Group boatObjectGroup = new Group(); private Group boatObjectGroup = new Group();
private List<Node> mapTokens; private List<Node> mapTokens;
private AnimationTimer playerBoatAnimationTimer; private AnimationTimer playerBoatAnimationTimer;
private Group trail = new Group(); private Group trail = new Group();
private Double windDir; private Double windDir;
private Skybox skybox;
public GameView3D () { public GameView3D () {
isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y); isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y);
@@ -80,7 +86,7 @@ public class GameView3D extends GameView{
canvasWidth = canvasHeight = 300; canvasWidth = canvasHeight = 300;
for (PerspectiveCamera pc : Arrays.asList(isometricCam, topDownCam, chaseCam)) { for (PerspectiveCamera pc : Arrays.asList(isometricCam, topDownCam, chaseCam)) {
pc.setFarClip(600); pc.setFarClip(100000);
pc.setNearClip(0.1); pc.setNearClip(0.1);
pc.setFieldOfView(FOV); pc.setFieldOfView(FOV);
} }
@@ -88,21 +94,26 @@ public class GameView3D extends GameView{
gameObjects = new Group(); gameObjects = new Group();
root3D = new Group(isometricCam, gameObjects); root3D = new Group(isometricCam, gameObjects);
view = new SubScene( view = new SubScene(
root3D, 1000, 1000, true, SceneAntialiasing.BALANCED root3D, 5000, 3000, true, SceneAntialiasing.BALANCED
); );
view.setCamera(isometricCam); view.setCamera(isometricCam);
skybox = new Skybox(new Image(getClass().getResourceAsStream("/images/skybox.jpg")), 100000, isometricCam);
skybox.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS));
Model land = ModelFactory.importModel(ModelType.LAND_SMOOTH);
land.getAssets().getTransforms().add(new Rotate(90, Rotate.X_AXIS));
gameObjects.getChildren().addAll( gameObjects.getChildren().addAll(
ModelFactory.importModel(ModelType.OCEAN).getAssets(), raceBorder, trail, markers, tokens, skybox, land.getAssets()
raceBorder, trail, markers, tokens
); );
view.sceneProperty().addListener((obs, old, scene) -> { view.sceneProperty().addListener((obs, old, scene) -> {
if (scene != null) { if (scene != null) {
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement); scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement);
} }
}); });
} }
@Override @Override
@@ -156,6 +167,7 @@ public class GameView3D extends GameView{
} }
createMarkArrows(); createMarkArrows();
course.get(0).getMarks().forEach((mark -> markerObjects.get(mark).showNextExitArrow()));
//Scale race to markers if there is no border. //Scale race to markers if there is no border.
if (borderPoints == null) { if (borderPoints == null) {
@@ -262,6 +274,7 @@ public class GameView3D extends GameView{
public void cameraMovement(KeyEvent event) { public void cameraMovement(KeyEvent event) {
GameKeyBind keyBinds = GameKeyBind.getInstance(); GameKeyBind keyBinds = GameKeyBind.getInstance();
KeyAction keyPressed = keyBinds.getKeyAction(event.getCode()); KeyAction keyPressed = keyBinds.getKeyAction(event.getCode());
if (keyPressed != null) {
switch (keyPressed) { switch (keyPressed) {
case ZOOM_IN: case ZOOM_IN:
((RaceCamera) view.getCamera()).zoomIn(); ((RaceCamera) view.getCamera()).zoomIn();
@@ -286,6 +299,7 @@ public class GameView3D extends GameView{
break; break;
} }
} }
}
private void toggleCamera() { private void toggleCamera() {
Camera currCamera = view.getCamera(); Camera currCamera = view.getCamera();
@@ -314,11 +328,8 @@ public class GameView3D extends GameView{
wakesGroup.getChildren().add(newBoat.getWake()); wakesGroup.getChildren().add(newBoat.getWake());
wakes.add(newBoat.getWake()); wakes.add(newBoat.getWake());
boatObjectGroup.getChildren().add(newBoat); boatObjectGroup.getChildren().add(newBoat);
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { clientYacht.addLocationListener(this::updateBoatLocation);
BoatObject bo = boatObjects.get(boat); clientYacht.addColorChangeListener(this::updateBoatColor);
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
});
if (clientYacht.getSourceId().equals( if (clientYacht.getSourceId().equals(
ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { ViewManager.getInstance().getGameClient().getServerThread().getClientId())) {
@@ -336,6 +347,23 @@ public class GameView3D extends GameView{
return view; return view;
} }
/**
* Updates the boatObjects color with that of the clientYachts object. Used in notification from
* a listener on this attribute in clientYacht to re paint the boat mesh
*
* @param clientYacht The yacht to update the colour for
*/
private void updateBoatColor(ClientYacht clientYacht) {
boatObjects.get(clientYacht).setFill(clientYacht.getColour());
}
private void updateBoatLocation(ClientYacht boat, Double lat, Double lon, Double heading,
Boolean sailIn, Double velocity) {
BoatObject bo = boatObjects.get(boat);
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
}
/** /**
* Adds a border to the GameView and rescales to the size of the border, does not rescale if a * Adds a border to the GameView and rescales to the size of the border, does not rescale if a
* border already exists. Assumes the border is larger than the course. * border already exists. Assumes the border is larger than the course.
@@ -413,7 +441,27 @@ public class GameView3D extends GameView{
mapTokens = new ArrayList<>(); mapTokens = new ArrayList<>();
for (Token token : newTokens) { for (Token token : newTokens) {
Point2D location = scaledPoint.findScaledXY(token.getLat(), token.getLng()); Point2D location = scaledPoint.findScaledXY(token.getLat(), token.getLng());
Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets();
ModelType modelType = null;
switch (token.getTokenType()) {
case BOOST:
modelType = ModelType.VELOCITY_PICKUP;
break;
case HANDLING:
modelType = ModelType.HANDLING_PICKUP;
break;
case BUMPER:
modelType = ModelType.BUMPER_PICKUP;
break;
case RANDOM:
modelType = ModelType.RANDOM_PICKUP;
break;
case WIND_WALKER:
modelType = ModelType.WIND_WALKER_PICKUP;
break;
}
Node tokenObject = ModelFactory.importModel(modelType).getAssets();
tokenObject.setLayoutX(location.getX()); tokenObject.setLayoutX(location.getX());
tokenObject.setLayoutY(location.getY()); tokenObject.setLayoutY(location.getY());
mapTokens.add(tokenObject); mapTokens.add(tokenObject);
@@ -461,7 +509,7 @@ public class GameView3D extends GameView{
private void updateMarkArrows (ClientYacht yacht, int legNumber) { private void updateMarkArrows (ClientYacht yacht, int legNumber) {
CompoundMark compoundMark; CompoundMark compoundMark;
if (legNumber - 1 >= 0) { if (legNumber - 1 >= 0 && legNumber-1 < course.size()) {
Sounds.playMarkRoundingSound(); Sounds.playMarkRoundingSound();
compoundMark = course.get(legNumber-1); compoundMark = course.get(legNumber-1);
for (Mark mark : compoundMark.getMarks()) { for (Mark mark : compoundMark.getMarks()) {
@@ -469,7 +517,7 @@ public class GameView3D extends GameView{
} }
} }
CompoundMark nextMark = null; CompoundMark nextMark = null;
if (legNumber < course.size() - 1) { if (legNumber < course.size()) {
Sounds.playMarkRoundingSound(); Sounds.playMarkRoundingSound();
nextMark = course.get(legNumber); nextMark = course.get(legNumber);
for (Mark mark : nextMark.getMarks()) { for (Mark mark : nextMark.getMarks()) {
+13 -7
View File
@@ -4,6 +4,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.util.Pair; import javafx.util.Pair;
@@ -34,6 +35,8 @@ public class MapMaker {
private int index = 0; private int index = 0;
private XMLGenerator xmlGenerator = new XMLGenerator(); private XMLGenerator xmlGenerator = new XMLGenerator();
private List<String> maps = new ArrayList<>(Arrays.asList("default.xml", "horseshoe.xml"));
public static MapMaker getInstance() { public static MapMaker getInstance() {
if (instance == null) { if (instance == null) {
instance = new MapMaker(); instance = new MapMaker();
@@ -42,15 +45,17 @@ public class MapMaker {
} }
private MapMaker() { private MapMaker() {
File dir = new File(MapMaker.class.getResource("/maps/").getPath()); for (String mapPath : maps){
File[] directoryListing = dir.listFiles(); String path = ("/maps/" + mapPath);
if (directoryListing != null) {
for (File child : directoryListing) {
Pair<RegattaXMLTemplate, RaceXMLTemplate> regattaRace = XMLParser.parseRaceDef( Pair<RegattaXMLTemplate, RaceXMLTemplate> regattaRace = XMLParser.parseRaceDef(
child.getAbsolutePath(), "", 1, null, false path, "", 1, null, false
); );
filePaths.add(child.getAbsolutePath());
RegattaXMLTemplate regattaTemplate = regattaRace.getKey(); RegattaXMLTemplate regattaTemplate = regattaRace.getKey();
filePaths.add(path);
regattas.add(new RegattaXMLData( regattas.add(new RegattaXMLData(
regattaTemplate.getRegattaId(), regattaTemplate.getRegattaId(),
regattaTemplate.getName(), regattaTemplate.getName(),
@@ -79,9 +84,10 @@ public class MapMaker {
new ArrayList<>(race.getCompoundMarks().values()), new ArrayList<>(race.getCompoundMarks().values()),
race.getMarkSequence(), race.getCourseLimit() race.getMarkSequence(), race.getCourseLimit()
)); ));
races.add(race); races.add(race);
} }
}
} }
public void next() { public void next() {
@@ -6,11 +6,10 @@ import seng302.gameServer.ServerDescription;
import javax.jmdns.JmDNS; import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent; import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener; import javax.jmdns.ServiceListener;
import javax.jmdns.impl.JmDNSImpl;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.Set;
import static seng302.gameServer.ServerAdvertiser.getLocalHostIp; import static seng302.gameServer.ServerAdvertiser.getLocalHostIp;
@@ -18,6 +17,7 @@ import static seng302.gameServer.ServerAdvertiser.getLocalHostIp;
* Listens for servers on the local network * Listens for servers on the local network
*/ */
public class ServerListener{ public class ServerListener{
private static Integer SERVICE_REFRESH_INTERVAL = 5 * 1000;
private static ServerListener instance; private static ServerListener instance;
private ServerListenerDelegate delegate; private ServerListenerDelegate delegate;
private JmDNS jmdns = null; private JmDNS jmdns = null;
@@ -91,8 +91,16 @@ public class ServerListener{
private ServerListener() throws IOException { private ServerListener() throws IOException {
jmdns = JmDNS.create(InetAddress.getByName(getLocalHostIp())); jmdns = JmDNS.create(InetAddress.getByName(getLocalHostIp()));
listener = new GameServeMonitor(); listener = new GameServeMonitor();
jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener); jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener);
/*new Timer().schedule(new TimerTask() {
@Override
public void run() {
refresh();
}
}, 50, SERVICE_REFRESH_INTERVAL);*/
} }
public static ServerListener getInstance() throws IOException { public static ServerListener getInstance() throws IOException {
@@ -110,4 +118,25 @@ public class ServerListener{
public void setDelegate(ServerListenerDelegate delegate){ public void setDelegate(ServerListenerDelegate delegate){
this.delegate = delegate; this.delegate = delegate;
} }
public void refresh(){
ArrayList<ServerDescription> servers = new ArrayList<>(listener.servers);
for (ServerDescription serverDescription : servers){
if (serverDescription.hasExpired()){
jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverDescription.getName());
}
else{
serverDescription.hasBeenRefreshed();
}
}
for (ServerDescription server : servers){
if (server.serverShouldBeRemoved()){
listener.servers.remove(server);
delegate.serverRemoved(new ArrayList<ServerDescription>(listener.servers));
}
}
}
} }
@@ -16,8 +16,8 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera {
private final Double MAX_Y = 170.0; private final Double MAX_Y = 170.0;
private final Double PAN_LIMIT = 160.0; private final Double PAN_LIMIT = 160.0;
private final Double NEAR_ZOOM_LIMIT = -50.0; private final Double NEAR_ZOOM_LIMIT = -30.0;
private final Double FAR_ZOOM_LIMIT = -160.0; private final Double FAR_ZOOM_LIMIT = -180.0;
private Double horizontalPan; private Double horizontalPan;
private Double verticalPan; private Double verticalPan;
@@ -29,7 +29,8 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera {
super(true); super(true);
transforms = this.getTransforms(); transforms = this.getTransforms();
zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; // zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0;
zoomFactor = FAR_ZOOM_LIMIT;
horizontalPan = cameraStartX; horizontalPan = cameraStartX;
verticalPan = cameraStartY; verticalPan = cameraStartY;
@@ -11,9 +11,9 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject;
public class TopDownCamera extends PerspectiveCamera implements RaceCamera { public class TopDownCamera extends PerspectiveCamera implements RaceCamera {
private final Double PAN_LIMIT = 30.0; private final Double PAN_LIMIT = 40d;
private final Double NEAR_ZOOM_LIMIT = -30.0; private final Double NEAR_ZOOM_LIMIT = -35d;
private final Double FAR_ZOOM_LIMIT = -130.0; private final Double FAR_ZOOM_LIMIT = -145d;
private final Double ZOOM_STEP = 2.5; private final Double ZOOM_STEP = 2.5;
private ObservableList<Transform> transforms; private ObservableList<Transform> transforms;
@@ -2,12 +2,6 @@ package seng302.visualiser.controllers;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXDialog;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@@ -19,6 +13,7 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import seng302.discoveryServer.DiscoveryServerClient;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
@@ -33,6 +28,13 @@ import seng302.visualiser.MapPreview;
import seng302.visualiser.controllers.cells.PlayerCell; import seng302.visualiser.controllers.cells.PlayerCell;
import seng302.visualiser.controllers.dialogs.BoatCustomizeController; import seng302.visualiser.controllers.dialogs.BoatCustomizeController;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
public class LobbyController implements Initializable { public class LobbyController implements Initializable {
private final double INITIAL_MAP_HEIGHT = 770d; private final double INITIAL_MAP_HEIGHT = 770d;
@@ -53,6 +55,8 @@ public class LobbyController implements Initializable {
private Label mapName; private Label mapName;
@FXML @FXML
private AnchorPane serverMap; private AnchorPane serverMap;
@FXML
private Label roomLabel;
//---------FXML END---------// //---------FXML END---------//
private RaceState raceState; private RaceState raceState;
@@ -64,7 +68,7 @@ public class LobbyController implements Initializable {
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
roomLabel.setText("");
this.playerBoats = ViewManager.getInstance().getGameClient().getAllBoatsMap(); this.playerBoats = ViewManager.getInstance().getGameClient().getAllBoatsMap();
if (this.playersColor == null) { if (this.playersColor == null) {
@@ -87,6 +91,21 @@ public class LobbyController implements Initializable {
serverName.setText(ViewManager.getInstance().getProperty("serverName")); serverName.setText(ViewManager.getInstance().getProperty("serverName"));
mapName.setText(ViewManager.getInstance().getProperty("mapName")); mapName.setText(ViewManager.getInstance().getProperty("mapName"));
int tries = 0;
while (DiscoveryServerClient.getRoomCode() == null && tries <= 10){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
tries ++;
}
if (DiscoveryServerClient.getRoomCode() != null){
setRoomCode(DiscoveryServerClient.getRoomCode());
}
ViewManager.getInstance().getPlayerList().addListener((ListChangeListener<String>) c -> Platform.runLater(this::refreshPlayerList)); ViewManager.getInstance().getPlayerList().addListener((ListChangeListener<String>) c -> Platform.runLater(this::refreshPlayerList));
ViewManager.getInstance().getPlayerList().setAll(ViewManager.getInstance().getPlayerList().sorted()); ViewManager.getInstance().getPlayerList().setAll(ViewManager.getInstance().getPlayerList().sorted());
@@ -137,6 +156,7 @@ public class LobbyController implements Initializable {
return customizationDialog; return customizationDialog;
} }
/** /**
* Initializes a top down preview of the race course map. * Initializes a top down preview of the race course map.
*/ */
@@ -205,7 +225,7 @@ public class LobbyController implements Initializable {
} }
private void leaveLobby() { private void leaveLobby() {
System.out.println("LEFT LOBBY");
ViewManager.getInstance().getGameClient().stopGame(); ViewManager.getInstance().getGameClient().stopGame();
ViewManager.getInstance().goToStartView(); ViewManager.getInstance().goToStartView();
} }
@@ -231,4 +251,8 @@ public class LobbyController implements Initializable {
public void closeCustomizationDialog() { public void closeCustomizationDialog() {
customizationDialog.close(); customizationDialog.close();
} }
public void setRoomCode(String roomCode) {
roomLabel.setText("Room: " + roomCode);
}
} }
@@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javafx.animation.RotateTransition;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
@@ -29,6 +30,7 @@ import javafx.scene.control.ComboBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
@@ -42,11 +44,13 @@ import javafx.scene.shape.Polyline;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import javafx.util.Duration;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.TokenType;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.GameView3D; import seng302.visualiser.GameView3D;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController; import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
@@ -63,7 +67,13 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject;
public class RaceViewController extends Thread implements ImportantAnnotationDelegate { public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
private final int CHAT_LIMIT = 128; private final int CHAT_LIMIT = 128;
private static final Double ICON_BLINK_TIMEOUT_RATIO = 0.6;
private static final Integer ICON_BLINK_PERIOD = 500;
@FXML
private AnchorPane loadingScreenPane;
@FXML
private ImageView loadingScreen;
@FXML @FXML
private Pane basePane; private Pane basePane;
@FXML @FXML
@@ -106,6 +116,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private Label windSpeedLabel; private Label windSpeedLabel;
@FXML @FXML
private Label positionLabel, boatSpeedLabel, boatHeadingLabel; private Label positionLabel, boatSpeedLabel, boatHeadingLabel;
@FXML
private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon;
//Race Data //Race Data
private Map<Integer, ClientYacht> participants; private Map<Integer, ClientYacht> participants;
@@ -126,7 +138,31 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private JFXDialog finishScreenDialog; private JFXDialog finishScreenDialog;
private FinishDialogController finishDialogController; private FinishDialogController finishDialogController;
//Icon stuff
private Timer blinkingTimer = new Timer();
private ImageView iconToDisplay;
private Double lastWindDirection;
public void initialize() { public void initialize() {
contentStackPane.setVisible(false);
Image loadingImage = new Image("PP.png");
loadingScreen.setImage(loadingImage);
//Centers the Image within the image view
double w = 0;
double h = 0;
double ratioX = loadingScreen.getFitWidth() / loadingImage.getWidth();
double ratioY = loadingScreen.getFitHeight() / loadingImage.getHeight();
double reduceRatio = 0;
if(ratioX >= ratioY) {
reduceRatio = ratioY;
} else {
reduceRatio = ratioX;
}
w = loadingImage.getWidth() * reduceRatio;
h = loadingImage.getHeight() * reduceRatio;
loadingScreen.setX((loadingScreen.getFitWidth() - w) / 2);
loadingScreen.setY((loadingScreen.getFitHeight() - h) / 2);
Sounds.stopMusic(); Sounds.stopMusic();
Sounds.playRaceMusic(); Sounds.playRaceMusic();
@@ -156,13 +192,29 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
chatHistory.decreaseOpacity(); chatHistory.decreaseOpacity();
} }
}); });
lastWindDirection = 0d;
} }
public void showFinishDialog(ArrayList<ClientYacht> finishedBoats) { public void showFinishDialog(ArrayList<ClientYacht> finishedBoats) {
System.out.println("called");
raceState.setRaceStarted(false); raceState.setRaceStarted(false);
ViewManager.getInstance().getGameClient().getServerThread().ignoreDC();
createFinishDialog(finishedBoats); createFinishDialog(finishedBoats);
} }
public void showView(){
loadingScreenPane.setVisible(false);
contentStackPane.setVisible(true);
Platform.runLater(new Runnable() {
@Override
public void run() {
contentStackPane.requestFocus();
}
});
}
/** /**
* Create finishScreenDialog and set up finishDialogController. * Create finishScreenDialog and set up finishDialogController.
*/ */
@@ -183,6 +235,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}); });
} }
public void loadRace ( public void loadRace (
Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState, Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState,
ClientYacht player) { ClientYacht player) {
@@ -201,6 +254,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
}); });
player.addPowerUpListener(this::displayPowerUpIcon);
player.addPowerDownListener(this::removeIcon);
updateOrder(raceState.getPlayerPositions()); updateOrder(raceState.getPlayerPositions());
gameView = new GameView3D(); gameView = new GameView3D();
Platform.runLater(() -> { Platform.runLater(() -> {
@@ -235,6 +291,68 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
Platform.runLater(this::initializeUpdateTimer); Platform.runLater(this::initializeUpdateTimer);
} }
/**
* Displays the relevant icon, starts blinking it when it is close to turning off and then
* switches it off after the tokens time out
*
* @param yacht The yacht only for which we are displaying the icon
* @param tokenType The type of token, indicating what icon needs to be displayed
*/
private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) {
if (yacht == player) {
if (iconToDisplay != null) {
iconToDisplay.setVisible(false);
}
switch (tokenType) {
case BOOST:
iconToDisplay = velocityIcon;
break;
case HANDLING:
iconToDisplay = handlingIcon;
break;
case WIND_WALKER:
iconToDisplay = windWalkerIcon;
break;
case BUMPER:
iconToDisplay = bumperIcon;
break;
case RANDOM:
iconToDisplay = badRandomIcon;
break;
default:
iconToDisplay = velocityIcon;
}
//Turn icon on
iconToDisplay.setVisible(true);
//Start blinking icon towards end
if (blinkingTimer != null) {
blinkingTimer.cancel();
}
blinkingTimer = new Timer("Blinking Timer");
blinkingTimer.schedule(new TimerTask() {
Boolean isVisible = true;
@Override
public void run() {
isVisible = !isVisible;
iconToDisplay.setVisible(isVisible);
}
}, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD);
}
}
public void removeIcon(ClientYacht yacht) {
if (yacht == player) {
blinkingTimer.cancel();
iconToDisplay.setVisible(false);
iconToDisplay = null;
}
}
/** /**
* The important annotations have been changed, update this view * The important annotations have been changed, update this view
* *
@@ -325,7 +443,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
*/ */
private void updateWindDirection(double direction) { private void updateWindDirection(double direction) {
windDirectionLabel.setText(String.format("%.1f°", direction)); windDirectionLabel.setText(String.format("%.1f°", direction));
windImageView.setRotate(direction); RotateTransition rt = new RotateTransition(Duration.millis(300), windImageView);
rt.setByAngle(direction - lastWindDirection);
rt.setCycleCount(3);
rt.setAutoReverse(true);
rt.play();
lastWindDirection = direction;
// windImageView.setRotate(direction);
} }
/** /**
@@ -7,6 +7,7 @@ import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator; import com.jfoenix.validation.RequiredFieldValidator;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -22,11 +23,22 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.discoveryServer.DiscoveryServerClient;
import seng302.discoveryServer.util.ServerListing;
import seng302.gameServer.ServerDescription; import seng302.gameServer.ServerDescription;
import seng302.gameServer.messages.ServerRegistrationMessage;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.ServerListener; import seng302.visualiser.ServerListener;
import seng302.visualiser.ServerListenerDelegate; import seng302.visualiser.ServerListenerDelegate;
import seng302.visualiser.controllers.cells.ServerCell; import seng302.visualiser.controllers.cells.ServerCell;
import seng302.visualiser.controllers.dialogs.DirectConnectController;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import seng302.visualiser.controllers.dialogs.ServerCreationController;
import seng302.visualiser.validators.HostNameFieldValidator; import seng302.visualiser.validators.HostNameFieldValidator;
import seng302.visualiser.validators.NumberRangeValidator; import seng302.visualiser.validators.NumberRangeValidator;
import seng302.visualiser.validators.ValidationTools; import seng302.visualiser.validators.ValidationTools;
@@ -46,15 +58,29 @@ public class ServerListController implements Initializable, ServerListenerDelega
private JFXButton serverListHostButton; private JFXButton serverListHostButton;
//Direct Connect //Direct Connect
@FXML @FXML
private JFXButton connectButton; private JFXButton directConnectButton;
@FXML
private JFXTextField serverHostName;
@FXML @FXML
private JFXTextField serverPortNumber; private JFXTextField serverPortNumber;
@FXML
private JFXButton roomConnectButton;
@FXML
private JFXTextField roomNumber;
@FXML
private JFXButton autoSelectGame;
//---------FXML END---------// //---------FXML END---------//
private Label noServersFound; private Label noServersFound;
private Logger logger = LoggerFactory.getLogger(ServerListController.class); private Logger logger = LoggerFactory.getLogger(ServerListController.class);
private JFXDialog directConnectDialog;
private JFXDialog serverCreationDialog;
private List<ServerCreationDialogListener> serverCreationDialogListeners = new ArrayList<>();
@FunctionalInterface
public interface ServerCreationDialogListener {
void notifyClosure();
}
// TODO: 12/09/17 ajm412: break this method down, its way too long. // TODO: 12/09/17 ajm412: break this method down, its way too long.
@Override @Override
@@ -62,13 +88,25 @@ public class ServerListController implements Initializable, ServerListenerDelega
serverListVBox.minWidthProperty().bind(serverListScrollPane.widthProperty()); serverListVBox.minWidthProperty().bind(serverListScrollPane.widthProperty());
// Set Event Bindings // Set Event Bindings
connectButton.setOnMouseEntered(event -> Sounds.playHoverSound()); directConnectButton.setOnMouseEntered(event -> Sounds.playHoverSound());
serverListHostButton.setOnMouseEntered(event -> Sounds.playHoverSound()); serverListHostButton.setOnMouseEntered(event -> Sounds.playHoverSound());
connectButton.setOnMouseReleased(event -> {
attemptToDirectConnect();
roomNumber.setOnKeyPressed(event -> {
if (event.getCode().equals(KeyCode.ENTER)) {
connectToRoomCode(roomNumber.getText());
}
});
directConnectButton.setOnMouseReleased(event -> {
directConnectDialog.show();
Sounds.playButtonClick(); Sounds.playButtonClick();
}); });
for (JFXTextField textField : Arrays.asList(serverHostName, serverPortNumber)) {
directConnectDialog = createDirectConnectDialog();
for (JFXTextField textField : Arrays.asList(roomNumber)) {
// Event for pressing enter to submit direct connection // Event for pressing enter to submit direct connection
textField.setOnKeyPressed(event -> { textField.setOnKeyPressed(event -> {
if (event.getCode().equals(KeyCode.ENTER)) { if (event.getCode().equals(KeyCode.ENTER)) {
@@ -82,15 +120,41 @@ public class ServerListController implements Initializable, ServerListenerDelega
textField.getValidators().add(validator); textField.getValidators().add(validator);
} }
autoSelectGame.setOnMouseReleased(e -> {
ServerListing listing;
DiscoveryServerClient client = new DiscoveryServerClient();
try {
listing = client.getRandomServer();
} catch (Exception e1) {
ViewManager.getInstance().showErrorSnackBar("Unable to connect to matchmaking server. Are you connected to the internet?");
return;
}
if (client.didFail()){
return;
}
if (listing == null || listing.equals(ServerRegistrationMessage.getEmptyRegistration())) {
ViewManager.getInstance().showErrorSnackBar("There are currently no servers available for you to connect to.");
return;
}
ViewManager.getInstance().getGameClient().runAsClient(listing.getAddress(), listing.getPortNumber());
});
/*
// Validating the hostname // Validating the hostname
HostNameFieldValidator hostNameValidator = new HostNameFieldValidator(); HostNameFieldValidator hostNameValidator = new HostNameFieldValidator();
hostNameValidator.setMessage("Host name incorrect"); hostNameValidator.setMessage("Host name incorrect");
serverHostName.getValidators().add(hostNameValidator); roomCodeInput.getValidators().add(hostNameValidator);
// Validating the port number // Validating the port number
NumberRangeValidator portNumberValidator = new NumberRangeValidator(1025, 65536); NumberRangeValidator portNumberValidator = new NumberRangeValidator(1025, 65536);
portNumberValidator.setMessage("Port number incorrect"); portNumberValidator.setMessage("Port number incorrect");
serverPortNumber.getValidators().add(portNumberValidator); serverPortNumber.getValidators().add(portNumberValidator);
TODO later
*/
// Start listening for servers on network // Start listening for servers on network
try { try {
@@ -111,10 +175,17 @@ public class ServerListController implements Initializable, ServerListenerDelega
); );
serverListVBox.getChildren().add(noServersFound); serverListVBox.getChildren().add(noServersFound);
roomConnectButton.setOnMouseReleased(e -> {
String roomCode = roomNumber.getText();
connectToRoomCode(roomCode);
});
// Set up dialog for server creation // Set up dialog for server creation
serverListHostButton.setOnAction(action -> { serverListHostButton.setOnAction(action -> {
showServerCreationDialog(); showServerCreationDialog();
}); });
addServerCreationDialogListener(this::closeServerCreationDialog);
} }
/** /**
@@ -125,9 +196,11 @@ public class ServerListController implements Initializable, ServerListenerDelega
FXMLLoader dialogContent = new FXMLLoader(getClass().getResource( FXMLLoader dialogContent = new FXMLLoader(getClass().getResource(
"/views/dialogs/ServerCreationDialog.fxml")); "/views/dialogs/ServerCreationDialog.fxml"));
try { try {
JFXDialog dialog = new JFXDialog(serverListMainStackPane, dialogContent.load(), serverCreationDialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
DialogTransition.CENTER); DialogTransition.CENTER);
dialog.show(); ServerCreationController serverCreationController = dialogContent.getController();
serverCreationController.setListener(serverCreationDialogListeners);
serverCreationDialog.show();
Sounds.playButtonClick(); Sounds.playButtonClick();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -136,13 +209,35 @@ public class ServerListController implements Initializable, ServerListenerDelega
}); });
} }
private JFXDialog createDirectConnectDialog() {
FXMLLoader dialog = new FXMLLoader(
getClass().getResource("/views/dialogs/DirectConnect.fxml"));
JFXDialog dcDialog = null;
try {
dcDialog = new JFXDialog(serverListMainStackPane, dialog.load(),
JFXDialog.DialogTransition.CENTER);
} catch (IOException e) {
e.printStackTrace();
}
DirectConnectController controller = dialog.getController();
return dcDialog;
}
private void closeServerCreationDialog() {
serverCreationDialog.close();
}
/** /**
* Validates the connection and attempts to connect to a given hostname and port number. * Validates the connection and attempts to connect to a given hostname and port number.
*/ */
private void attemptToDirectConnect() { private void attemptToDirectConnect() {
if (validateDirectConnection(serverHostName.getText(), serverPortNumber.getText())) { /*if (validateDirectConnection(serverHostName.getText(), serverPortNumber.getText())) {
DirectConnect(); DirectConnect();
} }*/
} }
/** /**
@@ -152,10 +247,40 @@ public class ServerListController implements Initializable, ServerListenerDelega
* @return boolean value if host and port number are valid values * @return boolean value if host and port number are valid values
*/ */
private Boolean validateDirectConnection(String hostName, String portNumber) { private Boolean validateDirectConnection(String hostName, String portNumber) {
Boolean hostNameValid = ValidationTools.validateTextField(serverHostName); /*Boolean hostNameValid = ValidationTools.validateTextField(serverHostName);
*
Boolean portNumberValid = ValidationTools.validateTextField(serverPortNumber); Boolean portNumberValid = ValidationTools.validateTextField(serverPortNumber);
return hostNameValid && portNumberValid; return hostNameValid && portNumberValid;*/
return true;
}
private void connectToRoomCode(String roomCode){
DiscoveryServerClient client = new DiscoveryServerClient();
ServerListing serverListing;
if (client.didFail()){
return;
}
try {
serverListing = client.getServerForRoomCode(roomCode);
} catch (Exception e) {
ViewManager.getInstance().showErrorSnackBar("Error connecting to matchmaking server. Please try again later.");
return;
}
if (serverListing == null || serverListing.equals(new ServerListing("","","", 0, 0))){
ViewManager.getInstance().showErrorSnackBar("No servers could be found with that room code.");
return;
}
try {
ViewManager.getInstance().getGameClient().runAsClient(serverListing.getAddress(), serverListing.getPortNumber());
}
catch (Exception e) {
ViewManager.getInstance().showErrorSnackBar("Error connecting to matchmaking service.");
}
} }
/** /**
@@ -163,7 +288,7 @@ public class ServerListController implements Initializable, ServerListenerDelega
*/ */
private void DirectConnect() { private void DirectConnect() {
Sounds.playButtonClick(); Sounds.playButtonClick();
ViewManager.getInstance().getGameClient().runAsClient(serverHostName.getText(), Integer.parseInt(serverPortNumber.getText())); // ViewManager.getInstance().getGameClient().runAsClient(serverHostName.getText(), Integer.parseInt(serverPortNumber.getText()));
} }
/** /**
@@ -204,4 +329,14 @@ public class ServerListController implements Initializable, ServerListenerDelega
public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) { public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) {
Platform.runLater(() -> refreshServers(servers)); Platform.runLater(() -> refreshServers(servers));
} }
private void addServerCreationDialogListener(
ServerCreationDialogListener serverCreationDialogListener) {
serverCreationDialogListeners.add(serverCreationDialogListener);
}
private void removeServerCreationDialogListener(
ServerCreationDialogListener serverCreationDialogListener) {
serverCreationDialogListeners.remove(serverCreationDialogListener);
}
} }
@@ -0,0 +1,58 @@
package seng302.visualiser.controllers;
import com.jfoenix.controls.JFXDecorator;
import com.jfoenix.controls.JFXSnackbar;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import seng302.gameServer.ServerAdvertiser;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Created by Kusal on 26-Sep-17.
*/
public class SplashScreenController implements Initializable{
@FXML
private StackPane rootPane;
@Override
public void initialize(URL location, ResourceBundle resources) {
new SplashScreen().start();
}
class SplashScreen extends Thread {
public void run(){
try {
Thread.sleep(2000);
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
Stage stage = new Stage();
ViewManager.getInstance().initialStartView(stage);
} catch (Exception e) {
e.printStackTrace();
}
rootPane.getScene().getWindow().hide();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@@ -59,7 +59,7 @@ public class StartScreenController implements Initializable{
/** /**
* Changes the view to the Server Browser. * Changes the view to the Server Browser.
*/ */
private void goToServerBrowser() { public void goToServerBrowser() {
try { try {
ViewManager.getInstance().setScene(serverList); ViewManager.getInstance().setScene(serverList);
} catch (Exception e) { } catch (Exception e) {
@@ -6,30 +6,27 @@ import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialog.DialogTransition; import com.jfoenix.controls.JFXDialog.DialogTransition;
import com.jfoenix.controls.JFXSnackbar; import com.jfoenix.controls.JFXSnackbar;
import com.jfoenix.svg.SVGGlyph; import com.jfoenix.svg.SVGGlyph;
import java.io.IOException;
import java.util.HashMap;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Cursor; import javafx.scene.*;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.ServerAdvertiser; import seng302.gameServer.ServerAdvertiser;
import seng302.utilities.BonjourInstallChecker;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.GameClient; import seng302.visualiser.GameClient;
import seng302.visualiser.controllers.dialogs.KeyBindingDialogController; import seng302.visualiser.controllers.dialogs.KeyBindingDialogController;
import seng302.visualiser.controllers.dialogs.PopupDialogController; import seng302.visualiser.controllers.dialogs.PopupDialogController;
import java.io.IOException;
import java.util.HashMap;
public class ViewManager { public class ViewManager {
private static ViewManager instance; private static ViewManager instance;
@@ -56,10 +53,20 @@ public class ViewManager {
if (instance == null) { if (instance == null) {
instance = new ViewManager(); instance = new ViewManager();
} }
return instance; return instance;
} }
public void initialiseSplashScreen(Stage stage) throws IOException {
this.stage = stage;
Parent root = FXMLLoader.load(getClass().getResource("/views/SplashScreen.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Party Parrots At Sea");
stage.getIcons().add(new Image(getClass().getResourceAsStream("/PP.png")));
stage.setScene(scene);
stage.initStyle(StageStyle.UNDECORATED);
stage.show();
}
/** /**
* Initialize the start view in the given stage. * Initialize the start view in the given stage.
*/ */
@@ -67,7 +74,6 @@ public class ViewManager {
this.stage = stage; this.stage = stage;
Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml")); Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml"));
stage.setTitle("Party Parrots At Sea"); stage.setTitle("Party Parrots At Sea");
JFXDecorator decorator = new JFXDecorator(stage, root, false, true, true); JFXDecorator decorator = new JFXDecorator(stage, root, false, true, true);
decorator.setCustomMaximize(true); decorator.setCustomMaximize(true);
decorator.applyCss(); decorator.applyCss();
@@ -102,8 +108,6 @@ public class ViewManager {
gameClient.stopGame(); gameClient.stopGame();
System.exit(0); System.exit(0);
}); });
jfxSnackbar = new JFXSnackbar(decorator);
} }
/** /**
@@ -188,6 +192,7 @@ public class ViewManager {
} }
}); });
jfxSnackbar = new JFXSnackbar(decorator);
} }
/** /**
@@ -268,16 +273,9 @@ public class ViewManager {
jfxSnackbar.show(snackbarText, 1500); jfxSnackbar.show(snackbarText, 1500);
} }
/**
* Determines if a PC has compatibility with the bonjour protocol for server detection.
*/
private void checkCompatibility() {
if (BonjourInstallChecker.isBonjourSupported()) {
BonjourInstallChecker.openInstallUrl();
}
}
private void closeAll() { private void closeAll() {
if (stage!= null) stage.close();
try { try {
ServerAdvertiser.getInstance().unregister(); ServerAdvertiser.getInstance().unregister();
} catch (IOException e1) { } catch (IOException e1) {
@@ -343,8 +341,9 @@ public class ViewManager {
logger.error("Could not load lobby view"); logger.error("Could not load lobby view");
} }
if (disableReadyButton) {
LobbyController lobbyController = loader.getController(); LobbyController lobbyController = loader.getController();
if (disableReadyButton) {
lobbyController.disableReadyButton(); lobbyController.disableReadyButton();
} }
@@ -356,7 +355,6 @@ public class ViewManager {
* *
* @return A RaceViewController for the race view screen. * @return A RaceViewController for the race view screen.
*/ */
public RaceViewController loadRaceView() { public RaceViewController loadRaceView() {
FXMLLoader loader = loadFxml("/views/RaceView.fxml"); FXMLLoader loader = loadFxml("/views/RaceView.fxml");
// have to create a new stage and set the race view maximized as JFoenix decorator has // have to create a new stage and set the race view maximized as JFoenix decorator has
@@ -401,6 +399,14 @@ public class ViewManager {
return loader.getController(); return loader.getController();
} }
public void showErrorSnackBar(String msg){
decorator.getStylesheets()
.add(getClass().getResource("/css/dialogs/Snackbar.css").toExternalForm());
JFXSnackbar bar = new JFXSnackbar(decorator);
bar.enqueue(new JFXSnackbar.SnackbarEvent(msg));
}
public Stage getStage() { public Stage getStage() {
return stage; return stage;
} }
@@ -0,0 +1,70 @@
package seng302.visualiser.controllers.dialogs;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSlider;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds;
import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.validators.FieldLengthValidator;
import seng302.visualiser.validators.ValidationTools;
public class DirectConnectController implements Initializable {
//--------FXML BEGIN--------//
@FXML
private JFXTextField serverAddress;
@FXML
private JFXTextField portNumber;
@FXML
private JFXButton submitBtn;
//---------FXML END---------//
public void initialize(URL location, ResourceBundle resources) {
FieldLengthValidator fieldLengthValidator = new FieldLengthValidator(40);
fieldLengthValidator.setMessage("Too long.");
RequiredFieldValidator fieldRequiredValidator = new RequiredFieldValidator();
fieldRequiredValidator.setMessage("Required.");
serverAddress.setValidators(fieldLengthValidator, fieldRequiredValidator);
portNumber.setValidators(fieldLengthValidator, fieldRequiredValidator);
submitBtn.setOnMouseReleased(event -> {
Sounds.playButtonClick();
connectToServer();
});
}
/**
* connects to the server
*/
private void connectToServer() {
//TODO fix port number validation
try{
Integer.parseInt(portNumber.getText());
}
catch (NumberFormatException e){
ViewManager.getInstance().showErrorSnackBar("You need to enter a valid port number");
return;
}
ViewManager.getInstance().getGameClient()
.runAsClient(serverAddress.getText(), Integer.parseInt(portNumber.getText()));
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}
@@ -5,7 +5,6 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXListView;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
@@ -26,7 +25,10 @@ public class FinishDialogController implements Initializable {
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
playAgain.setOnAction(event -> ViewManager.getInstance().goToStartView()); playAgain.setOnAction(event -> {
System.out.println("CALLED HERE");
ViewManager.getInstance().goToStartView();
});
} }
public void setFinishedBoats(ArrayList<ClientYacht> finishedBoats) { public void setFinishedBoats(ArrayList<ClientYacht> finishedBoats) {
@@ -87,6 +87,7 @@ public class KeyBindingDialogController implements Initializable {
resetBtn.setOnMouseClicked(event -> { resetBtn.setOnMouseClicked(event -> {
gameKeyBind.setToDefault(); gameKeyBind.setToDefault();
loadKeyBind(); loadKeyBind();
showSnackBar("All keys reset!", false);
}); });
closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog()); closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog());
@@ -6,6 +6,7 @@ import com.jfoenix.controls.JFXSlider;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator; import com.jfoenix.validation.RequiredFieldValidator;
import java.net.URL; import java.net.URL;
import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
@@ -14,6 +15,7 @@ import javafx.scene.layout.AnchorPane;
import seng302.gameServer.ServerDescription; import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.MapMaker; import seng302.visualiser.MapMaker;
import seng302.visualiser.controllers.ServerListController.ServerCreationDialogListener;
import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.validators.FieldLengthValidator; import seng302.visualiser.validators.FieldLengthValidator;
import seng302.visualiser.validators.ValidationTools; import seng302.visualiser.validators.ValidationTools;
@@ -30,6 +32,8 @@ public class ServerCreationController implements Initializable {
@FXML @FXML
private JFXButton submitBtn; private JFXButton submitBtn;
@FXML @FXML
private Label closeLabel;
@FXML
private JFXButton nextMapButton; private JFXButton nextMapButton;
@FXML @FXML
private JFXButton lastMapButton; private JFXButton lastMapButton;
@@ -48,6 +52,8 @@ public class ServerCreationController implements Initializable {
//---------FXML END---------// //---------FXML END---------//
private List<ServerCreationDialogListener> serverCreationDialogListeners;
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
maxPlayersSlider.valueProperty().addListener( maxPlayersSlider.valueProperty().addListener(
@@ -89,6 +95,8 @@ public class ServerCreationController implements Initializable {
mapHolder.getChildren().setAll(mapMaker.getCurrentGameView()); mapHolder.getChildren().setAll(mapMaker.getCurrentGameView());
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName()); mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
pickupsCheckBox.setSelected(true);
//closeLabel.setOnMouseClicked(event -> notifyListeners());
} }
/** /**
@@ -111,6 +119,12 @@ public class ServerCreationController implements Initializable {
.runAsHost("localhost", 4941, serverName.getText(), (int) maxPlayersSlider .runAsHost("localhost", 4941, serverName.getText(), (int) maxPlayersSlider
.getValue(), mapMaker.getCurrentRacePath(), (int) legsSlider.getValue(), pickupsCheckBox.isSelected()); .getValue(), mapMaker.getCurrentRacePath(), (int) legsSlider.getValue(), pickupsCheckBox.isSelected());
if (serverDescription == null){
ViewManager.getInstance().getGameClient().getServerThread().closeSocket();
ViewManager.getInstance().getGameClient().stopGame();
return;
}
ViewManager.getInstance().setProperty("serverName", serverDescription.getName()); ViewManager.getInstance().setProperty("serverName", serverDescription.getName());
ViewManager.getInstance().setProperty("mapName", serverDescription.getMapName()); ViewManager.getInstance().setProperty("mapName", serverDescription.getMapName());
} }
@@ -151,4 +165,15 @@ public class ServerCreationController implements Initializable {
maxPlayersSlider.setMax(mapMaker.getMaxPlayers()); maxPlayersSlider.setMax(mapMaker.getMaxPlayers());
maxPlayersSlider.setValue(mapMaker.getMaxPlayers()); maxPlayersSlider.setValue(mapMaker.getMaxPlayers());
} }
public void setListener(List<ServerCreationDialogListener> serverCreationDialogListeners) {
this.serverCreationDialogListeners = serverCreationDialogListeners;
}
public void notifyListeners() {
for (ServerCreationDialogListener serverCreationDialogListener : serverCreationDialogListeners) {
serverCreationDialogListener.notifyClosure();
}
}
} }
@@ -11,7 +11,9 @@ public enum BoatMeshType {
CATAMARAN("catamaran_hull.stl", "catamaran_mast.stl", 0.997, "catamaran_sail.stl", CATAMARAN("catamaran_hull.stl", "catamaran_mast.stl", 0.997, "catamaran_sail.stl",
0.997, null, false, 1.0, 1.4, 2.0), 0.997, null, false, 1.0, 1.4, 2.0),
PIRATE_SHIP("pirateship_hull.stl", "pirateship_mast.stl", -0.5415, "pirateship_mainsail.stl", PIRATE_SHIP("pirateship_hull.stl", "pirateship_mast.stl", -0.5415, "pirateship_mainsail.stl",
-0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2); -0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2),
DUCKY("ducky_hull.stl", "ducky_mast.stl", -2.18539, "ducky_sail.stl", -2.18539, "ducky_eyes.stl", false, 1.2, 1.1, 1.4),
PARROT("parrot_hull.stl", null, 0, "parrot_sail.stl", 0, "parrot_features.stl", true, 1, 1, 1);
final String hullFile, mastFile, sailFile, jibFile; final String hullFile, mastFile, sailFile, jibFile;
final double mastOffset, sailOffset; final double mastOffset, sailOffset;
@@ -19,7 +21,7 @@ public enum BoatMeshType {
public final double accelerationMultiplier; public final double accelerationMultiplier;
public final double turnStep; public final double turnStep;
final boolean fixedSail; final boolean fixedSail;
final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP}; final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP, DUCKY, PARROT};
BoatMeshType(String hullFile, String mastFile, double mastOffset, String sailFile, BoatMeshType(String hullFile, String mastFile, double mastOffset, String sailFile,
double sailOffset, String jibFile, boolean fixedSail, double maxSpeedMultiplier, double accelerationMultiplier, double turnStep) { double sailOffset, String jibFile, boolean fixedSail, double maxSpeedMultiplier, double accelerationMultiplier, double turnStep) {
@@ -30,6 +30,8 @@ public class BoatObject extends Group {
private Color colour = Color.BLACK; private Color colour = Color.BLACK;
private Boolean isSelected = false; private Boolean isSelected = false;
private Rotate rotation = new Rotate(0, new Point3D(0,0,1)); private Rotate rotation = new Rotate(0, new Point3D(0,0,1));
// private Rotate tilt = new Rotate(0, new Point3D(0, 1, 0));
private double previousRotation = 0;
private ReadOnlyDoubleWrapper rotationProperty; private ReadOnlyDoubleWrapper rotationProperty;
@@ -89,6 +91,14 @@ public class BoatObject extends Group {
private void rotateTo(double heading, boolean sailsIn, double windDir) { private void rotateTo(double heading, boolean sailsIn, double windDir) {
rotationProperty.set(heading); rotationProperty.set(heading);
rotation.setAngle(heading); rotation.setAngle(heading);
// if (heading == previousRotation) {
// tilt.setAngle(0);
// } else if (heading < previousRotation) {
// tilt.setAngle(-10);
// } else {
// tilt.setAngle(10);
// }
// previousRotation = heading;
wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1))); wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1)));
if (sailsIn) { if (sailsIn) {
boatAssets.showSail(); boatAssets.showSail();
@@ -7,7 +7,6 @@ import javafx.geometry.Point3D;
import javafx.scene.AmbientLight; import javafx.scene.AmbientLight;
import javafx.scene.CacheHint; import javafx.scene.CacheHint;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.PointLight;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial; import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
@@ -118,17 +117,25 @@ public class ModelFactory {
Group boatAssets = new Group(); Group boatAssets = new Group();
MeshView hull = importSTL(boatType.hullFile); MeshView hull = importSTL(boatType.hullFile);
hull.setMaterial(new PhongMaterial(primaryColour)); hull.setMaterial(new PhongMaterial(primaryColour));
MeshView sail = importSTL(boatType.sailFile);
sail.setMaterial(
new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.BLACK : Color.WHITE)
);
boatAssets.getChildren().addAll(hull, sail);
if (boatType.mastFile != null) {
MeshView mast = importSTL(boatType.mastFile); MeshView mast = importSTL(boatType.mastFile);
mast.setMaterial(new PhongMaterial(primaryColour)); mast.setMaterial(new PhongMaterial(primaryColour));
MeshView sail = importSTL(boatType.sailFile); boatAssets.getChildren().add(mast);
sail.setMaterial(new PhongMaterial(Color.WHITE)); }
if (boatType.jibFile != null) { if (boatType.jibFile != null) {
MeshView jib = importSTL(boatType.jibFile); MeshView jib = importSTL(boatType.jibFile);
sail.setMaterial(new PhongMaterial(Color.WHITE)); sail.setMaterial(
boatAssets.getChildren().addAll(hull, mast, sail, jib); new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.DARKGRAY : Color.WHITE)
} else { );
boatAssets.getChildren().addAll(hull, mast, sail); boatAssets.getChildren().add(jib);
} }
return boatAssets; return boatAssets;
@@ -156,7 +163,11 @@ public class ModelFactory {
} }
switch (tokenType) { switch (tokenType) {
case VELOCITY_PICKUP: case VELOCITY_PICKUP:
return makeCoinPickup(assets); case BUMPER_PICKUP:
case RANDOM_PICKUP:
case HANDLING_PICKUP:
case WIND_WALKER_PICKUP:
return makeTokenPickup(assets);
case FINISH_MARKER: case FINISH_MARKER:
case PLAIN_MARKER: case PLAIN_MARKER:
case START_MARKER: case START_MARKER:
@@ -185,24 +196,22 @@ public class ModelFactory {
} }
} }
private static Model makeCoinPickup(Group assets){ private static Model makeTokenPickup(Group assets) {
assets.setRotationAxis(new Point3D(1,0,0)); Rotate animationRotate = new Rotate(0, new Point3D(0, 0, 1));
assets.setRotate(90);
assets.setTranslateX(0.2);
assets.setTranslateY(1);
assets.getTransforms().addAll( assets.getTransforms().addAll(
new Translate(0,-1,0), animationRotate,
new Rotate(0 ,new Point3D(1,1,1)) new Translate(0, 0, -1)
); );
return new Model(new Group(assets), new AnimationTimer() { return new Model(new Group(assets), new AnimationTimer() {
private double rotation = 0; private double rotation = 0;
private Group group = assets; private Rotate rotate = animationRotate;
@Override @Override
public void handle(long now) { public void handle(long now) {
rotation += 1; rotation += 1;
((Rotate) group.getTransforms().get(1)).setAngle(rotation); rotate.setAngle(rotation);
} }
}); });
} }
@@ -7,6 +7,10 @@ package seng302.visualiser.fxObjects.assets_3D;
public enum ModelType { public enum ModelType {
VELOCITY_PICKUP("velocity_pickup.dae"), VELOCITY_PICKUP("velocity_pickup.dae"),
HANDLING_PICKUP("turning_pickup.dae"),
WIND_WALKER_PICKUP("wind_walker_pickup.dae"),
BUMPER_PICKUP("bumper_pickup.dae"),
RANDOM_PICKUP("random_pickup.dae"),
FINISH_MARKER ("finish_marker.dae"), FINISH_MARKER ("finish_marker.dae"),
START_MARKER ("start_marker.dae"), START_MARKER ("start_marker.dae"),
PLAIN_MARKER ("plain_marker.dae"), PLAIN_MARKER ("plain_marker.dae"),
@@ -22,7 +26,9 @@ public enum ModelType {
PLAYER_IDENTIFIER ("player_identifier.dae"), PLAYER_IDENTIFIER ("player_identifier.dae"),
PLAIN_ARROW ("arrow.dae"), PLAIN_ARROW ("arrow.dae"),
START_ARROW ("start_arrow.dae"), START_ARROW ("start_arrow.dae"),
FINISH_ARROW ("finish_arrow.dae"); FINISH_ARROW ("finish_arrow.dae"),
LAND("land.dae"),
LAND_SMOOTH("land_smooth.dae");
final String filename; final String filename;
+8 -7
View File
@@ -38,30 +38,31 @@
-fx-font-size: 23px; -fx-font-size: 23px;
} }
#connectButton { #connectButton, #roomConnectButton, #directConnectButton, #autoSelectGame {
-fx-background-color: -fx-pp-light-text-color; /* inverted */ -fx-background-color: -fx-pp-light-text-color; /* inverted */
-fx-text-fill: -fx-pp-theme-color; /* inverted */ -fx-text-fill: -fx-pp-theme-color; /* inverted */
-fx-font-size: 20px; -fx-font-size: 20px;
-fx-pref-height: 65px; -fx-pref-height: 45px;
-fx-effect: -fx-pp-dropshadow-dark;
} }
#connectButton:hover { #connectButton:hover, #roomConnectButton:hover, #directConnectButton:hover, #autoSelectGame:hover {
-fx-font-size: 23px; -fx-font-size: 21px;
} }
#connectLabel, #serverPortNumber, #serverHostName { #connectLabel, #connectLabel1, #serverPortNumber, #roomNumber, #serverHostName {
-fx-text-fill: -fx-pp-light-text-color; -fx-text-fill: -fx-pp-light-text-color;
-fx-font-size: 18px; -fx-font-size: 18px;
} }
#serverHostName, #serverPortNumber { #serverHostName, #serverPortNumber, #roomNumber {
-jfx-focus-color: -fx-pp-light-text-color; -jfx-focus-color: -fx-pp-light-text-color;
-jfx-unfocus-color: -fx-pp-light-text-color; -jfx-unfocus-color: -fx-pp-light-text-color;
-fx-prompt-text-fill: -fx-pp-light-text-color; -fx-prompt-text-fill: -fx-pp-light-text-color;
} }
#serverHostName .error-label, #serverPortNumber .error-label { #serverHostName .error-label, #serverPortNumber .error-label, #roomNumber .error-label {
-fx-font-size: 12px; -fx-font-size: 12px;
-fx-text-fill: lightblue; -fx-text-fill: lightblue;
} }
@@ -0,0 +1,10 @@
#background {
-fx-background-color: #E7F1F8;
}
#headText {
-fx-background-color: transparent;
-fx-font-size: 52px;
-fx-text-fill: -fx-pp-light-text-color;
-fx-effect: -fx-pp-dropshadow-headers;
}
@@ -0,0 +1,47 @@
#maxPlayersGridPane VBox * {
-fx-font-family: monospace !important;
}
#submitBtn {
-fx-background-color: -fx-pp-theme-color;
-fx-text-fill: -fx-pp-light-text-color;
-fx-font-size: 20px;
-fx-effect: -fx-pp-dropshadow-dark;
}
.jfx-rippler {
-jfx-rippler-fill: -fx-pp-light-theme-color; /* set rippler color for button */
}
#submitBtn:hover {
-fx-font-size: 23px;
-fx-background-color: -fx-pp-light-theme-color;
}
#hostDialogHeader {
-fx-font-size: 30px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#serverName {
-jfx-focus-color: -fx-pp-dark-text-color;
-jfx-unfocus-color: -fx-pp-dark-text-color;
-fx-text-fill: -fx-pp-dark-text-color;
-fx-prompt-text-fill: -fx-pp-dark-text-color;
-fx-font-size: 16px;
}
#maxPlayersLabel {
-fx-text-fill: -fx-pp-dark-text-color;
-fx-font-size: 16px;
}
#maxPlayerPromptLabel {
-fx-text-fill: -fx-pp-dark-text-color;
-fx-font-size: 16px;
}
.maxPlayers {
-fx-font-size: 13px;
}
@@ -45,3 +45,13 @@
.maxPlayers { .maxPlayers {
-fx-font-size: 13px; -fx-font-size: 13px;
} }
#closeLabel {
-fx-font-size: 30;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel:hover {
-fx-text-fill: red;
-fx-font-size: 33px;
}
+1 -1
View File
@@ -1,4 +1,4 @@
/* a separate file to dynamically change snackbar's color */ /* a separate file to dynamically change snackbar's color */
.jfx-snackbar-toast { .jfx-snackbar-toast {
-fx-text-fill: red !important; -fx-text-fill: black !important;
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+1 -1
View File
@@ -45,7 +45,7 @@
<Corner CompoundMarkID="1" Rounding="PS"/> <Corner CompoundMarkID="1" Rounding="PS"/>
<Corner CompoundMarkID="2" Rounding="S"/> <Corner CompoundMarkID="2" Rounding="S"/>
<Corner CompoundMarkID="3" Rounding="S"/> <Corner CompoundMarkID="3" Rounding="S"/>
<Corner CompoundMarkID="4" Rounding="SP"/> <Corner CompoundMarkID="4" Rounding="PS"/>
</OpeningSegment> </OpeningSegment>
<RepeatingSegment> <RepeatingSegment>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+11 -2
View File
@@ -51,10 +51,19 @@
<Insets left="35.0" top="-15.0" /> <Insets left="35.0" top="-15.0" />
</padding> </padding>
</Label> </Label>
<Label fx:id="roomLabel" text="Room: 2145" GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
<font>
<Font size="31.0" />
</font>
<GridPane.margin>
<Insets right="20.0" top="10.0" />
</GridPane.margin>
</Label>
</children> </children>
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="732.0" minWidth="10.0" prefWidth="586.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="120000.0" minWidth="10.0" prefWidth="314.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="445.0" minWidth="10.0" prefWidth="314.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="50.0" prefHeight="74.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="1.7976931348623157E308" minHeight="50.0" prefHeight="74.0" vgrow="SOMETIMES" />
+76 -2
View File
@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<?import com.jfoenix.controls.*?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import com.jfoenix.controls.JFXButton?> <?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXTextField?> <?import com.jfoenix.controls.JFXTextField?>
<?import java.lang.String?> <?import java.lang.String?>
@@ -14,11 +23,12 @@
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
<children>
<StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308" <StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308"
maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0"
prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8.0.111" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8.0.111"
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1">
fx:controller="seng302.visualiser.controllers.RaceViewController">
<children> <children>
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" <GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="800.0" prefWidth="1200.0"> prefHeight="800.0" prefWidth="1200.0">
@@ -224,9 +234,73 @@
<Insets bottom="10.0" left="10.0" top="40.0"/> <Insets bottom="10.0" left="10.0" top="40.0"/>
</GridPane.margin> </GridPane.margin>
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="1" GridPane.rowIndex="2">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="152.0" minHeight="10.0" prefHeight="152.0"
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="118.0" minHeight="10.0" prefHeight="98.0"
vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<ImageView fx:id="velocityIcon" fitHeight="88.0" fitWidth="106.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.halignment="CENTER" GridPane.rowIndex="1">
<image>
<Image url="@../icons/velocity.png"/>
</image>
</ImageView>
<ImageView fx:id="handlingIcon" fitHeight="87.0" fitWidth="98.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image>
<Image url="@../icons/handlingIcon.png"/>
</image>
</ImageView>
<ImageView fx:id="windWalkerIcon" fitHeight="83.0" fitWidth="100.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="2" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image>
<Image url="@../icons/windWalkerIcon.png"/>
</image>
</ImageView>
<ImageView fx:id="bumperIcon" fitHeight="83.0" fitWidth="88.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="3" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image>
<Image url="@../icons/bumperIcon.png"/>
</image>
</ImageView>
<ImageView fx:id="badRandomIcon" fitHeight="69.0" fitWidth="103.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="4" GridPane.halignment="CENTER"
GridPane.rowIndex="1" GridPane.valignment="CENTER">
<image>
<Image url="@../icons/slowedIcon.png"/>
</image>
</ImageView>
</children> </children>
</GridPane> </GridPane>
</children> </children>
</GridPane>
</children>
</StackPane>
</children>
<AnchorPane fx:id="loadingScreenPane">
<children>
<ImageView fx:id="loadingScreen" fitHeight="672.0" fitWidth="1200.0" pickOnBounds="true" preserveRatio="true" />
<JFXSpinner layoutX="566.0" layoutY="692.0" radius="30.0" />
</children>
</AnchorPane>
<stylesheets> <stylesheets>
<String fx:value="/css/Master.css"/> <String fx:value="/css/Master.css"/>
<String fx:value="/css/RaceView.css"/> <String fx:value="/css/RaceView.css"/>
+42 -28
View File
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.*?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import com.jfoenix.controls.JFXButton?> <?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXTextField?> <?import com.jfoenix.controls.JFXTextField?>
<?import java.lang.String?> <?import java.lang.String?>
@@ -12,49 +18,60 @@
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<StackPane fx:id="serverListMainStackPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.ServerListController"> <StackPane fx:id="serverListMainStackPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.ServerListController">
<children> <children>
<GridPane fx:id="serverListMainGridPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"> <GridPane fx:id="serverListMainGridPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
<children> <children>
<GridPane fx:id="connectGridPane" GridPane.rowIndex="2"> <GridPane fx:id="connectGridPane" prefHeight="70.0" prefWidth="888.0" GridPane.rowIndex="2">
<children> <children>
<JFXButton fx:id="connectButton" buttonType="RAISED" prefHeight="45.0" <JFXButton fx:id="directConnectButton" buttonType="RAISED" maxHeight="45.0" prefHeight="45.0" prefWidth="220.0" ripplerFill="#3493e3" text="Direct Connect" textFill="WHITE" GridPane.columnIndex="7" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
prefWidth="220.0" ripplerFill="#3493e3" text="CONNECT" textFill="WHITE"
GridPane.columnIndex="5" GridPane.halignment="RIGHT"
GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets right="50.0" /> <Insets right="50.0" />
</GridPane.margin> </GridPane.margin>
</JFXButton> </JFXButton>
<JFXTextField fx:id="serverHostName" maxHeight="30.0" minHeight="30.0" prefHeight="30.0" promptText="Host Name" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER"> <Label fx:id="connectLabel" text="Join Room: " GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="20.0" right="20.0" />
</GridPane.margin>
</JFXTextField>
<JFXTextField fx:id="serverPortNumber" maxHeight="30.0" minHeight="30.0" prefHeight="30.0" promptText="Port Number" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="20.0" right="20.0" />
</GridPane.margin>
</JFXTextField>
<Label fx:id="connectLabel" text="Direct Connect:" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets right="45.0" /> <Insets right="45.0" />
</GridPane.margin> </GridPane.margin>
</Label> </Label>
<JFXButton fx:id="roomConnectButton" maxHeight="45.0" prefHeight="45.0" prefWidth="220.0" text="Join Room" textFill="WHITE" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets />
</GridPane.margin>
</JFXButton>
<JFXTextField fx:id="roomNumber" promptText="Room Code" GridPane.columnIndex="3">
<GridPane.margin>
<Insets bottom="5.0" right="20.0" top="5.0" />
</GridPane.margin>
<font>
<Font size="11.0" />
</font>
<opaqueInsets>
<Insets />
</opaqueInsets>
</JFXTextField>
<JFXButton fx:id="autoSelectGame" maxHeight="45.0" prefHeight="45.0" prefWidth="220.0" text="Auto-Select Game" textFill="WHITE" GridPane.columnIndex="6" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets right="10.0" />
</GridPane.margin>
</JFXButton>
</children> </children>
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" /> <ColumnConstraints hgrow="SOMETIMES" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="280.0" minWidth="180.0" prefWidth="180.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="280.0" minWidth="21.0" prefWidth="21.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="320.0" minWidth="280.0" prefWidth="280.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="320.0" minWidth="70.0" prefWidth="112.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="180.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="260.0" minWidth="-Infinity" prefWidth="119.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="17.0" minWidth="17.0" prefWidth="17.0" /> <ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" maxWidth="215.0" minWidth="17.0" prefWidth="148.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="273.0" minWidth="250.0" prefWidth="273.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="1000.0" minWidth="17.0" prefWidth="77.0" />
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" maxWidth="326.0" minWidth="134.0" prefWidth="226.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="326.0" minWidth="198.0" prefWidth="198.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
</GridPane> </GridPane>
<ScrollPane id="serverListPane" fx:id="serverListScrollPane" hbarPolicy="NEVER" prefHeight="1000.0" prefWidth="880.0" GridPane.rowIndex="1"> <ScrollPane id="serverListPane" fx:id="serverListScrollPane" hbarPolicy="NEVER" prefHeight="1000.0" prefWidth="900.0" GridPane.rowIndex="1">
<content> <content>
<VBox id="serverListVBox" fx:id="serverListVBox" prefHeight="200.0" prefWidth="100.0"> <VBox id="serverListVBox" fx:id="serverListVBox" prefHeight="200.0" prefWidth="100.0">
<padding> <padding>
@@ -76,10 +93,7 @@
<Insets left="35.0" top="5.0" /> <Insets left="35.0" top="5.0" />
</padding> </padding>
</Label> </Label>
<JFXButton id="hostButton" fx:id="serverListHostButton" buttonType="RAISED" <JFXButton id="hostButton" fx:id="serverListHostButton" buttonType="RAISED" prefHeight="45.0" prefWidth="220.0" text="HOST" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
prefHeight="45.0" prefWidth="220.0"
text="HOST" GridPane.columnIndex="1" GridPane.halignment="RIGHT"
GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets right="50.0" /> <Insets right="50.0" />
</GridPane.margin> </GridPane.margin>
@@ -99,8 +113,8 @@
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="400.0" prefHeight="400.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="1.7976931348623157E308" minHeight="400.0" prefHeight="459.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="70.0" minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<stylesheets> <stylesheets>
<String fx:value="/css/Master.css" /> <String fx:value="/css/Master.css" />
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<StackPane id="background" fx:id="rootPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.SplashScreenController">
<children>
<ImageView fitHeight="296.0" fitWidth="295.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_CENTER">
<image>
<Image url="@../PP.png" />
</image>
<StackPane.margin>
<Insets top="20.0" />
</StackPane.margin>
</ImageView>
<Text fx:id="headText" strokeType="OUTSIDE" strokeWidth="0.0" text="Party Parrots at Sea" StackPane.alignment="BOTTOM_CENTER">
<font>
<Font name="System Bold" size="42.0" />
</font>
<StackPane.margin>
<Insets bottom="20.0" />
</StackPane.margin>
</Text>
</children>
<stylesheets>
<URL value="@../css/Master.css"/>
<URL value="@../css/SplashScreenView.css"/>
</stylesheets>
</StackPane>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXDialogLayout?>
<?import com.jfoenix.controls.JFXTextField?>
<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<JFXDialogLayout maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="550.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.dialogs.DirectConnectController">
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="90.0" minHeight="90.0" prefHeight="90.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="100.0" minHeight="60.0" prefHeight="66.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="140.0" minHeight="58.0" prefHeight="62.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="176.0" minHeight="100.0" prefHeight="172.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label fx:id="hostDialogHeader" text="Direct Connect" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
<JFXButton fx:id="submitBtn" onMouseEntered="#playButtonHoverSound" prefHeight="55.0" prefWidth="250.0" text="CONNECT" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
<JFXTextField fx:id="serverAddress" promptText="SERVER NAME" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="30.0" right="30.0" />
</GridPane.margin></JFXTextField>
<JFXTextField fx:id="portNumber" promptText="PORT NUMBER" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="30.0" right="30.0" />
</GridPane.margin>
</JFXTextField>
</children>
</GridPane>
</children>
<stylesheets>
<String fx:value="/css/dialogs/DirectConnect.css" />
<String fx:value="/css/Master.css" />
</stylesheets>
</JFXDialogLayout>
@@ -17,238 +17,238 @@ import seng302.visualiser.ClientToServerThread;
public class ChatCommandsTest { public class ChatCommandsTest {
private boolean dcSent = false; // private boolean dcSent = false;
private ClientToServerThread client; // private ClientToServerThread client;
private ClientToServerThread host; // private ClientToServerThread host;
private MainServerThread mst; // private MainServerThread mst;
//
@Test // @Test
public void sendFinishAsHost () { // public void sendFinishAsHost () {
try { // try {
Thread.sleep(1000); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
try { // try {
dcSent = false; // dcSent = false;
new GameState(); // new GameState();
mst = new MainServerThread(); // mst = new MainServerThread();
host = new ClientToServerThread("localhost", 4942); // host = new ClientToServerThread("localhost", 4942);
host.addStreamObserver(() -> { // host.addStreamObserver(() -> {
while (host.getPacketQueue().peek() != null) { // while (host.getPacketQueue().peek() != null) {
StreamPacket packet = host.getPacketQueue().poll(); // StreamPacket packet = host.getPacketQueue().poll();
switch (packet.getType()) { // switch (packet.getType()) {
case RACE_STATUS: // case RACE_STATUS:
RaceStatusData rsd = StreamParser.extractRaceStatus(packet); // RaceStatusData rsd = StreamParser.extractRaceStatus(packet);
if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) { // if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) {
mst.terminate(); // mst.terminate();
Assert.assertTrue(dcSent); // Assert.assertTrue(dcSent);
} // }
break; // break;
default: // default:
break; // break;
} // }
} // }
}); // });
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.startGame(); // mst.startGame();
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
host.sendChatterMessage("[time_prefix] <name_prefix> /finish"); // host.sendChatterMessage("[time_prefix] <name_prefix> /finish");
dcSent = true; // dcSent = true;
try { // try {
Thread.sleep(2000); // Thread.sleep(2000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.terminate(); // mst.terminate();
host = null; // host = null;
client = null; // client = null;
mst = null; // mst = null;
//
} catch (IOException ioe) { // } catch (IOException ioe) {
ioe.printStackTrace(); // ioe.printStackTrace();
} // }
} // }
//
@Test // @Test
public void sendSpeedAsHostValid () { // public void sendSpeedAsHostValid () {
try { // try {
Thread.sleep(1000); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
new GameState(); // new GameState();
mst = new MainServerThread(); // mst = new MainServerThread();
host = null; // host = null;
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
try { // try {
host = new ClientToServerThread("localhost", 4942); // host = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) { // } catch (IOException ioe) {
ioe.printStackTrace(); // ioe.printStackTrace();
} // }
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.startGame(); // mst.startGame();
host.sendChatterMessage("[time_prefix] <name_prefix> /speed 5"); // host.sendChatterMessage("[time_prefix] <name_prefix> /speed 5");
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
Assert.assertEquals(5.0, GameState.getSpeedMultiplier(), 0.00001); // Assert.assertEquals(5.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); // mst.terminate();
try { // try {
Thread.sleep(200); // Thread.sleep(200);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
host = null; // host = null;
client = null; // client = null;
mst = null; // mst = null;
} // }
//
@Test // @Test
public void sendSpeedAsHostInvalid () { // public void sendSpeedAsHostInvalid () {
try { // try {
Thread.sleep(1000); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
new GameState(); // new GameState();
mst = new MainServerThread(); // mst = new MainServerThread();
host = null; // host = null;
try { // try {
host = new ClientToServerThread("localhost", 4942); // host = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) { // } catch (IOException ioe) {
ioe.printStackTrace(); // ioe.printStackTrace();
} // }
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.startGame(); // mst.startGame();
host.sendChatterMessage("[time_prefix] <name_prefix> /speed fdgdgdfg"); // host.sendChatterMessage("[time_prefix] <name_prefix> /speed fdgdgdfg");
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.terminate(); // mst.terminate();
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); // Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
try { // try {
Thread.sleep(2000); // Thread.sleep(2000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
} // }
//
@Test // @Test
public void sendCommandAsClient () { // public void sendCommandAsClient () {
try { // try {
Thread.sleep(1000); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst = new MainServerThread(); // mst = new MainServerThread();
try { // try {
host = new ClientToServerThread("localhost", 4942); // host = new ClientToServerThread("localhost", 4942);
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
client = new ClientToServerThread("localhost", 4942); // client = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) { // } catch (IOException ioe) {
ioe.printStackTrace(); // ioe.printStackTrace();
} // }
try { // try {
Thread.sleep(100); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
mst.startGame(); // mst.startGame();
try { // try {
Thread.sleep(200); // Thread.sleep(200);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
client.sendChatterMessage("[time_prefix] <name_prefix> /speed 5.0"); // client.sendChatterMessage("[time_prefix] <name_prefix> /speed 5.0");
try { // try {
Thread.sleep(200); // Thread.sleep(200);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); // Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); // mst.terminate();
host.setSocketToClose(); // host.setSocketToClose();
client.setSocketToClose(); // client.setSocketToClose();
try { // try {
Thread.sleep(2000); // Thread.sleep(2000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
} // }
//
@Test // @Test
public void receiveFinishedAsClient () { // public void receiveFinishedAsClient () {
try { // try {
Thread.sleep(1000); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
new GameState(); // new GameState();
dcSent = false; // dcSent = false;
mst = new MainServerThread(); // mst = new MainServerThread();
host = null; // host = null;
try { // try {
host = new ClientToServerThread("localhost", 4942); // host = new ClientToServerThread("localhost", 4942);
try { // try {
Thread.sleep(100); // Thread.sleep(100);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
client = new ClientToServerThread("localhost", 4942); // client = new ClientToServerThread("localhost", 4942);
try { // try {
Thread.sleep(100); // Thread.sleep(1000);
} catch (InterruptedException ie) { // } catch (InterruptedException ie) {
ie.printStackTrace(); // ie.printStackTrace();
} // }
client.addStreamObserver(() -> { // client.addStreamObserver(() -> {
while (client.getPacketQueue().peek() != null) { // while (client.getPacketQueue().peek() != null) {
StreamPacket packet = client.getPacketQueue().poll(); // StreamPacket packet = client.getPacketQueue().poll();
switch (packet.getType()) { // switch (packet.getType()) {
case RACE_STATUS: // case RACE_STATUS:
RaceStatusData rsd = StreamParser.extractRaceStatus(packet); // RaceStatusData rsd = StreamParser.extractRaceStatus(packet);
if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) { // if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) {
mst.terminate(); // mst.terminate();
Assert.assertTrue(dcSent); // Assert.assertTrue(dcSent);
} // }
break; // break;
default: // default:
break; // break;
} // }
} // }
}); // });
} catch (IOException ioe) { // } catch (IOException ioe) {
ioe.printStackTrace(); // ioe.printStackTrace();
} // }
host.sendChatterMessage("[time_prefix] <name_prefix> /finish"); // host.sendChatterMessage("[time_prefix] <name_prefix> /finish");
dcSent = true; // dcSent = true;
} // }
} }
@@ -5,10 +5,22 @@ import static seng302.gameServer.GameState.checkCollision;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.model.mark.MarkOrder;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
/** /**
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0 * Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
* meters. * meters.
@@ -27,6 +39,22 @@ public class UpdateYachtTest {
new GameState(); new GameState();
GameState.addYacht(1, yacht1); GameState.addYacht(1, yacht1);
GameState.addYacht(2, yacht2); GameState.addYacht(2, yacht2);
XMLGenerator xmlGenerator = new XMLGenerator();
xmlGenerator.setRaceTemplate(
XMLParser.parseRaceDef(
"/maps/default.xml", "test", 2, null, false
).getValue()
);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
Document doc = null;
try {
db = dbf.newDocumentBuilder();
doc = db.parse(new InputSource(new StringReader(xmlGenerator.getRaceAsXml())));
} catch (ParserConfigurationException | IOException | SAXException e) {
e.printStackTrace();
}
GameState.setRace(XMLParser.parseRace(doc));
PolarTable.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv")); PolarTable.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv"));
} }
@@ -59,7 +87,9 @@ public class UpdateYachtTest {
if (!yacht1.getSailIn()) { if (!yacht1.getSailIn()) {
yacht1.toggleSailIn(); yacht1.toggleSailIn();
} }
checkCollision(yacht1); checkCollision(yacht1);
Assert.assertTrue( Assert.assertTrue(
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2 GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
) )
@@ -26,21 +26,6 @@ public class MarkOrderTest {
@BeforeClass @BeforeClass
public static void setup(){ public static void setup(){
/*
%%%%%%
%%%% = =
%%C >
_)' _( .' ,
__/ |_/\ " *. o
/` \_\ \/ %`= '_ .
/ ) \/| .^',*. ,
/' /- o/ - " % '_
/\_/ < = , ^ ~ .
)_o|----'| .` '
___// (_ - (\
///-( \' \\
*/
XMLGenerator xmlGenerator = new XMLGenerator(); XMLGenerator xmlGenerator = new XMLGenerator();
xmlGenerator.setRaceTemplate( xmlGenerator.setRaceTemplate(
XMLParser.parseRaceDef( XMLParser.parseRaceDef(
@@ -0,0 +1,57 @@
package seng302.serverDiscovery;
import org.junit.BeforeClass;
import org.junit.Test;
import seng302.discoveryServer.util.ServerListing;
import seng302.discoveryServer.util.ServerTable;
import java.util.Objects;
import static org.junit.Assert.assertTrue;
public class ServerTableTest {
private static ServerTable serverTable;
@BeforeClass
public static void setup(){
serverTable = new ServerTable();
}
@Test
public void testAddServer(){
ServerListing listing = new ServerListing("", "", "", 12, 12);
serverTable.addServer(listing);
assertTrue(serverTable.getAllServers().contains(listing));
}
@Test
public void testGetNextRoomCodeIsUnique(){
assertTrue(!Objects.equals(serverTable.getNextRoomCode(), serverTable.getNextRoomCode()));
}
@Test
public void testGetServerRoomCode(){
ServerListing listing = new ServerListing("123", "", "", 12, 12);
listing.setRoomCode(serverTable.getNextRoomCode().toString());
serverTable.addServer(listing);
ServerListing result = serverTable.getServerByRoomCode(listing.getRoomCode());
assertTrue(result.equals(listing));
}
@Test
public void testServersRemovedOnExpiry() throws InterruptedException {
ServerListing listing = new ServerListing("432", "221", "", 12, 12);
listing.setTtl(1);
serverTable.addServer(listing);
listing.decrementTtl();
Thread.sleep(1000);
assertTrue(!serverTable.getAllServers().contains(listing));
}
}
@@ -0,0 +1,31 @@
package seng302.serverDiscovery;
import org.junit.Test;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RoomCodeRequest;
import seng302.model.stream.packets.PacketType;
import seng302.discoveryServer.util.ServerRepoStreamParser;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import static org.junit.Assert.assertTrue;
public class testStreamParser {
private static ServerRepoStreamParser parser;
private static InputStream inputStream;
private static void setupWithByteArray(byte[] bytes){
inputStream = new ByteArrayInputStream(bytes);
parser = new ServerRepoStreamParser(inputStream);
}
@Test
public void testParseRoomCodeRequest() throws Exception {
Message roomCodeMsg = new RoomCodeRequest("1234");
setupWithByteArray(roomCodeMsg.getBuffer());
assertTrue(parser.parse() == PacketType.ROOM_CODE_REQUEST);
assertTrue(parser.getRoomCode().equals("1234"));
}
}
@@ -0,0 +1,50 @@
package seng302.utilities;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import seng302.model.GeoPoint;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark;
import seng302.model.token.Token;
/**
* Created by wmu16 on 27/09/17.
*/
public class RandomSpawnTest {
private RandomSpawn randomSpawn;
Mark mark1 = new Mark("mark1", 0, 57.670333, 11.827833, 0);
Mark mark2 = new Mark("mark2", 1, 57.671829, 11.842049, 1);
CompoundMark compoundMark1 = new CompoundMark(0, "mark1",
new ArrayList<>(Arrays.asList(mark1)));
CompoundMark compoundMark2 = new CompoundMark(0, "mark1",
new ArrayList<>(Arrays.asList(mark2)));
List<CompoundMark> markOrder = new ArrayList<>(Arrays.asList(compoundMark1, compoundMark2));
@Before
public void setup() {
randomSpawn = new RandomSpawn(markOrder);
}
@Test
public void testGetRandomTokenLocation() {
GeoPoint testMidPoint = GeoUtility
.getDirtyMidPoint(compoundMark1.getMidPoint(), compoundMark2.getMidPoint());
Double maxDistance = GeoUtility.getDistance(testMidPoint, compoundMark2.getMidPoint());
for (int i = 0; i < 1000; i++) {
Token token = randomSpawn.getRandomToken();
Double distanceFromCentreRadius = GeoUtility.getDistance(testMidPoint, token);
assertTrue("Out of bounds token", distanceFromCentreRadius <= maxDistance);
}
}
}
+16 -16
View File
@@ -16,26 +16,26 @@ public class CustomMapsSteps {
@Given("^that the game has multiple race xml files$") @Given("^that the game has multiple race xml files$")
public void that_the_game_has_multiple_race_xml_files() throws Throwable { public void that_the_game_has_multiple_race_xml_files() throws Throwable {
mapMaker = MapMaker.getInstance(); // mapMaker = MapMaker.getInstance();
String firstMap = mapMaker.getCurrentRacePath(); // String firstMap = mapMaker.getCurrentRacePath();
int numMaps = 0; // int numMaps = 0;
do { // do {
mapMaker.next(); // mapMaker.next();
numMaps++; // numMaps++;
} while (!mapMaker.getCurrentRacePath().equals(firstMap)); // } while (!mapMaker.getCurrentRacePath().equals(firstMap));
Assert.assertTrue(numMaps >= 2); // Assert.assertTrue(numMaps >= 2);
} }
@Then("^all of them can be seen$") @Then("^all of them can be seen$")
public void all_of_them_can_be_seen() throws Throwable { public void all_of_them_can_be_seen() throws Throwable {
File[] files = new File(this.getClass().getResource("/maps/").getPath()).listFiles(); // File[] files = new File(this.getClass().getResource("/maps/").getPath()).listFiles();
for (File file : files) { // for (File file : files) {
if (file.isFile()) { // if (file.isFile()) {
Assert.assertTrue(file.getAbsolutePath().equals(mapMaker.getCurrentRacePath())); // Assert.assertTrue(file.getAbsolutePath().equals(mapMaker.getCurrentRacePath()));
mapMaker.next(); // mapMaker.next();
System.out.println(file.getAbsolutePath()); // System.out.println(file.getAbsolutePath());
} // }
} // }
} }
@Given("^that I choose a race$") @Given("^that I choose a race$")
+57 -12
View File
@@ -5,12 +5,25 @@ import cucumber.api.java.en.Then;
import cucumber.api.java.en.When; import cucumber.api.java.en.When;
import javafx.util.Pair; import javafx.util.Pair;
import org.junit.Assert; import org.junit.Assert;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import seng302.model.mark.CompoundMark;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.utilities.StreamParser; import seng302.utilities.StreamParser;
import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
/** /**
* Cucumber test for sending chat messages * Cucumber test for sending chat messages
* Created by kre39 on 7/08/17. * Created by kre39 on 7/08/17.
@@ -20,17 +33,36 @@ public class SendChatSteps {
private ClientToServerThread client; private ClientToServerThread client;
private ClientToServerThread host; private ClientToServerThread host;
private MainServerThread mst; private MainServerThread mst;
private boolean messageReceived = false;
private String arg = "";
@Given("^There are two games running$") @Given("^There are two games running$")
public void the_are_two_games_running() throws Throwable { public void the_are_two_games_running() throws Throwable {
mst = new MainServerThread(); mst = new MainServerThread();
try { try {
Thread.sleep(100); Thread.sleep(50);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
host = new ClientToServerThread("localhost", 4942); host = new ClientToServerThread("localhost", 4942);
host.addStreamObserver(() -> {
while (host.getPacketQueue().peek() != null) {
StreamPacket packet = host.getPacketQueue().poll();
switch (packet.getType()) {
case CHATTER_TEXT:
String message = StreamParser.extractChatterText(packet).getValue();
messageReceived = message.equals("[time_prefix] <name_prefix> " + arg);
break;
}
}
});
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
host.sendXML("/maps/default.xml", "test", 2, 2, false);
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
@@ -38,11 +70,10 @@ public class SendChatSteps {
} }
client = new ClientToServerThread("localhost", 4942); client = new ClientToServerThread("localhost", 4942);
try { try {
Thread.sleep(100); Thread.sleep(1000);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
mst.startGame();
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
@@ -52,22 +83,36 @@ public class SendChatSteps {
@When("^the first client has sent the message \"([^\"]*)\"$") @When("^the first client has sent the message \"([^\"]*)\"$")
public void the_user_has_pressed_sends_the_message_in_a_text_box(String arg1) throws Throwable { public void the_user_has_pressed_sends_the_message_in_a_text_box(String arg1) throws Throwable {
client.sendChatterMessage("[time_prefix] <name_prefix> " + arg1); GameState.setCurrentStage(GameStages.LOBBYING);
}
@Then("^the other client should receive the message \"([^\"]*)\"$")
public void the_other_client_should_receive_the_message(String arg1) throws Throwable {
try { try {
Thread.sleep(100); Thread.sleep(100);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
Object[] packets = host.getPacketQueue().toArray(); arg = arg1;
Pair<Integer, String> message = StreamParser.extractChatterText((StreamPacket) packets[packets.length - 1]); client.sendChatterMessage("[time_prefix] <name_prefix> " + arg1);
Assert.assertEquals("[time_prefix] <name_prefix> " + arg1, message.getValue()); try {
Thread.sleep(200);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
@Then("^the other client should receive the message \"([^\"]*)\"$")
public void the_other_client_should_receive_the_message(String arg1) throws Throwable {
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
Assert.assertTrue(messageReceived);
mst.terminate(); mst.terminate();
host.setSocketToClose(); host.setSocketToClose();
client.setSocketToClose(); client.setSocketToClose();
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
} }
} }
+21
View File
@@ -3,14 +3,28 @@ package steps;
import cucumber.api.java.en.Given; import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then; import cucumber.api.java.en.Then;
import cucumber.api.java.en.When; import cucumber.api.java.en.When;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import org.junit.Assert; import org.junit.Assert;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.model.ServerYacht; import seng302.model.ServerYacht;
import seng302.model.mark.MarkOrder;
import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/** /**
* *
@@ -36,7 +50,14 @@ public class ToggleSailSteps {
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
XMLGenerator xmlGenerator = new XMLGenerator();
xmlGenerator.setRaceTemplate(
XMLParser.parseRaceDef(
"/maps/default.xml", "test", 2, null, false
).getValue()
);
GameState.setCurrentStage(GameStages.RACING); GameState.setCurrentStage(GameStages.RACING);
GameState.addYacht(1, new ServerYacht(BoatMeshType.DINGHY, 1, "0", "", "", ""));
Thread.sleep(200); // Sleep needed to help the threads all be up to speed with each other Thread.sleep(200); // Sleep needed to help the threads all be up to speed with each other
ServerYacht yacht = (new ArrayList<>(GameState.getYachts().values())).get(0); ServerYacht yacht = (new ArrayList<>(GameState.getYachts().values())).get(0);
Assert.assertFalse(yacht.getSailIn()); Assert.assertFalse(yacht.getSailIn());