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 !