package gregtech.api.net; import codechicken.lib.vec.Vector3; import gregtech.api.GTValues; import gregtech.api.block.ICustomParticleBlock; import gregtech.api.gui.ModularUI; import gregtech.api.gui.UIFactory; import gregtech.api.gui.impl.ModularUIContainer; import gregtech.api.gui.impl.ModularUIGui; import gregtech.api.util.GTLog; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.particle.ParticleManager; import net.minecraft.inventory.Container; import net.minecraft.network.INetHandler; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.PacketBuffer; import net.minecraft.util.IThreadListener; import net.minecraft.util.IntIdentityHashBiMap; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.FMLEventChannel; import net.minecraftforge.fml.common.network.FMLNetworkEvent; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.HashMap; public class NetworkHandler { public interface Packet { default FMLProxyPacket toFMLPacket() { return packet2proxy(this); } } @FunctionalInterface public interface PacketEncoder<T extends Packet> { void encode(T packet, PacketBuffer byteBuf); } @FunctionalInterface public interface PacketDecoder<T extends Packet> { T decode(PacketBuffer byteBuf); } @FunctionalInterface public interface PacketExecutor<T, R extends INetHandler> { void execute(T packet, R handler); } public static final class PacketCodec<T extends Packet> { public final PacketEncoder<T> encoder; public final PacketDecoder<T> decoder; public PacketCodec(PacketEncoder<T> encoder, PacketDecoder<T> decoder) { this.encoder = encoder; this.decoder = decoder; } } private static final HashMap<Class<? extends Packet>, PacketCodec<? extends Packet>> codecMap = new HashMap<>(); @SideOnly(Side.CLIENT) private static HashMap<Class<? extends Packet>, PacketExecutor<? extends Packet, NetHandlerPlayClient>> clientExecutors; private static final HashMap<Class<? extends Packet>, PacketExecutor<? extends Packet, NetHandlerPlayServer>> serverExecutors = new HashMap<>(); private static final IntIdentityHashBiMap<Class<? extends Packet>> packetMap = new IntIdentityHashBiMap<>(10); static { if (FMLCommonHandler.instance().getSide().isClient()) { clientExecutors = new HashMap<>(); } } public static FMLEventChannel channel; private NetworkHandler() { } public static void init() { channel = NetworkRegistry.INSTANCE.newEventDrivenChannel(GTValues.MODID); channel.register(new NetworkHandler()); PacketEncoder<PacketUIWidgetUpdate> widgetUpdateEncoder = (packet, buf) -> { buf.writeVarInt(packet.updateData.readableBytes()); buf.writeBytes(packet.updateData); buf.writeVarInt(packet.windowId); buf.writeVarInt(packet.widgetId); }; PacketDecoder<PacketUIWidgetUpdate> widgetUpdateDecoder = (buf) -> { ByteBuf directSliceBuffer = buf.readBytes(buf.readVarInt()); ByteBuf copiedDataBuffer = Unpooled.copiedBuffer(directSliceBuffer); directSliceBuffer.release(); return new PacketUIWidgetUpdate( buf.readVarInt(), buf.readVarInt(), new PacketBuffer(copiedDataBuffer)); }; registerPacket(1, PacketUIOpen.class, new PacketCodec<>( (packet, buf) -> { buf.writeVarInt(packet.serializedHolder.readableBytes()); buf.writeBytes(packet.serializedHolder); buf.writeVarInt(packet.uiFactoryId); buf.writeVarInt(packet.windowId); buf.writeVarInt(packet.initialWidgetUpdates.size()); for (PacketUIWidgetUpdate widgetUpdate : packet.initialWidgetUpdates) { widgetUpdateEncoder.encode(widgetUpdate, buf); } }, (buf) -> { ByteBuf directSliceBuffer = buf.readBytes(buf.readVarInt()); ByteBuf copiedDataBuffer = Unpooled.copiedBuffer(directSliceBuffer); directSliceBuffer.release(); int uiFactoryId = buf.readVarInt(); int windowId = buf.readVarInt(); ArrayList<PacketUIWidgetUpdate> initialWidgetUpdates = new ArrayList<>(); int initialWidgetUpdatesCount = buf.readVarInt(); for (int i = 0; i < initialWidgetUpdatesCount; i++) { initialWidgetUpdates.add(widgetUpdateDecoder.decode(buf)); } return new PacketUIOpen( uiFactoryId, new PacketBuffer(copiedDataBuffer), windowId, initialWidgetUpdates); } )); registerPacket(2, PacketUIWidgetUpdate.class, new PacketCodec<>( (packet, buf) -> { buf.writeVarInt(packet.updateData.readableBytes()); buf.writeBytes(packet.updateData); buf.writeVarInt(packet.windowId); buf.writeVarInt(packet.widgetId); }, (buf) -> { ByteBuf directSliceBuffer = buf.readBytes(buf.readVarInt()); ByteBuf copiedDataBuffer = Unpooled.copiedBuffer(directSliceBuffer); directSliceBuffer.release(); return new PacketUIWidgetUpdate( buf.readVarInt(), buf.readVarInt(), new PacketBuffer(copiedDataBuffer)); } )); registerPacket(3, PacketUIClientAction.class, new PacketCodec<>( (packet, buf) -> { buf.writeVarInt(packet.updateData.readableBytes()); buf.writeBytes(packet.updateData); buf.writeVarInt(packet.windowId); buf.writeVarInt(packet.widgetId); }, (buf) -> { ByteBuf directSliceBuffer = buf.readBytes(buf.readVarInt()); ByteBuf copiedDataBuffer = Unpooled.copiedBuffer(directSliceBuffer); directSliceBuffer.release(); return new PacketUIClientAction( buf.readVarInt(), buf.readVarInt(), new PacketBuffer(copiedDataBuffer)); } )); registerPacket(4, PacketBlockParticle.class, new PacketCodec<>( (packet, buf) -> { buf.writeBlockPos(packet.blockPos); buf.writeFloat((float) packet.entityPos.x); buf.writeFloat((float) packet.entityPos.y); buf.writeFloat((float) packet.entityPos.z); buf.writeVarInt(packet.particlesAmount); }, (buf) -> new PacketBlockParticle(buf.readBlockPos(), new Vector3(buf.readFloat(), buf.readFloat(), buf.readFloat()), buf.readVarInt()) )); registerServerExecutor(PacketUIClientAction.class, (packet, handler) -> { Container openContainer = handler.player.openContainer; if (openContainer instanceof ModularUIContainer && openContainer.windowId == packet.windowId) { ModularUI modularUI = ((ModularUIContainer) openContainer).getModularUI(); PacketBuffer buffer = packet.updateData; modularUI.guiWidgets.get(packet.widgetId).handleClientAction(buffer.readVarInt(), buffer); } }); if (FMLCommonHandler.instance().getSide().isClient()) { initClient(); } } @SideOnly(Side.CLIENT) private static void initClient() { registerClientExecutor(PacketUIOpen.class, (packet, handler) -> { UIFactory<?> uiFactory = UIFactory.FACTORY_REGISTRY.getObjectById(packet.uiFactoryId); if (uiFactory == null) { GTLog.logger.warn("Couldn't find UI Factory with id '{}'", packet.uiFactoryId); } else { uiFactory.initClientUI(packet.serializedHolder, packet.windowId, packet.initialWidgetUpdates); } }); registerClientExecutor(PacketUIWidgetUpdate.class, (packet, handler) -> { GuiScreen currentScreen = Minecraft.getMinecraft().currentScreen; if(currentScreen instanceof ModularUIGui) { ((ModularUIGui) currentScreen).handleWidgetUpdate(packet); } }); registerClientExecutor(PacketBlockParticle.class, (packet, handler) -> { World world = Minecraft.getMinecraft().world; IBlockState blockState = world.getBlockState(packet.blockPos); ParticleManager particleManager = Minecraft.getMinecraft().effectRenderer; ((ICustomParticleBlock) blockState.getBlock()).handleCustomParticle(world, packet.blockPos, particleManager, packet.entityPos, packet.particlesAmount); }); } public static <T extends Packet> void registerPacket(int packetId, Class<T> packetClass, PacketCodec<T> codec) { packetMap.put(packetClass, packetId); codecMap.put(packetClass, codec); } @SideOnly(Side.CLIENT) public static <T extends Packet> void registerClientExecutor(Class<T> packet, PacketExecutor<T, NetHandlerPlayClient> executor) { clientExecutors.put(packet, executor); } public static <T extends Packet> void registerServerExecutor(Class<T> packet, PacketExecutor<T, NetHandlerPlayServer> executor) { serverExecutors.put(packet, executor); } @SuppressWarnings("unchecked") public static FMLProxyPacket packet2proxy(Packet packet) { PacketCodec<Packet> codec = (PacketCodec<Packet>) codecMap.get(packet.getClass()); PacketBuffer buf = new PacketBuffer(Unpooled.buffer()); buf.writeVarInt(packetMap.getId(packet.getClass())); codec.encoder.encode(packet, buf); return new FMLProxyPacket(buf, GTValues.MODID); } @SuppressWarnings("unchecked") public static Packet proxy2packet(FMLProxyPacket packet) { PacketBuffer payload = (PacketBuffer) packet.payload(); Class<Packet> packetClass = (Class<Packet>) packetMap.get(payload.readVarInt()); PacketCodec<Packet> codec = (PacketCodec<Packet>) codecMap.get(packetClass); return codec.decoder.decode(payload); } public static TargetPoint blockPoint(World world, BlockPos blockPos) { return new TargetPoint(world.provider.getDimension(), blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5, 128.0); } @SubscribeEvent @SideOnly(Side.CLIENT) @SuppressWarnings("unchecked") public void onClientPacket(FMLNetworkEvent.ClientCustomPacketEvent event) { Packet packet = proxy2packet(event.getPacket()); if (clientExecutors.containsKey(packet.getClass())) { PacketExecutor<Packet, NetHandlerPlayClient> executor = (PacketExecutor<Packet, NetHandlerPlayClient>) clientExecutors.get(packet.getClass()); NetHandlerPlayClient handler = (NetHandlerPlayClient) event.getHandler(); IThreadListener threadListener = FMLCommonHandler.instance().getWorldThread(handler); if(threadListener.isCallingFromMinecraftThread()) { executor.execute(packet, handler); } else { threadListener.addScheduledTask(() -> executor.execute(packet, handler)); } } } @SubscribeEvent @SuppressWarnings("unchecked") public void onServerPacket(FMLNetworkEvent.ServerCustomPacketEvent event) { Packet packet = proxy2packet(event.getPacket()); if (serverExecutors.containsKey(packet.getClass())) { PacketExecutor<Packet, NetHandlerPlayServer> executor = (PacketExecutor<Packet, NetHandlerPlayServer>) serverExecutors.get(packet.getClass()); NetHandlerPlayServer handler = (NetHandlerPlayServer) event.getHandler(); IThreadListener threadListener = FMLCommonHandler.instance().getWorldThread(handler); if(threadListener.isCallingFromMinecraftThread()) { executor.execute(packet, handler); } else { threadListener.addScheduledTask(() -> executor.execute(packet, handler)); } } } }