Как правильно хандлить чужие пакеты?

Версия Minecraft
1.12.2
API
Forge
7,099
324
1,510
Хочу добавить дополнительную логику обработки пакета из другого мода.
Чтобы моя логика вызывалась после оригинального обработчика пакета.
Попробовал вот так:
Scala:
@SubscribeEvent
def blacklistResearchCategory1(e: FMLNetworkEvent.ServerConnectionFromClientEvent): Unit = { //Добавляем свой хандлер пакетов на сервере
    e.getManager.channel.pipeline
        .addLast(Main.modid + ":hook_packet_handler", new PacketHook) //Это пашет
}


class PacketHook extends ChannelInboundHandlerAdapter {

    override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
        super.channelRead(ctx, msg)
        println("channelRead", msg) //тут ничего не выводится в консоль
        msg match {
            case packet: PacketStartTheoryToServer => //Если что, пакет из таума
                //типо для пакета из другого мода делаю че-то свое
            case _ =>
        }
    }
}
Но это не пашет. Мой хандлер вроде ваще не улавливает пакеты. Как это сделать правильно?
 
Решение
Я щас понял, что можно зарегистрировать тот же самый пакет повторно, но с моим хандлером)))
:eek:💡
Java:
def getPrivateField[CL: ClassTag, A](value: CL, field: String): A = {
    val f = implicitly[ClassTag[CL]].runtimeClass.getDeclaredField(field)
    f.setAccessible(true)
    f.get(PacketHandler.INSTANCE).asInstanceOf[A]
}

def addThaumcraftPacketHandler(): Unit = {
    val packetCodec = getPrivateField[SimpleNetworkWrapper, SimpleIndexedCodec](PacketHandler.INSTANCE, "packetCodec")
    val types = getPrivateField[FMLIndexedMessageToMessageCodec[IMessage], Object2ByteMap[Class[_ <: IMessage]]](packetCodec, "types")
    val id = types.get(classOf[PacketStartTheoryToServer]) //Получаем айдишник пакета...
7,099
324
1,510
Немного поковырял:
если использовать addFirst вместо addLast, то хандлер улавливает пакеты
Однако, пакеты мода выглядят как CPacketCustomPayload
И мой хандлер вызывается до хандлера мода
 
7,099
324
1,510
А можно как-нить без хуков?
Вот тут дисиебен007 говорит, что это можно сделать при помощи пайплайна нетти
Собственно, мой код выше - попытка сделать это. Но оно не работает и я не понимаю, почему
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Вроде нельзя. Когда ты добавляешь в пайплайн через addLast, то добавляешь обработчик в самый конец. А так как перед твои обработчиком стоит форджевский, он обрабатывает пакет и не прокидывает дальше событие (можешь глянуть SimpleChannelHandlerWrapper, он наследует от SimpleChannelInboundHandler, который является завершающим, ибо никакого вызова super или fireChannelRead в методе channelRead нет), то есть он является конечным хандлером. Соответственно до твоего вообще не доходит.

Однако, пакеты мода выглядят как CPacketCustomPayload
А когда ты ставишь в начало, то там лежит еще майновский пакет и фордж его еще не обработал.
 
7,099
324
1,510
Попробовал такое:
  1. Добавляю свой хандлер после fml:packet_handler
  2. В channelRead копирую байтбуф пакета
  3. Вызываю super-метод
  4. Создаю экземпляр предполагаемого пакета
  5. Читаю в него ранее скопированный байтбуф
  6. Юзаю со своей логикой
Оно работает неплохо, однако, при одинаковых массивах байт в ByteBuf результат чтения long отличается. Из-за чего это может быть?
Scala:
@SubscribeEvent(priority = EventPriority.LOWEST) def blacklistResearchCategory1(e: FMLNetworkEvent.ServerConnectionFromClientEvent): Unit = {
    e.getManager.channel.pipeline
      .addAfter("fml:packet_handler", Main.advancedAuromancyModId + ":hook_packet_handler", new PacketHook)
}

class PacketHook extends ChannelInboundHandlerAdapter {

    override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
      try {
        msg match {
          case packet: FMLProxyPacket if packet.channel() == "thaumcraft" =>

            val packet2 = packet.copy().payload()

            packet2.readerIndex(0)
            packet2.writerIndex(9)
            //val packet3 = Unpooled.buffer(packet2.readableBytes())
            //packet3.writeBytes(packet2)

            //val thaumPacket = new PacketStartTheoryToServer()

              println("test", packet.payload().readLong()) //Отличается от того, что будет прочитано в оригинальном пакете, хотя массивы байт и индесы чтения одинаковые
              packet.payload().readerIndex(0)
              //thaumPacket.fromBytes(packet2)


            super.channelRead(ctx, msg)

            val internalChannel = NetworkRegistry.INSTANCE.getChannel("thaumcraft", Side.SERVER)
            val playServer = internalChannel.attr(NetworkRegistry.NET_HANDLER).get.asInstanceOf[NetHandlerPlayServer]
            val player = playServer.player
            val mainThread = player.getServerWorld

            mainThread.addScheduledTask(new Runnable() {
              override def run(): Unit = {
                //какая-то логика обработки
              }
            });

          case _ =>
            super.channelRead(ctx, msg)
        }
      } catch {
        case e: Throwable =>
          println("lol1")
          e.printStackTrace()
      }
    }

~~~
Очень странно, но если поставить индекс чтения на 1 - читаются правильные значения
 
Последнее редактирование:
7,099
324
1,510
Я щас понял, что можно зарегистрировать тот же самый пакет повторно, но с моим хандлером)))
:eek:💡
Java:
def getPrivateField[CL: ClassTag, A](value: CL, field: String): A = {
    val f = implicitly[ClassTag[CL]].runtimeClass.getDeclaredField(field)
    f.setAccessible(true)
    f.get(PacketHandler.INSTANCE).asInstanceOf[A]
}

def addThaumcraftPacketHandler(): Unit = {
    val packetCodec = getPrivateField[SimpleNetworkWrapper, SimpleIndexedCodec](PacketHandler.INSTANCE, "packetCodec")
    val types = getPrivateField[FMLIndexedMessageToMessageCodec[IMessage], Object2ByteMap[Class[_ <: IMessage]]](packetCodec, "types")
    val id = types.get(classOf[PacketStartTheoryToServer]) //Получаем айдишник пакета

    PacketHandler.INSTANCE.registerMessage[PacketStartTheoryToServer, IMessage](
        new IMessageHandler[PacketStartTheoryToServer, IMessage] {
            override def onMessage(message: PacketStartTheoryToServer, ctx: MessageContext): IMessage = {
                val r = message.onMessage(message, ctx)
                println("LOL")
                r
            }
        }, classOf[PacketStartTheoryToServer], id.toInt, Side.SERVER)
}
Просто и лаконично)
 
Последнее редактирование:
7,099
324
1,510
getPrivateField - функция, которая достает приватное поле заданного класса и кастует его к нужному типу.
Например,
getPrivateField[SimpleNetworkWrapper, SimpleIndexedCodec](PacketHandler.INSTANCE, "packetCodec")
означает
получение поля packetCodec класса SimpleNetworkWrapper у экземпляра PacketHandler.INSTANCE и приведение типа значения этого поля к SimpleIndexedCodec.
На джаве тоже самое можно написать так:
Java:
Field packetCodec = SimpleNetworkWrapper.class.getDeclaredField("packetCodec");
packetCodec.setAccessible(true);
SimpleIndexedCodec value = (SimpleIndexedCodec) packetCodec.get(PacketHandler.INSTANCE);
Разберись в java reflection api

Ну а суть решения в том, чтобы вытащить рефлексией все нужные значения и зарегать пакет заново со своим хандлером
 
Сверху