package speedytools.common.network; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; import net.minecraftforge.fml.relauncher.Side; import io.netty.buffer.ByteBuf; import net.minecraft.util.BlockPos; import speedytools.SpeedyToolsMod; import speedytools.common.selections.FillAlgorithmSettings; import speedytools.common.utilities.ErrorLog; /** * This class is used to communicate from the client to the server when it's necessary to generate a selection on the server * (if the selection is large, the client may have empty chunks in it. See notes/SelectionGeneration.txt) * Typical usage: * Client to Server: * (1) FILL command to perform a flood fill from the cursor * (2) ALL_IN_BOX command to select all in the given box region * (3) ABORT to stop the selection generation * (4) STATUS_REQUEST to ask the server to return an estimate of the fraction completed [0..1]. * * Server to Client: * (1) STATUS command in response to the STATUS_REQUEST message or a command. * * The commands contain a uniqueID, which is returned in the status messages. * */ public class Packet250ServerSelectionGeneration extends Packet250Base { public static Packet250ServerSelectionGeneration abortSelectionGeneration(int whichTaskID) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.ABORT, whichTaskID); assert (retval.checkInvariants()); return retval; } public static Packet250ServerSelectionGeneration requestStatus(int whichTaskID) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.STATUS_REQUEST, whichTaskID); assert (retval.checkInvariants()); return retval; } public static Packet250ServerSelectionGeneration replyFractionCompleted(int whichTaskID, float i_completedFraction) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.STATUS_REPLY, whichTaskID); retval.completedFraction = i_completedFraction; assert (retval.checkInvariants()); return retval; } public static Packet250ServerSelectionGeneration performBoundFill(FillAlgorithmSettings i_fillAlgorithmSettings, int whichTaskID, BlockPos i_corner1, BlockPos i_corner2) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.BOUND_FILL, whichTaskID); retval.fillAlgorithmSettings = i_fillAlgorithmSettings; // retval.cursorPosition = i_cursorPosition; retval.corner1 = i_corner1; retval.corner2 = i_corner2; assert (retval.checkInvariants()); retval.packetIsValid = true; return retval; } public static Packet250ServerSelectionGeneration performUnboundFill(FillAlgorithmSettings i_fillAlgorithmSettings, int whichTaskID) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.UNBOUND_FILL, whichTaskID); retval.fillAlgorithmSettings = i_fillAlgorithmSettings; // retval.cursorPosition = i_cursorPosition; assert (retval.checkInvariants()); retval.packetIsValid = true; return retval; } public static Packet250ServerSelectionGeneration performAllInBox(int whichTaskID, BlockPos i_corner1, BlockPos i_corner2) { Packet250ServerSelectionGeneration retval = new Packet250ServerSelectionGeneration(Command.ALL_IN_BOX, whichTaskID); retval.corner1 = i_corner1; retval.corner2 = i_corner2; assert (retval.checkInvariants()); retval.packetIsValid = true; return retval; } @Override public void readFromBuffer(ByteBuf buf) { packetIsValid = false; try { byte commandValue = buf.readByte(); command = Command.byteToCommand(commandValue); if (command == null) return; uniqueID = buf.readInt(); // System.out.println("Packet250ServerSelectionGeneration: cmd=" + command + ", ID = " + uniqueID); //todo remove switch (command) { case STATUS_REQUEST: case ABORT: { break; } case STATUS_REPLY: { completedFraction = buf.readFloat(); break; } case BOUND_FILL: { fillAlgorithmSettings = FillAlgorithmSettings.createFromBuffer(buf);// = MatcherType.byteToMatcherType(buf.readByte()); // cursorPosition = readBlockPos(buf); corner1 = readBlockPos(buf); corner2 = readBlockPos(buf); break; } case UNBOUND_FILL: { fillAlgorithmSettings = FillAlgorithmSettings.createFromBuffer(buf); //MatcherType.byteToMatcherType(buf.readByte()); // cursorPosition = readBlockPos(buf); break; } case ALL_IN_BOX: { corner1 = readBlockPos(buf); corner2 = readBlockPos(buf); break; } default: { ErrorLog.defaultLog().info("Invalid command " + command + " in readFromBuffer in " + this.getClass().getName()); return; } } } catch (IndexOutOfBoundsException ioe) { ErrorLog.defaultLog().info("Exception while reading " + this.getClass().getName() + ": " + ioe); return; } if (!checkInvariants()) return; packetIsValid = true; } @Override public void writeToBuffer(ByteBuf buf) { if (!isPacketIsValid()) { ErrorLog.defaultLog().info("Tried to send invalid packet in writeToBuffer in " + this.getClass().getName()); return; } if (!checkInvariants()) { ErrorLog.defaultLog().info("Invariants failed on packet in writeToBuffer in " + this.getClass().getName()); return; } buf.writeByte(command.getCommandID()); buf.writeInt(uniqueID); switch (command) { case STATUS_REQUEST: case ABORT: { break; } case STATUS_REPLY: { buf.writeFloat(completedFraction); break; } case BOUND_FILL: { fillAlgorithmSettings.writeToBuffer(buf); // writeBlockPos(buf, cursorPosition); writeBlockPos(buf, corner1); writeBlockPos(buf, corner2); break; } case UNBOUND_FILL: { fillAlgorithmSettings.writeToBuffer(buf); // writeBlockPos(buf, cursorPosition); break; } case ALL_IN_BOX: { writeBlockPos(buf, corner1); writeBlockPos(buf, corner2); break; } default: { ErrorLog.defaultLog().info("Invalid command " + command + " in readFromBuffer in " + this.getClass().getName()); return; } } } private BlockPos readBlockPos(ByteBuf buf) { int x = buf.readInt(); int y = buf.readInt(); int z = buf.readInt(); BlockPos chunkCoordinates = new BlockPos(x, y, z); return chunkCoordinates; } private void writeBlockPos(ByteBuf buf, BlockPos chunkCoordinates) { buf.writeInt(chunkCoordinates.getX()); buf.writeInt(chunkCoordinates.getY()); buf.writeInt(chunkCoordinates.getZ()); } public static enum Command { ABORT(150), UNBOUND_FILL(151), BOUND_FILL(152), ALL_IN_BOX(153), STATUS_REQUEST(154), STATUS_REPLY(155); public byte getCommandID() {return commandID;} private static Command byteToCommand(byte value) { for (Command command : Command.values()) { if (value == command.getCommandID()) return command; } return null; } private Command(int i_commandID) { commandID = (byte)i_commandID; } private final byte commandID; } // public static enum MatcherType { // ANY_NON_AIR(163), STARTING_BLOCK_ONLY(164); // // public byte getMatcherTypeID() {return matcherTypeID;} // // private static MatcherType byteToMatcherType(byte value) // { // for (MatcherType matcherType : MatcherType.values()) { // if (value == matcherType.getMatcherTypeID()) return matcherType; // } // return null; // } // // private MatcherType(int i_matcherTypeID) {matcherTypeID = (byte)i_matcherTypeID;} // private final byte matcherTypeID; // } public Command getCommand() { assert (checkInvariants()); return command; } // public MatcherType getMatcherType() // { // assert (checkInvariants()); // return matcherType; // } public int getUniqueID() { return uniqueID; } /** * Register the handler for this packet * @param packetHandlerRegistry * @param packetHandlerMethod * @param side */ public static void registerHandler(PacketHandlerRegistry packetHandlerRegistry, PacketHandlerMethod packetHandlerMethod, Side side) { switch (side) { case SERVER: { serverSideHandler = packetHandlerMethod; break; } case CLIENT: { clientSideHandler = packetHandlerMethod; break; } default: { assert false : "Tried to register Packet250ServerSelectionGeneration on side " + side; } } packetHandlerRegistry.getSimpleNetworkWrapper().registerMessage(CommonMessageHandler.class, Packet250ServerSelectionGeneration.class, Packet250Types.PACKET250_SERVER_SELECTION_GENERATION.getPacketTypeID(), side); } public interface PacketHandlerMethod { public Packet250ServerSelectionGeneration handlePacket(Packet250ServerSelectionGeneration packet250CloneToolUse, MessageContext ctx); } private static Packet250ServerSelectionGeneration handleMessage(final Packet250ServerSelectionGeneration message, final MessageContext ctx) { Runnable messageProcessor = null; switch (ctx.side) { case CLIENT: { if (clientSideHandler == null) { ErrorLog.defaultLog().severe("Packet250ServerSelectionGeneration received but not registered on client."); } else { messageProcessor = new Runnable() { @Override public void run() { clientSideHandler.handlePacket(message, ctx); } }; } break; } case SERVER: { if (serverSideHandler == null) { ErrorLog.defaultLog().severe("Packet250ServerSelectionGeneration received but not registered."); } else { messageProcessor = new Runnable() { @Override public void run() { serverSideHandler.handlePacket(message, ctx); } }; } break; } default: { ErrorLog.defaultLog().severe("Packet250ServerSelectionGeneration received on wrong side " + ctx.side); } } if (messageProcessor != null) { boolean success = SpeedyToolsMod.proxy.enqueueMessageOnCorrectThread(ctx, messageProcessor); if (!success) { ErrorLog.defaultLog().severe("Packet250ServerSelectionGeneration failed to handle Packet"); } } return null; } public static class CommonMessageHandler implements IMessageHandler<Packet250ServerSelectionGeneration, Packet250ServerSelectionGeneration> { public Packet250ServerSelectionGeneration onMessage(Packet250ServerSelectionGeneration message, MessageContext ctx) { return handleMessage(message, ctx); } } // public static class ServerMessageHandler implements IMessageHandler<Packet250ServerSelectionGeneration, Packet250ServerSelectionGeneration> // { // public Packet250ServerSelectionGeneration onMessage(Packet250ServerSelectionGeneration message, MessageContext ctx) // { // return handleMessage(message, ctx); // } // } private Packet250ServerSelectionGeneration(Command command, int whichTaskID) { this.command = command; this.uniqueID = whichTaskID; this.packetIsValid = true; } public Packet250ServerSelectionGeneration() // used by Netty; invalid until populated by the packet handler { } /** * checks this class to see if it is internally consistent * @return true if ok, false if bad. */ private boolean checkInvariants() { if (command == null) return false; switch (command) { case STATUS_REPLY: case STATUS_REQUEST: case ABORT: { return (fillAlgorithmSettings == null && corner1 == null && corner2 == null); } case ALL_IN_BOX: { return (fillAlgorithmSettings == null && corner1 != null && corner2 != null); } case UNBOUND_FILL: { return (fillAlgorithmSettings != null && corner1 == null && corner2 == null); } case BOUND_FILL: { return (fillAlgorithmSettings != null && corner1 != null && corner2 != null); } default: { return false; } } } private Command command; public float getCompletedFraction() { return completedFraction; } private float completedFraction; // public BlockPos getCursorPosition() { // return new BlockPos(cursorPosition); // } public BlockPos getCorner1() { return new BlockPos(corner1); } public BlockPos getCorner2() { return new BlockPos(corner2); } public FillAlgorithmSettings getFillAlgorithmSettings() { return fillAlgorithmSettings; } private FillAlgorithmSettings fillAlgorithmSettings; // private BlockPos cursorPosition; private BlockPos corner1; private BlockPos corner2; private int uniqueID; private static PacketHandlerMethod serverSideHandler; private static PacketHandlerMethod clientSideHandler; }