package net.minecraftplus._api.minecraft;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.FMLEmbeddedChannel;
import net.minecraftforge.fml.common.network.FMLOutboundHandler;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* Packet pipeline class. Directs all registered packet data to be handled by the packets themselves.
* @author sirgingalot
* some code from: cpw
*/
@ChannelHandler.Sharable
public class PacketChannel extends MessageToMessageCodec<FMLProxyPacket, Packet>
{
private String modChannel;
private EnumMap<Side, FMLEmbeddedChannel> channels;
private LinkedList<Class<? extends Packet>> packets = new LinkedList<Class<? extends Packet>>();
private boolean hasPostInit = false;
public PacketChannel(String parChannel)
{
this.modChannel = parChannel;
}
/**
* Register your packet with the pipeline. Discriminators are automatically set.
*
* @param parClass the class to register
*
* @return whether registration was successful. Failure may occur if 256 packets have been registered or if the registry already contains this packet
*/
public boolean registerPacket(Class<? extends Packet> parClass)
{
if (this.packets.size() > 256)
{
// You should log here!!
return false;
}
if (this.packets.contains(parClass))
{
// You should log here!!
return false;
}
if (this.hasPostInit)
{
// You should log here!!
return false;
}
this.packets.add(parClass);
return true;
}
// In line encoding of the packet, including discriminator setting
@Override
protected void encode(ChannelHandlerContext ctx, Packet msg, List<Object> out) throws Exception
{
ByteBuf buffer = Unpooled.buffer();
Class<? extends Packet> msgClass = msg.getClass();
if (!this.packets.contains(msg.getClass()))
{
throw new NullPointerException("No Packet Registered for: " + msg.getClass().getCanonicalName());
}
byte discriminator = (byte) this.packets.indexOf(msgClass);
buffer.writeByte(discriminator);
msg.encode(ctx, buffer);
FMLProxyPacket proxyPacket = new FMLProxyPacket((PacketBuffer) buffer.copy(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
out.add(proxyPacket);
}
// In line decoding and handling of the packet
@Override
protected void decode(ChannelHandlerContext ctx, FMLProxyPacket msg, List<Object> out) throws Exception
{
ByteBuf payload = msg.payload();
byte discriminator = payload.readByte();
Class<? extends Packet> packetClass = this.packets.get(discriminator);
if (packetClass == null)
{
throw new NullPointerException("No packet registered for discriminator: " + discriminator);
}
Packet pkt = packetClass.newInstance();
pkt.decode(ctx, payload.slice());
EntityPlayer player;
switch (FMLCommonHandler.instance().getEffectiveSide())
{
case CLIENT:
player = this.getClientPlayer();
pkt.onClientSide(player);
break;
case SERVER:
INetHandler netHandler = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get();
player = ((NetHandlerPlayServer) netHandler).playerEntity;
pkt.onServerSide(player);
break;
default:
}
out.add(pkt);
}
// Method to call from FMLInitializationEvent
public void initialize()
{
this.channels = NetworkRegistry.INSTANCE.newChannel(this.modChannel, this);
}
// Method to call from FMLPostInitializationEvent
// Ensures that packet discriminators are common between server and client by using logical sorting
public void postInitialize()
{
if (this.hasPostInit)
{
return;
}
this.hasPostInit = true;
Collections.sort(this.packets, new Comparator<Class<? extends Packet>>() {
@Override
public int compare(Class<? extends Packet> parClass1, Class<? extends Packet> parClass2)
{
int com = String.CASE_INSENSITIVE_ORDER.compare(parClass1.getCanonicalName(), parClass2.getCanonicalName());
if (com == 0)
{
com = parClass1.getCanonicalName().compareTo(parClass2.getCanonicalName());
}
return com;
}});
}
@SideOnly(Side.CLIENT)
private EntityPlayer getClientPlayer()
{
return Minecraft.getMinecraft().thePlayer;
}
/**
* Send this message to everyone.
* <p/>
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message The message to send
*/
public void sendToAll(Packet message)
{
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALL);
this.channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Send this message to the specified player.
* <p/>
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message The message to send
* @param player The player to send it to
*/
public void sendTo(Packet message, EntityPlayerMP player)
{
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER);
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(player);
this.channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Send this message to everyone within a certain range of a point.
* <p/>
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message The message to send
* @param point The {@link cpw.mods.fml.common.network.NetworkRegistry.TargetPoint} around which to send
*/
public void sendToAllAround(Packet message, NetworkRegistry.TargetPoint point)
{
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALLAROUNDPOINT);
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
this.channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Send this message to everyone within the supplied dimension.
* <p/>
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message The message to send
* @param dimensionId The dimension id to target
*/
public void sendToDimension(Packet message, int dimensionId)
{
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.DIMENSION);
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(dimensionId);
this.channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Send this message to the server.
* <p/>
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message The message to send
*/
public void sendToServer(Packet message)
{
this.channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER);
this.channels.get(Side.CLIENT).writeAndFlush(message);
}
public boolean isInitialized()
{
return this.hasPostInit;
}
}