MFF

    Minecraft Forge France
    • Récent
    • Mots-clés
    • Populaire
    • Utilisateurs
    • Groupes
    • Forge Events
      • Automatique
      • Foncé
      • Clair
    • S'inscrire
    • Se connecter

    Difficultés à modifier FoodStats de PlayerEntity

    Planifier Épinglé Verrouillé Déplacé Résolu 1.16.x
    1.16.x
    4 Messages 2 Publieurs 373 Vues 2 Watching
    Charger plus de messages
    • Du plus ancien au plus récent
    • Du plus récent au plus ancien
    • Les plus votés
    Répondre
    • Répondre à l'aide d'un nouveau sujet
    Se connecter pour répondre
    Ce sujet a été supprimé. Seuls les utilisateurs avec les droits d'administration peuvent le voir.
    • PchomP Hors-ligne
      Pchom
      dernière édition par Pchom

      Hello !

      Je suis un peu rouillé en modding, donc j’ai probablement fait des horreurs, mais j’essaie de refaire le mod Oversaturation (https://github.com/truenachtara/Oversaturation) en 1.16.x, pour m’amuser un peu plus dans ma survie solo… et c’est plus compliqué que prévu.

      L’idée, c’est donc de remplacer la méthode addStats de la classe FoodStats pour retirer la limite de 20.
      Malheureusement, l’attribut foodStats de PlayerEntity n’est plus public et n’a pas de setter.

      J’ai donc essayé de créer une classe héritant de ServerPlayerEntity qui rajoute uniquement ce setter (moins je fais de modifications, mieux je me porte). Le contenu est le suivant :

      public class BetterPlayerEntity extends ServerPlayerEntity {
      
          public BetterPlayerEntity(MinecraftServer server, ServerWorld worldIn, GameProfile profile) {
              super(server, worldIn, profile, new PlayerInteractionManager(worldIn));
          }
      
          public void setFoodStats(FoodStats stats) {
              this.foodStats = stats;
          }
      }
      

      Le new PlayerInteractionManager me permet de ne pas avoir tout un tas d’erreurs lors d’interactions avec le monde par la suite. Le code est inspiré de ce qui se trouve dans PlayerList#func_232644_a_, qui semble être la méthode utilisée pour le respawn d’un joueur.

      Jusqu’ici, tout va bien, mais il faut maintenant que je remplace le joueur par mon nouveau joueur. J’utilise donc l’event EntityJoinWorldEvent pour modifier.

          @SubscribeEvent
          public void onEntityJoinWorld(EntityJoinWorldEvent event) {
              if (event.getEntity() instanceof ServerPlayerEntity){
                  ServerPlayerEntity player = (ServerPlayerEntity) event.getEntity();
                  FoodStats oldStats = player.getFoodStats();
                  if (!(oldStats instanceof UncappedFoodStats)) {
                      UncappedFoodStats newStats = new UncappedFoodStats();
                      CompoundNBT foodnbt = new CompoundNBT();
                      oldStats.write(foodnbt);
                      newStats.read(foodnbt);
      
                      BetterPlayerEntity playerEntity = new BetterPlayerEntity(player.server, player.getServerWorld(), player.getGameProfile());
                      playerEntity.copyFrom(player, true);
                      playerEntity.setFoodStats(newStats);
                      player.copyFrom(playerEntity, true);
      
                      playerEntity.remove(false);
                  }
              }
          }
      

      UncappedFoodStats est une classe héritant de FoodStats et modifiant uniquement la méthode addStats.
      Le problème de cette méthode est que je dois créer une nouvelle instance de BetterPlayerEntity, qui va donc appeler les constructeurs de ServerPlayerEntity, PlayerEntity, LivingEntity, …
      Je me retrouve donc avec deux instances du même joueur dans le monde, et je n’en veux qu’une seule.
      Étant donné que la Map de PlayerList n’est pas accessible (map entre UUID et ServerPlayerEntity), réutiliser le joueur déjà créé à l’origine pour simplement lui copier mon joueur custom me semble être le plus simple. Le code fonctionne, mais de manière très temporaire (de moins d’une seconde à une vingtaine de secondes au plus, avant le crash).

      Le problème vient sûrement du fait que deux entités correspondant au joueur existent, car j’ai un crash lors d’un tick (une NPE).

      Voici les logs :

      net.minecraft.crash.ReportedException: Ticking memory connection
      	at net.minecraft.network.NetworkSystem.tick(NetworkSystem.java:154) ~[forge:?] {re:classloading}
      	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:898) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:820) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:84) ~[forge:?] {re:classloading,pl:runtimedistcleaner:A}
      	at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:663) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.server.MinecraftServer.lambda$startServer$0(MinecraftServer.java:233) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_241] {}
      Caused by: java.lang.NullPointerException
      	at net.minecraft.item.crafting.ServerRecipeBook.sendPacket(ServerRecipeBook.java:60) ~[forge:?] {re:classloading}
      	at net.minecraft.item.crafting.ServerRecipeBook.add(ServerRecipeBook.java:38) ~[forge:?] {re:classloading}
      	at net.minecraft.entity.player.ServerPlayerEntity.unlockRecipes(ServerPlayerEntity.java:1076) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.entity.player.ServerPlayerEntity.unlockRecipes(ServerPlayerEntity.java:1086) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.advancements.AdvancementRewards.apply(AdvancementRewards.java:65) ~[forge:?] {re:classloading}
      	at net.minecraft.advancements.PlayerAdvancements.grantCriterion(PlayerAdvancements.java:209) ~[forge:?] {re:classloading}
      	at net.minecraft.advancements.ICriterionTrigger$Listener.grantCriterion(ICriterionTrigger.java:34) ~[forge:?] {re:classloading}
      	at net.minecraft.advancements.criterion.AbstractCriterionTrigger.triggerListeners(AbstractCriterionTrigger.java:68) ~[forge:?] {re:classloading}
      	at net.minecraft.advancements.criterion.InventoryChangeTrigger.trigger(InventoryChangeTrigger.java:56) ~[forge:?] {re:classloading}
      	at net.minecraft.advancements.criterion.InventoryChangeTrigger.test(InventoryChangeTrigger.java:52) ~[forge:?] {re:classloading}
      	at net.minecraft.entity.player.ServerPlayerEntity.sendSlotContents(ServerPlayerEntity.java:988) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.inventory.container.Container.detectAndSendChanges(Container.java:141) ~[forge:?] {re:classloading}
      	at net.minecraft.inventory.container.Container.addListener(Container.java:102) ~[forge:?] {re:classloading}
      	at net.minecraft.entity.player.ServerPlayerEntity.addSelfToInternalCraftingInventory(ServerPlayerEntity.java:341) ~[forge:?] {re:classloading,pl:accesstransformer:B}
      	at net.minecraft.server.management.PlayerList.initializeConnectionToPlayer(PlayerList.java:231) ~[forge:?] {re:classloading}
      	at net.minecraft.network.login.ServerLoginNetHandler.tryAcceptPlayer(ServerLoginNetHandler.java:122) ~[forge:?] {re:classloading}
      	at net.minecraft.network.login.ServerLoginNetHandler.tick(ServerLoginNetHandler.java:66) ~[forge:?] {re:classloading}
      	at net.minecraft.network.NetworkManager.tick(NetworkManager.java:244) ~[forge:?] {re:classloading}
      	at net.minecraft.network.NetworkSystem.tick(NetworkSystem.java:151) ~[forge:?] {re:classloading}
      	... 6 more
      

      J’ai bien songé à utiliser la méthode de respawn de PlayerList pour que la map prenne mon instance de joueur, mais il faut toujours que je supprime proprement l’autre joueur, et c’est ce qui me pose problème.
      Du coup je suis un peu coincé pour la suite, je ne vois pas trop ce que je peux changer 😞

      Merci d’avance pour votre aide !

      > *Si vous ne pouvez expliquer un concept à un enfant de six ans, c’est que vous ne le comprenez pas c…

      PchomP 1 réponse Dernière réponse Répondre Citer 0
      • PchomP Hors-ligne
        Pchom @Pchom
        dernière édition par robin4002

        Du coup, j’ai fini par trouver entre temps.

        Il manquait de quoi supprimer le “nouveau” joueur du monde, et de quoi faire des copies complètes entre les joueurs (pour être certain d’avoir toutes les infos). Du coup j’ai refait une petite fonction copy qui reprend en gros le code de PlayerList et tout a l’air de fonctionner sans crash depuis

            @SubscribeEvent
            public void onEntityJoinWorld(EntityJoinWorldEvent event) {
                if (event.getEntity() instanceof ServerPlayerEntity){
                    ServerPlayerEntity player = (ServerPlayerEntity) event.getEntity();
                    FoodStats oldStats = player.getFoodStats();
                    if (!(oldStats instanceof UncappedFoodStats)) {
                        UncappedFoodStats newStats = new UncappedFoodStats();
                        CompoundNBT foodnbt = new CompoundNBT();
                        oldStats.write(foodnbt);
                        newStats.read(foodnbt);
        
                        BetterPlayerEntity playerEntity = new BetterPlayerEntity(player.server, player.getServerWorld(), player.getGameProfile());
                        copy(player, playerEntity);
                        playerEntity.setFoodStats(newStats);
                        player.getServerWorld().removePlayer(playerEntity, true);
                        copy(playerEntity, player);
        
                        playerEntity.remove(false);
                    }
                }
            }
        
            private void copy(ServerPlayerEntity source, ServerPlayerEntity dest) {
                dest.connection = source.connection;
                dest.copyFrom(source, true);
                dest.setEntityId(source.getEntityId());
                dest.setPrimaryHand(source.getPrimaryHand());
        
                for(String s : source.getTags()) {
                    dest.addTag(s);
                }
        
                dest.addSelfToInternalCraftingInventory();
            }
        

        > *Si vous ne pouvez expliquer un concept à un enfant de six ans, c’est que vous ne le comprenez pas c…

        1 réponse Dernière réponse Répondre Citer 0
        • robin4002R Hors-ligne
          robin4002 Moddeurs confirmés Rédacteurs Administrateurs
          dernière édition par

          Sinon il n’aurait pas été plus simple de changer la valeur par réflexion ?

          PchomP 1 réponse Dernière réponse Répondre Citer 0
          • PchomP Hors-ligne
            Pchom @robin4002
            dernière édition par

            @robin4002 Si, c’est 20x plus simple ^^’
            J’avais mal fait ma réflexion donc j’avais abandonné cette idée, mais en le refaisant j’ai réussi à le faire marcher, et ça réduit les risques de casser quelque chose involontairement

            > *Si vous ne pouvez expliquer un concept à un enfant de six ans, c’est que vous ne le comprenez pas c…

            1 réponse Dernière réponse Répondre Citer 1
            • 1 / 1
            • Premier message
              Dernier message
            Design by Woryk
            ContactMentions Légales

            MINECRAFT FORGE FRANCE © 2024

            Powered by NodeBB