[data-attachment-api-v1] Attached data can get clobbered client-side for some block entities
Roundaround opened this issue ยท 4 comments
Unless I'm misunderstanding something about how we're supposed to integrate with the data-attachment-api-v1 for block entities, it seems like using it with (almost) any block entity that has other update-able state won't work. The API mixins to BlockEntity at createNbt, but a lot of the block entities (e.g. beacons and signs) in vanilla don't actually call that when generating update packets (i.e. through world.updateListeners).
In my mod I've attached additional data to signs, but any time world.updateListeners is called for any reason (changed text, applied dye or wax, etc.), my attached data gets wiped client-side when the sign generates its update packet NBT through createComponentlessNbt and sends it to all the clients without any attached data.
From what I can tell all block entities go through BlockEntityUpdateS2CPacket.create(this) at least before diverging in paths, which uses BlockEntity::toInitialChunkDataNbt to generate the NBT. So to me the best way to hook into it would be to mixin to BlockEntityUpdateS2CPacket.create and wrap the BiFunction parameter (BlockEntity::toInitialChunkDataNbt) so we can modify the NBT it spits out, right?
Just to test I implemented this additional mixin in my own mod and everything seems to be working, but I don't know if there are any potential side effects I've overlooked:
https://github.com/Roundaround/mc-fabric-item-signs/blob/1.21.5/src/main/java/me/roundaround/itemsigns/mixin/BlockEntityUpdateS2CPacketMixin.java
EDIT: Made a repro mod:
Here's a basic repro mod of the issue
https://github.com/Roundaround/fabric-example-mod/tree/issue-4638
The important bits are:
- The attachment, with persistence and syncing enabled
- Setting the attachment
- Basic mixin that just renders the current attachment value in a typical nametag thing over the sign. Important aspect is that it is reading the attachment client-side
With this setup, if you place a sign, you should see a red X above it (because no attachment has been set yet).
If you sneak-click the sign, the attachment will be created/set with a value of 1, so the red X should change to a green 1. Continuing to sneak-click it will continue incrementing.
If you then modify the sign's state in some other way (i.e. edit the text, apply dye or wax, etc.), the green number will be reset back to a red X, as the client "forgets" the attached data. This however is client-side only, meaning if you sneak-click the sign again, it will continue incrementing the value and the tag will return back to a green number like expected.
Just had a closer look at this, this fix is not correct as it will now start trying to sync none synced data attachments. The inital syncing of block entity data should be handled here along with the chunk.
If I'm understanding correctly, the solution then might just be to merge data attachments in the read mixin rather than overwrite them, right?
If all changes to data attachments happen outside of this normal Minecraft sync infrastructure anyway (and therefore skip BlockEntity.read entirely), we don't ever need to remove any data attachments in the process of reading from NBT. So instead of entirely clobbering the existing data, we can read if any exist on the NBT and merge them in...I think
EDIT: This is all of course in theory...I don't have anything set up to modify Fabric API source code and test it out within my mod, and I don't actually understand the whole project setup for running game tests and such for Fabric API directly ๐
I don't have any more time today but I'll try to come up with a simple repro mod tomorrow.
In the meantime, you can take a peek at my mod from before my "fix":
https://github.com/Roundaround/mc-fabric-item-signs/tree/4ca388df2ab1e9a71d845e12f8e00c079dd73721
Particularly where I'm setting the attachment:
If I just use markDirty it works, but editing the sign text or applying dye/wax causes the clients to get a new update packet without the attached data, so they think it's been wiped. Modifying it to call updateListeners instead makes that data wipe happen immediately on edit.
Just had a closer look at this, this fix is not correct as it will now start trying to sync none synced data attachments. The inital syncing of block entity data should be handled here along with the chunk.
Can you provide steps to reproduce the issue?
Here's a basic repro mod of the issue (I'll also edit it into the original post):
https://github.com/Roundaround/fabric-example-mod/tree/issue-4638
The important bits are:
- The attachment, with persistence and syncing enabled
- Setting the attachment
- Basic mixin that just renders the current attachment value in a typical nametag thing over the sign. Important aspect is that it is reading the attachment client-side
With this setup, if you place a sign, you should see a red X above it (because no attachment has been set yet).
If you sneak-click the sign, the attachment will be created/set with a value of 1, so the red X should change to a green 1. Continuing to sneak-click it will continue incrementing.
If you then modify the sign's state in some other way (i.e. edit the text, apply dye or wax, etc.), the green number will be reset back to a red X, as the client "forgets" the attached data. This however is client-side only, meaning if you sneak-click the sign again, it will continue incrementing the value and the tag will return back to a green number like expected.