package uk.co.wehavecookies56.kk.common.network.packet;
import java.io.IOException;
import com.google.common.base.Throwables;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.IThreadListener;
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 uk.co.wehavecookies56.kk.common.KingdomKeys;
public abstract class AbstractMessage<T extends AbstractMessage<T>> implements IMessage, IMessageHandler<T, IMessage> {
/**
* Some PacketBuffer methods throw IOException - default handling propagates
* the exception. if an IOException is expected but should not be fatal,
* handle it within this method.
*/
protected abstract void read (PacketBuffer buffer) throws IOException;
/**
* Some PacketBuffer methods throw IOException - default handling propagates
* the exception. if an IOException is expected but should not be fatal,
* handle it within this method.
*/
protected abstract void write (PacketBuffer buffer) throws IOException;
/**
* Called on whichever side the message is received; for bidirectional
* packets, be sure to check side If {@link #requiresMainThread()} returns
* true, this method is guaranteed to be called on the main Minecraft thread
* for this side.
*/
public abstract void process (EntityPlayer player, Side side);
/**
* If message is sent to the wrong side, an exception will be thrown during
* handling
*
* @return True if the message is allowed to be handled on the given side
*/
protected boolean isValidOnSide (Side side) {
return true; // default allows handling on both sides, i.e. a
// bidirectional packet
}
/**
* Whether this message requires the main thread to be processed (i.e. it
* requires that the world, player, and other objects are in a valid state).
*/
protected boolean requiresMainThread () {
return true;
}
@Override
public void fromBytes (ByteBuf buffer) {
try {
read(new PacketBuffer(buffer));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@Override
public void toBytes (ByteBuf buffer) {
try {
write(new PacketBuffer(buffer));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
// =====================================================================//
/*
* Make the implementation final so child classes don't need to bother with
* it, since the message class shouldn't have anything to do with the
* handler. This is simply to avoid having to have:
*
* public static class Handler extends GenericMessageHandler<OpenGuiMessage>
* {}
*
* in every single message class for the sole purpose of registration.
*/
@Override
public final IMessage onMessage (T msg, MessageContext ctx) {
if (!msg.isValidOnSide(ctx.side))
throw new RuntimeException("Invalid side " + ctx.side.name() + " for " + msg.getClass().getSimpleName());
else if (msg.requiresMainThread())
checkThreadAndEnqueue(msg, ctx);
else
msg.process(KingdomKeys.proxy.getPlayerEntity(ctx), ctx.side);
return null;
}
/**
* 1.8 ONLY: Ensures that the message is being handled on the main thread
*/
private static final <T extends AbstractMessage<T>> void checkThreadAndEnqueue (final AbstractMessage<T> msg, final MessageContext ctx) {
IThreadListener thread = KingdomKeys.proxy.getThreadFromContext(ctx);
if (!thread.isCallingFromMinecraftThread()) thread.addScheduledTask(new Runnable() {
@Override
public void run () {
msg.process(KingdomKeys.proxy.getPlayerEntity(ctx), ctx.side);
}
});
}
/**
* Messages that can only be sent from the server to the client should use
* this class
*/
public static abstract class AbstractClientMessage<T extends AbstractMessage<T>> extends AbstractMessage<T> {
@Override
protected final boolean isValidOnSide (Side side) {
return side.isClient();
}
}
/**
* Messages that can only be sent from the client to the server should use
* this class
*/
public static abstract class AbstractServerMessage<T extends AbstractMessage<T>> extends AbstractMessage<T> {
@Override
protected final boolean isValidOnSide (Side side) {
return side.isServer();
}
}
}