Tavi007 2 Posted March 9 Share Posted March 9 Hey, I stumbled across a bug in my mod, when using the workbench (and presumably any other sort of crafting station). I have an itemstack capability, that gets its default value through json files. So usually when the AttachCapabilityEvent<ItemStack> triggers, I can succesfully add these values to the item. However when the result slot in the workbench container updates to a new item, the attach event does not trigger and the capability stays empty. I added a hook using ItemCraftedEvent, so the values are being set, when the crafting results gets picked up, but I also added some costum tooltips, that displays the capability data. So currently it will not show any of my data, when the mouse hovers above the crafting result. Only after picking it up, the correct data can be seen. This might confuse the player, so I would like to know, if there is method for setting the default values the moment the resulting stack is generated. Also I fear, that the ItemCraftedEvent does not trigger for machines from other mods. This would mean, that it would be possible to create itemstacks with incorrect capability data. I hope my explanation is clear enough. If not, I can add some pictures to show you, what I mean. Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 You cannot apply default values to all item stacks. There is no way. If you want your capability to have default values, you need to lazily initialize them whenever they are queried. For example in your capability: class MyCap { private String name = null; public String getName() { if (this.name == null) { this.name = "default value"; } return this.name; } } 8 minutes ago, Tavi007 said: I have an itemstack capability, that gets its default value through json files. This sounds very suspicious. What are you talking about? Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 just two maps and two strings. I use json files, so anyone can override them with datapacks. Basically each item, that should have default values, also has a corresponding json file. (if an item does not have a corresponding json file, then it will get default default values.) The information in the files are loaded into a Map<ResourceLocation, DataFromJson> once when the server starts, so i can get them whenever i need them. When the attach event (or the item craft event) trigger i resolve the resourceLocation, get the DataFromJson and then set the itemstack capability. As long as the Map doesn't get humongous, this method shouldn't be a performance issure. Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 Yeah, it would be better to just lazily initialize the values in the capability. However AttachCapabilitiesEvent is triggered directly from the ItemStack constructor. So it is impossible to have an ItemStack object and not have AttackCapabilitiesEvent fire for it. Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 Hmm, I already thought, that it would work like this. I guess, that the itemstack in the result slot gets created as an air item. Therefor the attachCapability event gets triggered with an air item and the default default values will be used. So what happens with the result stack, when I place the last iron ingot for crafting an iron sword and the result slot gets updated? And how could I detect that exact moment? Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 IRecipe#getCraftingResult will be called and the resulting ItemStack will be put in the output slot. For simple recipes like shaped recipes this will use ItemStack#copy. That will create a new ItemStack (which will fire AttachCapabilitiesEvent) and after that deserialize any capability data that was present in the old ItemStack. So the event will definitely fire. However again, you should not use the event for this. Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 Ah, so whenever I craft an item, the values, that were set in attachCapability will be overriden by the desirialized cpability from the old stack (which was an air item). That explains the bug. I could change the constructor of my capabilities to use ItemStack and then read from my Map<ResourceLocation, DataFromJson> in there, but I don't see, how this will solve the bug. Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 20 minutes ago, Tavi007 said: Ah, so whenever I craft an item, the values, that were set in attachCapability will be overriden by the desirialized cpability from the old stack (which was an air item). That explains the bug. No. The "old stack" in this case is the recipe's internal ItemStack (the recipe result). It needs to be copied, because it will be modified when its taken from the slot - and the recipe obviously doesn't want its internal stack to to be modified in size or otherwise. At this point I think you need to post a Git repo of your mod so I can look at this in more detail. Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 https://github.com/Tavi007/ElementalCombat you have to look in api/attack or api/defense for the capability Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 What's happening here is the following: When the game starts up, the recipes are deserialized. This will make new ItemStack instances (of their outputs, for example). These will trigger your AttachCapabilitiesEvent and you attach your capability. You then read the default data, but your JSON stuff is not yet loaded, because this is very early in the game's startup. You therefor just attach the default data. When the stack is then copied a new stack is created, the attach event is fired, you attach the (now correctly loaded) defaults. Those are then overwritten by deserialization of the existing capabiltiy data: the stack already had your capability applied (with the incorrect empty defaults from before). The solution is like I said to not immediately attach the defaults but to lazily initialize the data in your capabilities. Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 Okay, got it. I think, I can use the AttackDataAPI and DefenseDataAPI classes for this, because I (and anyone else) should always use these to interact with my capabilities.Or would this be bad practice? Thank you for your help anyway Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 I don't know why you make it a separate class. The capability is designed for this specifically. Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 Now you lost me. Where am I supposed to lazily initialise the values? In AttackData, in AttackDataCapability or somewhere complelty different? Quote Link to post Share on other sites
diesieben07 7786 Posted March 9 Share Posted March 9 In the capability. It holds the data, right? So in your getter there, lazily initialize the values. 3 hours ago, diesieben07 said: For example in your capability: class MyCap { private String name = null; public String getName() { if (this.name == null) { this.name = "default value"; } return this.name; } } Quote Link to post Share on other sites
Tavi007 2 Posted March 9 Author Share Posted March 9 ah okay. I will see, what will work best for me. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.