package data;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* @author Michel Bartsch
*
* This class is part of the data wich are send to the robots.
* It just represents this data, reads and writes between C-structure and
* Java, nothing more.
*/
public class GameControlData implements Serializable
{
private static final long serialVersionUID = 5061539348771652049L;
/** Some constants from the C-structure. */
public static final int GAMECONTROLLER_RETURNDATA_PORT = 3939; // port to receive return-packets on
public static final int GAMECONTROLLER_GAMEDATA_PORT= 3838; // port to send game state packets to
public static final String GAMECONTROLLER_STRUCT_HEADER = "RGme";
public static final byte GAMECONTROLLER_STRUCT_VERSION = 9;
public static final byte TEAM_BLUE = 0;
public static final byte TEAM_RED = 1;
public static final byte TEAM_YELLOW = 2;
public static final byte TEAM_BLACK = 3;
public static final byte DROPBALL = -128;
public static final byte GAME_ROUNDROBIN = 0;
public static final byte GAME_PLAYOFF = 1;
public static final byte GAME_DROPIN = 2;
public static final byte STATE_INITIAL = 0;
public static final byte STATE_READY = 1;
public static final byte STATE_SET = 2;
public static final byte STATE_PLAYING = 3;
public static final byte STATE_FINISHED = 4;
public static final byte STATE2_NORMAL = 0;
public static final byte STATE2_PENALTYSHOOT = 1;
public static final byte STATE2_OVERTIME = 2;
public static final byte STATE2_TIMEOUT = 3;
public static final byte C_FALSE = 0;
public static final byte C_TRUE = 1;
/** The size in bytes this class has packed. */
public static final int SIZE =
4 + // header
2 + // version
1 + // packet number
1 + // numPlayers
1 + // gameType
1 + // gameState
1 + // firstHalf
1 + // kickOffTeam
1 + // secGameState
1 + // dropInTeam
2 + // dropInTime
2 + // secsRemaining
2 + // secondaryTime
2 * TeamInfo.SIZE;
/** The size in bytes this class has packed for protocol version 7. */
public static final int SIZE7 =
4 + // header
4 + // version
1 + // numPlayers
1 + // gameState
1 + // firstHalf
1 + // kickOffTeam
1 + // secGameState
1 + // dropInTeam
2 + // dropInTime
4 + // secsRemaining
2 * TeamInfo.SIZE7;
//this is streamed
// GAMECONTROLLER_STRUCT_HEADER // header to identify the structure
// GAMECONTROLLER_STRUCT_VERSION // version of the data structure
public byte packetNumber = 0;
public byte playersPerTeam = (byte)Rules.league.teamSize; // The number of players on a team
public byte gameType = GAME_ROUNDROBIN; // type of the game (GAME_ROUNDROBIN, GAME_PLAYOFF, GAME_DROPIN)
public byte gameState = STATE_INITIAL; // state of the game (STATE_READY, STATE_PLAYING, etc)
public byte firstHalf = C_TRUE; // 1 = game in first half, 0 otherwise
public byte kickOffTeam; // the next team to kick off
public byte secGameState = STATE2_NORMAL; // Extra state information - (STATE2_NORMAL, STATE2_PENALTYSHOOT, etc)
public byte dropInTeam; // team that caused last drop in
protected short dropInTime = -1; // number of seconds passed since the last drop in. -1 before first dropin
public short secsRemaining = (short) Rules.league.halfTime; // estimate of number of seconds remaining in the half
public short secondaryTime = 0; // sub-time (remaining in ready state etc.) in seconds
public TeamInfo[] team = new TeamInfo[2];
/**
* Creates a new GameControlData.
*/
public GameControlData()
{
for (int i=0; i<team.length; i++) {
team[i] = new TeamInfo();
}
team[0].teamColor = TEAM_BLUE;
team[1].teamColor = TEAM_RED;
}
/**
* Returns the corresponding byte-stream of the state of this object.
*
* @return the corresponding byte-stream of the state of this object
*/
public ByteBuffer toByteArray()
{
AdvancedData data = (AdvancedData) this;
ByteBuffer buffer = ByteBuffer.allocate(SIZE);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(GAMECONTROLLER_STRUCT_HEADER.getBytes(), 0, 4);
buffer.putShort(GAMECONTROLLER_STRUCT_VERSION);
buffer.put(packetNumber);
buffer.put(playersPerTeam);
buffer.put(gameType);
if (secGameState == STATE2_NORMAL && gameState == STATE_PLAYING
&& data.getSecondsSince(data.whenCurrentGameStateBegan) < Rules.league.playOffDelayedSwitchToPlaying) {
buffer.put(STATE_SET);
} else {
buffer.put(gameState);
}
buffer.put(firstHalf);
buffer.put(kickOffTeam);
buffer.put(secGameState);
buffer.put(dropInTeam);
buffer.putShort(dropInTime);
buffer.putShort(secsRemaining);
buffer.putShort(secondaryTime);
for (TeamInfo aTeam : team) {
buffer.put(aTeam.toByteArray());
}
return buffer;
}
/**
* Returns the corresponding byte-stream of the state of this object in
* the format of protocol version 7.
*
* @return the corresponding byte-stream of the state of this object
*/
public ByteBuffer toByteArray7()
{
ByteBuffer buffer = ByteBuffer.allocate(SIZE7);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(GAMECONTROLLER_STRUCT_HEADER.getBytes(), 0, 4);
buffer.putInt(7); // version = 7
buffer.put(playersPerTeam);
buffer.put(gameState);
buffer.put(firstHalf);
buffer.put(kickOffTeam == DROPBALL ? 2 : team[kickOffTeam == team[0].teamNumber ? 0 : 1].teamColor);
buffer.put(secGameState);
buffer.put(dropInTeam == -1 ? -1 : team[dropInTeam == team[0].teamNumber ? 0 : 1].teamColor);
buffer.putShort(dropInTime);
buffer.putInt(secsRemaining);
// in version 7, the broadcasted team data was sorted by team color
if (team[0].teamColor == TEAM_BLUE) {
buffer.put(team[0].toByteArray7());
buffer.put(team[1].toByteArray7());
} else {
buffer.put(team[1].toByteArray7());
buffer.put(team[0].toByteArray7());
}
return buffer;
}
/**
* Unpacking the C-structure to the Java class.
*
* @param buffer The buffered C-structure.
* @return Whether the structure was well formed. That is, it must have the proper
* {@link #GAMECONTROLLER_STRUCT_VERSION} set.
*/
public boolean fromByteArray(ByteBuffer buffer)
{
buffer.order(ByteOrder.LITTLE_ENDIAN);
byte[] header = new byte[4];
buffer.get(header, 0, 4);
if (buffer.getShort() != GAMECONTROLLER_STRUCT_VERSION) {
return false;
}
packetNumber = buffer.get();
playersPerTeam = buffer.get();
gameType = buffer.get();
gameState = buffer.get();
firstHalf = buffer.get();
kickOffTeam = buffer.get();
secGameState = buffer.get();
dropInTeam = buffer.get();
dropInTime = buffer.getShort();
secsRemaining = buffer.getShort();
secondaryTime = buffer.getShort();
for (int i=0; i<team.length; i++) {
team[i].fromByteArray(buffer);
}
return true;
}
@Override
public String toString()
{
String out = "";
String temp;
out += " Header: "+GAMECONTROLLER_STRUCT_HEADER+"\n";
out += " Version: "+GAMECONTROLLER_STRUCT_VERSION+"\n";
out += " Packet Number: "+(packetNumber & 0xFF)+"\n";
out += " Players per Team: "+playersPerTeam+"\n";
switch (gameType) {
case GAME_ROUNDROBIN: temp = "round robin"; break;
case GAME_PLAYOFF: temp = "playoff"; break;
case GAME_DROPIN: temp = "drop-in"; break;
default: temp = "undefinied("+gameType+")";
}
out += " gameType: "+temp+"\n";
switch (gameState) {
case STATE_INITIAL: temp = "initial"; break;
case STATE_READY: temp = "ready"; break;
case STATE_SET: temp = "set"; break;
case STATE_PLAYING: temp = "playing"; break;
case STATE_FINISHED: temp = "finish"; break;
default: temp = "undefinied("+gameState+")";
}
out += " gameState: "+temp+"\n";
switch (firstHalf) {
case C_TRUE: temp = "true"; break;
case C_FALSE: temp = "false"; break;
default: temp = "undefinied("+firstHalf+")";
}
out += " firstHalf: "+temp+"\n";
out += " kickOffTeam: "+kickOffTeam+"\n";
switch (secGameState) {
case STATE2_NORMAL: temp = "normal"; break;
case STATE2_PENALTYSHOOT: temp = "penaltyshoot"; break;
case STATE2_OVERTIME: temp = "overtime"; break;
case STATE2_TIMEOUT: temp = "timeout"; break;
default: temp = "undefinied("+secGameState+")";
}
out += " secGameState: "+temp+"\n";
out += " dropInTeam: "+dropInTeam+"\n";
out += " dropInTime: "+dropInTime+"\n";
out += " secsRemaining: "+secsRemaining+"\n";
out += " secondaryTime: "+secondaryTime+"\n";
return out;
}
}