would allow the player to duplicate the trident.
The problem is that Inventory#setStorageContents updates all inventory
slots (even those that haven't actually changed) and Minecraft is not
able to deal with changes to the currently used ItemStack instance.
The fix is to only update those slots that have actually changed by
comparing the new inventory contents with the current ones before
updating individual slots. This also has the minor benefit of not having
to send the player inventory slot update packets for slots that haven't
actually changed.
This only works if the active item is not actually affected by the
inventory manipulation.
A side effect of this was that the shopkeeper entity would get respawned, even though it was not supposed to get spawned or even after the shopkeeper was already deleted.
This was resolved by remembering the last object id we stored the shopkeeper by.
player shopkeepers will now periodically (roughly once every 5 seconds)
check if their chest is still present and otherwise delete themselves.
This allows them to detect when other plugins, such as WorldEdit, remove
the shop chest.
The setting 'deleting-player-shop-returns-creation-item' applies to
these checks as well and controls whether to drop a shop creation item
for every removed shopkeeper.
Internal:
* Added AbstractShopkeeper#tick which gets invoked roughly once per
second for all shopkeepers in currently active chunks.
This will typically occur with every (even minor) minecraft updates. A warning is logged if an offer could not be migrated.
Added debug option 'item-migrations' to log whenever a shopkeeper performs item migrations.
I wasn't able to reproduce this myself yet, but according to some reports villager shopkeepers would sometimes lose their profession.
Setting their experience to something above 0 is an attempt to resolve this.
Due to a Minecraft bug (MC-141494) interacting with a villager while holding a written book in the main or off hand results in weird glitches and tricks the plugin into thinking that the editor or trading UI got opened even though the book got opened instead.
We therefore ignore any interactions with shopkeeper mobs for now when the interacting player is holding a written book.
The book shopkeepers were updated to ignore them, since supporting them would require fundamental changes to how book prices are stored and how books are identified.
Those books can't be created in vanilla Minecraft, so this shouldn't be a severe limitation.
This caused subsequent reloads to always trigger a 'migration' / forced without actually being required.
Other internal change:
The save task was using a copy of the save data to protect against concurrent modifications while an async save is in progress.
However, since the actual save data did not get modified during ongoing saves anyways, this copy is redundant and was therefore removed.
Command changes:
* Changed: The list and remove commands accept player uuids now and ignore the case when comparing player names.
* Changed: The list and remove commands handle ambiguous player names now: If there are shops of different players matching the given player name, an error message is shown and the player needs to be specified by uuid instead. If a player with matching name is online, that player is used for the command (regardless of if the given player name is ambiguous).
* Changed: The shops affected by the remove command are now determined before asking for the users confirmation. This allows detecting ambiguous player names and missing player information before prompting the command executor for confirmation. A minor side effect of this is that any shops created after the command invocation are no longer affected by the remove command once it gets confirmed.
* Internal: Refactored ObjectIdArgument and all player/shopkeeper name/uuid/id arguments.
* Added ObjectByIdArgument which contains most of the shared logic now.
* Added ShopkeeperIdArgument.
* Added TransformedArgument which allows transforming of parsed arguments.
* Various refactoring to name/id matching.
Text changes:
* Added: The list and remove commands now show the player's uuid as hover text and allow it to be copied into the chat input via shift clicking.
* Internal: Made various changes in order to support Minecraft's text features such as hover events, click events, insertions, etc.:
* Text was changed to more closely match the behavior of text with legacy color codes.
* Most texts are now parsed and stored in a new format internally.
* Text and TextBuilder allow for fluent Text construction.
* Moved everything Text related into separate package.
* SpigotText isn't used directly anymore, but via TextUtils.
* The commands library was updated to use the new text representation everywhere now.
* Internal: Various changes to TextUtils and argument replacement:
* Arguments can be arbitrary objects now and Suppliers can be used to dynamically lookup argument values.
* By iterating over the input String only once and using a map for argument lookup, the replacement of arguments in plain text should be faster now.
* Placeholders in the new Text-based text get parsed only once at plugin startup and use a Map based argument lookup as well, so replacement should be faster there as well.
* Placeholder keys are now specified without the surrounding braces.
* Moved newline pattern and splitting into StringUtils.
* Started moving common shopkeeper message arguments into AbstractShopkeeper. But this is not used yet.
* Added a few StringUtils and Text tests.
Changed messages:
* Added {shopsCount} argument to shop removal confirmation messages:
* msg-confirm-remove-admin-shops
* msg-confirm-remove-own-shops
* msg-confirm-remove-player-shops
* msg-confirm-remove-all-player-shops
New messages:
* msg-ambiguous-player-name
* msg-ambiguous-player-name-entry
* msg-ambiguous-player-name-more
* The delayed chunk activation tasks get cancelled now if the chunk gets unloaded again before it got activated. This resolves a few inconsistencies such as duplicate or out-of-order chunk activation and deactivation handling when chunks get loaded and unloaded very frequently.
* Similarly the respawn task on world saves gets cancelled now if the world gets unloaded.
* Shopkeeper spawning is skipped if there is a respawn pending due to a world save.
* Shopkeepers are now stored by world internally. This might speed up a few tasks which only affect the shopkeepers of a specific world.
* Config: Added debug option 'shopkeeper-activation'. Various debug output related to chunk/world loading/unloading/saving and spawning/despawning of shopkeepers was moved into this debug category.
* Debug messages are now created lazily only when they are actually required.
* Added/Debug: The "/shopkeepers check" command now outputs some additional information on active chunks.
* Internal: Moved all logic from WorldListener into SKShopkeeperRegistry.
* Internal: Various internally used methods are now hidden.
* API: Various changes to ShopkeeperRegistry. See the changelog.
TODO: A follow-up commit will fix the usage of ShopkeeperRegistry#getShopkeeperByName.
Started using the new Spigot text feature at a few places:
The give and transfer command show the player's uuid as hover text now and allow it to be copied into the chat input via shift clicking.
List and remove commands follow soon.
Also:
* Fixed: FeaturesChart was missing the CollectionUtils rename.