Иконка ресурса

Фикс невидимых нпс

Версия(и) Minecraft
1.7.10
На своем сервере, используя мод Custom NPC, заметили довольно тупую проблему - если нпс был убит в чанке, который позже выгрузился до его возрождения, то при следующем заходе в чанк, когда нпс уже должен был возродиться, клиент его просто не видит.
Так же, если убить нпс, выйти из чанка, выгрузив его, и зайти обратно, то мертвый нпс, которому была установлена настройка "Отображать труп", не сработает, и вместо заявленного отображения трупа, у вас будет целое ничего. С сущностью вы взаимодействовать не сможете никак, а при его респавне на сервере, клиентам ничего не будет послано. И, к тому же, если сущность на сервере ожила, но игрок ее не видит, то он действительно охренеет, если по нему начнет шмалять нпс из ниоткуда.
Конечно, это решается реконнектом на сервер, но кому это надо? А если у вас какой нибудь антирелог?
Чтобы это пофиксить, у меня ушел небольшой вечер на поиск нужного эвента и отладка кода.
Описываю каждый шаг, что я сделал, и прикрепляю код.
1) Поиск эвента
Хуки городить не хотелось, решил поискать эвенты. Сначала думал какой нибудь ChunkLoad, но он не срабатывал как мне нужно (мб потому что я тестил в одиночке), и потом нашел замечательный на 1.7.10 ChunkWatchEvent. Его суть в чем - когда игрок летит, и ему сервак собирается отправить чанк для отрисовки, файрится этот эвент. В нем есть ChunkCoordinateIntPair и сам игрок, которому мы потом и отправим пакет с синхроном. Как это выглядит:
Java:
    @SubscribeEvent
    public void onNPCTracking(ChunkWatchEvent.Watch event) {
        EntityPlayerMP player = event.player;
        World w = event.player.worldObj;
        //Получаем чанк по координатам из эвента
        Chunk chunk = w.getChunkFromChunkCoords(event.chunk.chunkXPos, event.chunk.chunkZPos);
        //Далее все просто - проходимся по списку существ в полученном чанке, и актуализируем данные, если это наш нпс
        for(int i = 0; i < chunk.entityLists.length; i++) {
            List entities = chunk.entityLists[i];
            for(Object ent : entities) {
                if(ent instanceof EntityNPCInterface) {
                    EntityNPCInterface npc = (EntityNPCInterface) ent;
                    NBTTagCompound entNBT = new NBTTagCompound();
                    npc.writeToNBT(entNBT);
                    //Итак, игрок у нас есть, НБТ с данными нпс у нас есть. Отправляем пакет на клиент игроку, полученному ранее
                    //Не забудьте, что отправлять еще нужно и entityID, по нему будет искать сущность на клиенте
                }
            }
        }
    }
Надеюсь, что вы знаете как создавать, регистрировать и отправлять пакет, гайдов - много.

Отлично, пакет отправляется игроку, которому нужен этот чанк, и в котором есть наши нпс.
Пакет прилетел, но данные нужно обработать. Делается это так:
Java:
        @SideOnly(Side.CLIENT)
        private void packetHandle(YourYobaPacketWithNPCData packet) {
            Minecraft mc = Minecraft.getMinecraft();
            World w = mc.theWorld;
            //Ищем нашу сущность по ид из пакета
            Entity ent = w.getEntityByID(packet.entID);
            if(ent == null) {
                //Если вдруг сущности каким то невьебенным образом нет на клиенте, то обработаем и этот момент
                ent = new EntityCustomNpc(w);
                EntityNPCInterface npc = (EntityNPCInterface) ent;
                npc.readFromNBT(packet.nbt);
                if( npc.deathTime < 1) {
                    npc.deathTime = 0; //Нпс может прилететь живой, но с deathTime > 0, что странно
                    npc.isDead = false;
                    npc.hasDied = false;
                    npc.wasKilled = false; //Это поле приватное, у меня есть сорсы, поэтому я просто поменял доступ, в вашей ситуации можно юзать рефлексию
                    //Еще, желательно обновить здоровье нпс, потому что у нас оно недосинхронизировалось
                }
                if(!mc.theWorld.loadedEntityList.contains(npc)) {
                    mc.theWorld.loadedEntityList.add(npc);
                }
            } else if(ent instanceof  EntityNPCInterface) {
                //У меня на тестах сущность обычно всегда находилась, поэтому падала в этот участок кода
                //Здесь все просто, кастим к нашему нпс, читаем в ней данные из пришедшего НБТ
                //и если сущности реально нет в загруженном списке, то спавним
                EntityNPCInterface npc = (EntityNPCInterface) ent;
                npc.readFromNBT(packet.nbt);
                if( npc.deathTime < 1) {
                    npc.deathTime = 0; //Нпс может прилететь живой, но с deathTime > 0, что странно
                    npc.isDead = false;
                    npc.hasDied = false;
                    npc.wasKilled = false; //Это поле приватное, у меня есть сорсы, поэтому я просто поменял доступ, в вашей ситуации можно юзать рефлексию
                    //Еще, желательно обновить здоровье нпс, потому что у нас оно возможно недосинхронизировалось, но была такая проблема - нпс живой, но у него анимация, будто он умер, и лежит на земле. Хотя может это зависит от deathTime, хз, в кастоме ковыряться это иметь попную боль
                }
                if(!mc.theWorld.loadedEntityList.contains(npc)) {
                    mc.theWorld.loadedEntityList.add(npc);
                }

            }

        }

Как я пришел к такому коду вообще. Решил на клиенте отдебажить это через LivingUpdateEvent на клиенте, мол, файрится ли вообще эвент для моей проблемной сущности - оказалось нет, поэтому я поискал, посмотрел, оно вызывает onUpdate только для загруженных сущностей, что логично.
Поэтому нужно ее в этот список загруженных сущностей через spawnEntityInWorld(...)
Хз, может кому то пригодится

Так же, еще существует проблема, после респавна игрока, с некоторыми сущностями нельзя взаимодействовать. Фикс есть вот в этой теме:
  • Like
Реакции: TheLivan и LIRAY
Автор
fukkivdan
Просмотры
391
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок

Другие ресурсы пользователя fukkivdan

Последние обновления

  1. Edited V1.1

    Потестили наш код более подробно, выявил недочеты, исправил их и обновил здесь, все в обработке...
Сверху