-
Notifications
You must be signed in to change notification settings - Fork 150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Metadata shift of some blocks after adding Block of Granite #1361
Comments
I haven't found a way to hook into Forge's detection of the world being saved by an older version of GTCE, but there is an event that can be listened to if an ID is missing, which mods like AdvancedRocketry have used successfully to remap items. By changing instances of The trick of course is knowing what the old mappings were; the ordinals used for |
This is unexpected behavior which should not occurred. It seems that compressed blocks are not tied to material ID system but they work with ids based on order of blocks which needs to have this variant. As shown here https://github.com/GregTechCE/GregTech/blob/master/src/main/java/gregtech/common/blocks/MetaBlocks.java#L236 . This problem occurred in version 1.10.5 released on 2020-11-10. Quick fix for this issue could be registering Granite with latest id +1 which would shift blocks back to origin position - saving Omnifactory and few other which did not upgraded yet. But at same time it would destroy all Granite products and shift blocks for people who already upgraded. I am quite torn how to handle this and would like to get some input on it. |
The solution for this needs several parts:
Part (3) necessarily entails retaining the existing logic for the purposes of figuring out how the blocks would have been mapped before the addition of the Granite material in 1.10.5. Custom materials are not possible for GTCE to know about at compile time, thus the old mapping must be computed at runtime. The conversion also needs to be performed atomically, otherwise we risk leaving the world in an inconsistent state. Doing the conversion piecemeal seems risky to me, even if we mark chunks along the way. If the game crashes after a chunk is converted but before it is marked, reopening the game would run the update a second time and it's anyone's guess where things would end up. An atomic conversion, even if it takes a little while on larger worlds, also reduces the complexity since you perform it once and you're done; there's nothing else to check going forward.
Storage Drawers seem to handle item registry changes fine, but when AE2 encounters an unregistered item it just deletes it. I ran into this a while ago in Omni when LibVulpes changed the registry name for Dilithium Crystals. Everything else converted their contents to the new ID just fine, except AE2 which just deleted all of it from the drive. The ID shifting that happened both when we tried to use an ID in a gap range for Microversium and when Granite was added in 1.10.5 didn't break the drawers, it just changed what items were contained in them. I believe in the former case that blocks of plastic in a drawer suddenly turned into blocks of meat. A simple improvement to the existing mapping scheme would be something like this: MetaBlocks could be generated not using encounter order but rather a modulus based on the material ID. This will ensure that a particular material ID will always map to the same value, independently of other materials registered. The computation is also very simple, requiring no special mapping logic. For example HSS-G has the material ID 302. You would perform integer division and modulus with 16 (the cap for MetaBlock subitems) resulting in The substantial benefits of this approach have the tradeoff of sparse population of MetaBlocks. If materials in a sequence frequently do not have a block form, there will be unused IDs. This means there will be a larger number of The above would take care of how to futureproof the mapping, except in cases where a mod or modpack register an item in a range that is eventually reserved by GTCE. This would result in a registry conflict, with the custom material being rejected. This is a separate issue that intersects with this one, but can be dealt with separately. It's mitigated by opting for high IDs that are unlikely to be added for a long time, but ultimately unavoidable without some way to segregate native materials from custom ones. The above all addresses point (1) at the start of this comment. How do we apply this to the subsequent points? I propose that it would make the most sense to leverage Forge's missing registry event which is fired on world load. Forge automatically creates a backup of the world when this happens, which improves the safety of the conversion process. It also ensures that it is applied all at once when the world is first loaded, after which no additional work needs to be done. This event may be triggered on world load by changing the MetaBlock registry name assigned during the proposed new mapping process from We would use the old MetaBlock packing algorithm to determine what the old order is (accommodating for shifting if applicable) and remap each MetaBlock to the corresponding new registry value. That should suitably cover points (2) and (3). |
I've done quite a lot of research on this matter, just out of fun and curiosity, and would like to share my progress on it so far. I like the modulus based Metadata calculation and the move to a new registry name demonstrated by @Exaxxion as a way to deal with this and futureproof the system, and in the future, custom registered materials could have their own separate set of blocks/items etc. Though this isn't a must for fixing this issue. Unfortunately, Forge's MissingMappings event doesn't support editing meta values, it just creates a one-on-one registry name reference, so that anything accessing by the old registry name gets served with the Item/Block of the new registry name. So the most we can do using MissingMappings is to remap At this point, I believe there are 2 ways to tackle the problem:
Using the first solution, a manual backup would need to be created on the first invoke of the MissingMappings event. If using the second solution, a manual world backup can be created at the WorldLoad event, at that point, even the level.dat should still be untouched. The order of the events is: MissingMappings -> WorldLoad -> Chunk/BlockEntity/Entity DataFixers invoked for each chunk that isn't yet fixed. There is no way to remap metadata of blocks "atomically" in one go AFAIK, but DataFixers are a vanilla feature, and should be reasonably safe I'd assume. Fixing happens as each chunk gets loaded, most certainly before ticking begins, because otherwise it would be an issue with vanilla hoppers etc. as well. After fixes are run, the chunk is marked with the version specified in the used CHUNK type DataFixer implementation (paired with the Behaviour of FixTypes (according to my testing):
Some mods might get confused, but there is a backup made for reverting the changes. Applied Energistics 2 might be one of these mods, however during my testing with a small network (Medium voltage level Omnifactory world, and a test world with just AE2, Actually Additions and Storage Drawers), I haven't experienced any data loss or other issue. As long as only the registry name and Damage are modified, AE should have no reason to work inconsistently, even though they claim it is not a good idea, and that AE's NBT might not be clean (whatever that means, looking at their code they just write normal NBT data) (also this). At the point DataFixers are run, the TileEntities might not even be initialized yet, let alone ticking. (I've even thrown in a fix that replaces all found ItemStacks with granite, and it did survive, though it wasn't ideal, still the same amount of types occupied in each cell, unless I modify it's contents, and even after modification, there is a lot of garbage data in the cell's NBT. This was just an extreme test case which doesn't apply to the current issue.) Most mods don't go this far to implement data fixing, and just tell their users to use a new world, but it was fun to play around with it. My example mod for a complete implementation of the described DataFixing above can be found here. Explanation of the versions in the builds folder: version 1.7 shifts all stones' meta (1 through 6) down 1 both in world and in inventories. There is also a version with the This implementation is hacky, and needs more testing to be considered safe. Testing with my example mod is a bit difficult, because data fixes are only enabled if level.dat contains an examplemod version between 1.1 and 1.3. This can be manually added/edited with a preferred NBT editor, and the actual data fixing is guaranteed to run only once for already existing chunks, and never for newly generated ones (by the vanilla DataFixer design). I would consider the concept tested enough, so the rest of the testing would need to be after being implemented in Gregtech. People reading this can still help by testing with my example mod if they have the time to experiment a bit, and report back any issues they find. If somebody takes on implementing this, that's great. Otherwise, I might do it in a week or two, if somebody gives me the green light. But I can't promise anything right now. I've yet to set up a Gregtech dev environment and look through/understand its Material registry system. |
Thank you both on this in depth analysis. Proposal by @Exaxxion regarding what needs to be fixed and when seems correct. And suggested implementation for handling compressed is solid I am for implementing it that way. Regarding conversion of current data proposal of using DataFixer by @gabor6505 sounds to me as way to go. But I did only read upon it here and on provided case studies. From my point of view we need to sets of them one from before 1.10.5 and from after 1.10.5 . And from given proposal option 2 seems more straightforward and reliable to me. If anyone has other ideas or improvements upon this or concerns please step forward of I will give it go signal. |
I don't see any concerns or additional discussion regarding this. So this is go signal for implementing this as discussed above and in a way selected in my last comment. |
@gabor6505 I was taking a look at your examples and noticed that you used |
@ALongStringOfNumbers In the case of GTCE, another variable should be created that is true when the mod version in the save is less than The most tricky thing imo will be to construct the 2 mapping definitions, for which the mod needs to figure out what meta was given to each block in the given version, (also including additions from external addons like CraftTweaker?) Responding to @LAGIdiot,
We could just use 2 different lists of mapping definitions in the same DataFixers (like I just described above), the plan is to directly convert from either state to the new, fixed state if I'm not mistaken? If using 2 different sets of DataFixers for direct conversion, then the versions of both sets should be set to the same, which indicates that the stuff they processed has been converted to the new state. (If anything similar happens or something needs to be remapped in the future, it will be quite convenient to do so, just create new DataFixers with a higher version, and Minecraft will deal with first converting stuff with the lower version one if need to be, and then the higher one.) Btw, I was using Oracle's GPL v2 licensed VersionUtil for version comparison, that class could probably be rewritten with a shorter implementation. |
The crafttweaker materials are added in preinit I believe, with the registration of all metablocks, so there should not be any special cases needed for them, as they would have already been registered and in the material system by the time the fixers are called in init |
Describe the bug
When granite was made into a GTCE material, a Block of Granite was added as a Gregtech block. However, this block was added near the end of the compressed block set, specifically at
gregtech:compressed_16:12
, which had the effect of bumping up all the meta numbers of the blocks that were originally atgregtech:compressed_16:12
and beyond,gregtech:compressed_18:0
being the final compressed block, nowgregtech:compressed_18:1
after the addition of the Block of Granite.This caused the shift of any existing blocks in the world, in something like a Storage Drawer or the Player Inventory, transmuting one block into the block preceding it in meta numbers. In addition, it would affect scripts of anyone who makes packs and performed any operation on blocks that were at
compressed_16:12
or further.Versions
Forge: 14.23.5.2847
GTCE:
Modpack: This was originally found in Omnifactory (See Nomifactory/Nomifactory#662 ), but was then replicated in a minimal environment
Addons: SoG was installed in Omnifactory, but this can be replicated without SoG
Setup
Playing Solo
New world generated
Steps To Reproduce
To replicate the material transmutation in a minimal envionrment:
To replicate in a modpack environment:
/give @p gregtech:compressed_16 1 12
. This will be Block of Magnesia.Expected behavior
For no shift of existing material to occur. I do not know how well this could be accomplished, other than adding new blocks at the end of the compressed block list. This would mean instead of Block of Granite being compressed_16:12, it would be compressed_18:1, which would not sift the IDs of any of the blocks before it.
There might be some way instead to update the IDs of every block after the newly added block, so that materials are not changed during an update, however I do not know anything about this.
The text was updated successfully, but these errors were encountered: