UploadedJun 19, 2021
Supported Bukkit Versions
v2.13.0 for MC 1.17, 1.16.5, 1.15.2, 1.14.4
This update is rather large. I therefore only summarize the most important changes here. You can find the complete changelog in the Github repository: https://github.com/Shopkeepers/Shopkeepers/blob/master/CHANGELOG.md
This update has not yet been tested much on MC 1.17, which itself is probably not yet stable. Also, some of the other changes of this update bear the risk to introduce new bugs. This is why this version is initially marked as 'alpha'. I will promote this version to 'release' as soon as more time has gone by and no major issues have been identified. If you are upgrading to this version, be sure to create a backup of your server and shopkeepers data!
Update for MC 1.17:
- Added axolotls, glow squids, and goats to the by default enabled mob types. If you are upgrading, you will have to manually enable these new mob types.
- Added new editor features that are only available when using MC 1.17:
- Changing the axolotl variant.
- Toggling between a glowing and a dark glow squid.
- Toggling between a normal and a screaming goat. This editor option is only available if 'silence-living-shop-entities' is disabled.
- Toggle glowing text for sign shops.
- Known issues related to MC 1.17:
- Squids and glow squids can be pushed around. This issue might already have existed for some time. See https://bugs.mojang.com/browse/MC-89883
- The loading of entities is deferred from chunk loading in Minecraft now. I am not yet sure if this severely impacts the Shopkeepers plugin, but it certainly partially breaks the 'block-villager-spawns' and 'block-wandering-trader-spawns' settings currently. It is not yet clear how Spigot will update to account for this in order to retain backwards compatibility for plugins.
- The folder structure has changed:
- The save file is stored inside a new 'data' folder now. If no save file exists at the new location and a previous save file is found at the old location, it is automatically moved.
- Language files are located inside a new 'lang' folder now. Existing custom language files need to be manually moved!
- Trading logs (if enabled) are stored inside the folder 'trade-logs' now. Existing logs are not automatically moved to the new location!
- Removed the 1.16 'PIG_ZOMBIE' migration. We no longer automatically remove this mob type from the config, but only log a warning and then ignore it.
- Removed the migration from Citizens shopkeeper NPC ids to NPC unique ids (originally added in v2.4.0).
- The setting 'enable-spawn-verifier' is no longer used and is automatically removed from existing configs during the update.
- The data format of the CSV trade logs has changed. If you automatically process these CSV trade logs, you may have to update your programs to account for the new format.
- The permissions for the shopkeeper remove (all) command have been renamed. You may have to update your permissions setup.
- 'shopkeeper.remove.own' -> 'shopkeeper.remove-all.own'
- 'shopkeeper.remove.others' -> 'shopkeeper.remove-all.others'
- 'shopkeeper.remove.all' -> 'shopkeeper.remove-all.player'
- 'shopkeeper.remove.admin' -> 'shopkeeper.remove-all.admin'
Language file changes:
- The default messages are no longer stored inside the config. Instead, we always generate an 'en-default' language file. This file acts as a template for custom language files. It is not meant to be modified and is replaced with an up-to-date version on every plugin startup. Message keys no longer start with the 'msg' prefix.
- Config: Changed the default language from 'en' to 'en-default'. Existing configurations are automatically migrated.
- Some texts that were previously settings in the config are now regular messages, such as the inventory titles, nameplate prefix, and sign shop lines. The appearance of sign shops has slightly changed.
- Added warnings when the language file is missing messages or contains unexpected messages.
- Fixed: Color codes for default messages were not getting replaced. This has an effect when some messages of the specified language file cannot be loaded.
- When splitting messages into multiple lines, we take all Unicode line break characters into account now.
- Fixed: The default german translation was missing some translations.
- Several messages were changed, renamed, removed, or added. See the complete changelog for details. You will have to manually update your custom language files to adapt to these changes.
New feature: Placeholder items for player shops.
- When players set up the trades of their shopkeepers, they can now use renamed nametag items as substitutes for items that they don't have. The nametag's display name has to match the name of the substituted item type. Display names that don't match any english item type name won't work. Other properties of the substituted item cannot be specified.
- The parsing is lenient to some extent: The Minecraft namespace prefix ('minecraft:') is optional (some item type names are even too long to be used in anvils if the prefix is included). The upper and lower case of characters is not important. Leading and trailing whitespace is ignored. Spaces and dashes are converted to underscores. Any color codes are also ignored.
- Placeholder items are immediately converted to their substituted item inside the shopkeeper editor. If what appears to be a placeholder item is not converted, its display name probably does not match a known item type. It is not possible to setup player shopkeeper trades that buy or sell the placeholder items themselves. But normal nametags, and nametags whose names do not match any valid item type, are treated like normal items and can be traded as usual.
- Placeholder items are supported by the selling, buying, and trading player shopkeeper. The admin shopkeeper treats them like normal items.
- Placeholder items can not only be used for items that are bought, but also for items that are sold. This allows players to setup trades before they have the items required to fulfill these trades.
- It is also possible to specify basic enchanted books via placeholder items:
- The used naming format is '<enchantment> <level>'.
- The parsing is similar to that of item type names: The normalized display name has to match the id of the intended enchantment.
- Aliases have been defined for some of the enchantments, so that for example 'curse of binding' is correctly mapped to the enchantment with id 'binding_curse'.
- Only a single enchantment can be specified.
- The level can be freely specified, even outside the range of enchantment levels that can usually be obtained in vanilla Minecraft. Levels outside the range of shorts are truncated to the nearest valid short number. If no level is specified, or if the level cannot be parsed, the enchantment's minimum level is used (usually 1).
- Up to level ten, the level can also be specified via roman numerals ('I' to 'X'). It is also possible to specify the levels 'min' or 'max', which results in the normal minimum or maximum level of the specified enchantment to be used.
- It is also possible to specify basic potions, splash potions, lingering potions, and tipped arrows via placeholder items:
- The used naming formats are:
- Potion: '[long] [strong] [potion] [of] [long] [strong] <potion type> [2|ii] [potion] [2|ii]'
- Splash potion: '[long] [strong] <splash> [potion] [of] [long] [strong] <potion type> [2|ii] <splash> [potion] [2|ii]'
- Lingering potion: '[long] [strong] <lingering> [potion] [of] [long] [strong] <potion type> [2|ii] <lingering> [potion] [2|ii]'
- Tipped arrow: '[long] [strong] [tipped] [potion] <arrow> [of] [long] [strong] <potion type> [2|ii] [tipped] [potion] <arrow> [2|ii]'
- The parsing of the potion type is similar to that of enchantments: The normalized 'potion type' has to match the id of the intended potion type. These potion types cover the potions that are found inside the creative inventory. Custom potion items with arbitrary effects and properties are not supported.
- The keywords 'splash', 'lingering', and 'arrow' are used to identify the different item types. If none of these keywords is found, a normal potion item is assumed.
- The keywords 'long', 'strong', '2', and 'ii' specify long or strong variants of the specified type of potion. There are currently no potion variants that are both long and strong at the same time. Consequently, only one of these keywords is allowed to be used at the same time. However, we currently ignore any additional occurrences of the respectively other keywords. If the specified potion type does not support the selected variant, the keyword is currently ignored as well. But this might change and potentially become more strict in a future release.
- Each keyword can occur at most once, but there may be multiple valid locations at which it can occur (which is why the above formats mention some keywords multiple times). However, for simplicity, the parsing does not actually take the order or dependencies of words into account currently, but only checks for the presence of the various keywords. But this might change and potentially become more strict in a future release. Any other words in the above formats that were not mentioned as keywords are optional.
New feature: Trade notifications.
- Config: Added settings 'notify-players-about-trades' (default: disabled) and 'notify-shop-owners-about-trades' (default: enabled), which enable trade notifications for all players with certain permissions, or for shop owners about trades that take place in their own shops.
- Permissions: Added permissions 'shopkeeper.trade-notifications.admin' and 'shopkeeper.trade-notifications.player' (both default to false). If trade notifications are enabled (setting 'notify-players-about-trades'), players with these permissions will receive trade notifications for all admin or all player shops respectively.
- There are different sets of trade notification messages for different trade variations and contexts. The default messages do not make use of all of these variations, but this should provide a lot of flexibility when adjusting the messages.
- In order to avoid notification spam, the notifications for equal trades that take place in quick succession are merged into a single notification. However, this does not completely prevent players from potentially causing spam by trading. If this is a concern to you, you may have to disable the trade notifications for now, until more sophisticated countermeasures are available to guard against this kind of spam.
- A new editor option allows shop owners to disable trade notifications for individual shopkeepers. This only affects the trade notifications that are sent to the shop owner, not the general trade notifications that are sent to players with the respective permission.
- Added command "/shopkeeper notify trades", which allows players to disable trade notifications. This command requires the new permission 'shopkeeper.notify.trades' (default: true).
- Since we do not keep track of any user data yet, the trade notifications are only disabled for the current game session (until the player reconnects).
- The first trade notification received during the current game session notifies the player that they can disable trade notifications via this command. This hint message is clickable and will automatically insert the command to execute into the player's chat box. This hint is only sent to players that have the permission to use the command.
- Config: Added settings 'trade-notification-sound' (disabled by default) and 'shop-owner-trade-notification-sound'. These sound effects are played whenever a player (or shop owner) receives a trade notification. They can be disabled by setting them to an empty String inside the config.
Improvements related to the saving and loading of shopkeepers:
- Fixed: Improved the error checking for both the serialization and the writing of shopkeeper data. This should also resolve an issue with the save data being lost when the disk is full.
- We provide more detailed error and debug messages now when a save fails. For example, we provide better error messages if failures are caused by missing directory access permissions.
- Replaced the old Java IO based implementation of saving and loading shopkeeper data with a new NIO based implementation. This should also contribute to more accurate error messages in case of failures.
- Fixed/Debug: The logged number of shopkeepers that have been deleted since the last successful save might not have matched the actual number of deleted shopkeepers. This number did not take into account any shopkeepers that could not be deleted during previously failed save attempts.
- Fixed: We explicitly wait for the new shopkeeper data to be written to disk now before we replace the old save data. This should provide additional protection against data loss and corruption in cases of severe system failures, such as crashes, power losses, etc.
- We log a warning now if we are not able to atomically replace the old and new save data. There are several non-atomic fallback solutions for the case that the save file cannot be atomically renamed. However, these may result in data loss or corruption in case of severe system failures (hence the warning).
- During plugin shutdown, we explicitly verify now that there really is no unsaved shopkeeper data, and that there are no saves pending or still in progress. Otherwise, we log a warning. This warning may for example be encountered if the final save during plugin shutdown fails.
- Fixed: When a save fails, we also trigger a delayed save now even if there are no dirty shopkeepers. This is for example required if there has been an explicit save request, or if shopkeepers have been deleted. These changes would not be reliably persisted before.
- When a save fails, we no longer serialize the data of the affected shopkeepers again during the subsequent save attempts if this data is still up-to-date.
- The name of the temporary save file was slightly changed (`save.temp` -> `save.yml.tmp`).
- We log a warning now whenever a shopkeeper is loaded or created that uses a shop type or shop object type that is disabled. We still load the shopkeeper, so that in the case of a player shop the container is still protected. But we no longer attempt to spawn the shopkeeper. Even though these shopkeepers might in some cases still seem to work as normal, there is no guarantee for this. Admins are advised to either delete these shopkeepers, or change their shop (object) types to something else.
Changes to the CSV trade logging:
- The CSV data format has changed. If you automatically process the CSV trade logs, you may have to update your programs to account for the new format.
- Config: Renamed 'enable-purchase-logging' to 'log-trades-to-csv'. The previous config is automatically migrated.
- Config: Added setting 'log-item-metadata' (disabled by default). This enables the logging of item metadata.
- In order to represent the logged trades more compactly, we merge equivalent trades that are triggered in quick succession over a certain period of time. By default, we merge successive equal trades for up to 15 seconds, but only if there is no gap larger than 5 seconds between the individual trades. The new settings 'trade-log-merge-duration-ticks' and 'trade-log-next-merge-timeout-ticks' control these parameters. They also allow the trade merging to be disabled.
- Performance: The logging is performed asynchronously now, and in batches. When a trade takes place, we wait 30 seconds before we log the trade and any other trades that may have taken place during this duration.
- Debug: Improved the error handling and debug output related to the CSV trade logging.
Several performance improvements:
- Various performance improvements related to sign shops and the processing of physics events.
- For load balancing purposes, the activities of shopkeepers are distributed over multiple Minecraft ticks now. However, processing the shopkeepers every tick, even if only a portion of them needs to do any actual work, also comes with a certain overhead. We therefore do not distribute their activities over all Minecraft ticks, but instead process them in groups only every 5 ticks.
- Performance improvements related to the shopkeeper mob AI:
- Config: Added the setting 'mob-behavior-tick-period' with a default value of 3.
- This controls the rate at which we update the gravity and AI of shopkeeper mobs. Previously, these behaviors were updated every tick, as it is also the case in vanilla Minecraft. Values above 1 indicate a reduced tick rate, which result in a less smooth, less reactive, and possibly slower behavior in comparison to the behavior of mobs in vanilla Minecraft.
- In order to compensate for a reduced tick rate, some activities are scaled accordingly. This ensures, for example, that mobs still rotate their head at the same speed towards nearby players, or that mobs still fall at the same speed when being affected by gravity. Consequently, a reduced tick rate is less performance-intensive in total, but may be slightly more performance-intensive per individual behavior update.
- In my testing, the value 3 offered a large overall performance benefit with a relatively small additional performance impact per individual behavior update, while still providing an acceptable smooth mob behavior. Values above 3 are clearly noticeable and offer little additional benefit.
- Reduced the rate at which the shopkeeper mob AI activations are updated for online players from once every 20 ticks to once every 30 ticks. This seems to be sufficient to still fluently activate nearby shopkeeper mobs even for players that fly around in creative mode. We also immediately activate the shopkeeper mob AI in nearby chunks now whenever a player joins the server or teleports.
- Shopkeeper mobs no longer tick Minecraft's float behavior. This has no effect since they use the NoAI flag.
- The shopkeeper mob AI task is no longer dynamically started and stopped, but instead keeps running even if there are no entities to process currently. Frequently stopping and restarting the task is itself associated with a certain overhead. And even if there are shopkeeper mobs, we can now very cheaply skip their processing altogether when we know that there are currently no chunks with neither active AI nor gravity.
- Various other small optimizations to how the shopkeeper AI task checks if a mob is still valid, and how it stores, queries, and processes chunk and entity data.
- On some Paper versions, maybe due to their async chunk loading, the player's current chunk may sometimes not be loaded yet. We now avoid accessing (and thereby loading) the chunk when activating the AI and gravity behavior of nearby shopkeeper entities.
- Increased the chunk activation delay on chunk loads from 2 to 20 ticks. This further limits the rate at which shopkeepers may get spawned and despawned, for instance, when players frequently cross chunk boundaries back and forth. When a player joins the server or teleports, we immediately activate the chunks in a 2 chunk radius.
- Added a spawn queue for shopkeepers spawned on chunk loads.
- Spawning lots of shopkeepers at the same time can be quite costly performance-wise. This queue distributes the spawning of shopkeepers over multiple ticks to avoid performance drops.
- We spawn at most 6 shopkeepers every 3 ticks (roughly 40 shopkeepers per second): These numbers are a balance between keeping the number of shopkeepers spawned per cycle low, while avoiding the general performance overhead associated with higher spawn rates.
- In order to avoid players having to wait for shopkeepers to spawn, there are some situations in which we spawn the shopkeepers immediately instead of adding them to the queue. This includes: When a shopkeeper is newly created, when a shopkeeper is loaded (i.e. on plugin reloads), and after world saves. In the latter two cases, a potentially large number of shopkeepers is expected to be spawned at the same time. Due to its limited throughput, the queue would not be able to deal with this sudden peak appropriately. However, since these are situations that are associated with a certain performance impact anyways, we prefer to spawn all the affected shopkeepers immediately, instead of causing confusion among players by having them wait for the shopkeepers to respawn.
- The check for whether the shopkeeper mob moved and needs to be teleported back reuses the previous spawn location now. Previously, this would freshly calculate the spawn location each time, which also involves a ray trace to find the exact location for the mob to stand on.
- This change also ensures that shopkeeper mobs no longer start to fall if gravity is disabled and the block below them is broken. However, to account for shopkeepers that have previously been placed above passable or non-full blocks, which might have been broken since then, we still spawn shopkeeper mobs up to one block below their location even if gravity is disabled (e.g. when the chunk or the plugin are reloaded).
- Another side effect of this change is that if gravity is disabled, it is no longer as easily possible to force the shopkeeper mob to move back to its original location after the block it was previously standing on is broken. The shopkeeper will only check for a new spawn location now when it is being respawned. If the mob is supposed to dynamically move when the block below it is broken, gravity needs to be enabled.
- This reuse of the spawn location is limited to the movement check itself. If this detects that the shopkeeper actually moved and needs to be teleported back, a new spawn location is calculated as before. So if the shopkeeper previously spawned slightly below its actual spawn location (due to there missing some block), and gravity is enabled, players are still able to reset the shopkeeper's location by letting it fall due to gravity and then placing a block below its actual spawn location for the shopkeeper to stand on.
- There have been various internal changes that allow us to avoid copying and comparing item stacks in many situations.
- Improvements related to checking and deleting shopkeepers of inactive players.
- Various other minor internal improvements.
- The "/shopkeeper remove" command has been renamed to "/shopkeeper removeAll". All related permission nodes and some related messages have changed. The 'all' argument, which removes the player shops of all players, has been renamed to 'player'.
- The "removeAll" command prints the number of shopkeepers now that have been skipped because they have either already been removed while the command was waiting for confirmation, or because their removal has been cancelled by a plugin.
- Added command "/shopkeeper remove [shop]". This replaces the previous "remove" command and allows the removal of a single specific shopkeeper.
- Added permission 'shopkeeper.remove.own' (default: op): Allows the removal of own shops via command.
- Added permission 'shopkeeper.remove.others' (default: op): Allows the removal of shops of other players via command.
- Added permission 'shopkeeper.remove.admin' (default: op): Allows the removal of admin shops via command.
- Improved the feedback messages when a player tries to create a shopkeeper via command.
- When you target a container, it is now also possible to create an admin shop via command by explicitly specifying the admin shop type as command argument. Previously, the plugin would always assume that you are trying to create a player shop in this situation.
- There have been a few minor fixes related to command arguments that should result in more detailed error messages when they cannot be parsed.
Changes related to Citizens shopkeepers:
- Fixed: Previously, if the location of a Citizens shopkeeper changed due to the NPC moving around, it was not guaranteed whether or not that location change would be persisted.
- Similar to non-Citizens shopkeepers, we also periodically respawn Citizens shopkeepers now if they have previously been spawned (i.e. if they have an associated entity) but their entity has been removed, for example by another plugin. If the NPC has intentionally been despawned, it is not automatically respawned.
- Changes to Citizens shopkeeper NPC names:
- NPCs are initially created with an empty name now. Only if the NPC is of type player, we subsequently assign it a name, because this determines its initial skin. If the shopkeeper is a player shop, we use the shop owner's name as before. Otherwise, we try to use the name of the player who created the shopkeeper. If this is not available, we fallback to an empty name.
- We no longer add the nameplate prefix if the NPC is of type player, because this messes with the NPC's skin.
- When we set the NPC's name, we now also set the nameplate visibility: If the name is empty, the nameplate is hidden. Otherwise, the 'always-show-nameplates' setting determines whether the nameplate is always shown, or whether it is only shown when a player directly looks at the entity (this only works for non-player NPCs though).
- Config: Added setting 'default-citizen-npc-type' (default: 'PLAYER') which controls the entity type used by newly created Citizens shopkeeper NPCs.
- Admin shopkeepers also use player NPCs by default now. Using villager NPCs for them has caused some confusion in the past.
- This also resolves an issue for offline mode servers: On recent versions of Citizens, the creation of player NPCs fails if they share the same name as regular players on the server, due to some conflict with their UUIDs being the same.
- We no longer automatically delete invalid Citizens shopkeepers by default.
- This has caused some issue in the past due to Citizens shopkeepers being incorrectly classified as invalid in some situations. By default, we only log warnings now about any detected invalid Citizens shopkeepers, and instructions on how these shopkeepers can be deleted in order to get rid of these warnings.
- Config: Added setting 'delete-invalid-citizen-shopkeepers' (default: false). This setting can be used to enable the previous behavior of automatically deleting invalid Citizens shopkeepers again.
- Alternatively, the new command "/shopkeepers cleanupCitizenShopkeepers" can be used to manually check for and delete invalid Citizens shopkeepers. This command requires the new permission 'shopkeeper.cleanup-citizen-shopkeepers' (default: op).
- If a NPC is deleted and there are multiple shopkeepers associated with it, all of these shopkeepers are deleted now.
- There have been several other internal refactors related to Citizens shopkeepers. Check the complete changelog for details.
- Added command "/shopkeeper testDamage [damage] [times-per-tick] [duration-ticks]", which can be used to debug the performance of handling lots of damage events.
- Added debug command "/shopkeeper testSpawn [repetitions]", which measures the time it takes to respawn the active shopkeepers within the current chunk.
- Minor improvements and additions to the "/shopkeeper checkitem" command.
- Various small fixes and additions related to the "/shopkeeper check" command. For example, this also shows chunk activation timings now, and statistics on the current and maximum number of pending shopkeeper spawns. We also no longer reset the shopkeeper mob AI and gravity statistics when there are no more active entities.
- The command "/shopkeeper debug [option]" can toggle debug options now.
- It should be easier now to identify tasks inside timing reports, because they now use named classes instead of lambdas.
- Added debug option 'visualize-shopkeeper-ticks', which enables an in-game visualization of shopkeeper and shop object ticking activities by using particles.
- Added debug option 'regular-tick-activities'. The debug output for a few non-exceptional ticking activities, which could sometimes be considered spam if one is not specifically interested in them, such as shopkeepers teleporting back into place, or mobile Citizens shopkeepers updating their shopkeeper's location, are disabled by default now. This new debug option enables them again.
- Added debug information about the number of shopkeepers that actually had to be spawned or despawned during the spawning or despawning of a chunk's shopkeepers.
- When the result item of a trade does not match the expected result item, or when a strict item comparison fails, we additionally log the serialized Yaml representation of the involved items now.
- The output of the "/shopkeeper yaml" command is logged as a single multi-line message now. This should make it easier to copy it from the server log and paste it into the config (depends on the logging configuration though). Also, the output includes the ItemStack's serialized type key now, and the used keys are more context specific.
- We log more information now when we debug failed command executions.
- When the shopkeeper editor is closed, we log the number of shopkeeper offers that have changed.
- Players in creative mode are no longer ignored when they use the shop creation item.
- Fixed: The shop creation item can no longer be renamed in anvils if the 'prevent-shop-creation-item-regular-usage' setting is enabled.
- Changes to the maximum shops limit:
- Config: A value of '0' for the 'max-shops-per-player' setting no longer indicates no limit, but can be used to disable the creation and hiring of player shops. 'No limit' is indicated by a value of '-1' now. Any previous limit of '0' is automatically migrated.
- Permission: Added permission 'shopkeeper.maxshops.unlimited' (default: op), which disables the maximum shops limit for a player.
- Minor performance improvements related to how the maximum shops limit permissions, that are specified inside the config, are checked. An effect of this optimization is that it is only possible to increase the default limit for players, not decrease it.
- When the plugin runs on an unsupported server version, we no longer add a movement speed attribute modifier to mobs in order to make them stationary. This should no longer be required, because on all recent and supported server versions we use the NoAI flag to make the mobs stationary.
- There have been some changes to how shopkeepers and shop objects are ticked. This also replaces the previous task that periodically checked whether the shop object is still active or needs to be respawned. The setting 'enable-spawn-verifier' has been removed: The plugin always periodically checks now if the mobs are still there or if they need to be respawned, regardless of this setting.
- If a shop object cannot be respawned a few times in a row, the rate at which it reattempts the spawning is throttled now. Previously, it would permanently abort all respawn attempts in this case.
- When a shopkeeper is marked as dirty during ticking, we trigger a delayed save instead of an immediate save now.
- Fixed: Previously, after the Shopkeepers plugin has been dynamically reloaded, GriefPrevention prevented players from using the shop creation item or interact with shopkeepers in protected regions. This issue has been resolved by dynamically reordering the registered event handlers so that our event handlers are always executed first.
- We only print the shop creation item usage message if the player is still holding the item after a short delay now. This avoids message spam when a player quickly scrolls through the items on the hotbar via the mouse wheel.
- Fixed: We sometimes receive an additional left-click air interaction event when a player right-clicks air while looking at a block slightly outside of the interaction range. This led to the shop creation item switching between two selections back and forth for a single right-click. We ignore this additional left-click air interaction now by ignoring any subsequent interactions that occur within a brief time span of the last handled interaction. The ignored interactions are still cancelled to prevent any vanilla behaviors or side-effects of other event handlers, but they no longer change the player's selection.
- Fixed: Closing the shopkeeper editor did not immediately trigger a save, but relied on the periodic shopkeeper tick task to trigger a save. Also, we check now which trades have actually changed and only trigger a save if the shopkeeper was actually modified.
- Added an editor option to rename regular villagers. Unlike the renaming via nametags, this allows the use of color codes.
- Added an editor option to toggle the pumpkin head of snowman shopkeepers.
- Added an editor option to change the color of shulker shopkeepers.
- Added an editor option to toggle the invulnerability of regular non-shopkeeper villagers.
- Added sounds to the various buttons inside the editor menu.
- Players have to confirm now when they delete a shopkeeper via the editor. This also applies to the deletion of the villager via the editor for regular villagers.
- We also send the 'shop-removed' message now when a shopkeeper is deleted via the editor menu. Similarly, deleting a regular villager via the villager editor will also print a message now.
- When using the villager editor, we check more frequently now whether the villager still exist, and close the editor if it does not.
- Fixed: Striders, magma cubes, and blazes are now able to stand on top of lava. This only applies if they are not completely submerged by lava (in which case they don't float to the top as they do in vanilla Minecraft, but sink to the ground as before).
- When creating a shopkeeper via command, it is now possible to place it on top of liquids by targeting a water or lava block. If the player is under water (or inside of lava), the shopkeeper is placed at the targeted block as before. Since we spawn shopkeepers up to one block below their location, this still allows placing shopkeepers on the ground in shallow liquids, without them being continuously teleported back if they are not able to stand on top of the liquid.
- Buying shopkeepers can also buy written books and enchanted items now. Being able to buy written books is probably not really useful though, because any book item being bought has to nearly perfectly match the requested book item.
- Config: Added setting 'disable-inventory-verification' (default: false). Even though modded servers (Cauldron, Mohist, etc.) are not officially supported, this setting may help to resolve a particular known incompatibility with these types of servers.
- When the deletion of shopkeepers of inactive players is enabled, we not only check for inactive players during plugin startup, but also periodically now (roughly every 4 hours). This accounts for servers that keep running for long durations. We also log a message now whenever we check for shopkeepers of inactive players.
- Fixed: Editing and removing trades from a shopkeeper while another player is trading with it could result in an exception due to an issue with the insertion of empty dummy trades. These empty dummy trades are required because the Minecraft client cannot deal with the list of trades dynamically shrinking in size. However, since the insertion of empty dummy trades is rather confusing for players, we also try now to heuristically guess the trades that were removed and then instead insert blocked dummy trades that correspond to these trades.
- Fixed: Forge clients seem to send additional off-hand interactions when interacting with villagers. This breaks our villager editor, because it immediately closes the villager editor again and instead opens the regular villager trading interface. In an attempt to resolve this incompatibility, we now cancel all off-hand interactions with regular villagers if the player already has some inventory open.
- Fixed: Shopkeeper entities are now marked as invulnerable, so that other entities ignore them in various additional situations. For example, villagers are no longer panicked by nearby hostile mob shopkeepers.
- Added more player feedback messages for cases in which the trading may fail for some reason.
- Fixed: When handling a shopkeeper hire attempt, we first check now if the shopkeeper is still for hire.
- Fixed: Piglin brute mobs do not support a baby variant. Their shopkeeper type will therefore also no longer show the baby option in the editor.
- Fixed: Book shops logged warnings when they were loaded.
- Fixed: The shopkeeper metadata is also removed from an entity again now if the spawning failed.
- Metrics: The shopkeepers count chart groups its results into slightly more detailed categories now.
- Internal: Building requires Java 16 now. Building the Spigot dependencies requires both Java 8 and Java 16. The 'installSpigotDependencies' script will automatically install and switch between these JDK versions during the build.
- There have been major refactors related to how the config and language files are loaded.
- There have been various other, mostly internal changes that are not explicitly listed here. See the complete changelog if you are interested in these details.
Other config changes:
- The settings 'edit-regular-villagers' and 'edit-regular-wandering-traders' are now disabled by default. This feature seems to cause confusion for people who are not aware of it. Regular villagers can still be edited by default via the "editVillager" command.
- Added piglin brute to the by default enabled mob types.
- Added shulker to the by default enabled mob types. However, note that this mob will currently stay in its closed form due to its disabled AI.
- The default enabled shopkeeper mobs are now alphabetically sorted. This only applies to newly generated default configs.
- Fixed: The living shop object types were not registered in the order specified inside the config.
- The 'enabled-living-shops' setting is slightly more lenient now when parsing the specified mob types. Previously, the mob type names had to perfectly match the server's entity type names.
- Fixed: If the material of the configured shop creation item was invalid, the used default shop creation item used a display name with untranslated color codes.
- Fixed: Specifying meta data for items of type AIR (which do not support meta data) no longer results in an exception.
- Updated the description of some settings.
- Improved the validation and error feedback of some settings. For example, legacy item types, and item types that are not actually items, can no longer be specified inside the config. We log a warning when we encounter such an item type.
- There have been several API changes, including breaking API changes. See the complete changelog for details.
- The most notable breaking change is the introduction of 'UnmodifiableItemStack'. All methods that previously returned copies of internal item stacks will now return unmodifiable item stacks.
- Some methods and interfaces have been renamed. For example, 'TradingOffer' is now called 'TradeOffer'.
- Added factory methods to the TradeOffer, PriceOffer, and BookOffer interfaces. These are now preferred over the factory methods found in ShopkeepersAPI and ShopkeepersPlugin. The latter have been deprecated and may be removed in the future.
- PlayerCreatePlayerShopkeeperEvent and PlayerShopkeeperHireEvent: The meaning of the maximum shops limit has changed. A value of 0 or less no longer indicates 'no limit'.
- Fixed: Event handlers for the specific PlayerCreateShopkeeperEvent were not actually getting invoked.
- There have been some changes related to object ids. Object ids are no longer exposed in the API.
- There have been some changes around the meaning of 'active shopkeepers' and 'active shop objects', and the behavior of related methods. Also, several ShopObject methods were slightly optimized to no longer check if the shop object is currently active, but only if it has been spawned, which is sufficient most of the time. ShopObject#isSpawned has been added as a more lightweight variant of ShopObject#isActive.
- Various aspects and methods related to shop object spawning are no longer exposed in the API.
- We skip spawning and activating shopkeepers now if they are immediately removed again during the ShopkeeperAddEvent.
- Removed ShopkeeperRegistry#loadShopkeeper from the API. This isn't properly supported by the implementation currently.
- Removed Shopkeeper#isDirty() from the API.
- Added ShopkeeperStorage#saveIfDirty and #saveIfDirtyAndAwaitCompletion.
- Fixed: We now call the ShopkeeperEditedEvent when the player closes the editor and at least one trade offer has changed.
- The blocks of sign shopkeepers are also marked with the shopkeeper metadata now.
- Sign shop objects no longer return a name. We previously returned the sign's second line. However, this isn't really useful anymore because the exact sign contents are configurable now.
- CitizensShopObject#getName returns the NPC name instead of its 'full name' now (i.e. the name that more closely corresponds to the name that has been set via #setName).
- Added methods to BlockShopObjectType and EntityShopObjectType to query and check for shopkeepers of that specific type. This is less performance intensive compared to checking all shop object types when querying the ShopkeeperRegistry.
- There have been a few other small API additions, as well as several Javadoc improvements and clarifications. Check the complete changelog and the API documentation for details.
- Internal API: There have been several internal changes that may affect you if you rely on the internal API or implement custom shopkeeper types. See the complete changlog for details.
Known potential issues: See here.
If you like this plugin, consider making a donation.