GeorgH93 / Minepacks

Free and reliable backpack plugin for Bukkit/Spigot/Paper
https://www.spigotmc.org/resources/19286/
GNU General Public License v3.0
171 stars 75 forks source link

Preserve items when sharing a backpack between different MC Server versions? #176

Open gbl opened 3 years ago

gbl commented 3 years ago

Feature request

Feature description When invalid items are encountered in a backpack read from a database, try to preserve them, but don't allow taking them from the pack. This would allow networks that share backpacks between servers allow them to not upgrade everything at once.

Implementation suggestion

In the item deserializer, if an item is invalid for the current version of Minecraft, instead of removing it, serialize it to, say, a barrier item, possibly with a specific display name and/or lore, and with the complete NBT of the original item in a special tag. It doesn't have to be a barrier item, but those have the advantage of not being able to be gotten normally, and their "invalid item" texture.

In the Minepacks GUI, disallow removing barrier items from the backpack - just treat them as fixed slot entries.

In the item serializer, if a barrier item is encountered that has the special tag, serialize that special tag instead of the barrier item to restore the original item and NBT.

I'm aware that newer items in shulker boxes brought into the old world suffer from the same problem, but fortunately Minepacks has a feature not to allow putting shulkes into backpacks anyway.

How the feature is useful

I'm running several servers (1.15, 1.16, 1.17) in a Bungeecord network with a few friends. We decided to not upgrade the older severs, but add new worlds when the worldgen changed, in order to not have ugly borders between old and new chunks. Now we're looking for a way to transfer items between those servers, for which Minepacks is almost perfect. Except, of course, if someone forgets they have a netherite item in the backpack and open that pack on 1.15, where the item is invalid and disappears without a trace. (Actually, it seems like opening and looking at the backpack doesn't change it, even if the invalid item is shown as an empty slot, but as soon as something gets changed, the inventory gets serialized again which drops the item)

A kind of workaround is the "forbidden items" list; however, I'd like to allow transferring netherite tools between the 1.16 and 1.17 servers, where they exist, so forbidding everything that was introduced after 1.16 isn't a nice solution.

I'd give the change a try myself if you say you won't do that but accept a PR. A quick glance at the source makes me believe the inventory serializer is in one of your libs; I'd appreciate a pointer where to look in that case so I don't have to search everything myself.

GeorgH93 commented 3 years ago

Handling items that failed to de-serialize is something I wanted to do for quiet some time, but have not found the time yet.

Regarding opening backpacks from newer versions on older versions, this is tricky, as far as I am aware Mojang has not changed the NBT tags since 1.13, so at least for now it shouldn't be to much of an issue. But items containing bad NBT data can crash both the server and the client. Backpacks are currently stored in exactly the same way as the players inventory (using the code from Minecraft through reflection), this allows me to guarantee, that if you can store an item in the inventory or an chest you can also store it in the backpack without any problems. Updating items form the old format, to newer formats is also handled by the original Minecraft code. If you are interested in it, you can find it here. Opening backpacks written with a newer version of Minecraft can have items in 3 differnet ways:

  1. The item is fully valid on the old version (material exists, NBT tags are valid on that version)
  2. The items material does not exist in the older version, these shouldn't be a problem to handle, since they wont de-serialize, it would be possible to keep their original data and lock them (as you have suggested) This will also be the case for all materials that have changed their name, how they are stored (e.g. colored wool which used to use data values for the color and now uses different materials) or any item stored in the new material name way on Minecraft versions that still use material ids for storage.
  3. The items material exists in the old version, but it's NBT tags are not valid in that version, these are the trouble some items. Potential outcome of them are:
    • They lose some NBT tags (if they have some that that was introduced in later versions)
    • They crash the client or the server this is especially a problem with items that store other items in NBT tags, like shulkerboxes or prefilled chests
    • Their tags become messed up mostly text, since Minecraft now uses JSON formatted text for everything, but older Minecraft versions do not handle them, they will then use the JSON as normal text (e.g. item display name), when these items are then saved again the text will still be messed up on the new Minecraft versions

also going from MC >= 1.13 to MC < 1.13 will probably always fail.

Handling the 3. case will only be possible by creating a custom mapper that is capable of properly converting the data from newer versions back to the corresponding data on the old version (basically a implementation of com.mojang.datafixers.DataFixer in the other direction). I most definitely do not have the time to implement that and I am not sure if there even is much demand for that.

If you are fine with the risks involved with NBT tags, I guess what you want to do would be possible, as long as you do not open up a server older than 1.13.

(Actually, it seems like opening and looking at the backpack doesn't change it, even if the invalid item is shown as an empty slot, but as soon as something gets changed, the inventory gets serialized again which drops the item) That's because backpacks are only saved if something has been changed by the user or the API (reduces the system load, serializing all the items in the backpack takes quiet some time).

I guess I could add the code that keeps items that did not de-serialize correctly in the backpack to my v3.0 dev branch (if you are okay with running an not fully tested alpha version of the plugin).

gbl commented 3 years ago

I did experiment a bit further, with the current versions of MinePacks (2.4.2-Release, without PCGF).

I'm actually not too concerned about nbt tags getting lost if an item gets taken out of the pack and is put back in; that's a conscious action a player takes, on the server that doesn't support the tags. What I don't want to risk is a player to think "I need some glass blocks, I know I have a few stacks on the 1.15 server", switches to the server, puts the glass blocks in, switches back, only to realize their netherite tools they kept in the backpack are gone.

And I'm not that concerned about crashes beacuse of nbt, at least in 1.13+ versions; datapacks add tags all the time, and generally removing a datapack won't crash your world. Lots of plugins add custom NBT tags too. I think there are few places in the code that iterate over all tags that are present (except serializing), in general the code checks for specific tags, and just ignores tags it doesn't understand. (Of course that doesn't mean there's no such problems at all!)

So I'd think supporting the "lock items" feature for version 1.13+ will still be useful in most cases. Wasn't 1.13 the version where they changed the display name format as well? There will still be edge cases like soul speed disappearing, or the 1.17+ rgb names looking weird; shulker boxes need to be forbidden, and prefilled chests aren't obtainable normally anyway, so I guess the approach

(1.12 to 1.13 was such a big technical change anyway; and both versions are old by today's standards, so I agree there won't be much demand for that.)