Changer la texture d'une entité en jeu, depuis une url



  • Bonsoir à tous,

    Je fais suivre mon topic sur le forum anglophone de manière à obtenir des réponses sur tous les fronts. J'ai d'ailleurs remarqué que mon problème avait déjà trouvé une solution sur ce forum, mais je n'obtiens pas le même résultat.  😄

    Pour résumer (si vous n'avez pas envie de lire le post original au-dessus, quoi qu'il serait vraiment intéressant de s'y approcher puisque j'explique plutôt bien les choses), je cherche à modifier le skin d'une entité que j'ai créé moi-même (un NPC en somme), mais je souhaiterais le faire depuis une texture hébergée sur le net. Précisément, lorsque le joueur effectue un clic droit sur l'entité en question, le skin par défaut de l'entité doit être remplacé par le skin distant sur le web et je souhaiterais que tous les joueurs en soient notifiés sur le serveur, c'est à dire sans déconnexion/reconnexion.

    J'ai donc commencé par chercher de moi-même dans les classes Vanilla existantes et j'y ai remarqué une méthode getDownloadImageSkin dans la classe AbstractClientPlayer. J'ai donc tout bêtement copié-collé cette méthode dans la classe de mon entité et je l'ai arrangé à ma sauce. Vous pouvez voir que sur le topic sur le forum anglophone, ça me pond un problème de cast alors que je n'ai juste fait que copié-collé la méthode déjà existante. Enfin bref, depuis ma dernière réponse sur le topic, j'ai modifié la méthode pour qu'elle retourne comme il faut et sans cast le ThreadImageDownloadData. Seulement voilà, ça me pond un joli log error parlant de contexte OpenGL n'existant pas…

    Le message d'erreur exact est: No OpenGL context found in the current thread.

    
    [19:48:27] [Server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.player.PlayerInteractEvent$EntityInteract@20de580d:
    net.minecraft.util.ReportedException: Registering texture
    at net.minecraft.client.renderer.texture.TextureManager.loadTexture(TextureManager.java:89) ~[TextureManager.class:?]
    at net.theviolentsquirrels.craftandconquer.entity.EntityNPCDialog.getDownloadImageSkin(EntityNPCDialog.java:92) ~[EntityNPCDialog.class:?]
    at net.theviolentsquirrels.craftandconquer.event.NPCInteractEvent.onEntityNPCInteract(NPCInteractEvent.java:27) ~[NPCInteractEvent.class:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_6_NPCInteractEvent_onEntityNPCInteract_EntityInteract.invoke(.dynamic) ~[?:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90) ~[ASMEventHandler.class:?]
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:185) [EventBus.class:?]
    at net.minecraftforge.common.ForgeHooks.onInteractEntity(ForgeHooks.java:1008) [ForgeHooks.class:?]
    at net.minecraft.entity.player.EntityPlayer.interact(EntityPlayer.java:1246) [EntityPlayer.class:?]
    at net.minecraft.network.NetHandlerPlayServer.processUseEntity(NetHandlerPlayServer.java:1072) [NetHandlerPlayServer.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:93) [CPacketUseEntity.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:14) [CPacketUseEntity.class:?]
    at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:15) [PacketThreadUtil$1.class:?]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_111]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_111]
    at net.minecraft.util.Util.runTask(Util.java:25) [Util.class:?]
    at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:742) [MinecraftServer.class:?]
    at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:687) [MinecraftServer.class:?]
    at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156) [IntegratedServer.class:?]
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:536) [MinecraftServer.class:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]
    Caused by: java.lang.RuntimeException: No OpenGL context found in the current thread.
    at org.lwjgl.opengl.GLContext.getCapabilities(GLContext.java:124) ~[lwjgl-2.9.4-nightly-20150209.jar:?]
    at org.lwjgl.opengl.GL11.glGenTextures(GL11.java:1403) ~[lwjgl-2.9.4-nightly-20150209.jar:?]
    at net.minecraft.client.renderer.GlStateManager.generateTexture(GlStateManager.java:459) ~[GlStateManager.class:?]
    at net.minecraft.client.renderer.texture.TextureUtil.glGenTextures(TextureUtil.java:38) ~[TextureUtil.class:?]
    at net.minecraft.client.renderer.texture.AbstractTexture.getGlTextureId(AbstractTexture.java:54) ~[AbstractTexture.class:?]
    at net.minecraft.client.renderer.ThreadDownloadImageData.getGlTextureId(ThreadDownloadImageData.java:66) ~[ThreadDownloadImageData.class:?]
    at net.minecraft.client.renderer.texture.SimpleTexture.loadTexture(SimpleTexture.java:57) ~[SimpleTexture.class:?]
    at net.minecraft.client.renderer.ThreadDownloadImageData.loadTexture(ThreadDownloadImageData.java:83) ~[ThreadDownloadImageData.class:?]
    at net.minecraft.client.renderer.texture.TextureManager.loadTexture(TextureManager.java:67) ~[TextureManager.class:?]
    … 19 more
    [19:48:27] [Server thread/ERROR] [FML]: Index: 1 Listeners:
    [19:48:27] [Server thread/ERROR] [FML]: 0: NORMAL
    [19:48:27] [Server thread/ERROR] [FML]: 1: ASM: net.theviolentsquirrels.craftandconquer.event.NPCInteractEvent@1c1023e1 onEntityNPCInteract(Lnet/minecraftforge/event/entity/player/PlayerInteractEvent$EntityInteract;)V
    [19:48:27] [Server thread/FATAL]: Error executing task
    java.util.concurrent.ExecutionException: net.minecraft.util.ReportedException: Registering texture
    at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[?:1.8.0_111]
    at java.util.concurrent.FutureTask.get(FutureTask.java:192) ~[?:1.8.0_111]
    at net.minecraft.util.Util.runTask(Util.java:26) [Util.class:?]
    at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:742) [MinecraftServer.class:?]
    at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:687) [MinecraftServer.class:?]
    at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156) [IntegratedServer.class:?]
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:536) [MinecraftServer.class:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]
    Caused by: net.minecraft.util.ReportedException: Registering texture
    at net.minecraft.client.renderer.texture.TextureManager.loadTexture(TextureManager.java:89) ~[TextureManager.class:?]
    at net.theviolentsquirrels.craftandconquer.entity.EntityNPCDialog.getDownloadImageSkin(EntityNPCDialog.java:92) ~[EntityNPCDialog.class:?]
    at net.theviolentsquirrels.craftandconquer.event.NPCInteractEvent.onEntityNPCInteract(NPCInteractEvent.java:27) ~[NPCInteractEvent.class:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_6_NPCInteractEvent_onEntityNPCInteract_EntityInteract.invoke(.dynamic) ~[?:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90) ~[ASMEventHandler.class:?]
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:185) ~[EventBus.class:?]
    at net.minecraftforge.common.ForgeHooks.onInteractEntity(ForgeHooks.java:1008) ~[ForgeHooks.class:?]
    at net.minecraft.entity.player.EntityPlayer.interact(EntityPlayer.java:1246) ~[EntityPlayer.class:?]
    at net.minecraft.network.NetHandlerPlayServer.processUseEntity(NetHandlerPlayServer.java:1072) ~[NetHandlerPlayServer.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:93) ~[CPacketUseEntity.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:14) ~[CPacketUseEntity.class:?]
    at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:15) ~[PacketThreadUtil$1.class:?]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_111]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_111]
    at net.minecraft.util.Util.runTask(Util.java:25) ~[Util.class:?]
    … 5 more
    Caused by: java.lang.RuntimeException: No OpenGL context found in the current thread.
    at org.lwjgl.opengl.GLContext.getCapabilities(GLContext.java:124) ~[lwjgl-2.9.4-nightly-20150209.jar:?]
    at org.lwjgl.opengl.GL11.glGenTextures(GL11.java:1403) ~[lwjgl-2.9.4-nightly-20150209.jar:?]
    at net.minecraft.client.renderer.GlStateManager.generateTexture(GlStateManager.java:459) ~[GlStateManager.class:?]
    at net.minecraft.client.renderer.texture.TextureUtil.glGenTextures(TextureUtil.java:38) ~[TextureUtil.class:?]
    at net.minecraft.client.renderer.texture.AbstractTexture.getGlTextureId(AbstractTexture.java:54) ~[AbstractTexture.class:?]
    at net.minecraft.client.renderer.ThreadDownloadImageData.getGlTextureId(ThreadDownloadImageData.java:66) ~[ThreadDownloadImageData.class:?]
    at net.minecraft.client.renderer.texture.SimpleTexture.loadTexture(SimpleTexture.java:57) ~[SimpleTexture.class:?]
    at net.minecraft.client.renderer.ThreadDownloadImageData.loadTexture(ThreadDownloadImageData.java:83) ~[ThreadDownloadImageData.class:?]
    at net.minecraft.client.renderer.texture.TextureManager.loadTexture(TextureManager.java:67) ~[TextureManager.class:?]
    at net.theviolentsquirrels.craftandconquer.entity.EntityNPCDialog.getDownloadImageSkin(EntityNPCDialog.java:92) ~[EntityNPCDialog.class:?]
    at net.theviolentsquirrels.craftandconquer.event.NPCInteractEvent.onEntityNPCInteract(NPCInteractEvent.java:27) ~[NPCInteractEvent.class:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_6_NPCInteractEvent_onEntityNPCInteract_EntityInteract.invoke(.dynamic) ~[?:?]
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90) ~[ASMEventHandler.class:?]
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:185) ~[EventBus.class:?]
    at net.minecraftforge.common.ForgeHooks.onInteractEntity(ForgeHooks.java:1008) ~[ForgeHooks.class:?]
    at net.minecraft.entity.player.EntityPlayer.interact(EntityPlayer.java:1246) ~[EntityPlayer.class:?]
    at net.minecraft.network.NetHandlerPlayServer.processUseEntity(NetHandlerPlayServer.java:1072) ~[NetHandlerPlayServer.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:93) ~[CPacketUseEntity.class:?]
    at net.minecraft.network.play.client.CPacketUseEntity.processPacket(CPacketUseEntity.java:14) ~[CPacketUseEntity.class:?]
    at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:15) ~[PacketThreadUtil$1.class:?]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_111]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_111]
    at net.minecraft.util.Util.runTask(Util.java:25) ~[Util.class:?]
    … 5 more
    
    

    EntityNPCDialog (mon entité perso):

    
    public class                EntityNPCDialog extends EntityLiving {
    
    //    private static final DataParameter <integer>TEXTURE_INDEX = EntityDataManager.<integer>createKey(EntityNPCDialog.class, DataSerializers.VARINT);
    
       public                  EntityNPCDialog(World worldIn) {
           super(worldIn);
       }
    
    //    @Override
    //    protected void          entityInit() {
    //        super.entityInit();
    //
    //        this.dataManager.register(TEXTURE_INDEX, 0);
    //    }
    
       @Nullable
       @Override
       protected SoundEvent    getAmbientSound() {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
       @Nullable
       @Override
       protected SoundEvent    getHurtSound() {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
       @Nullable
       @Override
       protected SoundEvent    getDeathSound() {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
       @Override
       protected SoundEvent    getFallSound(int heightIn) {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
       @Override
       protected SoundEvent    getSwimSound() {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
       @Override
       protected SoundEvent    getSplashSound() {
           return (CraftAndConquerSounds.NPCDialogSoundEvent);
       }
    
    //    public void             setTextureIndex() {
    //        int                 textureID = this.dataManager.get(TEXTURE_INDEX);
    //
    //        this.dataManager.set(TEXTURE_INDEX, (textureID + 1 < 2) ? textureID + 1 : 0);
    //    }
    //
    //    public int              getTextureIndex() {
    //        return (this.dataManager.get(TEXTURE_INDEX));
    //    }
    
       public static ThreadDownloadImageData   getDownloadImageSkin(ResourceLocation resourceLocationIn) {
           TextureManager          textureManager = Minecraft.getMinecraft().getTextureManager();
           ThreadDownloadImageData threadDownloadImageData;
    
           threadDownloadImageData = new ThreadDownloadImageData(null, "http://imgur.com/Z0pbA3P.png", DefaultNPCSkin.getDefaultSkin(), new ImageBufferDownload() {
    
               @Override
               public BufferedImage    parseUserSkin(BufferedImage image) {
                   return (image);
               }
    
           });
    
           textureManager.loadTexture(resourceLocationIn, threadDownloadImageData);
    
           return (threadDownloadImageData);
       }
    }
    
    

    RenderNPCDialog (le renderer de mon entité):

    
    @SideOnly(Side.CLIENT)
    public class    RenderNPCDialog extends RenderLiving <entitynpcdialog>{
    
    //    public static final ResourceLocation[] NPC_DIALOG_TEXTURES = new ResourceLocation[] {
    //            new ResourceLocation(CraftAndConquer.MODID, "textures/entities/npc/npc_dialog.png"),
    //            new ResourceLocation(CraftAndConquer.MODID, "textures/entities/npc/npc_dialog_2.png"),
    //    };
    
       public      RenderNPCDialog(RenderManager renderManagerIn, ModelBase modelBaseIn, float shadowSizeIn) {
           super(renderManagerIn, modelBaseIn, shadowSizeIn);
       }
    
       @Override
       protected ResourceLocation  getEntityTexture(EntityNPCDialog entity) {
           return (DefaultNPCSkin.getDefaultSkin());
       }
    }
    
    

    DefaultNPCSkin (mon équivalent personnel de la classe Vanilla existante DefaultPlayerSkin):

    
    public class    DefaultNPCSkin {
    
       private static final ResourceLocation   NPC_DIALOG_DEFAULT_TEXTURE =
               new ResourceLocation(CraftAndConquer.MODID, "textures/entities/npc/npc_dialog.png");
    
       public static ResourceLocation          getDefaultSkin() {
           return (NPC_DIALOG_DEFAULT_TEXTURE);
       }
    }
    
    

    Je suis au courant que je dois sauvegarder l'unicité de la texture par entité dans les DataParameters, mais pour l'instant je préfère avancer étape par étape: je suis déjà bloqué à la base.  :dodgy:

    Qu'en pensez-vous ? :)</entitynpcdialog></integer></integer>


  • Administrateurs

    Salut,
    Le problème est que tu appels ta fonction getDownloadImageSkin depuis une autre fonction qui est exécuté soit dans le thread serveur, soit dans le thead de netty.
    Dans ces deux threads il n'y a pas d'OpenGL. OpenGL n'est que disponible dans le thread client.

    Si on suit le stacktrace :
    at net.theviolentsquirrels.craftandconquer.event.NPCInteractEvent.onEntityNPCInteract(NPCInteractEvent.java:27) ~[NPCInteractEvent.class:?]
    C'est à ce niveau qu'il y a le problème, tu ne devrais pas appeler la fonction getDownloadImageSkin ici.
    Tu cherches à faire quoi dans cette classe NPCInteractEvent ?



  • Lmao je pensais avoir fourni le code de cette classe dans mon post, my bad.

    
    public class        NPCInteractEvent {
    
       @SubscribeEvent
       public void     onEntityNPCInteract(PlayerInteractEvent.EntityInteract event) {
           ItemStack   heldItem = event.getEntityPlayer().getHeldItem(EnumHand.MAIN_HAND);
           ItemBase    npcManager = CraftAndConquerItems.npcManager;
    
           if (event.getTarget() instanceof EntityNPCDialog &&
                   event.getHand().equals(EnumHand.MAIN_HAND) &&
                   heldItem.getItem().equals(npcManager)) {
               if (!event.getWorld().isRemote)
                   EntityNPCDialog.getDownloadImageSkin(DefaultNPCSkin.getDefaultSkin());
           }
       }
    }
    
    

    Comme tu peux le constater, si j'effectue un clic-droit avec un item spécifique dans la main hand sur l'entité, je souhaiterais que sa texture change. À terme ce que je souhaiterais faire, c'est pouvoir customiser mon entité via une GUI et de ce fait pouvoir changer sa texture et appliquer la nouvelle en appuyant sur un bouton "save", et que ce changement de texture via url soit effectif dans l'immédiat et que tous les joueurs du serveur soit notifiés et qu'ils n'aient pas besoin de se déconnecter/reconnecter.


  • Administrateurs

    Pour changer la texture, il faudrait simplement modifier la valeur de l'url de la texture.
    Rien de plus.
    Ensuite côté client, lorsque l'url est changé, il faut en même temps lui demande de re-télécharger la texture. (à voir s'il y a moyen de détecter le changement d'une valeur d'un datawatcher, sinon passes par un paquet).



  • D'accord.

    Si j'ai bien compris le principe, ma méthode getDownloadImageSkin est correcte mais puisqu'elle est exécutée côté serveur dans le handling de mon event, elle ne fonctionne pas puisque évidemment le contexte OpenGL est côté client et non serveur. De ce fait, au lieu d'appeler cette méthode bêtement comme je le fais là, je devrais trouver un moyen de notifier tout le monde (Server –> Client) que le skin de cette entité a changé et qu'il faudrait télécharger la nouvelle texture qui lui est attribuée, right ?

    Les seuls moyens que je vois sont effectivement ou bien les DataWatchers, ou bien les Packets... Si je passe par Packet, je devrais en envoyer un du Server vers tous les Clients et du côté de l'handle client, appeler cette méthode getDownloadImageSkin.

    Pardon de pousser ma curiosité jusqu'au bout, mais comment devrais-je faire si je devais employer des DataWatchers ?


  • Administrateurs

    Oui c'est bien ça.
    Justement pour les data watchers je ne sais pas trop.
    Il faut voir s'il existe une fonction qui est appelé lors du changement d'une valeur.

    Sinon une solution serait de faire un variable lastTextureName, et si la valeur de lastTextureName != valeur du data watcher, alors lastTextureName prend la valeur du data watcher et tu applique le téléchargement de la texture.
    Mais ce n'est pas très propre car il faudrait faire la vérification à chaque tick …