Merge branch 'Story80_BoatCustomization' into 'develop'

Story 80: Boat Customization

# Implementation
- Added Messages for Boat Customization Requests and Responses.
- Added functionality to handle Name and Boat Color Customization. (Shapes would be a next sprint thing)

# Testing
- Manually tested personally, will test correctly when boat shapes have also been implemented.


See merge request !64
This commit is contained in:
Haoming Yin
2017-08-17 14:11:04 +12:00
26 changed files with 469 additions and 34 deletions
@@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javafx.scene.paint.Color;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -14,6 +15,7 @@ import org.w3c.dom.Document;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatStatus; import seng302.gameServer.messages.BoatStatus;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.MarkRoundingMessage; 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;
@@ -36,6 +38,7 @@ import seng302.utilities.XMLParser;
* Created by wmu16 on 10/07/17. * Created by wmu16 on 10/07/17.
*/ */
public class GameState implements Runnable { public class GameState implements Runnable {
@FunctionalInterface @FunctionalInterface
interface NewMessageListener { interface NewMessageListener {
@@ -57,6 +60,8 @@ public class GameState implements Runnable {
public static Double windDirection; public static Double windDirection;
private static Double windSpeed; private static Double windSpeed;
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
private static String hostIpAddress; 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;
@@ -88,6 +93,7 @@ public class GameState implements Runnable {
yachts = new HashMap<>(); yachts = new HashMap<>();
players = new ArrayList<>(); players = new ArrayList<>();
GameState.hostIpAddress = hostIpAddress; GameState.hostIpAddress = hostIpAddress;
customizationFlag = false;
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false; isRaceStarted = false;
@@ -414,7 +420,7 @@ public class GameState implements Runnable {
} }
/** /** lobbyController.setPlayerListSource(clientLobbyList);
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any * 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line * in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
* *
@@ -581,6 +587,28 @@ public class GameState implements Runnable {
return false; return false;
} }
/**
* Handles player customization.
*
* @param playerID The ID of the player being modified.
* @param requestType the type of player customization the player wants
* @param customizeData the data related to the customization (color, name, shape)
*/
public static void customizePlayer(long playerID, CustomizeRequestType requestType,
byte[] customizeData) {
ServerYacht playerYacht = yachts.get((int) playerID);
if (requestType.equals(CustomizeRequestType.NAME)) {
String name = new String(customizeData);
playerYacht.setBoatName(name);
} else if (requestType.equals(CustomizeRequestType.COLOR)) {
int red = customizeData[0] & 0xFF;
int green = customizeData[1] & 0xFF;
int blue = customizeData[2] & 0xFF;
Color yachtColor = Color.rgb(red, green, blue);
playerYacht.setBoatColor(yachtColor);
}
}
private static Mark checkMarkCollision(ServerYacht yacht) { private static Mark checkMarkCollision(ServerYacht yacht) {
Set<Mark> marksInRace = GameState.getMarks(); Set<Mark> marksInRace = GameState.getMarks();
@@ -660,4 +688,16 @@ public class GameState implements Runnable {
public static void addMarkPassListener(NewMessageListener listener) { public static void addMarkPassListener(NewMessageListener listener) {
markListeners.add(listener); markListeners.add(listener);
} }
public static void setCustomizationFlag() {
customizationFlag = true;
}
public static Boolean getCustomizationFlag() {
return customizationFlag;
}
public static void resetCustomizationFlag() {
customizationFlag = false;
}
} }
@@ -75,6 +75,14 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} catch (InterruptedException e) { } catch (InterruptedException e) {
serverLog("Interrupted exception in Main Server Thread thread sleep", 1); serverLog("Interrupted exception in Main Server Thread thread sleep", 1);
} }
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState
.getCustomizationFlag()) {
// TODO: 16/08/17 ajm412: This can probably be done in a nicer way via those fancy functional interfaces.
for (ServerToClientThread thread : serverToClientThreads) {
thread.sendSetupMessages();
}
GameState.resetCustomizationFlag();
}
if (GameState.getCurrentStage() == GameStages.PRE_RACE) { if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
updateClients(); updateClients();
@@ -1,11 +1,11 @@
package seng302.gameServer; package seng302.gameServer;
import java.util.Arrays; import java.util.Arrays;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.gameServer.messages.BoatAction;
public class ServerPacketParser { public class ServerPacketParser {
@@ -22,5 +22,11 @@ public class ServerPacketParser {
long value = Message.bytesToLong(Arrays.copyOfRange(payload, 0, 1)); long value = Message.bytesToLong(Arrays.copyOfRange(payload, 0, 1));
return ClientType.getClientType((int) value); return ClientType.getClientType((int) value);
} }
public static CustomizeRequestType extractCustomizationType(StreamPacket packet) {
byte[] payload = packet.getPayload();
long type = Message.bytesToLong(Arrays.copyOfRange(payload, 4, 5));
return CustomizeRequestType.getRequestType((int) type);
}
} }
@@ -10,6 +10,7 @@ import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
@@ -19,8 +20,19 @@ import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RegistrationResponseMessage;
import seng302.gameServer.messages.RegistrationResponseStatus;
import seng302.gameServer.messages.XMLMessage;
import seng302.gameServer.messages.XMLMessageSubType;
import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.gameServer.messages.YachtEventCodeMessage; import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.ServerYacht;
import seng302.model.stream.packets.PacketType; import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race; import seng302.model.stream.xml.generator.Race;
@@ -203,6 +215,18 @@ public class ServerToClientThread implements Runnable, Observer {
completeRegistration(requestedType); completeRegistration(requestedType);
break; break;
case RACE_CUSTOMIZATION_REQUEST:
Long sourceID = Message
.bytesToLong(Arrays.copyOfRange(payload, 0, 3));
CustomizeRequestType requestType = ServerPacketParser
.extractCustomizationType(
new StreamPacket(type, payloadLength, timeStamp, payload));
GameState.customizePlayer(sourceID, requestType,
Arrays.copyOfRange(payload, 6, payload.length));
GameState.setCustomizationFlag();
// TODO: 17/08/2017 ajm412: Send a response packet here, not really necessary until we do shapes.
break;
} }
} else { } else {
logger.warn("Packet has been dropped", 1); logger.warn("Packet has been dropped", 1);
@@ -0,0 +1,35 @@
package seng302.gameServer.messages;
// TODO: 14/08/17 ajm412: this may eventually need adjusting due to conforming to the agreed spec.
public class CustomizeRequestMessage extends Message {
private static int MESSAGE_LENGTH = 6;
//Message fields
private CustomizeRequestType customizeType;
private Integer payloadLength;
public CustomizeRequestMessage(CustomizeRequestType customizeType, double sourceID,
byte[] payload) {
payloadLength = payload.length;
setHeader(new Header(MessageType.CUSTOMIZATION_REQUEST, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putInt((int) sourceID, 4);
putInt((int) customizeType.getType(), 2);
putBytes(payload);
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_LENGTH + payloadLength; // placeholder
}
}
@@ -0,0 +1,31 @@
package seng302.gameServer.messages;
// TODO: 14/08/17 ajm412: this may eventually need adjusting due to conforming to the agreed spec.
public enum CustomizeRequestType {
NAME(0x00),
COLOR(0x01),
SHAPE(0x02);
private int type;
CustomizeRequestType(int type) {
this.type = type;
}
int getType() {
return this.type;
}
public static CustomizeRequestType getRequestType(int typeCode) {
switch (typeCode) {
case 0x00:
return NAME;
case 0x01:
return COLOR;
case 0x02:
return SHAPE;
default:
return null;
}
}
}
@@ -0,0 +1,28 @@
package seng302.gameServer.messages;
/**
* Created by ajm412 on 14/08/17.
*/
public class CustomizeResponseMessage extends Message {
private static int MESSAGE_LENGTH = 2;
public CustomizeResponseMessage(CustomizeResponseType responseType) {
setHeader(new Header(MessageType.CUSTOMIZATION_RESPONSE, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putInt(responseType.getType(), 2);
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_LENGTH; // placeholder
}
}
@@ -0,0 +1,34 @@
package seng302.gameServer.messages;
// TODO: 14/08/17 ajm412: this may eventually need adjusting due to conforming to the agreed spec.
public enum CustomizeResponseType {
SUCCESS(0x00),
FAILURE(0x01),
FAILURE_MALFORMED_DATA(0x02),
FAILURE_INCOMPATIBLE(0x03);
private int type;
CustomizeResponseType(int type) {
this.type = type;
}
int getType() {
return this.type;
}
public static CustomizeResponseType getResponseType(int typeCode) {
switch (typeCode) {
case 0x00:
return SUCCESS;
case 0x01:
return FAILURE;
case 0x02:
return FAILURE_MALFORMED_DATA;
case 0x03:
return FAILURE_INCOMPATIBLE;
default:
return null;
}
}
}
@@ -19,7 +19,10 @@ public enum MessageType {
AVERAGE_WIND(47), AVERAGE_WIND(47),
BOAT_ACTION(100), BOAT_ACTION(100),
REGISTRATION_REQUEST(101), REGISTRATION_REQUEST(101),
REGISTRATION_RESPONSE(102); REGISTRATION_RESPONSE(102),
CUSTOMIZATION_REQUEST(103),
CUSTOMIZATION_RESPONSE(104);
private int code; private int code;
@@ -78,6 +78,7 @@ public class ClientYacht extends Observable {
this.heading = 120.0; //In degrees this.heading = 120.0; //In degrees
this.currentVelocity = 0d; this.currentVelocity = 0d;
this.boatStatus = 1; this.boatStatus = 1;
this.colour = Color.rgb(0, 0, 0, 1.0);
} }
/** /**
+2 -7
View File
@@ -8,12 +8,7 @@ import javafx.scene.paint.Color;
public enum Colors { public enum Colors {
RED, PERU, GOLD, GREEN, BLUE, PURPLE, DEEPPINK, GRAY; RED, PERU, GOLD, GREEN, BLUE, PURPLE, DEEPPINK, GRAY;
static Integer index = 0; public static Color getColor(Integer index) {
return Color.valueOf(values()[index].toString());
public static Color getColor() {
if (index == 8) {
index = 0;
}
return Color.valueOf(values()[index++].toString());
} }
} }
@@ -3,6 +3,7 @@ package seng302.model;
import java.util.HashMap; import java.util.HashMap;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
@@ -30,6 +31,8 @@ public class ServerYacht extends Observable {
private String country; private String country;
private BoatStatus boatStatus; private BoatStatus boatStatus;
private Color boatColor;
//Location //Location
private Double lastHeading; private Double lastHeading;
@@ -67,6 +70,7 @@ public class ServerYacht extends Observable {
this.currentVelocity = 0d; //in mms-1 this.currentVelocity = 0d; //in mms-1
this.currentMarkSeqID = 0; this.currentMarkSeqID = 0;
this.legNumber = 0; this.legNumber = 0;
this.boatColor = Colors.getColor(sourceId - 1);
this.hasEnteredRoundingZone = false; this.hasEnteredRoundingZone = false;
this.hasPassedLine = false; this.hasPassedLine = false;
@@ -300,6 +304,10 @@ public class ServerYacht extends Observable {
return country; return country;
} }
public void setBoatName(String name) {
boatName = name;
shortName = name.split(" ")[0];
}
public GeoPoint getLocation() { public GeoPoint getLocation() {
return location; return location;
@@ -391,4 +399,12 @@ public class ServerYacht extends Observable {
return legNumber; return legNumber;
} }
public void setBoatColor(Color color) {
this.boatColor = color;
}
public Color getBoatColor() {
return boatColor;
}
} }
@@ -1,8 +1,5 @@
package seng302.model.stream.packets; package seng302.model.stream.packets;
/**
* Created by Kusal on 4/24/2017.
*/
public enum PacketType { public enum PacketType {
HEARTBEAT, HEARTBEAT,
RACE_STATUS, RACE_STATUS,
@@ -21,7 +18,9 @@ public enum PacketType {
BOAT_ACTION, BOAT_ACTION,
OTHER, OTHER,
RACE_REGISTRATION_REQUEST, RACE_REGISTRATION_REQUEST,
RACE_REGISTRATION_RESPONSE; RACE_REGISTRATION_RESPONSE,
RACE_CUSTOMIZATION_REQUEST,
RACE_CUSTOMIZATION_RESPONSE;
public static PacketType assignPacketType(int packetType, byte[] payload){ public static PacketType assignPacketType(int packetType, byte[] payload){
switch(packetType){ switch(packetType){
@@ -62,6 +61,10 @@ public enum PacketType {
return RACE_REGISTRATION_REQUEST; return RACE_REGISTRATION_REQUEST;
case 102: case 102:
return RACE_REGISTRATION_RESPONSE; return RACE_REGISTRATION_RESPONSE;
case 103:
return RACE_CUSTOMIZATION_REQUEST;
case 104:
return RACE_CUSTOMIZATION_RESPONSE;
default: default:
} }
return OTHER; return OTHER;
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javafx.scene.paint.Color;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -142,6 +143,7 @@ public class XMLParser {
XMLParser.getNodeAttributeString(currentBoat, "ShortName"), XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
XMLParser.getNodeAttributeString(currentBoat, "BoatName"), XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
XMLParser.getNodeAttributeString(currentBoat, "Country")); XMLParser.getNodeAttributeString(currentBoat, "Country"));
yacht.setColour(Color.web(getNodeAttributeString(currentBoat, "Color")));
if (yacht.getBoatType().equals("Yacht")) { if (yacht.getBoatType().equals("Yacht")) {
competingBoats.put(yacht.getSourceId(), yacht); competingBoats.put(yacht.getSourceId(), yacht);
} }
@@ -24,6 +24,8 @@ import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatActionMessage; import seng302.gameServer.messages.BoatActionMessage;
import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestMessage;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RegistrationRequestMessage; import seng302.gameServer.messages.RegistrationRequestMessage;
import seng302.gameServer.messages.RegistrationResponseStatus; import seng302.gameServer.messages.RegistrationResponseStatus;
@@ -173,6 +175,15 @@ public class ClientToServerThread implements Runnable {
clientLog("Closed connection to Server", 0); clientLog("Closed connection to Server", 0);
} }
public void sendCustomizationRequest(CustomizeRequestType reqType, byte[] payload) {
CustomizeRequestMessage requestMessage = new CustomizeRequestMessage(reqType, this.clientId, payload);
try {
os.write(requestMessage.getBuffer());
} catch (IOException e) {
logger.error("Could not send customization request");
}
}
/** /**
* Sends a request to the server asking for a source ID * Sends a request to the server asking for a source ID
@@ -16,6 +16,7 @@ import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatStatus; import seng302.gameServer.messages.BoatStatus;
import seng302.gameServer.messages.BoatAction;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
@@ -77,7 +78,6 @@ public class GameClient {
socketThread.addStreamObserver(this::parsePackets); socketThread.addStreamObserver(this::parsePackets);
LobbyController lobbyController = loadLobby(); LobbyController lobbyController = loadLobby();
lobbyController.setPlayerListSource(clientLobbyList);
lobbyController.disableReadyButton(); lobbyController.disableReadyButton();
if (regattaData != null){ if (regattaData != null){
@@ -130,6 +130,7 @@ public class GameClient {
}); });
this.lobbyController = lobbyController; this.lobbyController = lobbyController;
//server.setGameClient(this);
} }
private void loadStartScreen() { private void loadStartScreen() {
@@ -162,7 +163,12 @@ public class GameClient {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return fxmlLoader.getController(); LobbyController lobbyController = fxmlLoader.getController();
lobbyController.setSocketThread(socketThread);
lobbyController.setPlayerListSource(clientLobbyList);
lobbyController.setPlayerID(socketThread.getClientId());
return lobbyController;
} }
private void loadRaceView() { private void loadRaceView() {
@@ -236,7 +242,7 @@ public class GameClient {
); );
clientLobbyList.clear(); clientLobbyList.clear();
allBoatsMap.forEach((id, boat) -> allBoatsMap.forEach((id, boat) ->
clientLobbyList.add(id + " " + boat.getBoatName()) clientLobbyList.add(boat.getBoatName())
); );
raceState.setBoats(allBoatsMap.values()); raceState.setBoats(allBoatsMap.values());
break; break;
@@ -26,6 +26,7 @@ import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon; import javafx.scene.shape.Polygon;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.util.Duration; import javafx.util.Duration;
import seng302.model.ClientYacht;
import seng302.gameServer.messages.RoundingSide; import seng302.gameServer.messages.RoundingSide;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.Colors; import seng302.model.Colors;
@@ -473,19 +474,19 @@ public class GameView extends Pane {
public void setBoats(List<ClientYacht> yachts) { public void setBoats(List<ClientYacht> yachts) {
BoatObject newBoat; BoatObject newBoat;
final List<Group> wakes = new ArrayList<>(); final List<Group> wakes = new ArrayList<>();
for (ClientYacht yacht : yachts) { for (ClientYacht clientYacht : yachts) {
Paint colour = Colors.getColor(); Paint colour = clientYacht.getColour();
newBoat = new BoatObject(); newBoat = new BoatObject();
newBoat.addSelectedBoatListener(this::setSelectedBoat); newBoat.addSelectedBoatListener(this::setSelectedBoat);
newBoat.setFill(colour); newBoat.setFill(colour);
boatObjects.put(yacht, newBoat); boatObjects.put(clientYacht, newBoat);
createAndBindAnnotationBox(yacht, colour); createAndBindAnnotationBox(clientYacht, colour);
// 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);
trails.getChildren().add(newBoat.getTrail()); trails.getChildren().add(newBoat.getTrail());
// TODO: 1/08/17 Make this less vile to look at. // TODO: 1/08/17 Make this less vile to look at.
yacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
BoatObject bo = boatObjects.get(boat); BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon); Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
@@ -506,11 +507,11 @@ public class GameView extends Pane {
}); });
} }
private void createAndBindAnnotationBox(ClientYacht yacht, Paint colour) { private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) {
AnnotationBox newAnnotation = new AnnotationBox(); AnnotationBox newAnnotation = new AnnotationBox();
newAnnotation.setFill(colour); newAnnotation.setFill(colour);
newAnnotation.addAnnotation( newAnnotation.addAnnotation(
"name", "Player: " + yacht.getShortName() "name", "Player: " + clientYacht.getShortName()
); );
// newAnnotation.addAnnotation( // newAnnotation.addAnnotation(
// "velocity", // "velocity",
@@ -533,7 +534,7 @@ public class GameView extends Pane {
// return format.format(time); // return format.format(time);
// } // }
// ); // );
annotations.put(yacht, newAnnotation); annotations.put(clientYacht, newAnnotation);
} }
private void drawFps(Double fps) { private void drawFps(Double fps) {
@@ -0,0 +1,74 @@
package seng302.visualiser.controllers;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.TextField;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.visualiser.ClientToServerThread;
public class CustomizationController {
@FXML
private TextField nameField;
@FXML
private ColorPicker boatColorPicker;
@FXML
private Button customizeSubmit;
private LobbyController lc;
private ClientToServerThread socketThread;
private Stage windowStage;
public void initialize() {
}
public void setServerThread(ClientToServerThread ctsThread) {
this.socketThread = ctsThread;
}
@FXML
public void submitCustomization() {
System.out.println("Attempting to send");
socketThread.sendCustomizationRequest(CustomizeRequestType.NAME, nameField.getText().getBytes());
// TODO: 16/08/17 ajm412: Turn colors into byte array.
Color color = boatColorPicker.getValue();
short red = (short) (color.getRed() * 255);
short green = (short) (color.getGreen() * 255);
short blue = (short) (color.getBlue() * 255);
byte[] colorArray = new byte[3];
colorArray[0] = (byte) red;
colorArray[1] = (byte) green;
colorArray[2] = (byte) blue;
socketThread.sendCustomizationRequest(CustomizeRequestType.COLOR, colorArray);
lc.setPlayersColor(color);
windowStage.close();
}
public void setLobbyController(LobbyController lc) {
this.lc = lc;
}
public void setStage(Stage stage) {
this.windowStage = stage;
}
public void setPlayerName(String name) {
this.nameField.setText(name);
}
public void setPlayerColor(Color playerColor) {
this.boatColorPicker.setValue(playerColor);
}
}
@@ -1,20 +1,29 @@
package seng302.visualiser.controllers; package seng302.visualiser.controllers;
import java.util.*; import com.sun.media.jfxmedia.logging.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.Stage;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.model.Colors;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.visualiser.GameClient; import seng302.visualiser.ClientToServerThread;
/** /**
* A class describing the actions of the lobby screen * A class describing the actions of the lobby screen
@@ -37,6 +46,8 @@ public class LobbyController {
@FXML @FXML
private Button readyButton; private Button readyButton;
@FXML @FXML
private Button customizeButton;
@FXML
private TextArea playerOneTxt; private TextArea playerOneTxt;
@FXML @FXML
private TextArea playerTwoTxt; private TextArea playerTwoTxt;
@@ -77,7 +88,14 @@ public class LobbyController {
private List<TextArea> listViews = new ArrayList<>(); private List<TextArea> listViews = new ArrayList<>();
private RaceState raceState; private RaceState raceState;
private ClientToServerThread socketThread;
private Stage customizeStage;
private Color playersColor;
private int MAX_NUM_PLAYERS = 8; private int MAX_NUM_PLAYERS = 8;
private Integer playerID;
private List<LobbyCloseListener> lobbyListeners = new ArrayList<>(); private List<LobbyCloseListener> lobbyListeners = new ArrayList<>();
private ObservableList<String> players; private ObservableList<String> players;
@@ -106,6 +124,9 @@ public class LobbyController {
//Update players if one added. //Update players if one added.
for (int i = 0; i < players.size(); i++) { for (int i = 0; i < players.size(); i++) {
listViews.get(i).setText(players.get(i)); listViews.get(i).setText(players.get(i));
if (playerID == (i + 1)) {
listViews.get(i).setText(listViews.get(i).getText() + " (YOU)");
}
imageViews.get(i).setVisible(true); imageViews.get(i).setVisible(true);
} }
//Update empty text fields if player left. //Update empty text fields if player left.
@@ -130,6 +151,37 @@ public class LobbyController {
} }
} }
@FXML
public void customize() {
Parent root;
try {
FXMLLoader fxmlLoader = new FXMLLoader(LobbyController.class.getResource("/views/customizeView.fxml"));
root = fxmlLoader.load();
root.getStylesheets().add("/css/master.css");
customizeStage = new Stage();
customizeStage.setTitle("Customize Boat");
customizeStage.setScene(new Scene(root, 700, 450));
CustomizationController cc = fxmlLoader.getController();
cc.setServerThread(this.socketThread);
cc.setPlayerName(this.players.get(playerID - 1));
if (this.playersColor == null) {
this.playersColor = Colors.getColor(playerID - 1);
}
cc.setPlayerColor(this.playersColor);
cc.setStage(customizeStage); // pass the stage through so it can be closed later.
cc.setLobbyController(this);
customizeStage.show();
} catch (IOException e) {
Logger.logMsg(4, "Failed to load Customization View from resources.");
}
}
public void setSocketThread(ClientToServerThread thread) {
this.socketThread = thread;
}
@FXML @FXML
public void leaveLobbyButtonPressed() { public void leaveLobbyButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function! // TODO: 10/07/17 wmu16 - Finish function!
@@ -146,6 +198,7 @@ public class LobbyController {
for (LobbyCloseListener readyListener : lobbyListeners) for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.READY); readyListener.notify(CloseStatus.READY);
customizeButton.setDisable(true);
} }
public void setTitle (String title) { public void setTitle (String title) {
@@ -168,8 +221,18 @@ public class LobbyController {
Platform.runLater(this::updatePlayers); Platform.runLater(this::updatePlayers);
} }
public void setPlayerID(Integer id) {
playerID = id;
}
public void updateRaceState(RaceState raceState){ public void updateRaceState(RaceState raceState){
this.raceState = raceState; this.raceState = raceState;
/*if (this.customizeStage != null) {
this.customizeStage.close();
}*/ // TODO: 17/08/17 ajm412: close the customization window if the host starts the game while customizing
if (!customizeButton.isDisabled()) {
customizeButton.setDisable(true);
}
timeUntilStart.setText("Starting in: " + raceState.getRaceTimeStr()); timeUntilStart.setText("Starting in: " + raceState.getRaceTimeStr());
} }
@@ -177,4 +240,9 @@ public class LobbyController {
readyButton.setDisable(true); readyButton.setDisable(true);
readyButton.setVisible(false); readyButton.setVisible(false);
} }
public void setPlayersColor(Color playerColor) {
this.playersColor = playerColor;
}
} }
@@ -129,7 +129,7 @@ public class BoatObject extends Group {
* @param y The Y coordinate to move the boat to * @param y The Y coordinate to move the boat to
* @param rotation The rotation by which the boat moves * @param rotation The rotation by which the boat moves
* @param velocity The velocity the boat is moving * @param velocity The velocity the boat is moving
* @param sailIn * @param sailIn Boolean to toggle sail state.
*/ */
public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) { public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) {
Double dx = Math.abs(boatPoly.getLayoutX() - x); Double dx = Math.abs(boatPoly.getLayoutX() - x);
+1
View File
@@ -99,6 +99,7 @@ Table
-fx-text-fill: black; -fx-text-fill: black;
} }
/*server thread */
.ui-table .column-header { .ui-table .column-header {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
@@ -13,7 +13,7 @@
<Boats> <Boats>
<#list boats as boat> <#list boats as boat>
<Boat Type="Yacht" SourceID="${boat.sourceId}" ShapeID="4" HullNum="${boat.hullID}" StoweName="${boat.shortName}" ShortName="${boat.shortName}" <Boat Type="Yacht" SourceID="${boat.sourceId}" ShapeID="4" HullNum="${boat.hullID}" StoweName="${boat.shortName}" ShortName="${boat.shortName}"
BoatName="${boat.boatName}" Country="${boat.country}"> BoatName="${boat.boatName}" Country="${boat.country}" Color="${boat.boatColor}">
<GPSposition Z="0" Y="3.7" X="0" /> <GPSposition Z="0" Y="3.7" X="0" />
<MastTop Z="0" Y="6.2" X="0" /> <MastTop Z="0" Y="6.2" X="0" />
</Boat> </Boat>
+3 -1
View File
@@ -30,6 +30,7 @@
<GridPane prefHeight="166.0" prefWidth="1530.0" GridPane.rowIndex="2"> <GridPane prefHeight="166.0" prefWidth="1530.0" GridPane.rowIndex="2">
<columnConstraints> <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> </columnConstraints>
<rowConstraints> <rowConstraints>
@@ -37,7 +38,8 @@
</rowConstraints> </rowConstraints>
<children> <children>
<Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" /> <Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" />
<Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="1" GridPane.halignment="CENTER" /> <Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="2" GridPane.halignment="CENTER" />
<Button fx:id="customizeButton" focusTraversable="false" mnemonicParsing="false" onAction="#customize" text="Customization" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</children> </children>
</GridPane> </GridPane>
<GridPane GridPane.rowIndex="1"> <GridPane GridPane.rowIndex="1">
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ColorPicker?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Text?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: #2C2c36;"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.CustomizationController">
<columnConstraints>
<ColumnConstraints fillWidth="false" halignment="CENTER" hgrow="SOMETIMES"
maxWidth="1.7976931348623157E308" minWidth="1.0" percentWidth="10.0" prefWidth="1.0"/>
<ColumnConstraints fillWidth="false" halignment="CENTER" hgrow="SOMETIMES" maxWidth="299.0"
minWidth="10.0" percentWidth="30.0" prefWidth="299.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="299.0" minWidth="10.0" percentWidth="50.0"
prefWidth="154.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="123.0" minWidth="0.0" percentWidth="10.0"
prefWidth="122.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" percentHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" percentHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<TextField fx:id="nameField" maxWidth="200.0" minWidth="200.0" prefWidth="200.0"
GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1"
GridPane.valignment="CENTER"/>
<ColorPicker fx:id="boatColorPicker" prefWidth="200.0" style="-fx-background-color: eeeeee;"
GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="2"
GridPane.valignment="CENTER"/>
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Player Name"
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Color"
GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Button fx:id="customizeSubmit" mnemonicParsing="false" onAction="#submitCustomization"
text="Submit" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3"
GridPane.valignment="CENTER"/>
</children>
</GridPane>
+1 -1
View File
@@ -12,7 +12,7 @@ public class ColorsTest {
Color expectedColors[] = {Color.RED, Color.PERU, Color.GOLD, Color.GREEN, Color.BLUE, Color expectedColors[] = {Color.RED, Color.PERU, Color.GOLD, Color.GREEN, Color.BLUE,
Color.PURPLE, Color.DEEPPINK, Color.GRAY}; Color.PURPLE, Color.DEEPPINK, Color.GRAY};
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
Assert.assertEquals(expectedColors[i], Colors.getColor()); Assert.assertEquals(expectedColors[i], Colors.getColor(i));
} }
} }
} }
+1 -1
View File
@@ -21,7 +21,7 @@ public class YachtTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
y1 = new ServerYacht("Yacht", 101, "Y1", "Y1", "Yacht 1", "C1"); y1 = new ServerYacht("Yacht", 1, "Y1", "Y1", "Yacht 1", "C1");
gs = new GameState("localhost"); gs = new GameState("localhost");
} }