package codechicken.lib.packet; import codechicken.lib.data.MCDataIO; import codechicken.lib.data.MCDataInput; import codechicken.lib.data.MCDataOutput; import codechicken.lib.vec.BlockCoord; import com.google.common.collect.Maps; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.EncoderException; import io.netty.util.AttributeKey; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.INetHandler; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.Packet; import net.minecraft.network.PacketBuffer; import net.minecraft.network.play.INetHandlerPlayClient; import net.minecraft.network.play.INetHandlerPlayServer; import net.minecraft.server.MinecraftServer; import net.minecraft.server.management.PlayerManager.PlayerInstance; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.network.FMLEmbeddedChannel; import net.minecraftforge.fml.common.network.FMLOutboundHandler; import net.minecraftforge.fml.common.network.NetworkHandshakeEstablished; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.io.IOException; import java.util.EnumMap; import java.util.List; import java.util.zip.Deflater; import java.util.zip.Inflater; public final class PacketCustom extends PacketBuffer implements MCDataInput, MCDataOutput { public static interface ICustomPacketHandler { } public interface IClientPacketHandler extends ICustomPacketHandler { public void handlePacket(PacketCustom packetCustom, Minecraft mc, INetHandlerPlayClient handler); } public interface IServerPacketHandler extends ICustomPacketHandler { public void handlePacket(PacketCustom packetCustom, EntityPlayerMP sender, INetHandlerPlayServer handler); } public static AttributeKey<CustomInboundHandler> cclHandler = new AttributeKey<CustomInboundHandler>("ccl:handler"); @ChannelHandler.Sharable public static class CustomInboundHandler extends SimpleChannelInboundHandler<FMLProxyPacket> { public EnumMap<Side, CustomHandler> handlers = Maps.newEnumMap(Side.class); @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { super.handlerAdded(ctx); ctx.channel().attr(cclHandler).set(this); } @Override protected void channelRead0(ChannelHandlerContext ctx, FMLProxyPacket msg) throws Exception { handlers.get(ctx.channel().attr(NetworkRegistry.CHANNEL_SOURCE).get()).handle(ctx.channel().attr(NetworkRegistry.NET_HANDLER).get(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get(), new PacketCustom(msg.payload())); } } private static interface CustomHandler { public void handle(INetHandler handler, String channel, PacketCustom packet); } public static class ClientInboundHandler implements CustomHandler { private IClientPacketHandler handler; public ClientInboundHandler(ICustomPacketHandler handler) { this.handler = (IClientPacketHandler) handler; } @Override public void handle(final INetHandler netHandler, final String channel, final PacketCustom packet) { if (netHandler instanceof INetHandlerPlayClient) { Minecraft mc = Minecraft.getMinecraft(); if (!mc.isCallingFromMinecraftThread()) { mc.addScheduledTask(new Runnable() { public void run() { handle(netHandler, channel, packet); } }); } else { handler.handlePacket(packet, mc, (INetHandlerPlayClient) netHandler); } } else { System.err.println("Invalid INetHandler for PacketCustom on channel: " + channel); } } } public static class ServerInboundHandler implements CustomHandler { private IServerPacketHandler handler; public ServerInboundHandler(ICustomPacketHandler handler) { this.handler = (IServerPacketHandler) handler; } @Override public void handle(final INetHandler netHandler, final String channel, final PacketCustom packet) { if (netHandler instanceof NetHandlerPlayServer) { MinecraftServer mc = MinecraftServer.getServer(); if (!mc.isCallingFromMinecraftThread()) { mc.addScheduledTask(new Runnable() { public void run() { handle(netHandler, channel, packet); } }); } else { handler.handlePacket(packet, ((NetHandlerPlayServer) netHandler).playerEntity, (INetHandlerPlayServer) netHandler); } } else { System.err.println("Invalid INetHandler for PacketCustom on channel: " + channel); } } } public static interface IHandshakeHandler { public void handshakeRecieved(NetHandlerPlayServer netHandler); } public static class HandshakeInboundHandler extends ChannelInboundHandlerAdapter { public IHandshakeHandler handler; public HandshakeInboundHandler(IHandshakeHandler handler) { this.handler = handler; } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof NetworkHandshakeEstablished) { INetHandler netHandler = ((NetworkDispatcher) ctx.channel().attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).get()).getNetHandler(); if (netHandler instanceof NetHandlerPlayServer) { handler.handshakeRecieved((NetHandlerPlayServer) netHandler); } } else { ctx.fireUserEventTriggered(evt); } } } public static String channelName(Object channelKey) { if (channelKey instanceof String) { return (String) channelKey; } if (channelKey instanceof ModContainer) { String s = ((ModContainer) channelKey).getModId(); if (s.length() > 20) { throw new IllegalArgumentException("Mod ID (" + s + ") too long for use as channel (20 chars). Use a string identifier"); } return s; } ModContainer mc = FMLCommonHandler.instance().findContainerFor(channelKey); if (mc != null) { return mc.getModId(); } throw new IllegalArgumentException("Invalid channel: " + channelKey); } public static FMLEmbeddedChannel getOrCreateChannel(String channelName, Side side) { if (!NetworkRegistry.INSTANCE.hasChannel(channelName, side)) { NetworkRegistry.INSTANCE.newChannel(channelName, new CustomInboundHandler()); } return NetworkRegistry.INSTANCE.getChannel(channelName, side); } public static void assignHandler(Object channelKey, ICustomPacketHandler handler) { String channelName = channelName(channelKey); Side side = handler instanceof IServerPacketHandler ? Side.SERVER : Side.CLIENT; FMLEmbeddedChannel channel = getOrCreateChannel(channelName, side); channel.attr(cclHandler).get().handlers.put(side, side == Side.SERVER ? new ServerInboundHandler(handler) : new ClientInboundHandler(handler)); } public static void assignHandshakeHandler(Object channelKey, IHandshakeHandler handler) { FMLEmbeddedChannel channel = getOrCreateChannel(channelName(channelKey), Side.SERVER); channel.pipeline().addLast(new HandshakeInboundHandler(handler)); } private String channel; private int type; public PacketCustom(ByteBuf payload) { super(payload); type = readUnsignedByte(); if (type > 0x80) { decompress(); } type &= 0x7F; } public PacketCustom(Object channelKey, int type) { super(Unpooled.buffer()); if (type <= 0 || type >= 0x80) { throw new IllegalArgumentException("Packet type: " + type + " is not within required 0 < t < 0x80"); } this.channel = channelName(channelKey); this.type = type; writeByte(type); } /** * Decompresses the remaining ByteBuf (after type has been read) using Snappy */ private void decompress() { Inflater inflater = new Inflater(); try { int len = readInt(); byte[] out = new byte[len]; inflater.setInput(array(), readerIndex(), readableBytes()); inflater.inflate(out); clear(); writeByteArray(out); } catch (Exception e) { throw new EncoderException(e); } finally { inflater.end(); } } /** * Compresses the payload ByteBuf after the type byte */ private void do_compress() { Deflater deflater = new Deflater(); try { readerIndex(1); int len = readableBytes(); deflater.setInput(array(), readerIndex(), len); deflater.finish(); byte[] out = new byte[len]; int clen = deflater.deflate(out); if (clen >= len - 5 || !deflater.finished())//not worth compressing, gets larger { return; } clear(); writeByte(type | 0x80); writeVarInt(len); writeByteArray(out); } catch (Exception e) { throw new EncoderException(e); } finally { readerIndex(0); deflater.end(); } } public boolean incoming() { return channel == null; } public int getType() { return type & 0x7F; } public PacketCustom compress() { if (incoming()) { throw new IllegalStateException("Tried to compress an incoming packet"); } if ((type & 0x80) != 0) { throw new IllegalStateException("Packet already compressed"); } type |= 0x80; return this; } public PacketCustom writeBoolean(boolean b) { super.writeBoolean(b); return this; } public PacketCustom writeByte(int b) { super.writeByte(b); return this; } public PacketCustom writeShort(int s) { super.writeShort(s); return this; } public PacketCustom writeInt(int i) { super.writeInt(i); return this; } public PacketCustom writeFloat(float f) { super.writeFloat(f); return this; } public PacketCustom writeDouble(double d) { super.writeDouble(d); return this; } public PacketCustom writeLong(long l) { super.writeLong(l); return this; } @Override public PacketCustom writeChar(char c) { super.writeChar(c); return this; } public PacketCustom writeVarInt(int i) { writeVarIntToBuffer(i); return this; } public PacketCustom writeVarShort(int s) { MCDataIO.writeVarShort(this, s); return this; } public PacketCustom writeArray(byte[] barray) { writeBytes(barray); return this; } public PacketCustom writeString(String s) { super.writeString(s); return this; } public PacketCustom writeCoord(int x, int y, int z) { writeInt(x); writeInt(y); writeInt(z); return this; } public PacketCustom writeCoord(BlockCoord coord) { writeInt(coord.x); writeInt(coord.y); writeInt(coord.z); return this; } public PacketCustom writeItemStack(ItemStack stack) { MCDataIO.writeItemStack(this, stack); return this; } public PacketCustom writeNBTTagCompound(NBTTagCompound tag) { writeNBTTagCompoundToBuffer(tag); return this; } public PacketCustom writeFluidStack(FluidStack fluid) { MCDataIO.writeFluidStack(this, fluid); return this; } public short readUByte() { return readUnsignedByte(); } public int readUShort() { return readUnsignedShort(); } @Override public int readVarShort() { return MCDataIO.readVarShort(this); } @Override public int readVarInt() { return readVarIntFromBuffer(); } public BlockCoord readCoord() { return new BlockCoord(readInt(), readInt(), readInt()); } public byte[] readArray(int length) { return readBytes(length).array(); } public String readString() { return readStringFromBuffer(32767); } public ItemStack readItemStack() { return MCDataIO.readItemStack(this); } public NBTTagCompound readNBTTagCompound() { try { return readNBTTagCompoundFromBuffer(); } catch (IOException e) { throw new EncoderException(e); } } public FluidStack readFluidStack() { return MCDataIO.readFluidStack(this); } public FMLProxyPacket toPacket() { if (incoming()) { throw new IllegalStateException("Tried to write an incoming packet"); } if (readableBytes() > 32000 || (type & 0x80) != 0) { do_compress(); } return new FMLProxyPacket(new PacketBuffer(copy()), channel); } public void sendToPlayer(EntityPlayer player) { sendToPlayer(toPacket(), player); } public static void sendToPlayer(Packet packet, EntityPlayer player) { if (player == null) { sendToClients(packet); } else { ((EntityPlayerMP) player).playerNetServerHandler.sendPacket(packet); } } public void sendToClients() { sendToClients(toPacket()); } public static void sendToClients(Packet packet) { MinecraftServer.getServer().getConfigurationManager().sendPacketToAllPlayers(packet); } public void sendPacketToAllAround(double x, double y, double z, double range, int dim) { sendToAllAround(toPacket(), x, y, z, range, dim); } public static void sendToAllAround(Packet packet, double x, double y, double z, double range, int dim) { MinecraftServer.getServer().getConfigurationManager().sendToAllNear(x, y, z, range, dim, packet); } public void sendToDimension(int dim) { sendToDimension(toPacket(), dim); } public static void sendToDimension(Packet packet, int dim) { MinecraftServer.getServer().getConfigurationManager().sendPacketToAllPlayersInDimension(packet, dim); } public void sendToChunk(World world, int chunkX, int chunkZ) { sendToChunk(toPacket(), world, chunkX, chunkZ); } public static void sendToChunk(Packet packet, World world, int chunkX, int chunkZ) { PlayerInstance p = ((WorldServer) world).getPlayerManager().getPlayerInstance(chunkX, chunkZ, false); if (p != null) { p.sendToAllPlayersWatchingChunk(packet); } } public void sendToOps() { sendToOps(toPacket()); } public static void sendToOps(Packet packet) { for (EntityPlayerMP player : (List<EntityPlayerMP>) MinecraftServer.getServer().getConfigurationManager().playerEntityList) { if (MinecraftServer.getServer().getConfigurationManager().canSendCommands(player.getGameProfile())) { sendToPlayer(packet, player); } } } @SideOnly(Side.CLIENT) public void sendToServer() { sendToServer(toPacket()); } @SideOnly(Side.CLIENT) public static void sendToServer(Packet packet) { Minecraft.getMinecraft().getNetHandler().addToSendQueue(packet); } }