Enchantment scripts back-support #2738
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Welp.
Due to the large amount of changes, and a lot of the changes being intertwined with one another, I wasn't really able to split this into commits, so I'll just try and write an explanation for every part.
Note
Need to figure out how to deprecate
EnchantmentScriptContainer's meta before merge.Overall
Previously enchantments were created using an anonymous sub-class, which is not possible anymore due to them becoming
records.The new approach is just using the enchantment constructor normally, and attempting to wrangle the parameters into being the best fit for how stuff used to work.
This also means that some of the stuff that could previously hot-reload can't anymore, but I did my best to make sure that at least specifying dynamic values with tags works for the most part (and realistically production servers shouldn't be hot-reloading enchantment scripts too much anyway)
For 1.20, an anonymous sub-class is still possible, but some methods became
final& got similarly replaced with constructor usage.dist/pom.xmlslotskey1.21: Was replaced by
EquipmentSlotGroupinternally, and is now a list instead of an array; overall similar.raritykeyWas replaced by weight and anvil cost values internally, so
EnchantmentHelper$Raritywas created to back-support - it contains the old weight and anvil cost values for each rarity, and gets rarities from weights by matching the closest one.categorykeyReplaced with
HolderSet<Item> primaryItems(TagKeyon 1.20) internally, and back-supported with anEnchantmentHelperImpl$EnchantmentCategoryenum that holds the relevant item tag keys.full_namekey1.21: The only option is passing in a complete component now (which gets the enchantment level appended to it), best effort back-support by removing
<context.level>from the raw string (which is usually at then end of the string, where the level gets added internally) and parsing it once at load time.min_levelkeyJust not a thing you can set anymore, but does default to
1which is what most scripts probably had anyway.max_levelkeyNo problems, gets passed in as usual.
min_cost,max_costkeysCosts can't be dynamic anymore, and only offer the option of a constant cost or a base cost with a certain amount added for each level above first.
Back-supported with
EnchantmentHelperImpl#tryParseCost, which either:<context.level.mul[X]>and return a dynamic cost.treasure_only,is_curse,is_tradable,is_discoverablekeys1.21: Now managed by enchantment tags, which get set in
DenizenEnchantmentFixusing Paper's tag editing API.can_enchantkey1.21: Is based on item type now, which means slightly regression - otherwise fully back-supported via
EnchantmentHelperImpl$DynamicHolderSet, aHolderSetimplementation that contains everything from a certain registry, filtered by a predicate.1.20: Can still override the
canEnchantmethod, but now passes in an invalid item tag (just random letters), as enchantments must also have one of these for some internal checks.damage_bonus,damage_protectionkeys1.21: No longer their own thing, and are now an "Enchantment effect" internally; back-supported via
EnchantmentHelperImpl$DynamicContextAwareLevelValue, which acts both as the condition that checks the entity the effect is applying to (just returnstrueand stores it) and as the function that gets the effect based on the level, which lets it access the stored entity for the calculation.Both: The entity type part is also no longer a thing, back-supported with
EnchantmentHelperImpl$MobTypewhich stores the relevant entity tag for each type and finds the current type for an entity type based on that.after attack,after hurtkeys1.21: Can't really inject into that handling anymore, so back-supported with events in
EnchantmentHelper$EnchantmentBackSupportEvents.Also reordered params on
EnchantmentSciprtContainer#doPostHurt, for easier lambda usage.Other
RegistrationInfothat simulates being a vanilla enchantment, to avoid the server trying to serialize our custom enchantments.ClientboundRegistryDataPackets; since these are sent during the configuration mode, had to take a different approach for the packet interception -EnchntmentHelperImpl$EnchantmentExcludingChannelHandleris a nettyChannelOutboundHandlerAdapterthat registers itself with Paper's internalChannelInitializeListenersystem, so that it exists early enough.EnchantmentScriptContainergot#getKeyand#getScriptFromEnchantmentadded, andregisteredEnchantmentContainerschanged due to it being accessed async now (packet code in several places), I kinda just slappedConcurrentHashMapon it, but lmk if there's a different approach you'd prefer.EnchantmentHelper$EnchantmentBackSupportEventsalso has events & logic to callPlayer#updateInventoryfor stuff like grindstones/anvils, as the client seems to pre-calculate it and display no result otherwise.Depends#setupEnchantmentFixis now a thing.DenizenEnchantmentFixregistering dud enchantments on startup, andEnchantmentHelperImpl#replaceRegistryValuenow being a thing.EnchantmentHelper#getDamageBonus/getDamageProtection- the logic for that internally is now requires a full entity instance instead of a mob type and such, so it has best effort back-support via mocking an entity instance based on getting aMobType, choosing a random entity type from it's tag, and mocking an entity instance from that.Note
A lot of the new reflection added here doesn't use
ReflectionMappingsInfo, as this is Paper only either way & Paper runs on Mojang mappings now - this is just in the 1.21 impl.Note
I did add a
enchantmentScriptContainersdeprecation warning, but not really sure how would a deprecated script container meta look - should I still remove the description like tags/mechs?