package Factory.CustomMessages; import EnvironmentPluginAPI.CustomNetworkMessages.*; import Factory.GameLogic.Enums.Faction; import Factory.GameLogic.Exceptions.ConsistencyFaultException; import Factory.GameLogic.TransportTypes.*; import Factory.Interfaces.IHasConsistencyCheck; import java.io.*; import java.nio.ByteBuffer; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.UUID; /** * This is used to transport representations of the current state of the game via the network adapters. */ public class GameStateMessage extends NetworkMessage implements IHasConsistencyCheck, Serializable { // ------------------------------ FIELDS ------------------------------ private TGameState gameState; private byte[] raw; // --------------------------- CONSTRUCTORS --------------------------- public GameStateMessage(int clientId, TGameState gameState) { super(clientId); this.gameState = gameState; if (!isConsistent()) { throw new ConsistencyFaultException(); } } @Override public boolean isConsistent() { return this.gameState.isConsistent(); } /** * only for serializtion!! */ public GameStateMessage() { super(-1); } private TGameState decodeTGameState(ByteBuffer buffer) { TPlayer activePlayer = decodeTPlayer(buffer); int turn = buffer.getInt(); int round = buffer.getInt(); int dateTimeStringLength = buffer.getInt(); byte[] dateTimeStringByteArray = new byte[dateTimeStringLength]; for (int i = 0; i < dateTimeStringLength; i++) { dateTimeStringByteArray[i] = buffer.get(); } Date timeStarted = new Date(new String(dateTimeStringByteArray)); int factoryArrayLength = buffer.get(); List<TFactory> factoryList = new LinkedList<TFactory>(); for (int i = 0; i < factoryArrayLength; i++) { factoryList.add(decodeTFactory(buffer)); } int abstractFieldArrayX = buffer.getInt(); int abstractFieldArrayY = buffer.getInt(); TAbstractField[][] abstractFields = new TAbstractField[abstractFieldArrayX][abstractFieldArrayY]; for (int i = 0; i < abstractFieldArrayX; i++) { for (int j = 0; j < abstractFieldArrayY; j++) { abstractFields[i][j] = decodeTAbstractField(buffer); } } boolean won = false; if (buffer.hasRemaining()) { won = true; } return new TGameState(won, activePlayer, turn, round, timeStarted, factoryList, abstractFields); } private TFactory decodeTFactory(ByteBuffer buffer) { int remainingRoundsForRespawn = buffer.getInt(); int currentInfluence = buffer.getInt(); Faction owningFaction = Faction.values()[buffer.get()]; int factoryID = buffer.getInt(); return new TFactory(remainingRoundsForRespawn, currentInfluence, owningFaction, factoryID); } private TAbstractField decodeTAbstractField(ByteBuffer buffer) { TUnit occupant = null; int occupied = buffer.get(); if (occupied == 1) { occupant = decodeTUnit(buffer); } else if (occupied < 0 || occupied > 1) { throw new DecodingMessageFailedException("not correctly encoded if field is occupied."); } byte typeOfField = buffer.get(); switch (typeOfField) { case 0: return decodeTFactoryField(buffer, occupant); case 1: return decodeTInfluenceField(buffer, occupant); case 2: return decodeTNormalField(occupant); default: throw new DecodingMessageFailedException("field type incorrectly encoded"); } } private TUnit decodeTUnit(ByteBuffer buffer) { long unitIdMSB = buffer.getLong(); long unitIdLSB = buffer.getLong(); Faction faction = Faction.values()[buffer.getInt()]; return new TUnit(new UUID(unitIdMSB, unitIdLSB), faction); } private TFactoryField decodeTFactoryField(ByteBuffer buffer, TUnit occupant) { int factoryId = buffer.getInt(); return new TFactoryField(occupant, factoryId); } private TInfluenceField decodeTInfluenceField(ByteBuffer buffer, TUnit occupant) { int factoryId = buffer.getInt(); return new TInfluenceField(occupant, factoryId); } private TNormalField decodeTNormalField(TUnit occupant) { return new TNormalField(occupant); } private TPlayer decodeTPlayer(ByteBuffer buffer) { int playerNameLength = buffer.getInt(); byte[] playerNameByteArray = new byte[playerNameLength]; for (int i = 0; i < playerNameLength; i++) { playerNameByteArray[i] = buffer.get(); } String playerName = new String(playerNameByteArray); Faction playerFaction = Faction.values()[buffer.getInt()]; return new TPlayer(playerName, playerFaction); } // -------------------------- PUBLIC METHODS -------------------------- public TGameState getEnvironmentState() { return gameState; } // -------------------------- PRIVATE METHODS -------------------------- private byte[] encodeTGameState(TGameState gameState) { byte[] result = new byte[0]; result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(gameState.getTurn()).array()); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(gameState.getRound()).array()); byte[] startingTimeByteArray = gameState.getGameStartedAt().toString().getBytes(); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(startingTimeByteArray.length).array()); result = NetworkMessage.concatByteArrays(result, startingTimeByteArray); result = NetworkMessage.concatByteArrays(result, new byte[]{(byte) gameState.getFactories().size()}); for (TFactory factory : gameState.getFactories()) { result = NetworkMessage.concatByteArrays(result, encodeTFactory(factory)); } result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(gameState.getMapFields().length).array()); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(gameState.getMapFields()[0].length).array()); for (int i = 0; i < gameState.getMapFields().length; i++) { for (int j = 0; j < gameState.getMapFields()[0].length; j++) { result = NetworkMessage.concatByteArrays(result, encodeTAbstractField(gameState.getMapFields()[i][j])); } } if (gameState.hasClientMetGoal()) { result = NetworkMessage.concatByteArrays(result, ONE); } return result; } private byte[] encodeTFactory(TFactory factory) { byte[] result = new byte[0]; result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(factory.getRemainingRoundsForRespawn()).array()); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(factory.getCurrentInfluence()).array()); result = NetworkMessage.concatByteArrays(result, new byte[]{(byte) factory.getOwningFaction().ordinal()}); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(factory.getFactoryID()).array()); return result; } private byte[] encodeTAbstractField(TAbstractField abstractField) { byte[] result; if (abstractField.getOccupant() != null) { result = NetworkMessage.ONE; result = NetworkMessage.concatByteArrays(result, encodeTUnit(abstractField.getOccupant())); } else { result = NetworkMessage.ZERO; } if (abstractField instanceof TFactoryField) { result = NetworkMessage.concatByteArrays(result, NetworkMessage.ZERO); result = NetworkMessage.concatByteArrays(result, encodeTFactoryField((TFactoryField) abstractField)); } else if (abstractField instanceof TInfluenceField) { result = NetworkMessage.concatByteArrays(result, NetworkMessage.ONE); result = NetworkMessage.concatByteArrays(result, encodeTInfluenceField((TInfluenceField) abstractField)); } else if (abstractField instanceof TNormalField) { result = NetworkMessage.concatByteArrays(result, NetworkMessage.TWO); result = NetworkMessage.concatByteArrays(result, encodeTNormalField((TNormalField) abstractField)); } return result; } private byte[] encodeTUnit(TUnit unit) { byte[] unitIdMSB = ByteBuffer.allocate(8).putLong(unit.getUnitId().getMostSignificantBits()).array(); byte[] unitIdLSB = ByteBuffer.allocate(8).putLong(unit.getUnitId().getLeastSignificantBits()).array(); byte[] factionOrdinal = ByteBuffer.allocate(4).putInt(unit.getControllingFaction().ordinal()).array(); byte[] unitUUID = NetworkMessage.concatByteArrays(unitIdMSB, unitIdLSB); return NetworkMessage.concatByteArrays(unitUUID, factionOrdinal); } private byte[] encodeTFactoryField(TFactoryField field) { return ByteBuffer.allocate(4).putInt(field.getFactoryID()).array(); } private byte[] encodeTInfluenceField(TInfluenceField field) { return ByteBuffer.allocate(4).putInt(field.getFactoryID()).array(); } private byte[] encodeTNormalField(TNormalField field) { return NetworkMessage.EMPTY_BYTES; } private byte[] encodeTPlayer(TPlayer player) { byte[] result = new byte[0]; byte[] playerNameByteArray = player.getName().getBytes(); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(playerNameByteArray.length).array()); result = NetworkMessage.concatByteArrays(result, playerNameByteArray); result = NetworkMessage.concatByteArrays(result, ByteBuffer.allocate(4).putInt(player.getFaction().ordinal()).array()); return result; } // @Override // public void writeExternal(ObjectOutput out) throws IOException { // raw = encodeTGameState(gameState); // out.writeInt(raw.length); // System.out.println("game state encoded length of raw: " + raw.length); // out.write(raw); // System.out.println("game state writing succeeded"); // } // // @Override // public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // int length = in.readInt(); // raw = new byte[length]; // in.readFully(raw, 0, length); // gameState = decodeTGameState(ByteBuffer.wrap(raw)); // System.out.println("game state received length: " + length); // } }