package speedytools.common.network.multipart;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
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 speedytools.SpeedyToolsMod;
import speedytools.common.network.Packet250Base;
import speedytools.common.network.Packet250Types;
import speedytools.common.network.PacketHandlerRegistry;
import speedytools.common.utilities.ErrorLog;
import java.util.HashMap;
import java.util.Map;
/**
* This class is used to transmit information which is too large for a single packet
*/
public class Packet250MultipartSegment extends Packet250Base
{
/** Used to transmit one of the multipacket segments to the receiver (or abort transmission)
*/
public Packet250MultipartSegment(Packet250Types i_packet250Type, boolean i_abortTransmission,
int i_uniqueMultipartID,
short i_segmentNumber, short i_segmentSize, int i_fullMultipartLength, byte [] i_rawData)
{
packet250Type = i_packet250Type;
abortTransmission = i_abortTransmission;
uniqueMultipartID = i_uniqueMultipartID;
segmentNumber = i_segmentNumber;
segmentSize = i_segmentSize;
fullMultipartLength = i_fullMultipartLength;
rawData = i_rawData;
packetIsValid = true;
}
public Packet250MultipartSegment() // used by Netty to create packet; invalid until populated by packet handler
{
}
public Packet250Types getPacket250Type() {
return packet250Type;
}
public boolean isAbortTransmission() {
return abortTransmission;
}
public short getSegmentNumber() {
return segmentNumber;
}
public short getSegmentSize() {
return segmentSize;
}
public byte[] getRawData() {
return rawData;
}
public int getUniqueMultipartID() {
return uniqueMultipartID;
}
public int getFullMultipartLength() {
return fullMultipartLength;
}
@Override
public void readFromBuffer(ByteBuf buf) {
packetIsValid = false;
try {
packet250Type = Packet250Types.byteToPacket250Type(buf.readByte());
uniqueMultipartID = buf.readInt();
abortTransmission = buf.readBoolean();
segmentNumber = buf.readShort();
segmentSize = buf.readShort();
fullMultipartLength = buf.readInt();
int rawDataLength = buf.readShort();
rawData = new byte[rawDataLength];
if (rawDataLength != 0) {
buf.readBytes(rawData);
if (buf.readableBytes() != 0) {
ErrorLog.defaultLog().info("still had " + buf.readableBytes() + " left after reading " + rawDataLength + " bytes from Packet250MultipartSegment");
return;
}
}
} catch (IndexOutOfBoundsException ioe) {
ErrorLog.defaultLog().info("Exception while reading Packet250MultipartSegment: " + ioe);
return;
}
if (!checkInvariants()) return;
packetIsValid = true;
}
@Override
public void writeToBuffer(ByteBuf buf) {
if (!isPacketIsValid() || !checkInvariants()) return;
buf.writeByte(packet250Type.getPacketTypeID());
buf.writeInt(uniqueMultipartID);
buf.writeBoolean(abortTransmission);
buf.writeShort(segmentNumber);
buf.writeShort(segmentSize);
buf.writeInt(fullMultipartLength);
if (rawData == null) {
buf.writeShort(0);
} else {
buf.writeShort(rawData.length);
buf.writeBytes(rawData);
}
}
/**
* Register a handler for this packet
* @param packetHandlerRegistry
* @param packetHandlerMethod
* @param side
*/
public static void registerHandler(PacketHandlerRegistry packetHandlerRegistry, PacketHandlerMethod packetHandlerMethod, Side side, Packet250Types packet250Type) {
switch (side) {
case CLIENT: {
clientSideHandlers.put(packet250Type, packetHandlerMethod);
break;
}
case SERVER: {
serverSideHandlers.put(packet250Type, packetHandlerMethod);
break;
}
default: {
assert false : "Tried to register Packet250MultipartSegment on side " + side;
}
}
packetHandlerRegistry.getSimpleNetworkWrapper().registerMessage(CommonMessageHandler.class, Packet250MultipartSegment.class,
packet250Type.getPacketTypeID(), side);
}
public interface PacketHandlerMethod
{
public boolean handlePacket(Packet250MultipartSegment packet250MultipartSegment, MessageContext ctx);
}
public static class CommonMessageHandler implements IMessageHandler<Packet250MultipartSegment, IMessage>
{
/**
* Called when a message is received of the appropriate type. You can optionally return a reply message, or null if no reply
* is needed.
*
* @param message The message
* @return an optional return message
*/
public IMessage onMessage(final Packet250MultipartSegment message, final MessageContext ctx)
{
Packet250Types packet250Type = message.getPacket250Type();
PacketHandlerMethod handlerMethod = null;
switch (ctx.side) {
case CLIENT: {
handlerMethod = clientSideHandlers.get(packet250Type);
break;
}
case SERVER: {
handlerMethod = serverSideHandlers.get(packet250Type);
break;
}
default: assert false : "Received message on invalid side: " + ctx.side;
}
if (handlerMethod == null) {
ErrorLog.defaultLog().severe("Packet250MultipartSegment for packet type " + packet250Type + " received but not registered on side " + ctx.side);
} else {
final PacketHandlerMethod handlerMethodFinal = handlerMethod;
Runnable messageProcessor = new Runnable() {
@Override
public void run() {
handlerMethodFinal.handlePacket(message, ctx);
}
};
boolean success = SpeedyToolsMod.proxy.enqueueMessageOnCorrectThread(ctx, messageProcessor);
if (!success) {
ErrorLog.defaultLog().severe("Packet250MultipartSegment failed to handle Packet");
}
}
return null;
}
}
/**
* Checks if the packet is internally consistent
* @return true for success, false otherwise
*/
private boolean checkInvariants()
{
if (packet250Type == null) return false;
if (!abortTransmission) {
if (segmentSize < 0) return false;
if (rawData.length > segmentSize) return false;
}
return true;
}
private Packet250Types packet250Type;
private int uniqueMultipartID;
protected boolean abortTransmission; // these fields are protected to allow for testing by MultipartPacketTest
protected short segmentNumber;
protected short segmentSize;
protected int fullMultipartLength;
protected byte [] rawData;
private static Map<Packet250Types, PacketHandlerMethod> serverSideHandlers = new HashMap<Packet250Types, PacketHandlerMethod>();
private static Map<Packet250Types, PacketHandlerMethod> clientSideHandlers = new HashMap<Packet250Types, PacketHandlerMethod>();
}