Динамическая текстура рендерится только на внешней стороне модели | Нарушение скачивания файлов одним из модов сборки

Версия Minecraft
1.7.10
API
Forge
Небольшое вступление:
Я работаю над улучшенной версией замечательного мода Gravestones, исправляющей конфликты с некоторыми модами (к примеру с Сумеречным Лесом), и добавляющей функционал, который автор хотел добавить, но почему-то не довёл до ума. В частности речь об отображении скина игрока на одном из видов надгробий. Мне удалось реализовать эту функцию для игроков, находящихся на сервере на данный момент, по аналогии с головами игроков, при помощи следующего фрагмента кода (функция, разумеется, возвращает ResourceLocation скина):
Для игрока, находящегося на сервере:
EntityPlayer player = Minecraft.getMinecraft().theWorld.getPlayerEntityByName(playername);
if(player != null) {
    GameProfile gameprofile = player.getGameProfile();
    Minecraft minecraft = Minecraft.getMinecraft();
    Map<Type, MinecraftProfileTexture> map = minecraft.func_152342_ad().func_152788_a(gameprofile);
    if (map.containsKey(Type.SKIN)) {
    this.SKIN_ABSTRACT_PLAYER = minecraft.func_152342_ad().func_152792_a((MinecraftProfileTexture)map.get(Type.SKIN), Type.SKIN);
}
Но этот вариант не работает для игроков, на сервере не находящихся (в Интернете советуют использовать ту же функцию, скормив ей new GameProfile((UUID)null, playername), но я так и не смог реализовать это на практике). Единственное решение, которое у меня заработало, это скачивание скина с внешнего сервера с последующим сохранением его на диске и загрузкой в виде динамической текстуры:
Скачивание скина (за основу брал чужой код):
    public void downloadSkin()
    {
        HttpsURLConnection httpurlconnection = null;
        ResourceLocation resourcelocation = null;
        GraveStones.printDebugMessage("Downloading "+this.playername+"'s skin");
        String skinPath = "";
        try
        {
            httpurlconnection = (HttpsURLConnection)(new URL("https://mineskin.eu/skin/"+this.playername)).openConnection(Minecraft.getMinecraft().getProxy());
            httpurlconnection.setDoInput(true);
            httpurlconnection.setDoOutput(false);
            httpurlconnection.connect();
 
            if (httpurlconnection.getResponseCode() / 100 != 2)
            {
                GraveStones.printDebugMessage("Server response code did not return 200, skin servers might be down.");
            }
 
            BufferedImage bufferedimage;
            bufferedimage = ImageIO.read(httpurlconnection.getInputStream());
            skinPath = "./cachedImages/skins/"+this.playername+".png";
            File outputFile = new File(skinPath);
            ImageIO.write(bufferedimage, "png", outputFile);
        }
        catch (Exception exception)
        {
            GraveStones.printDebugMessage("Error occurred when downloading skin, however, skin servers seem to be up.");
        }
        finally
        {
            if (httpurlconnection != null)
            {
                httpurlconnection.disconnect();
            }
        }
    }
Загрузка изображения в качестве динамической текстуры:
                try {
                    BufferedImage bufferedImage = ImageIO.read(new File("./cachedImages/skins/"+playername+".png"));
                    if(bufferedImage.getWidth() == bufferedImage.getHeight())
                    {
                        bufferedImage = bufferedImage.getSubimage(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()/2);
                    }
                    this.SKIN_ABSTRACT_PLAYER = Minecraft.getMinecraft().getTextureManager().getDynamicTextureLocation(" ", new DynamicTexture(bufferedImage));
                } catch (IOException e) {
                    e.printStackTrace();
                    this.SKIN_ABSTRACT_PLAYER = this.SKIN_STEVE;
                }
Метод рабочий, но вызывает как минимум две проблемы, с которыми, я надеюсь, вы сможете мне помочь:
  1. [РЕШЕНО] Как и заявлено в заголовке, при использовании динамической текстуры, скин отображается только на гранях, видимых игроку. И если для самого скина это проблемой являться не будет (если только ваш скин не включает в себя прозрачные/полупрозрачные элементы), то головные уборы выглядят из-за этого крайне убого. Наглядный пример - на первом скрине рендер динамической текстуры, на втором - скина, взятого напрямую у игрока (внимание на ободок наушников и заднюю часть воротника):
    2023-01-24_13.19.11.png
    2023-01-24_13.59.28.png
    Текстуры для этих моделей задаются через net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer.bindTexture(ResourceLocation p_147499_1_), и я не уверен, что смогу это изменить, в связи с нехваткой опыта. Надеюсь, что вы сможете подсказать мне, как можно заставить динамическую текстуру рендериться с двух сторон, либо же как заставить работать new GameProfile((UUID)null, playername).
  2. Хотя загрузка скинов и работает идеально при запуске через Eclipse, при добавлении мода в сборку (собственно, ради чего всё и затевалось) сетевые функции дают сбой. Логи говорят, что скорее всего не срабатывает функция ImageIO.read(httpurlconnection.getInputStream()), но теоретически проблемы могут быть и с подключением к сайту. Полагаю, что проблему вызывает один из 114 модов сборки, но я не могу его идентифицировать. Удаление всех модов, так или иначе связанных с сетевыми функциями, ни к чему не привело. Разумеется, я не прошу идентифицировать этот мод за меня, но вдруг кто-то сталкивался с похожей проблемой или просто знает, что какой-то мод может блокировать сетевые функции. Если понадобится, список я предоставлю.
 
Последнее редактирование:
Решение
Итак, всё получилось, большое спасибо за помощь!
На случай, если кому-то понадобится код (он очень плохой и является сущим костылём, но тем не менее):
Головной убор рендерится функцией renderHeadwear(0, -0.25, 0, 0.56d)
Очень плохая реализация тесселятора:
    private void renderHeadwear(final double x, final double y, final double z, final double scale) {
        Tessellator tessellator = Tessellator.instance;
        tessellator.startDrawingQuads();
        //Правая грань
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*5, 0.25*1);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*5, 0.25*2);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*4, 0.25*2)...
Хмм, спасибо, теоретически помочь должно, если применимо конечно, учитывая, что у луча, насколько я помню, текстура задаётся только для одной грани, как у большинства блоков
Плюс в моде есть свой аналог луча маяка, возможно получится взять часть кода оттуда
Буду пробовать короче
 
Ну, в общем, решение такое же, как то, что мне уже попадалось, - использовать tesselator. К сожалению, я пока ещё не разобрался, как он работает. Поищу пока соответствующие статьи, но если кто-то даст мне ссылку на простой туториал или подскажет несколько команд - буду крайне признателен
 
Ещё проблема в том, что модель бюста здесь по сути является производной от модели игрока
Модель бюста:
public class ModelHead extends ModelBiped
{
    public ModelHead() {
        this.textureWidth = 64;
        this.textureHeight = 32;
        (this.bipedBody = new ModelRenderer((ModelBase)this, 16, 16)).addBox(-4.0f, 0.0f, -2.0f, 8, 6, 4);
        this.bipedBody.setRotationPoint(0.0f, 0.0f, 0.0f);
        (this.bipedRightArm = new ModelRenderer((ModelBase)this, 40, 16)).addBox(-3.0f, -2.0f, -2.0f, 4, 6, 4);
        this.bipedRightArm.setRotationPoint(-5.0f, 2.0f, 0.0f);
        this.bipedLeftArm = new ModelRenderer((ModelBase)this, 40, 16);
        this.bipedLeftArm.mirror = true;
        this.bipedLeftArm.addBox(-1.0f, -2.0f, -2.0f, 4, 6, 4);
        this.bipedLeftArm.setRotationPoint(5.0f, 2.0f, 0.0f);
        this.bipedLeftLeg.showModel = false;
        this.bipedRightLeg.showModel = false;
    }
  
    public void renderHead(final float f5) {
        this.bipedHead.render(f5);
        this.bipedHeadwear.render(f5);
        this.bipedBody.render(f5);
        this.bipedLeftArm.render(f5);
        this.bipedRightArm.render(f5);
    }
  
    public void showHead(final boolean b) {
        final ModelRenderer bipedHead = this.bipedHead;
        final ModelRenderer bipedHeadwear = this.bipedHeadwear;
        final ModelRenderer bipedBody = this.bipedBody;
        final ModelRenderer bipedLeftArm = this.bipedLeftArm;
        this.bipedRightArm.showModel = b;
        bipedLeftArm.showModel = b;
        bipedBody.showModel = b;
        bipedHeadwear.showModel = b;
        bipedHead.showModel = b;
    }
  
    public void render(final Entity entity, final float f, final float f1, final float f2, final float f3, final float f4, final float f5) {
        super.render(entity, f, f1, f2, f3, f4, f5);
        this.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
        this.renderHead(f5);
    }
}
Полагаю, чтобы реализовать рендер через тесселятор, мне нужно воткнуть его в функцию render?
 
954
60
135
но если кто-то даст мне ссылку на простой туториал
На сайте есть хороший, на мой взгляд, туториал.
мне нужно воткнуть его в функцию render?
Вроде бы да (я на версиях 1.12.2 и 1.16.5 в основном пишу, там несколько изменился подход к подобным вещам, но сама концепция та же).
 
Ну, пока что я сделал вот такой костыль. Тесселятор отдельно рендерит внутреннюю поверхность головного убора. Осталось только как-то наложить её на голову персонажа, и будет полноценный рендер с двух сторон. Правда пока что он отказывается накладываться, и тупо летает вокруг могилы)
2023-01-24_21.35.29.png
 
Итак, всё получилось, большое спасибо за помощь!
На случай, если кому-то понадобится код (он очень плохой и является сущим костылём, но тем не менее):
Головной убор рендерится функцией renderHeadwear(0, -0.25, 0, 0.56d)
Очень плохая реализация тесселятора:
    private void renderHeadwear(final double x, final double y, final double z, final double scale) {
        Tessellator tessellator = Tessellator.instance;
        tessellator.startDrawingQuads();
        //Правая грань
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*5, 0.25*1);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*5, 0.25*2);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*4, 0.25*2);
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*4, 0.25*1);
        //Задняя грань
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*8, 0.25*1);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*8, 0.25*2);
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*7, 0.25*2);
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*7, 0.25*1);
        //Левая грань
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*7, 0.25*1);
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*7, 0.25*2);
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*6, 0.25*2);
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*6, 0.25*1);
        //Передняя грань
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*6, 0.25*1);
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*6, 0.25*2);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*5, 0.25*2);
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*5, 0.25*1);
        //Нижняя грань
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*7, 0.25*0);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z+0.5*scale, 0.125*6, 0.25*0);
        tessellator.addVertexWithUV(x-0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*6, 0.25*1);
        tessellator.addVertexWithUV(x+0.5*scale, y+0.5*scale, z-0.5*scale, 0.125*7, 0.25*1);
        //Верхняя грань
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*6, 0.25*0);
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z+0.5*scale, 0.125*5, 0.25*0);
        tessellator.addVertexWithUV(x+0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*5, 0.25*1);
        tessellator.addVertexWithUV(x-0.5*scale, y-0.5*scale, z-0.5*scale, 0.125*6, 0.25*1);

        tessellator.draw();
    }
2023-01-24_22.34.46.png
 
Сверху