Fixed some weird bug with the messages package, trying to add some small QoL changes with potential interaction bugs with the customization/lobby menu.

tags : #story[1142]
This commit is contained in:
Alistair McIntyre
2017-08-17 13:13:07 +12:00
parent ac279583df
commit faf4600f51
51 changed files with 99 additions and 148 deletions
@@ -0,0 +1,39 @@
package seng302.gameServer.messages;
import java.util.HashMap;
import java.util.Map;
/**
* Created by kre39 on 12/07/17.
*/
public enum BoatAction {
VMG(1),
SAILS_IN(2),
SAILS_OUT(3),
TACK_GYBE(4),
UPWIND(5),
DOWNWIND(6),
MAINTAIN_HEADING(7);
private final int type;
private static final Map<Integer, BoatAction> intToTypeMap = new HashMap<>();
static {
for (BoatAction type : BoatAction.values()) {
intToTypeMap.put(type.getValue(), type);
}
}
BoatAction(int type){
this.type = type;
}
public static BoatAction getType(int value) {
return intToTypeMap.get(value);
}
public int getValue() {
return this.type;
}
}
@@ -0,0 +1,29 @@
package seng302.gameServer.messages;
/**
* Created by kre39 on 12/07/17.
*/
public class BoatActionMessage extends Message{
private final MessageType MESSAGE_TYPE = MessageType.BOAT_ACTION;
private final int MESSAGE_SIZE = 1;
private BoatAction actionType;
public BoatActionMessage(BoatAction actionType) {
this.actionType = actionType;
setHeader(new Header(MessageType.BOAT_ACTION, 0, (short) 1)); // the second variable is the source id
allocateBuffer();
writeHeaderToBuffer();
// Write message fields
putInt(actionType.getValue(), 1);
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_SIZE;
}
}
@@ -0,0 +1,162 @@
package seng302.gameServer.messages;
public class BoatLocationMessage extends Message {
private final int MESSAGE_SIZE = 56;
private long messageVersionNumber;
private long time;
private long sourceId;
private long sequenceNum;
private DeviceType deviceType;
private double latitude;
private double longitude;
private long altitude;
private Double heading;
private long pitch;
private long roll;
private long boatSpeed;
private long COG;
private long SOG;
private long apparentWindSpeed;
private long apparentWindAngle;
private long trueWindSpeed;
private long trueWindDirection;
private long trueWindAngle;
private long currentDrift;
private long currentSet;
private long rudderAngle;
/**
* Describes the location, altitude and sensor data from the boat.
*
* @param sourceId ID of the boat
* @param sequenceNum Sequence number of the message
* @param latitude The boats latitude
* @param longitude The boats longitude
* @param heading The boats heading
* @param boatSpeed The boats speed
*/
public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude,
double heading, long boatSpeed) {
messageVersionNumber = 1;
time = System.currentTimeMillis();
this.sourceId = sourceId;
this.sequenceNum = sequenceNum;
this.deviceType = DeviceType.RACING_YACHT;
this.latitude = latitude;
this.longitude = longitude;
this.altitude = 0;
this.heading = heading;
this.pitch = 0;
this.roll = 0;
this.boatSpeed = boatSpeed;
this.COG = 2;
this.SOG = boatSpeed;
this.apparentWindSpeed = 0;
this.apparentWindAngle = 0;
this.trueWindSpeed = 0;
this.trueWindDirection = 0;
this.trueWindAngle = 0;
this.currentDrift = 0;
this.currentSet = 0;
this.rudderAngle = 0;
setHeader(new Header(MessageType.BOAT_LOCATION, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
long headingToSend = (long) ((heading / 360.0) * 65535.0);
putByte((byte) messageVersionNumber);
putInt(time, 6);
putInt((int) sourceId, 4);
putUnsignedInt((int) sequenceNum, 4);
putByte((byte) deviceType.getCode());
putInt((int) latLonToBinaryPackedLong(latitude), 4);
putInt((int) latLonToBinaryPackedLong(longitude), 4);
putInt((int) altitude, 4);
putInt(headingToSend, 2);
putInt((int) pitch, 2);
putInt((int) roll, 2);
putInt((int) boatSpeed, 2);
putUnsignedInt((int) COG, 2);
putUnsignedInt((int) SOG, 2);
putUnsignedInt((int) apparentWindSpeed, 2);
putInt((int) apparentWindAngle, 2);
putUnsignedInt((int) trueWindSpeed, 2);
putUnsignedInt((int) trueWindDirection, 2);
putInt((int) trueWindAngle, 2);
putUnsignedInt((int) currentDrift, 2);
putUnsignedInt((int) currentSet, 2);
putInt((int) rudderAngle, 2);
writeCRC();
rewind();
}
/**
* Convert binary latitude or longitude to floating point number
*
* @param binaryPackedLatLon Binary packed lat OR lon
* @return Floating point lat/lon
*/
public static double binaryPackedToLatLon(long binaryPackedLatLon) {
return (double) binaryPackedLatLon * 180.0 / 2147483648.0;
}
/**
* Convert binary packed heading to floating point number
*
* @param binaryPackedHeading Binary packed heading
* @return heading as a decimal
*/
public static double binaryPackedHeadingToDouble(long binaryPackedHeading) {
return (double) binaryPackedHeading * 360.0 / 65536.0;
}
/**
* Convert binary packed wind angle to floating point number
*
* @param binaryPackedWindAngle Binary packed wind angle
* @return wind angle as a decimal
*/
public static double binaryPackedWindAngleToDouble(long binaryPackedWindAngle) {
return (double) binaryPackedWindAngle * 180.0 / 32768.0;
}
/**
* Convert a latitude or longitude to a binary packed long
*
* @param latLon A floating point latitude/longitude
* @return A binary packed lat/lon
*/
public static long latLonToBinaryPackedLong(double latLon) {
return (long) ((536870912 * latLon) / 45);
}
/**
* Convert a heading to a binary packed long
*
* @param heading A floating point heading
* @return A binary packed heading
*/
public static long headingToBinaryPackedLong(double heading) {
return (long) ((8192 * heading) / 45);
}
/**
* Convert a wind angle to a binary packed long
*
* @param windAngle Floating point wind angle
* @return A binary packed wind angle
*/
public static long windAngleToBinaryPackedLong(double windAngle) {
return (long) ((8192 * windAngle) / 45);
}
@Override
public int getSize() {
return MESSAGE_SIZE;
}
}
@@ -0,0 +1,25 @@
package seng302.gameServer.messages;
/**
* The current status of a boat
*/
public enum BoatStatus {
UNDEFINED(0),
PRESTART(1),
RACING(2),
FINISHED(3),
DNS(4),
DNF(5),
DSQ(6),
CS(7);
private long code;
BoatStatus(long code) {
this.code = code;
}
public long getCode(){
return code;
}
}
@@ -0,0 +1,85 @@
package seng302.gameServer.messages;
import java.nio.ByteBuffer;
/**
* The status of each boat, sent within a race status message
*/
public class BoatSubMessage{
private final int MESSAGE_SIZE = 20;
private long sourceId;
private BoatStatus boatStatus;
private long legNumber;
private long numberPenaltiesAwarded;
private long numberPenaltiesServed;
private long estimatedTimeAtNextMark;
private long estimatedTimeAtFinish;
private ByteBuffer buff = ByteBuffer.allocate(getSize());
private int buffPos = 0;
/**
* Boat Sub message from section 4.2 of the AC35 streaming data interface spec
* @param sourceId The source ID of the boat
* @param boatStatus The boats status
* @param legNumber The leg the boat is on (0= prestart, 1=start to first mark etc)
* @param numberPenaltiesAwarded The number of penalties awarded to the boat
* @param numberPenaltiesServed The number of penalties served to the boat
* @param estimatedTimeAtFinish The estimated time (UTC) the boat will finish the race
* @param estimatedTimeAtNextMark The estimated time (UTC) the boat will arrive at the next mark
*/
public BoatSubMessage(long sourceId, BoatStatus boatStatus, long legNumber, long numberPenaltiesAwarded, long numberPenaltiesServed,
long estimatedTimeAtFinish, long estimatedTimeAtNextMark){
this.sourceId = sourceId;
this.boatStatus = boatStatus;
this.legNumber = legNumber;
this.numberPenaltiesAwarded = numberPenaltiesAwarded;
this.numberPenaltiesServed = numberPenaltiesServed;
this.estimatedTimeAtFinish = estimatedTimeAtFinish;
this.estimatedTimeAtNextMark = estimatedTimeAtNextMark;
}
/**
* @return The size of this message in bytes
*/
public int getSize(){
return MESSAGE_SIZE;
}
private void putInBuffer(byte[] bytes, long val){
byte[] tmp = bytes.clone();
Message.reverse(tmp);
buff.put(tmp);
buffPos += tmp.length;
buff.position(buffPos);
}
/**
* @return a ByteBuffer containing this boat status message
*/
public ByteBuffer getByteBuffer(){
// Source ID, 4 bytes
putInBuffer(Message.intToByteArray(sourceId, 4), 4);
// Boat Status, 1 byte
putInBuffer(ByteBuffer.allocate(1).put((byte) (boatStatus.getCode() & 0xff)).array(), 1);
// Leg number, 1 byte
putInBuffer(ByteBuffer.allocate(1).put((byte) (legNumber & 0xff)).array(), 1);
// Number of penalties awarded, 1 byte
putInBuffer(ByteBuffer.allocate(1).put((byte) (numberPenaltiesAwarded & 0xff)).array(), 1);
// Number of penalties served, 1 byte
putInBuffer(ByteBuffer.allocate(1).put((byte) (numberPenaltiesServed & 0xff)).array(), 1);
// Estimated time at next mark, 6 bytes
putInBuffer(Message.intToByteArray((int) estimatedTimeAtNextMark, 6),6);
// Estimated time at finish, 6 bytes
putInBuffer(Message.intToByteArray((int) estimatedTimeAtFinish, 6), 6);
return buff;
}
}
@@ -0,0 +1,36 @@
package seng302.gameServer.messages;
public class ChatterMessage extends Message {
private final long MESSAGE_VERSION_NUMBER = 1;
private final int MESSAGE_SIZE = 3;
private int message_type;
private int message_size = 21;
private String message;
public ChatterMessage(int message_type, int message_size, String message) {
this.message_type = message_type;
this.message_size = message_size;
this.message = message;
setHeader(new Header(MessageType.CHATTER_TEXT, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putByte((byte) MESSAGE_VERSION_NUMBER);
putInt(message_type, 1);
putInt(message_size, 1);
putBytes(message.getBytes());
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_SIZE + message_size;
}
}
@@ -0,0 +1,33 @@
package seng302.gameServer.messages;
public enum ClientType {
SPECTATOR(0x00),
PLAYER(0x01),
CONTROL_TUTORIAL(0x02),
GHOST_MODE(0x03);
private int type;
ClientType(int type){
this.type = type;
}
public int getCode(){
return type;
}
public static ClientType getClientType(int typeCode){
switch (typeCode){
case 0x00:
return SPECTATOR;
case 0x01:
return PLAYER;
case 0x02:
return CONTROL_TUTORIAL;
case 0x03:
return GHOST_MODE;
default:
return PLAYER;
}
}
}
@@ -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;
}
}
}
@@ -0,0 +1,16 @@
package seng302.gameServer.messages;
public enum DeviceType {
UNKNOWN(0),
RACING_YACHT(1);
private long code;
DeviceType(long code) {
this.code = code;
}
public long getCode(){
return code;
}
}
@@ -0,0 +1,80 @@
package seng302.gameServer.messages;
import java.nio.ByteBuffer;
public class Header {
// From API spec
private final int syncByte1 = 0x47;
private final int syncByte2 = 0x83;
private MessageType messageType;
private int timeStamp;
private int sourceId;
private short messageLength;
private static final int MESSAGE_LEN = 15;
private ByteBuffer buff;
private int buffPos;
/**
* Message Header from section 3.2 of the AC35 Streaming
* Data spec
* @param messageType The type of the message following this header
* @param sourceId The message source (as defined in the spec)
* @param messageLength The length of the message following this header
*/
public Header(MessageType messageType, int sourceId, Short messageLength){
this.messageType = messageType;
this.sourceId = sourceId;
this.messageLength = messageLength;
timeStamp = (int) (System.currentTimeMillis() / 1000L);
buff = ByteBuffer.allocate(MESSAGE_LEN);
buffPos = 0;
}
private void putInBuffer(byte[] bytes, long val){
byte[] tmp = bytes.clone();
Message.reverse(tmp);
buff.put(tmp);
buffPos += tmp.length;
buff.position(buffPos);
}
/**
* Reset the buffer
*/
public void reset(){
buffPos = 0;
buff.clear();
buff.position(buffPos);
}
/**
* @return a ByteBuffer containing the message header
*/
public ByteBuffer getByteBuffer(){
reset();
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte1).array(), syncByte1);
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte2).array(), syncByte2);
putInBuffer(ByteBuffer.allocate(1).put((byte)messageType.getCode()).array(), messageType.getCode());
putInBuffer(Message.intToByteArray(timeStamp, 6), timeStamp);
putInBuffer(Message.intToByteArray(sourceId, 4), sourceId);
putInBuffer(Message.intToByteArray(messageLength, 2), messageLength);
return buff;
}
/**
* Returns the size of this message
* @return the size of the message
*/
public static Integer getSize(){
return MESSAGE_LEN;
}
}
@@ -0,0 +1,27 @@
package seng302.gameServer.messages;
public class Heartbeat extends Message {
private final int MESSAGE_SIZE = 4;
/**
* Heartbeat from the AC35 Streaming data spec
* @param seqNo Increment every time a message is sent
*/
public Heartbeat(int seqNo){
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putUnsignedInt(seqNo, 4);
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_SIZE;
}
}
@@ -0,0 +1,20 @@
package seng302.gameServer.messages;
/**
* Types of marks boats can round
*/
public enum MarkType {
UNKNOWN(0),
ROUNDING_MARK(1),
GATE(2);
private long code;
MarkType(long code) {
this.code = code;
}
public long getCode(){
return code;
}
}
@@ -0,0 +1,221 @@
package seng302.gameServer.messages;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.zip.CRC32;
public abstract class Message {
private final int CRC_SIZE = 4;
private Header header;
private ByteBuffer buffer;
private int bufferPosition;
private CRC32 crc;
/**
* @param header Set the header for this message
*/
void setHeader(Header header){
this.header = header;
}
/**
* @return the header specified for this message
*/
Header getHeader(){
return header;
}
/**
* @return the size of the message
*/
public abstract int getSize();
/**
* Allocate byte buffer to correct size
*/
void allocateBuffer(){
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
buffer.order(ByteOrder.LITTLE_ENDIAN);
bufferPosition = 0;
buffer.position(bufferPosition);
}
/**
* Write the set header to the byte buffer
*/
void writeHeaderToBuffer(){
buffer.put(getHeader().getByteBuffer().array());
bufferPosition += Header.getSize();
buffer.position(bufferPosition);
}
/**
* Move the buffer position by n bytes
* @param size Number of bytes to move the buffer by
*/
private void moveBufferPositionBy(int size){
bufferPosition += size;
buffer.position(bufferPosition);
}
/**
* Put an unsigned byte in the buffer
*/
void putUnsignedByte(byte b){
buffer.put(ByteBuffer.allocate(1).put((byte) (b & 0xff)).array());
moveBufferPositionBy(1);
}
/**
* Put an signed byte in the buffer
*/
void putByte(byte b){
buffer.put(ByteBuffer.allocate(1).put(b).array());
moveBufferPositionBy(1);
}
/**
* Place an unsigned integer of the specified length in the buffer
* @param val The integer value to add (Note: This must be long due to java not supporting unsigned integers)
* @param size The size of the int to be added to the buffer
*/
void putUnsignedInt(long val, int size){
if (size <= 1){
putUnsignedByte((byte) val);
}
else if (size < 4){
// Use short
byte[] tmp = Message.intToByteArray(val, size); //ByteBuffer.allocate(size).putShort((short) (val & 0xffff)).array();
reverse(tmp);
buffer.put(tmp);
moveBufferPositionBy(size);
}
else{
// Use int
byte[] tmp = Message.intToByteArray(val, size);
reverse(tmp);
moveBufferPositionBy(size);
}
}
/**
* Put a signed int of a specified length in the buffer
* @param val The integer value to add
* @param size The size of the integer to be added to the buffer
*/
void putInt(long val, int size){
if (size < 4){
byte[] tmp = Message.intToByteArray(val, size);
reverse(tmp);
buffer.put(tmp);
}
else{
byte[] tmp = Message.intToByteArray(val, size);
reverse(tmp);
buffer.put(tmp);
}
moveBufferPositionBy(size);
}
/**
* Write an array of bytes to the buffer
* @param bytes to write
*/
void putBytes(byte[] bytes){
buffer.put(bytes);
moveBufferPositionBy(bytes.length);
}
/**
* Write a ByteBuffer of bytes to the buffer
* @param bytes to write
* @param size number of bytes
*/
void putBytes(ByteBuffer bytes, int size){
buffer.put(bytes.array());
moveBufferPositionBy(size);
}
/**
* Calculate the CRC of the buffer and append it to the end of the buffer
*/
void writeCRC(){
crc = new CRC32();
buffer.position(0);
byte[] data = Arrays.copyOfRange(buffer.array(), 0, buffer.array().length-CRC_SIZE);
crc.update(data);
buffer.position(bufferPosition);
putInt((int) crc.getValue(), CRC_SIZE);
}
/**
* @return The current buffer as a byte array
*/
public byte[] getBuffer(){
return buffer.array();
}
/**
* Rewind the buffer to the beginning
*/
void rewind(){
buffer.flip();
}
/**
* Convert an integer to an array of bytes
* @param val The value to add
* @param len The width of the integer in the buffer
* @return A byte array to be sent
*/
public static byte[] intToByteArray(long val, int len){
int index = 0;
byte[] data = new byte[len];
for (int i = 0; i < len; i++){
data[len - index - 1] = (byte) (val & 0xFF);
val >>>= 8;
index++;
}
return data;
}
/**
* takes an array of up to 7 bytes in little endian format and
* returns a positive long constructed from the input bytes
*
* @param bytes the bytes to be converted to long
* @return a positive long if there is less than 8 bytes -1 otherwise
*/
public static long bytesToLong(byte[] bytes){
long partialLong = 0;
int index = 0;
for (byte b: bytes){
if (index > 6){
return -1;
}
partialLong = partialLong | (b & 0xFFL) << (index * 8);
index++;
}
return partialLong;
}
/**
* Reverse an array of bytes
* @param data The byte[] to reverse
*/
public static void reverse(byte[] data) {
for (int left = 0, right = data.length - 1; left < right; left++, right--) {
byte temp = (byte) (data[left] & 0xff);
data[left] = (byte) (data[right] & 0xff);
data[right] = (byte) (temp & 0xff);
}
}
}
@@ -0,0 +1,42 @@
package seng302.gameServer.messages;
/**
* Enum containing the types of messages
* sent by the server
*/
public enum MessageType {
HEARTBEAT(1),
RACE_STATUS(12),
DISPLAY_TEXT_MESSAGE(20),
XML_MESSAGE(26),
RACE_START_STATUS(27),
YACHT_EVENT_CODE(29),
YACHT_ACTION_CODE(31),
CHATTER_TEXT(36),
BOAT_LOCATION(37),
MARK_ROUNDING(38),
COURSE_WIND(44),
AVERAGE_WIND(47),
BOAT_ACTION(100),
REGISTRATION_REQUEST(101),
REGISTRATION_RESPONSE(102),
CUSTOMIZATION_REQUEST(103),
CUSTOMIZATION_RESPONSE(104);
private int code;
MessageType(int code){
this.code = code;
}
/**
* Get the message code (From the API Spec)
* @return the message code
*/
int getCode(){
return this.code;
}
}
@@ -0,0 +1,21 @@
package seng302.gameServer.messages;
/**
* The types of race start status messages
*/
public enum RaceStartNotificationType {
SET_RACE_START_TIME(1),
RACE_POSTPONED(2),
RACE_ABANDONED(3),
RACE_TERMINATED(4);
private final long type;
RaceStartNotificationType(long type) {
this.type = type;
}
long getType(){
return type;
}
}
@@ -0,0 +1,48 @@
package seng302.gameServer.messages;
public class RaceStartStatusMessage extends Message {
private final int MESSAGE_SIZE = 20;
private long version;
private long timeStamp;
private long ackNumber;
private long raceStartTime;
private long raceId;
private RaceStartNotificationType notificationType;
/**
* Message sent to clients with the expected start time of the race
* @param ackNumber Sequence number of message.
* @param raceStartTime Expected race start time
* @param raceId Race ID#
* @param notificationType Type of this notification
*/
public RaceStartStatusMessage(long ackNumber, long raceStartTime, long raceId, RaceStartNotificationType notificationType){
this.version = 1;
this.timeStamp = System.currentTimeMillis() / 1000L;
this.ackNumber = ackNumber;
this.raceStartTime = raceStartTime;
this.notificationType = notificationType;
this.raceId = raceId;
setHeader(new Header(MessageType.RACE_START_STATUS, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putUnsignedByte((byte) version);
putInt((int) timeStamp, 6);
putInt((int) ackNumber, 2);
putInt((int) raceStartTime, 6);
putInt((int) raceId, 4);
putUnsignedByte((byte) notificationType.getType());
writeCRC();
rewind();
}
@Override
public int getSize() {
return MESSAGE_SIZE;
}
}
@@ -0,0 +1,26 @@
package seng302.gameServer.messages;
/**
* The current status of the race
*/
public enum RaceStatus {
NOTACTIVE(0),
WARNING(1), // Between 3:00 and 1:00 before start
PREPARATORY(2), // Less than 1:00 before start
STARTED(3),
ABANDONED(6),
POSTPONED(7),
TERMINATED(8),
RACE_START_TIME_NOT_SET(9),
PRESTART(10); // More than 3:00 before start
private int code;
RaceStatus(int code){
this.code = code;
}
public int getCode(){
return this.code;
}
}
@@ -0,0 +1,79 @@
package seng302.gameServer.messages;
import java.util.List;
import java.util.zip.CRC32;
public class RaceStatusMessage extends Message{
private final MessageType MESSAGE_TYPE = MessageType.RACE_STATUS;
private final int MESSAGE_VERSION = 2; //Always set to 1
private final int MESSAGE_BASE_SIZE = 24;
private final double windDirFactor = 0x4000 / 90;
private long currentTime;
private long raceId;
private RaceStatus raceStatus;
private long expectedStartTime;
private double raceWindDirection;
private long windSpeed;
private long numBoatsInRace;
private RaceType raceType;
private List<BoatSubMessage> boats;
private CRC32 crc;
/**
* A message containing the current status of the race
* @param raceId The ID of the current race
* @param raceStatus The status of the race
* @param expectedStartTime The expected start time
* @param raceWindDirection The wind direction (north, east, south)
* @param windSpeed The wind speed in mm/sec
* @param numBoatsInRace The number of boats in the race
* @param raceType The race type (Match/fleet)
* @param sourceId The source of this message
* @param boats A list of boat status sub messages
*/
public RaceStatusMessage(long raceId, RaceStatus raceStatus, long expectedStartTime, double raceWindDirection,
long windSpeed, long numBoatsInRace, RaceType raceType, long sourceId, List<BoatSubMessage> boats){
currentTime = System.currentTimeMillis();
this.raceId = raceId;
this.raceStatus = raceStatus;
this.expectedStartTime = expectedStartTime;
this.raceWindDirection = raceWindDirection * windDirFactor;
this.windSpeed = windSpeed;
this.numBoatsInRace = numBoatsInRace;
this.raceType = raceType;
this.boats = boats;
crc = new CRC32();
setHeader(new Header(MESSAGE_TYPE, (int) sourceId, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putByte((byte) MESSAGE_VERSION);
putInt(currentTime, 6);
putInt((int) raceId, 4);
putByte((byte) raceStatus.getCode());
putInt(expectedStartTime, 6);
putInt((int) this.raceWindDirection, 2);
putInt((int) windSpeed, 2);
putByte((byte) numBoatsInRace);
putByte((byte) raceType.getCode());
for (BoatSubMessage boatSubMessage : boats){
putBytes(boatSubMessage.getByteBuffer(), boatSubMessage.getSize());
}
writeCRC();
rewind();
}
/**
* @return the size of this message in bytes
*/
@Override
public int getSize() {
return MESSAGE_BASE_SIZE + (20 * ((int) numBoatsInRace));
}
}
@@ -0,0 +1,20 @@
package seng302.gameServer.messages;
/**
* Enum containing the types of races
* sent by the server
*/
public enum RaceType {
MATCH_RACE(1),
FLEET_RACE(2);
private long code;
RaceType(long code){
this.code = code;
}
public long getCode(){
return code;
}
}
@@ -0,0 +1,22 @@
package seng302.gameServer.messages;
public class RegistrationRequestMessage extends Message {
private static int MESSAGE_LENGTH = 2;
public RegistrationRequestMessage(ClientType type){
setHeader(new Header(MessageType.REGISTRATION_REQUEST, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putInt(type.getCode(), 2);
writeCRC();
}
@Override
public int getSize() {
return MESSAGE_LENGTH;
}
}
@@ -0,0 +1,20 @@
package seng302.gameServer.messages;
public class RegistrationResponseMessage extends Message{
public RegistrationResponseMessage(int clientSourceID, RegistrationResponseStatus status){
setHeader(new Header(MessageType.REGISTRATION_RESPONSE, 1, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
putInt(clientSourceID, 4);
putInt(status.getCode(), 1);
writeCRC();
}
@Override
public int getSize() {
return 5;
}
}
@@ -0,0 +1,44 @@
package seng302.gameServer.messages;
public enum RegistrationResponseStatus {
SUCCESS_SPECTATING(0x00),
SUCCESS_PLAYING(0x01),
SUCCESS_TUTORIAL(0x02),
SUCCESS_GHOSTING(0x03),
FAILURE_GENERAL(0x10),
FAILURE_FULL(0x11);
private int code;
RegistrationResponseStatus(int code){
this.code = code;
}
/**
* Get the message code (From the API Spec)
* @return the message code
*/
int getCode(){
return this.code;
}
public static RegistrationResponseStatus getResponseStatus(int typeCode){
switch (typeCode){
case 0x00:
return SUCCESS_SPECTATING;
case 0x01:
return SUCCESS_PLAYING;
case 0x02:
return SUCCESS_TUTORIAL;
case 0x03:
return SUCCESS_GHOSTING;
case 0x10:
return FAILURE_GENERAL;
case 0x11:
return FAILURE_FULL;
default:
return FAILURE_GENERAL;
}
}
}
@@ -0,0 +1,21 @@
package seng302.gameServer.messages;
/**
* The status of a boat rounding a mark
*/
public enum RoundingBoatStatus {
UNKNOWN(0),
RACING(1),
DSQ(2),
WITHDRAWN(3);
private long code;
RoundingBoatStatus(long code) {
this.code = code;
}
public long getCode(){
return code;
}
}
@@ -0,0 +1,52 @@
package seng302.gameServer.messages;
/**
* The side the boat rounded the mark
*/
public enum RoundingSide {
UNKNOWN(0, "Unknown"),
PORT(1, "Port"),
STARBOARD(2, "Stbd"),
SP(3, "SP"),
PS(4, "PS");
private long code;
private String name;
RoundingSide(long code, String name) {
this.code = code;
this.name = name;
}
public long getCode(){
return code;
}
public String getName() {
return name;
}
public static RoundingSide getRoundingSide(String identifier) {
RoundingSide roundingSide = UNKNOWN;
switch (identifier) {
case "Unknown":
roundingSide = UNKNOWN;
break;
case "Port":
roundingSide = PORT;
break;
case "Stbd":
roundingSide = STARBOARD;
break;
case "SP":
roundingSide = SP;
break;
case "PS":
roundingSide = PS;
break;
}
return roundingSide;
}
}
@@ -0,0 +1,53 @@
package seng302.gameServer.messages;
public class XMLMessage extends Message{
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
private final int MESSAGE_VERSION = 1; //Always set to 1
private final int MESSAGE_SIZE = 14;
// Message fields
private long timeStamp;
private long ack = 0x00; //Unused
private XMLMessageSubType xmlMessageSubType;
private long length;
private long sequence;
private String content;
/**
* XML Message from the AC35 Streaming data spec
* @param content The XML content
* @param type The XML Message Sub Type
* @param sequenceNum sequenceNum
*/
public XMLMessage(String content, XMLMessageSubType type, long sequenceNum){
this.content = content;
this.xmlMessageSubType = type;
timeStamp = System.currentTimeMillis() / 1000L;
ack = 0;
length = this.content.length();
sequence = sequenceNum;
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
// Write message fields
putUnsignedByte((byte) MESSAGE_VERSION);
putInt((int) ack, 2);
putInt((int) timeStamp, 6);
putByte((byte)xmlMessageSubType.getType());
putInt((int) sequence, 2);
putInt((int) length, 2);
putBytes(content.getBytes());
writeCRC();
rewind();
}
/**
* @return The length of this message
*/
public int getSize(){
return MESSAGE_SIZE + content.length();
}
}
@@ -0,0 +1,20 @@
package seng302.gameServer.messages;
/**
* Enum containing the types of XML messages
*/
public enum XMLMessageSubType {
REGATTA(5),
RACE(6),
BOAT(7);
private int type;
XMLMessageSubType(int type){
this.type = type;
}
public int getType(){
return this.type;
}
}
@@ -0,0 +1,52 @@
package seng302.gameServer.messages;
/**
* Created by zyt10 on 10/08/17.
*/
public class YachtEventCodeMessage extends Message {
private final MessageType MESSAGE_TYPE = MessageType.YACHT_EVENT_CODE;
private final int MESSAGE_VERSION = 1; //Always set to 1
private final int MESSAGE_SIZE = 22;
// Message fields
private long timeStamp;
private long ack = 0x00; //Unused
private int raceId;
private int destSourceId;
private int incidentId;
private int eventId;
public YachtEventCodeMessage(Integer subjectId) {
timeStamp = System.currentTimeMillis() / 1000L;
ack = 0;
raceId = 1;
destSourceId = subjectId; // collision boat source id
incidentId = 0;
eventId = 33;
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
allocateBuffer();
writeHeaderToBuffer();
// Write message fields
putUnsignedByte((byte) MESSAGE_VERSION);
putInt((int) timeStamp, 6);
putInt((int) ack, 2);
putInt((int) raceId, 4);
putInt((int) destSourceId, 4);
putInt((int) incidentId, 4);
putInt((int) eventId, 1);
writeCRC();
rewind();
}
/**
* @return The length of this message
*/
public int getSize() {
return MESSAGE_SIZE;
}
}