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

  • Автор темы Автор темы 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

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

Есть такая функция: 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

Так вот, по первой ссылке пишут, что это можно исправить отправкой пакета "водителю" с информацией о том, что он, собственно, "водитель". Но какой пакет, с какими данными, и как отправлять, я понять не могу. Можете помочь?
В новых версиях пофикшено. У меня 1.10.2 - всё работает.
 
После установки "пассажира" - отправь пакет/
setPassenger(p);
PacketPlayOutMount packet = new PacketPlayOutMount(((CraftPlayer)p).getHandle());
for(Player ps : player.getWorld().getPlayers())
{
((CraftPlayer)ps).getHandle().playerConnection.sendPacket(packet);
}
В той теме пишут, что пакет нужно отправлять не всем игрокам, а лишь тому, кто стал "водителем".
И можете показать пример кода с помощью protocolLib? Спасибо.
 
В той теме пишут, что пакет нужно отправлять не всем игрокам, а лишь тому, кто стал "водителем".
И можете показать пример кода с помощью protocolLib? Спасибо.
Как раз таки без него
 
Ну ок, сделал так:

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;

Результата ноль. В чём ошибка?
 
Обожаю баги ядра.
В той теме пишут, что пакет нужно отправлять не всем игрокам, а лишь тому, кто стал "водителем".
Ну логично.
Код:
   Player player = ...;
   Entity passenger = ...;
   player.setPassenger(passenger);
   ((CraftPlayer) player).getHandle().playerConnection
      .sendPacket(new PacketPlayOutMount(((CraftEntity) passenger).getHandle()));
Проверяй.
Ах да, ещё можешь проверить, как это всё видит третий игрок.
 
Похоже на проблему в ядре. Сейчас попробую накостылить решение. Предварительно могу сказать, что в пакете отправляется массив айдишников пассажиров.
Мда, даже события 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'а тоже не подскажу, ибо не пользуюсь им вообще. Но уверен, что там должно быть даже проще и понятнее, чем у меня - всяческие врапперы делают свою работу.
 
Последнее редактирование:
ох, Как-же сложно то!
Спасибо, буду разбираться.


Мне каким-то случайным чудом удалось всё решить намного проще.
Нужно отправить такой пакет 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)...
 
ох, Как-же сложно то!
Спасибо, буду разбираться.
Мне каким-то чудом удалось всё решить намного проще.
Нужно отправить такой пакет player при player.setPassenger(entity) и при entity.leaveVehicle():

Код:
PacketPlayOutMount packet = new PacketPlayOutMount(((CraftEntity) player).getHandle());
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);

И всё работает как ожидается. В общем, мда.
Вторая часть моего кода с учётом того, что игрок самостоятельно захочет слезть с "водителя".
А вот что обычная посадка работает с PacketPlayOutMount без дополнительных данных - это странно.
 
Вторая часть моего кода с учётом того, что игрок самостоятельно захочет слезть с "водителя".
Аа, вот оно что. Значит, не всё так просто. Может, тогда стоит попробовать что-нибудь с EntityDismountEvent?
 
Теперь бы как-нибудь убрать зависимость от версии сервера (import net.minecraft.server.v1_10_R1.PacketPlayOutMount)...
Получаешь класс по имени, рефлексией создашь новый экземпляр.

Лично у меня для получения 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;
    }
}
Желательно даже кэшировать нужные классы.
Аа, вот оно что. Значит, не всё так просто. Может, тогда стоит попробовать что-нибудь с EntityDismountEvent?
А почему нет? Так даже получше будет.
Код:
@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 такое уже не прокатит. Пассажир фактически ещё не слез с "водителя", поэтому ядро по-прежнему будет добавлять айдишники сидящих при создании пакета. В этом случае нужно принудительно задавать отсутствие пассажиров при помощи рефлексии.
32YV6tM.png
nuOvuzp.png
 
Последнее редактирование:
Решил сделать универсальную утилиту. Сомневаюсь, конечно, что до новой версии разработчики не исправят, но вдруг...
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);
}

На этому, думаю, тему можно закрывать.
 
Статус
Закрыто для дальнейших ответов.
Назад
Верх