1. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Помогите Накрыть баг пакетом.

Тема в разделе "Разработка плагинов для новичков", создана пользователем molor, 10 авг 2016.

Статус темы:
Закрыта.
  1. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    Привет.

    Есть такая функция: fEntity.setPassenger(sEntity). Она делает sEntity пассажиром fEntity.
    И есть такой баг, связанный с ней: https://www.spigotmc.org/threads/spigot-1-9-player-setpassenger-help.130324/
    Этот баг проявляется только в том случае, если нужно сделать одного игрока пассажиром другого: player.setPassenger(otherPlayer). Но именно это мне и нужно.

    Из-за этого бага лишь пассажир (otherPlayer) видит то, что он, собственно, пассажир. Водитель же (player, назову его так) этого не видит (в данном случае, он голубого цвета): http://prnt.sc/c371f4

    Так вот, по первой ссылке пишут, что это можно исправить отправкой пакета "водителю" с информацией о том, что он, собственно, "водитель". Но какой пакет, с какими данными, и как отправлять, я понять не могу. Можете помочь?
     
  2. CoolBoy

    CoolBoy Активный участник Пользователь

    Баллы:
    96
    Имя в Minecraft:
    Xezard
    В новых версиях пофикшено. У меня 1.10.2 - всё работает.
     
  3. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    http://prnt.sc/c371f4 - 1.10.2. Снято в один момент. Что я делаю не так?
     
  4. xDark

    xDark Активный участник Пользователь

    Баллы:
    96
    После установки "пассажира" - отправь пакет/
    setPassenger(p);
    PacketPlayOutMount packet = new PacketPlayOutMount(((CraftPlayer)p).getHandle());
    for(Player ps : player.getWorld().getPlayers())
    {
    ((CraftPlayer)ps).getHandle().playerConnection.sendPacket(packet);
    }
     
  5. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    В той теме пишут, что пакет нужно отправлять не всем игрокам, а лишь тому, кто стал "водителем".
    И можете показать пример кода с помощью protocolLib? Спасибо.
     
  6. xDark

    xDark Активный участник Пользователь

    Баллы:
    96
    Как раз таки без него
     
  7. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    Ну ок, сделал так:

    player.setPassenger(entity);
    PacketPlayOutMount packet = new PacketPlayOutMount(((CraftPlayer) entity).getHandle());
    for (Player otherPlayer : player.getWorld().getPlayers()) {
    ((CraftPlayer) otherPlayer).getHandle().playerConnection.sendPacket(packet);
    }

    import'ы:
    import net.minecraft.server.v1_10_R1.PacketPlayOutMount;
    import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer;

    Результата ноль. В чём ошибка?
     
  8. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    Обожаю баги ядра.
    Ну логично.
    Код:
       Player player = ...;
       Entity passenger = ...;
       player.setPassenger(passenger);
       ((CraftPlayer) player).getHandle().playerConnection
          .sendPacket(new PacketPlayOutMount(((CraftEntity) passenger).getHandle()));
    Проверяй.
    Ах да, ещё можешь проверить, как это всё видит третий игрок.
     
  9. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    В чём я опять ошибся? "Водитель" - игрок в первом окне: http://
     
    Последнее редактирование: 10 авг 2016
  10. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    Похоже на проблему в ядре. Сейчас попробую накостылить решение. Предварительно могу сказать, что в пакете отправляется массив айдишников пассажиров.
    Мда, даже события vehicle не вызываются.
    В общем накостылил примерно это.

    Класс слушателя Bukkit-событий (например, PlayerListener):
    Код:
    public static Field passengersField;
    static {
        try {
            passengersField = PacketPlayOutMount.class.getDeclaredField("b");
            passengersField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
    
    @EventHandler(ignoreCancelled = true)
    public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent event) {
        Player player = event.getPlayer();
        Entity passenger = event.getRightClicked();
        player.setPassenger(passenger);
        PacketPlayOutMount packet = new PacketPlayOutMount(((CraftEntity) player).getHandle());
        try {
            passengersField.set(packet, new int[]{passenger.getEntityId()});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        ((CraftPlayer) player).getHandle().playerConnection
                .sendPacket(packet);
    }
    Класс слушателя пакетов (конкретно интересует PacketPlayInSteerVehicle):
    Код:
    if(((PacketPlayInSteerVehicle) packetObject).d() && player.getVehicle() instanceof CraftPlayer)
    {
        CraftPlayer vehicle = (CraftPlayer) player.getVehicle();
        PacketPlayOutMount packet = new PacketPlayOutMount(vehicle.getHandle());
        try {
            PlayerListener.passengersField.set(packet, new int[]{});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        vehicle.getHandle().playerConnection.sendPacket(packet);
    }
    Сразу скажу, что в данном случае могут быть какие-либо непредвиденные ошибки из-за неполноценности кода. Кто ж его знает, что ядро проверяет и делает при получении пакета PacketPlayInSteerVehicle.
    При должном рвении можно написать годный фикс, но мне, честно говоря, лень.
    Как это сделать при помощи ProtocolLib'а тоже не подскажу, ибо не пользуюсь им вообще. Но уверен, что там должно быть даже проще и понятнее, чем у меня - всяческие врапперы делают свою работу.
     
    Последнее редактирование: 10 авг 2016
  11. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    ох, Как-же сложно то!
    Спасибо, буду разбираться.


    Мне каким-то случайным чудом удалось всё решить намного проще.
    Нужно отправить такой пакет player при player.setPassenger(entity) и при entity.leaveVehicle():
    Код:
    PacketPlayOutMount packet = new PacketPlayOutMount(((CraftEntity) player).getHandle());
    ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
    И всё начинает работать так, как ожидается. В общем, мда.

    Теперь бы как-нибудь убрать зависимость от версии сервера (import net.minecraft.server.v1_10_R1.PacketPlayOutMount)...
     
  12. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    Вторая часть моего кода с учётом того, что игрок самостоятельно захочет слезть с "водителя".
    А вот что обычная посадка работает с PacketPlayOutMount без дополнительных данных - это странно.
     
  13. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    Аа, вот оно что. Значит, не всё так просто. Может, тогда стоит попробовать что-нибудь с EntityDismountEvent?
     
  14. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    Получаешь класс по имени, рефлексией создашь новый экземпляр.

    Лично у меня для получения NMS-классов примерно такой метод:
    Код:
    private static String version;
    static {
       String packageName = Bukkit.getServer().getClass().getPackage().getName();
       version = packageName.substring(packageName.lastIndexOf('.') + 1);
    }
    
    public static Class<?> getNmsClass(String className) {
        try {
            return Class.forName("net.minecraft.server." + version + "." + className);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    Желательно даже кэшировать нужные классы.
    А почему нет? Так даже получше будет.
    Код:
    @EventHandler(ignoreCancelled = false)
    public void onEntityDismount(EntityDismountEvent event){
        if(!(event.getDismounted() instanceof CraftPlayer)) return;
        CraftPlayer vehicle = (CraftPlayer) event.getDismounted();
        PacketPlayOutMount packet = new PacketPlayOutMount(vehicle.getHandle());
        try {
            passengersField.set(packet, new int[]{});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        vehicle.getHandle().playerConnection.sendPacket(packet);
    }

    Глянул, почему принудительное указание пассажиров не требуется.
    При создании PacketPlayOutMount ядро само добавляет айдишники пассажиров в поле-массив "b", поэтому вручную прописывать туда данные нужды нет.
    Вот только во время EntityDismountEvent такое уже не прокатит. Пассажир фактически ещё не слез с "водителя", поэтому ядро по-прежнему будет добавлять айдишники сидящих при создании пакета. В этом случае нужно принудительно задавать отсутствие пассажиров при помощи рефлексии.
    [​IMG][​IMG]
     
    Последнее редактирование: 10 авг 2016
  15. Автор темы
    molor

    molor Активный участник Пользователь

    Баллы:
    66
    Спасибо!
     
  16. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    Решил сделать универсальную утилиту. Сомневаюсь, конечно, что до новой версии разработчики не исправят, но вдруг...
    VehicleUtil.java

    Пример использования:
    Код:
    @EventHandler(ignoreCancelled = true)
    public void onPlayerInteractEntityEvent(PlayerInteractEntityEvent event) {
        VehicleUtil.setPassenger(event.getPlayer(), event.getRightClicked());
    }
    
    @EventHandler(ignoreCancelled = false)
    public void onEntityDismount(EntityDismountEvent event){
        VehicleUtil.onDismount(event);
    }
    На этому, думаю, тему можно закрывать.
     
Статус темы:
Закрыта.

Поделиться этой страницей