Хостинг серверов Minecraft playvds.com
  1. Вы находитесь в русском сообществе Bukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на русский язык плагины наших собратьев из других стран.
    Скрыть объявление

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

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

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

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

    Баллы:
    66
    Имя в Minecraft:
    molore
    Привет.

    Есть такая функция: 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. Хостинг MineCraft
    <
  3. CoolBoy

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

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

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

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

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

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

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

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

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

    Баллы:
    96
    Skype:
    ailyashevich
    Имя в Minecraft:
    xDark
    Как раз таки без него
     
  8. Автор темы
    molor

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

    Баллы:
    66
    Имя в Minecraft:
    molore
    Ну ок, сделал так:

    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;

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

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

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

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

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

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

    Баллы:
    76
    Похоже на проблему в ядре. Сейчас попробую накостылить решение. Предварительно могу сказать, что в пакете отправляется массив айдишников пассажиров.[DOUBLEPOST=1470846699,1470842998][/DOUBLEPOST]Мда, даже события 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
  12. Автор темы
    molor

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

    Баллы:
    66
    Имя в Minecraft:
    molore
    ох, Как-же сложно то!
    Спасибо, буду разбираться.
    [DOUBLEPOST=1470848408,1470847389][/DOUBLEPOST]
    Мне каким-то случайным чудом удалось всё решить намного проще.
    Нужно отправить такой пакет 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)...
     
  13. Dymeth

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

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

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

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

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

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

    Лично у меня для получения 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;
        }
    }
    Желательно даже кэшировать нужные классы.[DOUBLEPOST=1470849837,1470848746][/DOUBLEPOST]
    А почему нет? Так даже получше будет.
    Код:
    @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);
    }
    [DOUBLEPOST=1470850368][/DOUBLEPOST]Глянул, почему принудительное указание пассажиров не требуется.
    При создании PacketPlayOutMount ядро само добавляет айдишники пассажиров в поле-массив "b", поэтому вручную прописывать туда данные нужды нет.
    Вот только во время EntityDismountEvent такое уже не прокатит. Пассажир фактически ещё не слез с "водителя", поэтому ядро по-прежнему будет добавлять айдишники сидящих при создании пакета. В этом случае нужно принудительно задавать отсутствие пассажиров при помощи рефлексии.
    [​IMG][​IMG]
     
    Последнее редактирование: 10 авг 2016
  16. Автор темы
    molor

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

    Баллы:
    66
    Имя в Minecraft:
    molore
    Спасибо!
     
  17. Dymeth

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

    Баллы:
    76
    Решил сделать универсальную утилиту. Сомневаюсь, конечно, что до новой версии разработчики не исправят, но вдруг...
    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);
    }
    На этому, думаю, тему можно закрывать.
     
Статус темы:
Закрыта.

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