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

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

Discussion in 'Разработка плагинов для новичков' started by molor, Aug 10, 2016.

Thread Status:
Not open for further replies.
  1. Автор темы
    molor

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

    Trophy Points:
    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 Активный участник Пользователь

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

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

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

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

    Trophy Points:
    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 Активный участник Пользователь

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

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

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

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

    Trophy Points:
    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 Активный участник Пользователь

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

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

    Trophy Points:
    66
    Имя в Minecraft:
    molore
    В чём я опять ошибся? "Водитель" - игрок в первом окне: http://
     
    Last edited: Aug 10, 2016
  11. Dymeth

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

    Trophy Points:
    76
    Похоже на проблему в ядре. Сейчас попробую накостылить решение. Предварительно могу сказать, что в пакете отправляется массив айдишников пассажиров.[DOUBLEPOST=1470846699,1470842998][/DOUBLEPOST]Мда, даже события vehicle не вызываются.
    В общем накостылил примерно это.

    Класс слушателя Bukkit-событий (например, PlayerListener):
    Code:
    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):
    Code:
    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'а тоже не подскажу, ибо не пользуюсь им вообще. Но уверен, что там должно быть даже проще и понятнее, чем у меня - всяческие врапперы делают свою работу.
     
    Last edited: Aug 10, 2016
  12. Автор темы
    molor

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

    Trophy Points:
    66
    Имя в Minecraft:
    molore
    ох, Как-же сложно то!
    Спасибо, буду разбираться.
    [DOUBLEPOST=1470848408,1470847389][/DOUBLEPOST]
    Мне каким-то случайным чудом удалось всё решить намного проще.
    Нужно отправить такой пакет player при player.setPassenger(entity) и при entity.leaveVehicle():
    Code:
    PacketPlayOutMount packet = new PacketPlayOutMount(((CraftEntity) player).getHandle());
    ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
    И всё начинает работать так, как ожидается. В общем, мда.

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

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

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

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

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

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

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

    Лично у меня для получения NMS-классов примерно такой метод:
    Code:
    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]
    А почему нет? Так даже получше будет.
    Code:
    @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]
     
    Last edited: Aug 10, 2016
  16. Автор темы
    molor

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

    Trophy Points:
    66
    Имя в Minecraft:
    molore
    Спасибо!
     
  17. Dymeth

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

    Trophy Points:
    76
    Решил сделать универсальную утилиту. Сомневаюсь, конечно, что до новой версии разработчики не исправят, но вдруг...
    VehicleUtil.java

    Пример использования:
    Code:
    @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);
    }
    На этому, думаю, тему можно закрывать.
     
Thread Status:
Not open for further replies.

Share This Page