Передача информации Мод <=> Плагин

216
6
19
Для 1.7.10 и, возможно, выше.

Например, это может пригодится если вы хотите сделать меню кланов. У вас есть плагин (либо самописный, либо с исходниками) и вам необходимо реализовать на клиенте ГУИ с информацией и всякими кнопочкам для редактирования ("Выйти из клана", "Повысить игрока" и бла-бла-бла).

Мой основная тема.

Часть мода
Начнем сие процессию с создания класса-обработчика пакетов в МОДЕ.
Код:
/** Created by keelfy */
//Обязательно этот обработчик должен работать только на клиенте
@SideOnly(Side.CLIENT)
public class PacketHandlerClient
{
   //Список каналов
   private EnumMap<Side, FMLEmbeddedChannel> channels;

   public PacketHandlerClient()
   {
       //Добавляем в список новый канал, свой (будет использован для передачи данных)
       this.channels = NetworkRegistry.INSTANCE.newChannel("yourschanel", new ChannelCodec());

       //Задаем слушателя нашего канала (в моем случае, ChannelHandler(), этот класс объявится ниже)
       FMLEmbeddedChannel clientChannel = this.channels.get(Side.CLIENT);
       String codec = clientChannel.findChannelHandlerNameForType(ChannelCodec.class);
       clientChannel.pipeline().addAfter(codec, "yourschanel", new ChannelHandler());
   }

   //Создает пакет (нужный для отправки) из того класса-пакета, который был создан у нас в моде
   public Packet createPacketFrom(BasePacket msg, Side side)
   {
       return this.channels.get(side).generatePacketFrom(msg);
   }

   //Метод для отправки указанного пакета к серверу
   public void sendPacketToServer(Packet packet)
   {
       this.channels.get(Side.CLIENT).writeOutbound(packet);
   }

   //Метод для отправки указанного пакета к серверу (Перегруженный метод, создает пакет из нашего класса)
   public static void sendPacketToServer(BasePacket packet)
   {
       Packet generatedPacket = this.createPacketFrom(packet, Side.SERVER);
       this.sendPacketToServer(generatedPacket);
   }

   //Вот и ChannelHandler(), который я регистрировал как слушателя нашего канала выше
   //Класс BasePacket я создам позже, пока не обращайте внимания на ошибку
   private static class ChannelHandler extends SimpleChannelInboundHandler<BasePacket>
   {
       //Метод, слушающий канал (унаследован)
       @Override
       protected void channelRead0(ChannelHandlerContext ctx, BasePacket packet) throws Exception
       {
            //DEBUG
            System.out.println("Got packet: " + packet.getClass().getSimpleName());

           //Проверяем на то, какой пакет был получен от плагина
           try {
               //Нам необходим экземпляр класса обработчика пакетов
               PacketHandlerClient handler = new PacketHandlerClient();

               //Класс пакета так же, будет создан позже (Мы, ради  примера, попробуем передать данные об имени игрока через пакеты, как только игрок пошлет запрос на передачу (Да, я знаю что это бесполезно, но это все-таки пример :D))
               if (packet instanceof C0PacketName)
                   //Просто вызываем метод в котором будут происходить все нужные нам действия при получении пакета
                   handler.handleC0PacketName((C0PacketName) packet);

           } catch (Exception e) {
               e.printStackTrace();
           }
       }
   }

   //Этот класс отвечает за установку дискриминаторов для пакетов, без него сервер будет часто выдавать ошибку
   private static class ChannelCodec extends FMLIndexedMessageToMessageCodec<BasePacket>
   {

       public ChannelCodec()
       {
           for (EnumPacket type : EnumPacket.values())
           {
               this.addDiscriminator(type.ordinal(), type.getPacketClass());
           }
       }

       @Override
       public void encodeInto(ChannelHandlerContext ctx,BasePacket msg, ByteBuf target) throws Exception
       {
           msg.write(target);
       }

       @Override
       public void decodeInto(ChannelHandlerContext ctx, ByteBuf source, BasePacket msg)
       {
           msg.read(source);

       }
   }

   //Метод, вызванный в слушателе канала
   private void handleC0PacketName(C0PacketName packet)
   {
       //Выводим в консоль строку с из полученного пакета
       Constants.log.info(packet.payload);
       //Если вам необходимо задать, при получении пакета, какую-либо строку в гуи (Я надеюсь, с гуи, при необходимости вы разберетесь)
       //YourGUI.setString(packet.payload);
   }
}
Код:
/** Created by keelfy */
//Этот абстрактный класс - основа для остальных пакетов
public abstract class BasePacket {
  //Да-да, нам нужен пустой конструктор
  public BasePacket(){}

  //Метод для записи в поток данных
  public abstract void write(ByteBuf data) throws IndexOutOfBoundsException;

  //Метод для чтения из потока данных
  public abstract void read(ByteBuf data) throws IndexOutOfBoundsException;

  //ООО-чень полезный метод для записи строки в поток
  public void writeString(String string, ByteBuf data) throws IndexOutOfBoundsException
  {
      byte[] stringBytes = string.getBytes();
      data.writeInt(stringBytes.length);
      data.writeBytes(stringBytes);
  }

  //И для чтения строки из потока
  public String readString(ByteBuf data) throws IndexOutOfBoundsException
  {
      int length = data.readInt();
      byte[] stringBytes = new byte[length];
      data.readBytes(stringBytes);

      return new String(stringBytes);
  }
}
Так же, нам нужны абстрактные классы отдельно для серверных пакетов (те пакеты, что мод будет принимать) и для клиентских (те пакеты, которые будут отправлены на сервер)
Код:
//Абстрактный класс для клиента
/** Created by keelfy */
public abstract class AbstractPacketClient extends BasePacket
{
  @Override
  public final void write(ByteBuf data) {}
}

Код:
//Абстрактный класс для клиента
/** Created by keelfy */
public abstract class AbstractPacketServer extends BasePacket
{
  @Override
  public final void read(ByteBuf data) {}
}
Код:
/** Created by keelfy */
public enum EnumPacket
{
  //Клиентские пакеты
  GUILD_NAME(C0PacketName.class), //Этот пакет будет принимать отосланный сервером пакет с данными

  //Серверные пакеты
  GUILD_GETNAME(S0PacketGetName.class); //Этот пакет будет отправляться на сервер, из нужной части нашего мода, и говорить ему что нужно отправить в клиент иноформацию о нике игрока (в нашем случае)

//Методами ниже я получаю класс пакета (в скобочках указан), нужно для установки дискриминаторов
  private Class<? extends BasePacket> packetClass;
  private EnumPacket(Class<? extends BasePacket> packetClass) { this.packetClass = packetClass; }
  public Class<? extends BasePacket> getPacketClass() { return packetClass; }

}
Код:
//Т.к. этот пакет клиентский = то он будет лишь читать отосланные ему данные (Данные будут в виде строки, ибо мы будем отсылать информацию об имени игрока)
/** Created by keelfy */
public class C0PacketName extends AbstractPacketClient {
   public String payload = "";

   @Override
   public void read(ByteBuf data) throws IndexOutOfBoundsException {
       this.payload = this.readString(data);
   }
}
Код:
//Серверный пакет, грубо говоря, отправляющий запрос плагину на отправку ножного пакета клиенту
/** Created by keelfy */
public class S0PacketGetName extends AbstractPacketServer
{
   @Override
   public void write(ByteBuf data) throws IndexOutOfBoundsException {
       //Можно использовать любой тип данных для "сигнала" плагину
       data.writeBoolean(true);
   }

   /*
       Делать для каждого запроса новый пакет, ведь не выгодно для оптимизации, да?
       Тогда, может быть, стоит сделать как я - отправлять строку из Enum'a.
       То бишь создать перечисление и в конструкторе пакета указывать какой именно пункт из перечисления я хочу запросить у сервера.
       И в плагине создать абсолютно идентичное перечисление, а затем проверять какой пункт был отправлен с сервера.
       Пункты из перечисления можно передавать методом ordinal() он возвращает порядковое значение нужного вам пункта из Enum'a
   */
}
Под конец добавим строку в инициализацию мода
Код:
   new PacketHandlerClient();
Будем, для примера, посылать пакет-запрос на сервер по нажатию кнопки и выводить полученные данные в консоль
Созданим KeyHandler для обработчки биндов, а в чат информацию мы уже и так выводим
Код:
/** Created by keelfy */
public class KeyHandler
{
   private static final Minecraft mc = Minecraft.getMinecraft();
   private List<KeyBinding> keyBindings;

   public KeyHandler()
   {
       this.keyBindings = new ArrayList<KeyBinding>();
       this.keyBindings.add(new KeyBinding("Get Player Name", 'p', "key.categories.misc"));

       for (KeyBinding binding : this.keyBindings)
       {
           ClientRegistry.registerKeyBinding(binding);
       }
   }

   @SubscribeEvent
   public void key(KeyInputEvent event)
   {
       int key = Keyboard.getEventCharacter();

       for (KeyBinding binding : this.keyBindings)
       {
           if (key == binding.getKeyCode())
           {
               PacketHandlerClient.sendPacketToServer(new S0PacketGetName());
               return;
           }
       }
   }
}
И регистрируем обработчик биндов в инициализации
Код:
new KeyHandler();

Часть плагина
//ВНИМАНИЕ! Название каналов должно быть везде одинаковым! В моде, в плагине.
Код:
//В onEnable()
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, "yourchannelfrommod");
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, "yourchannelfrommod", new PacketHandlerPlugin(this));

//В onDisable()
Bukkit.getMessenger().unregisterIncomingPluginChannel(plugin, "yourchannelfrommod");
Базовый класс для пакетов
Код:
//Без комментариев. Идентичен с его собратом из мода
/** Created by keelfy */
public abstract class BasePacket
{
   public abstract void write(ByteBuffer data) throws BufferOverflowException;

   public abstract void read(ByteBuffer data) throws BufferUnderflowException;

   //Только добавился один метод. Отвечает за настройку размера пакета
   public abstract int getSize();

   public static void writeString(String string, ByteBuffer data) throws BufferOverflowException
   {
       byte[] stringBytes = string.getBytes();
       data.putInt(stringBytes.length);
       data.put(stringBytes);
   }

   public static String readString(ByteBuffer data) throws BufferUnderflowException
   {
       int length = data.getInt();
       byte[] stringBytes = new byte[length];
       data.get(stringBytes);

       return new String(stringBytes);
   }
}
2 абстрактных пакета для клиентских и серверных пакетов
Только в плагине значения клиентского и серверного пакетов меняются. Клиентский отправляет пакеты, серверный - наобарот
Код:
/** Created by keelfy */
public abstract class AbstractPacketServer extends BasePacket
{
  @Override
  public void read(ByteBuffer data) throws BufferUnderflowException {}
}
Код:
/** Created by keelfy */
public abstract class AbstractPacketClient extends BasePacket
{
   @Override
   public final void write(ByteBuffer data) throws BufferUnderflowException {}

   //При отправке нам необходимо указывать размер отправляемого пакета
   @Override
   public final int getSize()
   {
       return 0;
   }
}
Сами пакеты
Код:
//Пакет - приемник пакета в моде S0PacketGetName (Отправляет информацию)
/** Created by keelfy */
public class C0PacketGetName extends AbstractPacketClient
{
   //Пустой конструктор? Надо.
   public C0PacketGetName() {}

   @Override
   public void read(ByteBuffer data){} //Нам не надо ничего считывать из пакета, мы будем производить действия просто при его получении
}
Код:
//Пакет - отправитель для пакета в моде C0PacketName (Принимаем информацию)
/** Created by keelfy */
public class S0PacketName extends AbstractPacketServer {

   //Инициализируем пременную имени игрока
  public String name = "";

  public S0PacketName() {}

  //Указываем ник игрока при отправке пакета
  public S0PacketName(String name) {
     this.name = name;
  }

  //Отправляем ник в виде строки
  @Override
  public void write(ByteBuffer data) throws BufferOverflowException {
     writeString(name, data);
  }

  @Override
  public int getSize() {
     return Byte.SIZE;
  }
}
Код:
/** Created by keelfy */
public enum EnumPacket {
  //Клиентские пакеты
  GUILD_NAME(S0PacketName.class), //Этот пакет будет отправлять пакет с именем

   //Серверные пакеты
  GUILD_GETNAME(C0PacketGetName.class); //Этот пакет будет ловить информацию, отправленную модом

  private Class<? extends BasePacket> packetClass;
  private EnumPacket(Class<? extends BasePacket> packetClass) { this.packetClass = packetClass; }
  public Class<? extends BasePacket> getPacketClass() { return packetClass; }
}
Код:
//Сие творение ОТ ЧАСТИ не мое, так что не осмелюсь тут ставить свои копирайты
public class PacketManager
{
   //Коллекция дискриминаторов
   private static HashMap<Class<? extends BasePacket>, Byte> discriminators;
   //Коллекция классов-пакетов
   private static HashMap<Byte, Class<? extends BasePacket>> classes;

   public PacketManager()
   {
       //Инициализируем нужные переменные
       discriminators = new HashMap<Class<? extends BasePacket>, Byte>();
       classes = new HashMap<Byte, Class<? extends BasePacket>>();

       //Добавляем дискриминаторы для наших пакетов
       initDiscriminators();
   }

   private static void initDiscriminators()
   {
       //Система схожа с форджевской, только метод addDiscriminator() придется делать самим :с
       for (EnumPacket type : EnumPacket.values())
       {
           addDiscriminator((byte) type.ordinal(), type.getPacketClass());
       }
   }

   //Метод для добавления новых дискриминаторов
   public static void addDiscriminator(byte discriminator, Class<? extends BasePacket> clazz)
   {
       //Проверяем, не зарегистрирован ли уже указанный пакет
       if (!discriminators.containsKey(clazz) && !classes.containsKey(discriminator))
       {
           //Добавляем информацию о пакете и даем ему дискриминатор
           discriminators.put(clazz, discriminator);
           classes.put(discriminator, clazz);
       }
   }

   //Метод для получения дискриминатора
   public static byte getDiscriminator(Class<? extends BasePacket> clazz)
   {
       return discriminators.get(clazz);
   }

   //Метод для получения класса пакета по дискриминатору
   public static Class<? extends BasePacket> getDiscriminatorClass(byte discriminator)
   {
       return classes.get(discriminator);
   }

   //Метод опрделеяет какой пакет был получен в виде байтов и возвращает его
   public static BasePacket getPacketFromBytes(byte[] bytes) throws BufferUnderflowException, InstantiationException, IllegalAccessException
   {
       ByteBuffer data = ByteBuffer.wrap(bytes).asReadOnlyBuffer();
       byte discriminator = data.get();

       Class<? extends BasePacket> packetClass = getDiscriminatorClass(discriminator);
       BasePacket packet = packetClass.newInstance();

       packet.read(data);

       return packet;
   }

   //Метод записывает указанный пакет в байты для отправки
   public static byte[] getBytesFromPacket(BasePacket packet) throws BufferOverflowException
   {
       byte discriminator = getDiscriminator(packet.getClass());

       ByteBuffer buffer = ByteBuffer.allocate(packet.getSize() + Byte.SIZE);

       buffer.put(discriminator);
       packet.write(buffer);

       return buffer.array();
   }
}
Регистрируем дискриминаторы пакетов при запуске
Код:
//В onEnable()
new PacketManager();
Код:
/** Created by keelfy */
public class PacketHandlerPlugin implements PluginMessageListener
{
   //Нужный нам, экземпляр главного класса
   private static Main plugin;
   public PacketHandlerPlugin(Main plugin) {
       this.plugin = plugin;
   }

   @Override
   public void onPluginMessageReceived(String channel, Player player, byte[] bytes)
   {
      if(channel.equals("yourchannelfrommod")) { //Проверяем пакет

          BasePacket packet; //Класс базового пакета в плагине, создадим позже

          try{
              //PacketManager мы создадим чуть позже
              packet = PacketManager.getPacketFromBytes(bytes);

              //Проверяем, какой пакет мы получили
              if (packet instanceof C0PacketGetName)
              {
                  //После получения пакета отправляем клиенту информацию с ником игрока
                 sendPacketToPlayer(player, new S0PacketName(player.getDisplayName()));
              }
          } catch (Exception e){
              e.printStackTrace();
          }
      }
   }

   //Метод, отправляеющий пакет игроку
  public void sendPacketToPlayer(Player player, BasePacket packet)
   {
       try {
           //Переводим указанный пакет в байты
           PacketManager packetManager = new PacketManager();
           byte[] bytes = packetManager.getBytesFromPacket(packet);

           //Отправляем
           player.sendPluginMessage(plugin, "yourchannelfrommod", bytes);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
Регистрируем обработчик при запуске
Код:
//В onEnable()
new PacketHandlerPlugin(this);

Все, должно работать! Ошибки пишите в комментарии, отзывы и предложения туда же :D

Обещал выпустить эту тему намного раньше, но время не хватило даже на одну тему :c
 
Последнее редактирование:
На 1.8.8 не хочет работать. При создании в Эклипсе выдает ошибки в главном классе PacketHandlerClient.

Во всех остальных классах без ошибок.
 

timaxa007

Модератор
5,831
409
672
Первый скриншот, говорит о не достающих методов, которые нужно создать. Возможно старые методы не подходят для новых.


Возможно WGSPacket должен быть BasePacket. И возможно из-за этого он и попросил добавить не достающий метод.
 
В части мода ошибок нет, но вот в части плагина да. Видимо копировали код из своего проекта и кое какие методы остались оттуда...

Код:
//В onEnable()
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, "yourchannelfrommod");
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, "yourchannelfrommod", new PacketHandlerServer(this));

Хотя тут же указывается что название класса не PacketHandlerServer, а PacketHandlerPlugin.

Скопированный EnumPacket из мода выдает ошибку, пришлось поправить:
Код:
public enum EnumPacket
{
  //Клиентские пакеты
  GUILD_NAME(C0PacketGetName.class), //Этот пакет будет принимать отосланный сервером пакет с данными

  //Серверные пакеты
  GUILD_GETNAME(S0PacketName.class); //Этот пакет будет отправляться на сервер, из нужной части нашего мода, и говорить ему что нужно отправить в клиент иноформацию о нике игрока (в нашем случае)

 //Методами ниже я получаю класс пакета (в скобочках указан), нужно для установки дискриминаторов
  private Class<? extends BasePacket> packetClass;
  private EnumPacket(Class<? extends BasePacket> packetClass) { packetClass = packetClass; }
  public Class<? extends BasePacket> getPacketClass() { return packetClass; }

}

Много где добавил к методам void....

В итоге при отправке пакета из мода, во время его обработки Дискриминатором вылетает ошибка в лог сервера:
Код:
[00:41:52] [Server thread/WARN]: java.lang.NullPointerException
[00:41:52] [Server thread/WARN]: at com.packets.PacketManager.getPacketFromBytes(PacketManager.java:66)
[00:41:52] [Server thread/WARN]: at com.packets.PacketHandlerPlugin.onPluginMessageReceived(PacketHandlerPlugin.java:30)
[00:41:52] [Server thread/WARN]: at org.bukkit.plugin.messaging.StandardMessenger.dispatchIncomingMessage(StandardMessenger.java:427)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:2114)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload.a(SourceFile:55)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload.a(SourceFile:8)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13)
[00:41:52] [Server thread/WARN]: at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
[00:41:52] [Server thread/WARN]: at java.util.concurrent.FutureTask.run(Unknown Source)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.SystemUtils.a(SourceFile:44)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:715)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654)
[00:41:52] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557)
[00:41:52] [Server thread/WARN]: at java.lang.Thread.run(Unknown Source)


Код:
public class PacketManager
{
   //Коллекция дискриминаторов
   private HashMap<Class<? extends BasePacket>, Byte> discriminators;
   //Коллекция классов-пакетов
   private static HashMap<Byte, Class<? extends BasePacket>> classes;

   public PacketManager()
   {
       //Инициализируем нужные переменные
       this.discriminators = new HashMap<Class<? extends BasePacket>, Byte>();
       this.classes = new HashMap<Byte, Class<? extends BasePacket>>();

       //Добавляем дискриминаторы для наших пакетов
       this.initDiscriminators();
   }

   private void initDiscriminators()
   {
       //Система схожа с форджевской, только метод addDiscriminator() придется делать самим :с
       for (EnumPacket type : EnumPacket.values())
       {
           this.addDiscriminator((byte) type.ordinal(), type.getPacketClass());
       }
   }

   //Метод для добавления новых дискриминаторов
   public void addDiscriminator(byte discriminator, Class<? extends BasePacket> clazz)
   {
       //Проверяем, не зарегистрирован ли уже указанный пакет
       if (!this.discriminators.containsKey(clazz) && !this.classes.containsKey(discriminator))
       {
           //Добавляем информацию о пакете и даем ему дискриминатор
           this.discriminators.put(clazz, discriminator);
           this.classes.put(discriminator, clazz);
       }
   }

   //Метод для получения дискриминатора
   public byte getDiscriminator(Class<? extends BasePacket> clazz)
   {
       return this.discriminators.get(clazz);
   }

   //Метод для получения класса пакета по дискриминатору
   public static Class<? extends BasePacket> getDiscriminatorClass(byte discriminator)
   {
       return classes.get(discriminator);
   }

   //Метод опрделеяет какой пакет был получен в виде байтов и возвращает его
   public static BasePacket getPacketFromBytes(byte[] bytes) throws BufferUnderflowException, InstantiationException, IllegalAccessException
   {
       ByteBuffer data = ByteBuffer.wrap(bytes).asReadOnlyBuffer();
       byte discriminator = data.get();

       Class<? extends BasePacket> packetClass = getDiscriminatorClass(discriminator);
       BasePacket packet = packetClass.newInstance();

       packet.read(data);

       return packet;
   }

   //Метод записывает указанный пакет в байты для отправки
   public byte[] getBytesFromPacket(BasePacket packet) throws BufferOverflowException
   {
       byte discriminator = this.getDiscriminator(packet.getClass());

       ByteBuffer buffer = ByteBuffer.allocate(packet.getSize() + Byte.SIZE);

       buffer.put(discriminator);
       packet.write(buffer);

       return buffer.array();
   }

Где словил [font=Verdana, sans-serif]null пока не нашел ([/font]
 
216
6
19
null выдает из-за неправильного распределениях классов в EnumPacket.
Ошибочка вышла по поводу копирования этого класса. Там нужно заменить в переменных значения.

Например, у тебя в моде стояло значение
PACKET1(S0PacketMY);

То в плагине должно быть так:
PACKET1(C0PacketMY);

И соответственно пакет S0 - отправляет информацию из мода (и находится в моде)
А пакет C0 - ловит отправленную инфу

Так что проверяй.

(И зачем добавлял void? С этим косяков не должно быть. Максимум могут быть не выставлены статики)

А по поводу всех косяков в туториале - я копировал код из своего мода и у меня там реализованна немного другая схема по инициализации экземпляров классов (Получение через enum класса), в общем косяки исправлю.


Добавил в туториал пример EnumPacket в плагине и пофиксил неправильное название регистрируемого обработчика пакетов.
 
Итак, собрал плагин чисто по данным из урока. Ничего лишнего. Вот :
[video=youtube]http://youtu.be/qfFJb3h_gf4[/video]

Исправил кое-где ошибки. Результат тот же:
Код:
[21:46:48] [Server thread/WARN]: java.lang.NullPointerException
[21:46:48] [Server thread/WARN]: at main.PacketManager.getPacketFromBytes(PacketManager.java:66)
[21:46:48] [Server thread/WARN]: at main.PacketHandlerPlugin.onPluginMessageReceived(PacketHandlerPlugin.java:24)
[21:46:48] [Server thread/WARN]: at org.bukkit.plugin.messaging.StandardMessenger.dispatchIncomingMessage(StandardMessenger.java:427)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:2114)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload.a(SourceFile:55)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PacketPlayInCustomPayload.a(SourceFile:8)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13)
[21:46:48] [Server thread/WARN]: at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
[21:46:48] [Server thread/WARN]: at java.util.concurrent.FutureTask.run(Unknown Source)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.SystemUtils.a(SourceFile:44)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:715)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654)
[21:46:48] [Server thread/WARN]: at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557)
[21:46:48] [Server thread/WARN]: at java.lang.Thread.run(Unknown Source)
 
216
6
19
В файлах пакетов должны быть пустые конструкторы. Обязательно. И да, мой косяк - забыл сменить названия конструкторов и сделать поля в PacketManager'e статичными. Отныне сие косяки в туторе исправлены. Опробуй и расскажи про результат.
 
Onneros написал(а):
В файлах пакетов должны быть пустые конструкторы. Обязательно. И да, мой косяк - забыл сменить названия конструкторов и сделать поля в PacketManager'e статичными. Отныне сие косяки в туторе исправлены. Опробуй и расскажи про результат.

Та же самая ошибка. Один в один. Может попробуете сами по этим исходникам скомпилировать мод и плагин ? Просто непонятно, то ли у меня где то косяк, то ли в коде. (

Не могу никак отловить null. Ругается при объявлении BasePacket packet = packetClass.newInstance(); в

Код:
//Метод опрделеяет какой пакет был получен в виде байтов и возвращает его
 public static BasePacket getPacketFromBytes(byte[] bytes) throws BufferUnderflowException, InstantiationException, IllegalAccessException
 {
     ByteBuffer data = ByteBuffer.wrap(bytes).asReadOnlyBuffer();
     byte discriminator = data.get();

     Class<? extends BasePacket> packetClass = getDiscriminatorClass(discriminator);
     BasePacket packet = packetClass.newInstance();

     packet.read(data);

     return packet;
 }
 
[font=Monaco, Consolas, Courier, monospace]       Делать для каждого запроса новый пакет, ведь не выгодно для оптимизации, да?[/font]
[font=Monaco, Consolas, Courier, monospace]       Тогда, может быть, стоит сделать как я - отправлять строку из Enum'a.[/font]
[font=Monaco, Consolas, Courier, monospace]       То бишь создать перечисление и в конструкторе пакета указывать какой именно пункт из перечисления я хочу запросить у сервера.[/font]
[font=Monaco, Consolas, Courier, monospace]       И в плагине создать абсолютно идентичное перечисление, а затем проверять какой пункт был отправлен с сервера.[/font]
[font=Monaco, Consolas, Courier, monospace]       Пункты из перечисления можно передавать методом ordinal() он возвращает порядковое значение нужного вам пункта из Enum'a[/font]
[font=Monaco, Consolas, Courier, monospace]   */[/font]


Подскажи как отправить текстовую переменную и декодировать её на сервере из byte ?
 
Onneros написал(а):
Mastaxys написал(а):
Подскажи как отправить текстовую переменную и декодировать её на сервере из byte ?
 В BasePacket не спроста есть методы "writeString" & "readString"

Согласен, я видел их. но просто не до конца понял как их использовать на примере )
 
7,099
324
1,509
Что конкретно не понятно?
readString читает строку из ByteBuf и возвращает ее, writeString записывает
 
hohserg написал(а):
Что конкретно не понятно?
readString читает строку из ByteBuf и возвращает ее, writeString записывает

Извиняюсь что туплю. как? )
есть у клиента переменная String как её засунуть в пакет перед отправкой?
 
2,505
81
397
Что в примере непонятного?
Код:
  //Отправляем ник в виде строки
@Override
public void write(ByteBuffer data) throws BufferOverflowException {
    writeString(name, data);
}
 
Всё вроде бы хорошо, плагин принимает пакеты, НО во время декомпиляции сваливается в null. Именно во время распознавания пакета через BasePacket. Эта проблема ТОЛЬКО на 1.8.8, на 1.7.10 всё нормально. Может у кого то будет минутка ( десять ) проверить в чём может быть беда на 1.8.8 ?
 
7,099
324
1,509
Scala:
Option(<potetialNullValue>).map(i=>println(i.kek()))
 
Сверху