aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-API-Patches-Unmapped
diff options
context:
space:
mode:
authorKyle Wood <[email protected]>2021-04-24 17:01:33 -0500
committerKyle Wood <[email protected]>2021-04-25 18:37:43 -0500
commit3093b81fee3064603c368ab934eddf66ce304433 (patch)
treecb99f05b5f31de92c41af4cc40b4bef5f3cbf573 /Spigot-API-Patches-Unmapped
parent1af696a05d21cbdd7b5a7170f95598c013257588 (diff)
downloadPaper-3093b81fee3064603c368ab934eddf66ce304433.tar.gz
Paper-3093b81fee3064603c368ab934eddf66ce304433.zip
Move patches
Diffstat (limited to 'Spigot-API-Patches-Unmapped')
-rw-r--r--Spigot-API-Patches-Unmapped/0001-POM-changes.patch106
-rw-r--r--Spigot-API-Patches-Unmapped/0002-Add-FastUtil-to-Bukkit.patch24
-rw-r--r--Spigot-API-Patches-Unmapped/0003-Paper-Utils.patch28
-rw-r--r--Spigot-API-Patches-Unmapped/0004-Timings-v2.patch3759
-rw-r--r--Spigot-API-Patches-Unmapped/0005-Adventure.patch3734
-rw-r--r--Spigot-API-Patches-Unmapped/0006-Player-affects-spawning-API.patch33
-rw-r--r--Spigot-API-Patches-Unmapped/0007-Add-getTPS-method.patch49
-rw-r--r--Spigot-API-Patches-Unmapped/0008-Entity-Origin-API.patch66
-rw-r--r--Spigot-API-Patches-Unmapped/0009-Version-Command-2.0.patch185
-rw-r--r--Spigot-API-Patches-Unmapped/0010-Add-PlayerLocaleChangeEvent.patch62
-rw-r--r--Spigot-API-Patches-Unmapped/0011-Add-player-view-distance-API.patch39
-rw-r--r--Spigot-API-Patches-Unmapped/0012-Add-BeaconEffectEvent.patch98
-rw-r--r--Spigot-API-Patches-Unmapped/0013-Add-PlayerInitialSpawnEvent.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0014-Automatically-disable-plugins-that-fail-to-load.patch21
-rw-r--r--Spigot-API-Patches-Unmapped/0015-Expose-server-CommandMap.patch50
-rw-r--r--Spigot-API-Patches-Unmapped/0016-Graduate-bungeecord-chat-API-from-spigot-subclasses.patch124
-rw-r--r--Spigot-API-Patches-Unmapped/0017-Player-Tab-List-and-Title-APIs.patch577
-rw-r--r--Spigot-API-Patches-Unmapped/0018-Add-exception-reporting-event.patch604
-rw-r--r--Spigot-API-Patches-Unmapped/0019-Fix-ServerListPingEvent-flagging-as-Async.patch27
-rw-r--r--Spigot-API-Patches-Unmapped/0020-Add-BaseComponent-sendMessage-methods-to-CommandSend.patch54
-rw-r--r--Spigot-API-Patches-Unmapped/0021-Add-methods-for-working-with-arrows-stuck-in-living-.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0022-Complete-resource-pack-API.patch118
-rw-r--r--Spigot-API-Patches-Unmapped/0023-Use-ASM-for-event-executors.patch398
-rw-r--r--Spigot-API-Patches-Unmapped/0024-Add-a-call-helper-to-Event.patch34
-rw-r--r--Spigot-API-Patches-Unmapped/0025-Add-sender-name-to-commands.yml-replacement.patch43
-rw-r--r--Spigot-API-Patches-Unmapped/0026-Add-command-to-reload-permissions.yml-and-require-co.patch104
-rw-r--r--Spigot-API-Patches-Unmapped/0027-Custom-replacement-for-eaten-items.patch48
-rw-r--r--Spigot-API-Patches-Unmapped/0028-Entity-AddTo-RemoveFrom-World-Events.patch79
-rw-r--r--Spigot-API-Patches-Unmapped/0029-EntityPathfindEvent.patch95
-rw-r--r--Spigot-API-Patches-Unmapped/0030-Reduce-thread-synchronization-in-MetadataStoreBase.patch90
-rw-r--r--Spigot-API-Patches-Unmapped/0031-Add-MetadataStoreBase.removeAll-Plugin.patch46
-rw-r--r--Spigot-API-Patches-Unmapped/0032-Add-PlayerUseUnknownEntityEvent.patch58
-rw-r--r--Spigot-API-Patches-Unmapped/0033-Add-handshake-event-to-allow-plugins-to-handle-clien.patch290
-rw-r--r--Spigot-API-Patches-Unmapped/0034-Arrow-pickup-rule-API.patch49
-rw-r--r--Spigot-API-Patches-Unmapped/0035-EntityRegainHealthEvent-isFastRegen-API.patch42
-rw-r--r--Spigot-API-Patches-Unmapped/0036-LootTable-API.patch402
-rw-r--r--Spigot-API-Patches-Unmapped/0037-Add-EntityZapEvent.patch123
-rw-r--r--Spigot-API-Patches-Unmapped/0038-Misc-Utils.patch46
-rw-r--r--Spigot-API-Patches-Unmapped/0039-Allow-Reloading-of-Command-Aliases.patch108
-rw-r--r--Spigot-API-Patches-Unmapped/0040-Add-source-to-PlayerExpChangeEvent.patch54
-rw-r--r--Spigot-API-Patches-Unmapped/0041-Add-ProjectileCollideEvent.patch79
-rw-r--r--Spigot-API-Patches-Unmapped/0042-Add-String-based-Action-Bar-API.patch86
-rw-r--r--Spigot-API-Patches-Unmapped/0043-Add-API-methods-to-control-if-armour-stands-can-move.patch32
-rw-r--r--Spigot-API-Patches-Unmapped/0044-IllegalPacketEvent.patch89
-rw-r--r--Spigot-API-Patches-Unmapped/0045-Fireworks-API-s.patch28
-rw-r--r--Spigot-API-Patches-Unmapped/0046-PlayerTeleportEndGatewayEvent.patch42
-rw-r--r--Spigot-API-Patches-Unmapped/0047-Provide-E-TE-Chunk-count-stat-methods.patch46
-rw-r--r--Spigot-API-Patches-Unmapped/0048-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch122
-rw-r--r--Spigot-API-Patches-Unmapped/0049-Expose-WorldBorder-isInBounds-Location-check.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0050-Add-configuration-option-to-prevent-player-names-fro.patch60
-rw-r--r--Spigot-API-Patches-Unmapped/0051-Fix-upstream-javadoc-warnings-and-errors.patch46
-rw-r--r--Spigot-API-Patches-Unmapped/0052-Item-canEntityPickup.patch31
-rw-r--r--Spigot-API-Patches-Unmapped/0053-PlayerPickupItemEvent-setFlyAtPlayer.patch54
-rw-r--r--Spigot-API-Patches-Unmapped/0054-PlayerAttemptPickupItemEvent.patch102
-rw-r--r--Spigot-API-Patches-Unmapped/0055-Add-UnknownCommandEvent.patch126
-rw-r--r--Spigot-API-Patches-Unmapped/0056-Basic-PlayerProfile-API.patch351
-rw-r--r--Spigot-API-Patches-Unmapped/0057-Shoulder-Entities-Release-API.patch37
-rw-r--r--Spigot-API-Patches-Unmapped/0058-Profile-Lookup-Events.patch174
-rw-r--r--Spigot-API-Patches-Unmapped/0059-Entity-fromMobSpawner.patch23
-rw-r--r--Spigot-API-Patches-Unmapped/0060-Improve-the-Saddle-API-for-Horses.patch83
-rw-r--r--Spigot-API-Patches-Unmapped/0061-ensureServerConversions-API.patch62
-rw-r--r--Spigot-API-Patches-Unmapped/0062-Add-getI18NDisplayName-API.patch52
-rw-r--r--Spigot-API-Patches-Unmapped/0063-ProfileWhitelistVerifyEvent.patch157
-rw-r--r--Spigot-API-Patches-Unmapped/0064-Make-plugins-list-alphabetical.patch56
-rw-r--r--Spigot-API-Patches-Unmapped/0065-LivingEntity-setKiller.patch26
-rw-r--r--Spigot-API-Patches-Unmapped/0066-Handle-plugin-prefixes-in-implementation-logging-con.patch41
-rw-r--r--Spigot-API-Patches-Unmapped/0067-Allow-plugins-to-use-SLF4J-for-logging.patch51
-rw-r--r--Spigot-API-Patches-Unmapped/0068-Add-workaround-for-plugins-modifying-the-parent-of-t.patch117
-rw-r--r--Spigot-API-Patches-Unmapped/0069-Add-PlayerJumpEvent.patch118
-rw-r--r--Spigot-API-Patches-Unmapped/0070-Expose-client-protocol-version-and-virtual-host.patch71
-rw-r--r--Spigot-API-Patches-Unmapped/0071-Add-PlayerArmorChangeEvent.patch149
-rw-r--r--Spigot-API-Patches-Unmapped/0072-API-to-get-a-BlockState-without-a-snapshot.patch31
-rw-r--r--Spigot-API-Patches-Unmapped/0073-AsyncTabCompleteEvent.patch266
-rw-r--r--Spigot-API-Patches-Unmapped/0074-Display-warning-on-deprecated-recipe-API.patch35
-rw-r--r--Spigot-API-Patches-Unmapped/0075-PlayerPickupExperienceEvent.patch93
-rw-r--r--Spigot-API-Patches-Unmapped/0076-ExperienceOrbMergeEvent.patch102
-rw-r--r--Spigot-API-Patches-Unmapped/0077-Ability-to-apply-mending-to-XP-API.patch50
-rw-r--r--Spigot-API-Patches-Unmapped/0078-PreCreatureSpawnEvent.patch127
-rw-r--r--Spigot-API-Patches-Unmapped/0079-PlayerNaturallySpawnCreaturesEvent.patch80
-rw-r--r--Spigot-API-Patches-Unmapped/0080-Add-setPlayerProfile-API-for-Skulls.patch78
-rw-r--r--Spigot-API-Patches-Unmapped/0081-Fill-Profile-Property-Events.patch176
-rw-r--r--Spigot-API-Patches-Unmapped/0082-PlayerAdvancementCriterionGrantEvent.patch75
-rw-r--r--Spigot-API-Patches-Unmapped/0083-Add-ArmorStand-Item-Meta.patch96
-rw-r--r--Spigot-API-Patches-Unmapped/0084-Optimize-Hoppers.patch39
-rw-r--r--Spigot-API-Patches-Unmapped/0085-Tameable-getOwnerUniqueId-API.patch42
-rw-r--r--Spigot-API-Patches-Unmapped/0086-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch91
-rw-r--r--Spigot-API-Patches-Unmapped/0087-Add-extended-PaperServerListPingEvent.patch381
-rw-r--r--Spigot-API-Patches-Unmapped/0088-Player.setPlayerProfile-API.patch40
-rw-r--r--Spigot-API-Patches-Unmapped/0089-getPlayerUniqueId-API.patch58
-rw-r--r--Spigot-API-Patches-Unmapped/0090-Add-legacy-ping-support-to-PaperServerListPingEvent.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0091-Add-method-to-open-already-placed-sign.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0092-Add-Ban-Methods-to-Player-Objects.patch253
-rw-r--r--Spigot-API-Patches-Unmapped/0093-EndermanEscapeEvent.patch102
-rw-r--r--Spigot-API-Patches-Unmapped/0094-Enderman.teleportRandomly.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0095-Additional-world.getNearbyEntities-API-s.patch292
-rw-r--r--Spigot-API-Patches-Unmapped/0096-Location.isChunkLoaded-API.patch18
-rw-r--r--Spigot-API-Patches-Unmapped/0097-Expand-World.spawnParticle-API-and-add-Builder.patch579
-rw-r--r--Spigot-API-Patches-Unmapped/0098-EndermanAttackPlayerEvent.patch116
-rw-r--r--Spigot-API-Patches-Unmapped/0099-Close-Plugin-Class-Loaders-on-Disable.patch141
-rw-r--r--Spigot-API-Patches-Unmapped/0100-WitchConsumePotionEvent.patch122
-rw-r--r--Spigot-API-Patches-Unmapped/0101-WitchThrowPotionEvent.patch93
-rw-r--r--Spigot-API-Patches-Unmapped/0102-Location.toBlockLocation-toCenterLocation.patch43
-rw-r--r--Spigot-API-Patches-Unmapped/0103-PotionEffect-clone-methods.patch44
-rw-r--r--Spigot-API-Patches-Unmapped/0104-WitchReadyPotionEvent.patch93
-rw-r--r--Spigot-API-Patches-Unmapped/0105-ItemStack-getMaxItemUseDuration.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0106-Add-EntityTeleportEndGatewayEvent.patch43
-rw-r--r--Spigot-API-Patches-Unmapped/0107-Make-shield-blocking-delay-configurable.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0108-EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch44
-rw-r--r--Spigot-API-Patches-Unmapped/0109-Add-getNearbyXXX-methods-to-Location.patch275
-rw-r--r--Spigot-API-Patches-Unmapped/0110-PlayerReadyArrowEvent.patch107
-rw-r--r--Spigot-API-Patches-Unmapped/0111-Add-EntityKnockbackByEntityEvent.patch94
-rw-r--r--Spigot-API-Patches-Unmapped/0112-Expand-Explosions-API.patch200
-rw-r--r--Spigot-API-Patches-Unmapped/0113-ItemStack-API-additions-for-quantity-flags-lore.patch206
-rw-r--r--Spigot-API-Patches-Unmapped/0114-LivingEntity-Hand-Raised-Item-Use-API.patch52
-rw-r--r--Spigot-API-Patches-Unmapped/0115-RangedEntity-API.patch192
-rw-r--r--Spigot-API-Patches-Unmapped/0116-Add-World.getEntity-UUID-API.patch28
-rw-r--r--Spigot-API-Patches-Unmapped/0117-InventoryCloseEvent-Reason-API.patch93
-rw-r--r--Spigot-API-Patches-Unmapped/0118-Entity-getChunk-API.patch33
-rw-r--r--Spigot-API-Patches-Unmapped/0119-Add-an-asterisk-to-legacy-API-plugins.patch66
-rw-r--r--Spigot-API-Patches-Unmapped/0120-EnderDragon-Events.patch225
-rw-r--r--Spigot-API-Patches-Unmapped/0121-PlayerLaunchProjectileEvent.patch95
-rw-r--r--Spigot-API-Patches-Unmapped/0122-PlayerElytraBoostEvent.patch97
-rw-r--r--Spigot-API-Patches-Unmapped/0123-EntityTransformedEvent.patch104
-rw-r--r--Spigot-API-Patches-Unmapped/0124-Allow-disabling-armour-stand-ticking.patch32
-rw-r--r--Spigot-API-Patches-Unmapped/0125-SkeletonHorse-Additions.patch92
-rw-r--r--Spigot-API-Patches-Unmapped/0126-Expand-Location-Manipulation-API.patch66
-rw-r--r--Spigot-API-Patches-Unmapped/0127-Expand-ArmorStand-API.patch103
-rw-r--r--Spigot-API-Patches-Unmapped/0128-AnvilDamageEvent.patch160
-rw-r--r--Spigot-API-Patches-Unmapped/0129-Remove-deadlock-risk-in-firing-async-events.patch137
-rw-r--r--Spigot-API-Patches-Unmapped/0130-Add-hand-to-bucket-events.patch130
-rw-r--r--Spigot-API-Patches-Unmapped/0131-Add-TNTPrimeEvent.patch126
-rw-r--r--Spigot-API-Patches-Unmapped/0132-Provide-Chunk-Coordinates-as-a-Long-API.patch72
-rw-r--r--Spigot-API-Patches-Unmapped/0133-Async-Chunks-API.patch534
-rw-r--r--Spigot-API-Patches-Unmapped/0134-Make-EnderDragon-extend-Mob.patch19
-rw-r--r--Spigot-API-Patches-Unmapped/0135-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch57
-rw-r--r--Spigot-API-Patches-Unmapped/0136-Don-t-use-snapshots-for-Timings-Tile-Entity-reports.patch19
-rw-r--r--Spigot-API-Patches-Unmapped/0137-Allow-Blocks-to-be-accessed-via-a-long-key.patch169
-rw-r--r--Spigot-API-Patches-Unmapped/0138-Slime-Pathfinder-Events.patch217
-rw-r--r--Spigot-API-Patches-Unmapped/0139-isChunkGenerated-API.patch57
-rw-r--r--Spigot-API-Patches-Unmapped/0140-Add-More-Creeper-API.patch91
-rw-r--r--Spigot-API-Patches-Unmapped/0141-Add-PhantomPreSpawnEvent.patch71
-rw-r--r--Spigot-API-Patches-Unmapped/0142-Add-source-block-to-BlockPhysicsEvent.patch24
-rw-r--r--Spigot-API-Patches-Unmapped/0143-Inventory-removeItemAnySlot.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0144-Add-ray-tracing-methods-to-LivingEntity.patch148
-rw-r--r--Spigot-API-Patches-Unmapped/0145-Improve-death-events.patch181
-rw-r--r--Spigot-API-Patches-Unmapped/0146-Mob-Pathfinding-API.patch259
-rw-r--r--Spigot-API-Patches-Unmapped/0147-Expose-attack-cooldown-methods-for-Player.patch37
-rw-r--r--Spigot-API-Patches-Unmapped/0148-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch319
-rw-r--r--Spigot-API-Patches-Unmapped/0149-Performance-Concurrency-Improvements-to-Permissions.patch113
-rw-r--r--Spigot-API-Patches-Unmapped/0150-Add-ItemStackRecipeChoice-Draft-API.patch66
-rw-r--r--Spigot-API-Patches-Unmapped/0151-Implement-furnace-cook-speed-multiplier-API.patch38
-rw-r--r--Spigot-API-Patches-Unmapped/0152-PreSpawnerSpawnEvent.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0153-Material-API-additions.patch41
-rw-r--r--Spigot-API-Patches-Unmapped/0154-Add-Material-Tags.patch1044
-rw-r--r--Spigot-API-Patches-Unmapped/0155-Allow-setting-the-vex-s-summoner.patch40
-rw-r--r--Spigot-API-Patches-Unmapped/0156-Add-LivingEntity-getTargetEntity.patch105
-rw-r--r--Spigot-API-Patches-Unmapped/0157-Add-sun-related-API.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0158-Here-s-Johnny.patch42
-rw-r--r--Spigot-API-Patches-Unmapped/0159-Turtle-API.patch283
-rw-r--r--Spigot-API-Patches-Unmapped/0160-Add-spectator-target-events.patch141
-rw-r--r--Spigot-API-Patches-Unmapped/0161-Add-more-Witch-API.patch54
-rw-r--r--Spigot-API-Patches-Unmapped/0162-Make-the-default-permission-message-configurable.patch57
-rw-r--r--Spigot-API-Patches-Unmapped/0163-Support-cancellation-supression-of-EntityDismount-Ve.patch109
-rw-r--r--Spigot-API-Patches-Unmapped/0164-Add-more-Zombie-API.patch66
-rw-r--r--Spigot-API-Patches-Unmapped/0165-Change-the-reserved-channel-check-to-be-sensible.patch34
-rw-r--r--Spigot-API-Patches-Unmapped/0166-Add-PlayerConnectionCloseEvent.patch136
-rw-r--r--Spigot-API-Patches-Unmapped/0167-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch62
-rw-r--r--Spigot-API-Patches-Unmapped/0168-BlockDestroyEvent.patch110
-rw-r--r--Spigot-API-Patches-Unmapped/0169-Add-ItemStack-Recipe-API-helper-methods.patch70
-rw-r--r--Spigot-API-Patches-Unmapped/0170-Add-WhitelistToggleEvent.patch52
-rw-r--r--Spigot-API-Patches-Unmapped/0171-Annotation-Test-changes.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0172-Entity-getEntitySpawnReason.patch27
-rw-r--r--Spigot-API-Patches-Unmapped/0173-Add-GS4-Query-event.patch424
-rw-r--r--Spigot-API-Patches-Unmapped/0174-Add-PlayerPostRespawnEvent.patch64
-rw-r--r--Spigot-API-Patches-Unmapped/0175-Ignore-package-private-methods-for-nullability-annot.patch20
-rw-r--r--Spigot-API-Patches-Unmapped/0176-Flip-some-Spigot-API-null-annotations.patch127
-rw-r--r--Spigot-API-Patches-Unmapped/0177-Server-Tick-Events.patch110
-rw-r--r--Spigot-API-Patches-Unmapped/0178-PlayerDeathEvent-getItemsToKeep.patch63
-rw-r--r--Spigot-API-Patches-Unmapped/0179-Add-Heightmap-API.patch196
-rw-r--r--Spigot-API-Patches-Unmapped/0180-Mob-Spawner-API-Enhancements.patch41
-rw-r--r--Spigot-API-Patches-Unmapped/0181-Add-BlockSoundGroup-interface.patch94
-rw-r--r--Spigot-API-Patches-Unmapped/0182-Amend-PlayerInteractAtEntityEvent-javadoc-for-ArmorS.patch20
-rw-r--r--Spigot-API-Patches-Unmapped/0183-Increase-custom-payload-channel-message-size.patch21
-rw-r--r--Spigot-API-Patches-Unmapped/0184-Expose-the-internal-current-tick.patch38
-rw-r--r--Spigot-API-Patches-Unmapped/0185-PlayerDeathEvent-shouldDropExperience.patch79
-rw-r--r--Spigot-API-Patches-Unmapped/0186-Add-effect-to-block-break-naturally.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0187-Add-ThrownEggHatchEvent.patch129
-rw-r--r--Spigot-API-Patches-Unmapped/0188-Entity-Jump-API.patch88
-rw-r--r--Spigot-API-Patches-Unmapped/0189-add-hand-to-BlockMultiPlaceEvent.patch29
-rw-r--r--Spigot-API-Patches-Unmapped/0190-Add-tick-times-API.patch62
-rw-r--r--Spigot-API-Patches-Unmapped/0191-Expose-MinecraftServer-isRunning.patch44
-rw-r--r--Spigot-API-Patches-Unmapped/0192-Disable-Sync-Events-firing-Async-errors-during-shutd.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0193-Make-JavaPluginLoader-thread-safe.patch59
-rw-r--r--Spigot-API-Patches-Unmapped/0194-Add-Player-Client-Options-API.patch202
-rw-r--r--Spigot-API-Patches-Unmapped/0195-Add-PlayerAttackEntityCooldownResetEvent.patch88
-rw-r--r--Spigot-API-Patches-Unmapped/0196-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch21
-rw-r--r--Spigot-API-Patches-Unmapped/0197-Villager-Restocks-API.patch31
-rw-r--r--Spigot-API-Patches-Unmapped/0198-Expose-game-version.patch50
-rw-r--r--Spigot-API-Patches-Unmapped/0199-Add-item-slot-convenience-methods.patch283
-rw-r--r--Spigot-API-Patches-Unmapped/0200-Add-Mob-Goal-API.patch481
-rw-r--r--Spigot-API-Patches-Unmapped/0201-World-view-distance-api.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0202-Add-villager-reputation-API.patch177
-rw-r--r--Spigot-API-Patches-Unmapped/0203-Spawn-Reason-API.patch62
-rw-r--r--Spigot-API-Patches-Unmapped/0204-Potential-bed-API.patch33
-rw-r--r--Spigot-API-Patches-Unmapped/0205-Prioritise-own-classes-where-possible.patch80
-rw-r--r--Spigot-API-Patches-Unmapped/0206-Add-Raw-Byte-ItemStack-Serialization.patch56
-rw-r--r--Spigot-API-Patches-Unmapped/0207-Provide-a-useful-PluginClassLoader-toString.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0208-Inventory-getHolder-method-without-block-snapshot.patch51
-rw-r--r--Spigot-API-Patches-Unmapped/0209-Expose-Arrow-getItemStack.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0210-Add-and-implement-PlayerRecipeBookClickEvent.patch96
-rw-r--r--Spigot-API-Patches-Unmapped/0211-Support-components-in-ItemMeta.patch93
-rw-r--r--Spigot-API-Patches-Unmapped/0212-added-2-new-TargetReasons-for-1.16-mob-behavior.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0213-Add-entity-liquid-API.patch46
-rw-r--r--Spigot-API-Patches-Unmapped/0214-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch211
-rw-r--r--Spigot-API-Patches-Unmapped/0215-Allow-delegation-to-vanilla-chunk-gen.patch85
-rw-r--r--Spigot-API-Patches-Unmapped/0216-Support-hex-colors-in-getLastColors.patch34
-rw-r--r--Spigot-API-Patches-Unmapped/0217-Add-setMaxPlayers-API.patch48
-rw-r--r--Spigot-API-Patches-Unmapped/0218-Add-moon-phase-API.patch65
-rw-r--r--Spigot-API-Patches-Unmapped/0219-Add-playPickupItemAnimation-to-LivingEntity.patch39
-rw-r--r--Spigot-API-Patches-Unmapped/0220-Add-BellRingEvent.patch69
-rw-r--r--Spigot-API-Patches-Unmapped/0221-Brand-support.patch27
-rw-r--r--Spigot-API-Patches-Unmapped/0222-Add-more-Evoker-API.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0223-Add-a-way-to-get-translation-keys-for-blocks-entitie.patch98
-rw-r--r--Spigot-API-Patches-Unmapped/0224-Create-HoverEvent-from-ItemStack-Entity.patch73
-rw-r--r--Spigot-API-Patches-Unmapped/0225-Add-additional-open-container-api-to-HumanEntity.patch103
-rw-r--r--Spigot-API-Patches-Unmapped/0226-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0227-Entity-isTicking.patch21
-rw-r--r--Spigot-API-Patches-Unmapped/0228-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch19
-rw-r--r--Spigot-API-Patches-Unmapped/0229-Villager-resetOffers.patch22
-rw-r--r--Spigot-API-Patches-Unmapped/0230-Player-elytra-boost-API.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0231-Add-getOfflinePlayerIfCached-String.patch68
-rw-r--r--Spigot-API-Patches-Unmapped/0232-Add-ignore-discounts-API.patch52
-rw-r--r--Spigot-API-Patches-Unmapped/0233-Item-no-age-no-player-pickup.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0234-Beacon-API-custom-effect-ranges.patch37
-rw-r--r--Spigot-API-Patches-Unmapped/0235-Add-API-for-quit-reason.patch79
-rw-r--r--Spigot-API-Patches-Unmapped/0236-Add-Destroy-Speed-API.patch41
-rw-r--r--Spigot-API-Patches-Unmapped/0237-Add-LivingEntity-clearActiveItem.patch24
-rw-r--r--Spigot-API-Patches-Unmapped/0238-Add-PlayerItemCooldownEvent.patch89
-rw-r--r--Spigot-API-Patches-Unmapped/0239-More-lightning-API.patch49
-rw-r--r--Spigot-API-Patches-Unmapped/0240-Add-PlayerShearBlockEvent.patch120
-rw-r--r--Spigot-API-Patches-Unmapped/0241-Enable-multi-release-plugin-jars.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0242-Player-Chunk-Load-Unload-Events.patch100
-rw-r--r--Spigot-API-Patches-Unmapped/0243-Expose-LivingEntity-hurt-direction.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0244-added-PlayerTradeEvent.patch133
-rw-r--r--Spigot-API-Patches-Unmapped/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch23
-rw-r--r--Spigot-API-Patches-Unmapped/0246-Add-TargetHitEvent-API.patch81
-rw-r--r--Spigot-API-Patches-Unmapped/0247-Additional-Block-Material-API-s.patch57
-rw-r--r--Spigot-API-Patches-Unmapped/0248-Add-API-to-get-Material-from-Boats-and-Minecarts.patch58
-rw-r--r--Spigot-API-Patches-Unmapped/0249-Add-PlayerFlowerPotManipulateEvent.patch94
-rw-r--r--Spigot-API-Patches-Unmapped/0250-Zombie-API-breaking-doors.patch30
-rw-r--r--Spigot-API-Patches-Unmapped/0251-Add-EntityLoadCrossbowEvent.patch113
-rw-r--r--Spigot-API-Patches-Unmapped/0252-Added-WorldGameRuleChangeEvent.patch103
-rw-r--r--Spigot-API-Patches-Unmapped/0253-Added-ServerResourcesReloadedEvent.patch60
-rw-r--r--Spigot-API-Patches-Unmapped/0254-Add-BlockFailedDispenseEvent.patch69
-rw-r--r--Spigot-API-Patches-Unmapped/0255-Added-PlayerLecternPageChangeEvent.patch127
-rw-r--r--Spigot-API-Patches-Unmapped/0256-Added-PlayerLoomPatternSelectEvent.patch89
-rw-r--r--Spigot-API-Patches-Unmapped/0257-Better-AnnotationTest-printout.patch69
-rw-r--r--Spigot-API-Patches-Unmapped/0258-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch67
-rw-r--r--Spigot-API-Patches-Unmapped/0259-Add-sendOpLevel-API.patch28
-rw-r--r--Spigot-API-Patches-Unmapped/0260-Add-StructureLocateEvent.patch167
-rw-r--r--Spigot-API-Patches-Unmapped/0261-Make-ProjectileHitEvent-Cancellable.patch49
-rw-r--r--Spigot-API-Patches-Unmapped/0262-Return-chat-component-with-empty-text-instead-of-thr.patch20
-rw-r--r--Spigot-API-Patches-Unmapped/0263-Add-BlockPreDispenseEvent.patch72
-rw-r--r--Spigot-API-Patches-Unmapped/0264-Added-Vanilla-Entity-Tags.patch43
-rw-r--r--Spigot-API-Patches-Unmapped/0265-added-Wither-API.patch45
-rw-r--r--Spigot-API-Patches-Unmapped/0266-Added-PlayerChangeBeaconEffectEvent.patch153
-rw-r--r--Spigot-API-Patches-Unmapped/0267-Add-dropLeash-variable-to-EntityUnleashEvent.patch78
-rw-r--r--Spigot-API-Patches-Unmapped/0268-Added-PlayerStonecutterRecipeSelectEvent.patch71
-rw-r--r--Spigot-API-Patches-Unmapped/0269-EntityMoveEvent.patch107
-rw-r--r--Spigot-API-Patches-Unmapped/0270-add-DragonEggFormEvent.patch75
-rw-r--r--Spigot-API-Patches-Unmapped/0271-Allow-adding-items-to-BlockDropItemEvent.patch19
-rw-r--r--Spigot-API-Patches-Unmapped/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch26
-rw-r--r--Spigot-API-Patches-Unmapped/0273-living-entity-allow-attribute-registration.patch25
-rw-r--r--Spigot-API-Patches-Unmapped/0274-Add-missing-effects.patch76
-rw-r--r--Spigot-API-Patches-Unmapped/0275-Expose-Tracked-Players.patch33
-rw-r--r--Spigot-API-Patches-Unmapped/0276-Cache-the-result-of-Material-isBlock.patch38
-rw-r--r--Spigot-API-Patches-Unmapped/0277-Add-worldborder-events.patch308
-rw-r--r--Spigot-API-Patches-Unmapped/0278-added-PlayerNameEntityEvent.patch130
-rw-r--r--Spigot-API-Patches-Unmapped/0279-Add-recipe-to-cook-events.patch69
-rw-r--r--Spigot-API-Patches-Unmapped/0280-Add-Block-isValidTool.patch26
-rw-r--r--Spigot-API-Patches-Unmapped/0281-Implement-Keyed-on-World.patch164
-rw-r--r--Spigot-API-Patches-Unmapped/0282-fix-Inventory-getContents-null-annotations.patch22
-rw-r--r--Spigot-API-Patches-Unmapped/0283-Item-Rarity-API.patch88
-rw-r--r--Spigot-API-Patches-Unmapped/0284-Expose-protocol-version.patch23
-rw-r--r--Spigot-API-Patches-Unmapped/0285-add-isDeeplySleeping-to-HumanEntity.patch26
-rw-r--r--Spigot-API-Patches-Unmapped/0286-add-consumeFuel-to-FurnaceBurnEvent.patch44
-rw-r--r--Spigot-API-Patches-Unmapped/0287-add-get-set-drop-chance-to-EntityEquipment.patch43
-rw-r--r--Spigot-API-Patches-Unmapped/0288-Added-PlayerDeepSleepEvent.patch58
-rw-r--r--Spigot-API-Patches-Unmapped/0289-More-World-API.patch131
-rw-r--r--Spigot-API-Patches-Unmapped/0290-Added-PlayerBedFailEnterEvent.patch131
290 files changed, 35350 insertions, 0 deletions
diff --git a/Spigot-API-Patches-Unmapped/0001-POM-changes.patch b/Spigot-API-Patches-Unmapped/0001-POM-changes.patch
new file mode 100644
index 0000000000..ef977bfb1e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0001-POM-changes.patch
@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Tue, 1 Mar 2016 00:16:08 +0100
+Subject: [PATCH] POM changes
+
+
+diff --git a/pom.xml b/pom.xml
+index 5f3253b9c4a533e746707d602d4a7988519742ef..61b8ee4e3e122dd2671f50ea3b432e4abd4600a2 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -2,33 +2,34 @@
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
++ <parent>
++ <groupId>com.destroystokyo.paper</groupId>
++ <artifactId>paper-parent</artifactId>
++ <version>dev-SNAPSHOT</version>
++ </parent>
+
+- <groupId>org.spigotmc</groupId>
+- <artifactId>spigot-api</artifactId>
++ <groupId>com.destroystokyo.paper</groupId>
++ <artifactId>paper-api</artifactId>
+ <version>1.16.5-R0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+- <name>Spigot-API</name>
+- <url>https://www.spigotmc.org/</url>
++ <name>Paper-API</name>
++ <url>https://github.com/PaperMC/Paper</url>
+ <description>An enhanced plugin API for Minecraft servers.</description>
+
+ <properties>
+- <skipTests>true</skipTests>
++ <!-- <skipTests>true</skipTests> Paper - This [was] not going to end well -->
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+- <distributionManagement>
++ <repositories>
+ <repository>
+- <id>spigotmc-releases</id>
+- <url>https://hub.spigotmc.org/nexus/content/repositories/releases/</url>
++ <id>sonatype</id>
++ <url>https://oss.sonatype.org/content/groups/public/</url>
+ </repository>
+- <snapshotRepository>
+- <id>spigotmc-snapshots</id>
+- <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
+- </snapshotRepository>
+- </distributionManagement>
++ </repositories>
+
+ <dependencies>
+ <dependency>
+@@ -37,6 +38,20 @@
+ <version>2.6</version>
+ <scope>compile</scope>
+ </dependency>
++ <!-- bundled with Minecraft, shouldn't ever change -->
++ <dependency>
++ <groupId>com.google.code.findbugs</groupId>
++ <artifactId>jsr305</artifactId>
++ <version>1.3.9</version>
++ <scope>compile</scope>
++ </dependency>
++ <!-- used in previous versions of the API -->
++ <dependency>
++ <groupId>com.googlecode.json-simple</groupId>
++ <artifactId>json-simple</artifactId>
++ <version>1.1.1</version>
++ <scope>compile</scope>
++ </dependency>
+ <!-- bundled with Minecraft, should be kept in sync -->
+ <dependency>
+ <groupId>com.google.guava</groupId>
+@@ -93,6 +108,7 @@
+ </dependencies>
+
+ <build>
++ <defaultGoal>clean install</defaultGoal>
+ <plugins>
+ <plugin>
+ <groupId>net.md-5</groupId>
+@@ -111,10 +127,6 @@
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+- <configuration>
+- <!-- we use the Eclipse compiler as it doesn't need a JDK -->
+- <compilerId>eclipse</compilerId>
+- </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+@@ -164,6 +176,7 @@
+ </excludes>
+ </filter>
+ </filters>
++ <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
+ <!-- when downloading via Maven we can pull depends individually -->
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ </configuration>
diff --git a/Spigot-API-Patches-Unmapped/0002-Add-FastUtil-to-Bukkit.patch b/Spigot-API-Patches-Unmapped/0002-Add-FastUtil-to-Bukkit.patch
new file mode 100644
index 0000000000..a5e6eda88d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0002-Add-FastUtil-to-Bukkit.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 1 Apr 2016 00:02:47 -0400
+Subject: [PATCH] Add FastUtil to Bukkit
+
+Doesn't expose to plugins, just allows Paper-API to use it for optimization
+
+diff --git a/pom.xml b/pom.xml
+index 61b8ee4e3e122dd2671f50ea3b432e4abd4600a2..12306d830c6889c2c9b12699abebe0411262aef6 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -32,6 +32,12 @@
+ </repositories>
+
+ <dependencies>
++ <dependency>
++ <groupId>it.unimi.dsi</groupId>
++ <artifactId>fastutil</artifactId>
++ <version>8.2.2</version>
++ <scope>provided</scope>
++ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
diff --git a/Spigot-API-Patches-Unmapped/0003-Paper-Utils.patch b/Spigot-API-Patches-Unmapped/0003-Paper-Utils.patch
new file mode 100644
index 0000000000..a647b698ad
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0003-Paper-Utils.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 23 Feb 2019 11:26:21 -0500
+Subject: [PATCH] Paper Utils
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/util/SneakyThrow.java b/src/main/java/com/destroystokyo/paper/util/SneakyThrow.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9db0056ab94145819628b3ad8d8d26130d117fcf
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/util/SneakyThrow.java
+@@ -0,0 +1,16 @@
++package com.destroystokyo.paper.util;
++
++import org.jetbrains.annotations.NotNull;
++
++public class SneakyThrow {
++
++ public static void sneaky(@NotNull Throwable exception) {
++ SneakyThrow.<RuntimeException>throwSneaky(exception);
++ }
++
++ @SuppressWarnings("unchecked")
++ private static <T extends Throwable> void throwSneaky(@NotNull Throwable exception) throws T {
++ throw (T) exception;
++ }
++
++}
diff --git a/Spigot-API-Patches-Unmapped/0004-Timings-v2.patch b/Spigot-API-Patches-Unmapped/0004-Timings-v2.patch
new file mode 100644
index 0000000000..1f718baa86
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0004-Timings-v2.patch
@@ -0,0 +1,3759 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 29 Feb 2016 18:48:17 -0600
+Subject: [PATCH] Timings v2
+
+
+diff --git a/src/main/java/co/aikar/timings/FullServerTickHandler.java b/src/main/java/co/aikar/timings/FullServerTickHandler.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..dfaa266ff53e43ad48dc5a5a5657fe70600f539a
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/FullServerTickHandler.java
+@@ -0,0 +1,85 @@
++package co.aikar.timings;
++
++import static co.aikar.timings.TimingsManager.*;
++
++import org.bukkit.Bukkit;
++import org.jetbrains.annotations.NotNull;
++
++public class FullServerTickHandler extends TimingHandler {
++ private static final TimingIdentifier IDENTITY = new TimingIdentifier("Minecraft", "Full Server Tick", null);
++ final TimingData minuteData;
++ double avgFreeMemory = -1D;
++ double avgUsedMemory = -1D;
++ FullServerTickHandler() {
++ super(IDENTITY);
++ minuteData = new TimingData(id);
++
++ TIMING_MAP.put(IDENTITY, this);
++ }
++
++ @NotNull
++ @Override
++ public Timing startTiming() {
++ if (TimingsManager.needsFullReset) {
++ TimingsManager.resetTimings();
++ } else if (TimingsManager.needsRecheckEnabled) {
++ TimingsManager.recheckEnabled();
++ }
++ return super.startTiming();
++ }
++
++ @Override
++ public void stopTiming() {
++ super.stopTiming();
++ if (!isEnabled() || Bukkit.isStopping()) {
++ return;
++ }
++ if (TimingHistory.timedTicks % 20 == 0) {
++ final Runtime runtime = Runtime.getRuntime();
++ double usedMemory = runtime.totalMemory() - runtime.freeMemory();
++ double freeMemory = runtime.maxMemory() - usedMemory;
++ if (this.avgFreeMemory == -1) {
++ this.avgFreeMemory = freeMemory;
++ } else {
++ this.avgFreeMemory = (this.avgFreeMemory * (59 / 60D)) + (freeMemory * (1 / 60D));
++ }
++
++ if (this.avgUsedMemory == -1) {
++ this.avgUsedMemory = usedMemory;
++ } else {
++ this.avgUsedMemory = (this.avgUsedMemory * (59 / 60D)) + (usedMemory * (1 / 60D));
++ }
++ }
++
++ long start = System.nanoTime();
++ TimingsManager.tick();
++ long diff = System.nanoTime() - start;
++ TIMINGS_TICK.addDiff(diff, null);
++ // addDiff for TIMINGS_TICK incremented this, bring it back down to 1 per tick.
++ record.setCurTickCount(record.getCurTickCount()-1);
++
++ minuteData.setCurTickTotal(record.getCurTickTotal());
++ minuteData.setCurTickCount(1);
++
++ boolean violated = isViolated();
++ minuteData.processTick(violated);
++ TIMINGS_TICK.processTick(violated);
++ processTick(violated);
++
++
++ if (TimingHistory.timedTicks % 1200 == 0) {
++ MINUTE_REPORTS.add(new TimingHistory.MinuteReport());
++ TimingHistory.resetTicks(false);
++ minuteData.reset();
++ }
++ if (TimingHistory.timedTicks % Timings.getHistoryInterval() == 0) {
++ TimingsManager.HISTORY.add(new TimingHistory());
++ TimingsManager.resetTimings();
++ }
++ Bukkit.getUnsafe().reportTimings();
++ }
++
++ boolean isViolated() {
++ return record.getCurTickTotal() > 50000000;
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/NullTimingHandler.java b/src/main/java/co/aikar/timings/NullTimingHandler.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9b45ce887b9172f30302b83fe24b99b76b16dac3
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/NullTimingHandler.java
+@@ -0,0 +1,68 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public final class NullTimingHandler implements Timing {
++ public static final Timing NULL = new NullTimingHandler();
++ @NotNull
++ @Override
++ public Timing startTiming() {
++ return this;
++ }
++
++ @Override
++ public void stopTiming() {
++
++ }
++
++ @NotNull
++ @Override
++ public Timing startTimingIfSync() {
++ return this;
++ }
++
++ @Override
++ public void stopTimingIfSync() {
++
++ }
++
++ @Override
++ public void abort() {
++
++ }
++
++ @Nullable
++ @Override
++ public TimingHandler getTimingHandler() {
++ return null;
++ }
++
++ @Override
++ public void close() {
++
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4e6e1b8e8aeb07e34536941d2cbfc25e5cfa6c27
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java
+@@ -0,0 +1,83 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.EventException;
++import org.bukkit.event.Listener;
++import org.bukkit.plugin.EventExecutor;
++import org.bukkit.plugin.Plugin;
++
++import java.lang.reflect.Method;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class TimedEventExecutor implements EventExecutor {
++
++ private final EventExecutor executor;
++ private final Timing timings;
++
++ /**
++ * Wraps an event executor and associates a timing handler to it.
++ *
++ * @param executor Executor to wrap
++ * @param plugin Owning plugin
++ * @param method EventHandler method
++ * @param eventClass Owning class
++ */
++ public TimedEventExecutor(@NotNull EventExecutor executor, @NotNull Plugin plugin, @Nullable Method method, @NotNull Class<? extends Event> eventClass) {
++ this.executor = executor;
++ String id;
++
++ if (method == null) {
++ if (executor.getClass().getEnclosingClass() != null) { // Oh Skript, how we love you
++ method = executor.getClass().getEnclosingMethod();
++ }
++ }
++
++ if (method != null) {
++ id = method.getDeclaringClass().getName();
++ } else {
++ id = executor.getClass().getName();
++ }
++
++
++ final String eventName = eventClass.getSimpleName();
++ boolean verbose = "BlockPhysicsEvent".equals(eventName);
++ this.timings = Timings.ofSafe(plugin, (verbose ? "## " : "") +
++ "Event: " + id + " (" + eventName + ")");
++ }
++
++ @Override
++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
++ if (event.isAsynchronous() || !Timings.timingsEnabled || !Bukkit.isPrimaryThread()) {
++ executor.execute(listener, event);
++ return;
++ }
++ try (Timing ignored = timings.startTiming()){
++ executor.execute(listener, event);
++ }
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/Timing.java b/src/main/java/co/aikar/timings/Timing.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a21e5ead5024fd0058c5e3302d8201dd249d32bc
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/Timing.java
+@@ -0,0 +1,83 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Provides an ability to time sections of code within the Minecraft Server
++ */
++public interface Timing extends AutoCloseable {
++ /**
++ * Starts timing the execution until {@link #stopTiming()} is called.
++ *
++ * @return Timing
++ */
++ @NotNull
++ Timing startTiming();
++
++ /**
++ * <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
++ *
++ * Will automatically be called when this Timing is used with try-with-resources
++ */
++ void stopTiming();
++
++ /**
++ * Starts timing the execution until {@link #stopTiming()} is called.
++ *
++ * But only if we are on the primary thread.
++ *
++ * @return Timing
++ */
++ @NotNull
++ Timing startTimingIfSync();
++
++ /**
++ * <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
++ *
++ * <p>Will automatically be called when this Timing is used with try-with-resources</p>
++ *
++ * But only if we are on the primary thread.
++ */
++ void stopTimingIfSync();
++
++ /**
++ * @deprecated Doesn't do anything - Removed
++ */
++ @Deprecated
++ void abort();
++
++ /**
++ * Used internally to get the actual backing Handler in the case of delegated Handlers
++ *
++ * @return TimingHandler
++ */
++ @Nullable
++ TimingHandler getTimingHandler();
++
++ @Override
++ void close();
++}
+diff --git a/src/main/java/co/aikar/timings/TimingData.java b/src/main/java/co/aikar/timings/TimingData.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a5d13a1e44edb861f45c83a9b4309fbf799d407d
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingData.java
+@@ -0,0 +1,122 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++
++import static co.aikar.util.JSONUtil.toArray;
++
++/**
++ * <p>Lightweight object for tracking timing data</p>
++ *
++ * This is broken out to reduce memory usage
++ */
++class TimingData {
++ private final int id;
++ private int count = 0;
++ private int lagCount = 0;
++ private long totalTime = 0;
++ private long lagTotalTime = 0;
++ private int curTickCount = 0;
++ private long curTickTotal = 0;
++
++ TimingData(int id) {
++ this.id = id;
++ }
++
++ private TimingData(TimingData data) {
++ this.id = data.id;
++ this.totalTime = data.totalTime;
++ this.lagTotalTime = data.lagTotalTime;
++ this.count = data.count;
++ this.lagCount = data.lagCount;
++ }
++
++ void add(long diff) {
++ ++curTickCount;
++ curTickTotal += diff;
++ }
++
++ void processTick(boolean violated) {
++ totalTime += curTickTotal;
++ count += curTickCount;
++ if (violated) {
++ lagTotalTime += curTickTotal;
++ lagCount += curTickCount;
++ }
++ curTickTotal = 0;
++ curTickCount = 0;
++ }
++
++ void reset() {
++ count = 0;
++ lagCount = 0;
++ curTickTotal = 0;
++ curTickCount = 0;
++ totalTime = 0;
++ lagTotalTime = 0;
++ }
++
++ protected TimingData clone() {
++ return new TimingData(this);
++ }
++
++ @NotNull
++ List<Object> export() {
++ List<Object> list = toArray(
++ id,
++ count,
++ totalTime);
++ if (lagCount > 0) {
++ list.add(lagCount);
++ list.add(lagTotalTime);
++ }
++ return list;
++ }
++
++ boolean hasData() {
++ return count > 0;
++ }
++
++ long getTotalTime() {
++ return totalTime;
++ }
++
++ int getCurTickCount() {
++ return curTickCount;
++ }
++
++ void setCurTickCount(int curTickCount) {
++ this.curTickCount = curTickCount;
++ }
++
++ long getCurTickTotal() {
++ return curTickTotal;
++ }
++
++ void setCurTickTotal(long curTickTotal) {
++ this.curTickTotal = curTickTotal;
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingHandler.java b/src/main/java/co/aikar/timings/TimingHandler.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..199789d56d22fcb1b77ebd56805cc28aa5a5ab0a
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingHandler.java
+@@ -0,0 +1,226 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import co.aikar.util.LoadingIntMap;
++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
++
++import java.util.ArrayDeque;
++import java.util.Deque;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.logging.Level;
++import java.util.logging.Logger;
++
++import org.bukkit.Bukkit;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++class TimingHandler implements Timing {
++
++ private static AtomicInteger idPool = new AtomicInteger(1);
++ private static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
++ final int id = idPool.getAndIncrement();
++
++ final TimingIdentifier identifier;
++ private final boolean verbose;
++
++ private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
++
++ final TimingData record;
++ private TimingHandler startParent;
++ private final TimingHandler groupHandler;
++
++ private long start = 0;
++ private int timingDepth = 0;
++ private boolean added;
++ private boolean timed;
++ private boolean enabled;
++
++ TimingHandler(@NotNull TimingIdentifier id) {
++ this.identifier = id;
++ this.verbose = id.name.startsWith("##");
++ this.record = new TimingData(this.id);
++ this.groupHandler = id.groupHandler;
++
++ TimingIdentifier.getGroup(id.group).handlers.add(this);
++ checkEnabled();
++ }
++
++ final void checkEnabled() {
++ enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
++ }
++
++ void processTick(boolean violated) {
++ if (timingDepth != 0 || record.getCurTickCount() == 0) {
++ timingDepth = 0;
++ start = 0;
++ return;
++ }
++
++ record.processTick(violated);
++ for (TimingData handler : children.values()) {
++ handler.processTick(violated);
++ }
++ }
++
++ @NotNull
++ @Override
++ public Timing startTimingIfSync() {
++ startTiming();
++ return this;
++ }
++
++ @Override
++ public void stopTimingIfSync() {
++ stopTiming();
++ }
++
++ @NotNull
++ public Timing startTiming() {
++ if (!enabled || !Bukkit.isPrimaryThread()) {
++ return this;
++ }
++ if (++timingDepth == 1) {
++ startParent = TIMING_STACK.peekLast();
++ start = System.nanoTime();
++ }
++ TIMING_STACK.addLast(this);
++ return this;
++ }
++
++ public void stopTiming() {
++ if (!enabled || timingDepth <= 0 || start == 0 || !Bukkit.isPrimaryThread()) {
++ return;
++ }
++
++ popTimingStack();
++ if (--timingDepth == 0) {
++ addDiff(System.nanoTime() - start, startParent);
++ startParent = null;
++ start = 0;
++ }
++ }
++
++ private void popTimingStack() {
++ TimingHandler last;
++ while ((last = TIMING_STACK.removeLast()) != this) {
++ last.timingDepth = 0;
++ if ("Minecraft".equalsIgnoreCase(last.identifier.group)) {
++ Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Look above this for any errors and report this to Paper unless it has a plugin in the stack trace (" + last.identifier + " did not stopTiming)");
++ } else {
++ Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Report this to the plugin " + last.identifier.group + " (Look for errors above this in the logs) (" + last.identifier + " did not stopTiming)", new Throwable());
++ }
++
++ boolean found = TIMING_STACK.contains(this);
++ if (!found) {
++ // We aren't even in the stack... Don't pop everything
++ TIMING_STACK.addLast(last);
++ break;
++ }
++ }
++ }
++
++ @Override
++ public final void abort() {
++
++ }
++
++ void addDiff(long diff, @Nullable TimingHandler parent) {
++ if (parent != null) {
++ parent.children.get(id).add(diff);
++ }
++
++ record.add(diff);
++ if (!added) {
++ added = true;
++ timed = true;
++ TimingsManager.HANDLERS.add(this);
++ }
++ if (groupHandler != null) {
++ groupHandler.addDiff(diff, parent);
++ groupHandler.children.get(id).add(diff);
++ }
++ }
++
++ /**
++ * Reset this timer, setting all values to zero.
++ */
++ void reset(boolean full) {
++ record.reset();
++ if (full) {
++ timed = false;
++ }
++ start = 0;
++ timingDepth = 0;
++ added = false;
++ children.clear();
++ checkEnabled();
++ }
++
++ @NotNull
++ @Override
++ public TimingHandler getTimingHandler() {
++ return this;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ return (this == o);
++ }
++
++ @Override
++ public int hashCode() {
++ return id;
++ }
++
++ /**
++ * This is simply for the Closeable interface so it can be used with try-with-resources ()
++ */
++ @Override
++ public void close() {
++ stopTimingIfSync();
++ }
++
++ public boolean isSpecial() {
++ return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
++ }
++
++ boolean isTimed() {
++ return timed;
++ }
++
++ public boolean isEnabled() {
++ return enabled;
++ }
++
++ @NotNull
++ TimingData[] cloneChildren() {
++ final TimingData[] clonedChildren = new TimingData[children.size()];
++ int i = 0;
++ for (TimingData child : children.values()) {
++ clonedChildren[i++] = child.clone();
++ }
++ return clonedChildren;
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingHistory.java b/src/main/java/co/aikar/timings/TimingHistory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ddaed81275fcc12d1671b668697acf318e96888b
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingHistory.java
+@@ -0,0 +1,354 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import co.aikar.timings.TimingHistory.RegionData.RegionId;
++import com.google.common.base.Function;
++import com.google.common.collect.Sets;
++import org.bukkit.Bukkit;
++import org.bukkit.Chunk;
++import org.bukkit.Material;
++import org.bukkit.World;
++import org.bukkit.block.BlockState;
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.EntityType;
++import org.bukkit.entity.Player;
++import co.aikar.util.LoadingMap;
++import co.aikar.util.MRUMapCache;
++
++import java.lang.management.ManagementFactory;
++import java.util.Collection;
++import java.util.EnumMap;
++import java.util.List;
++import java.util.Map;
++import java.util.Set;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++import static co.aikar.timings.TimingsManager.FULL_SERVER_TICK;
++import static co.aikar.timings.TimingsManager.MINUTE_REPORTS;
++import static co.aikar.util.JSONUtil.*;
++
++@SuppressWarnings({"deprecation", "SuppressionAnnotation", "Convert2Lambda", "Anonymous2MethodRef"})
++public class TimingHistory {
++ public static long lastMinuteTime;
++ public static long timedTicks;
++ public static long playerTicks;
++ public static long entityTicks;
++ public static long tileEntityTicks;
++ public static long activatedEntityTicks;
++ private static int worldIdPool = 1;
++ static Map<String, Integer> worldMap = LoadingMap.newHashMap(new Function<String, Integer>() {
++ @NotNull
++ @Override
++ public Integer apply(@Nullable String input) {
++ return worldIdPool++;
++ }
++ });
++ private final long endTime;
++ private final long startTime;
++ private final long totalTicks;
++ private final long totalTime; // Represents all time spent running the server this history
++ private final MinuteReport[] minuteReports;
++
++ private final TimingHistoryEntry[] entries;
++ final Set<Material> tileEntityTypeSet = Sets.newHashSet();
++ final Set<EntityType> entityTypeSet = Sets.newHashSet();
++ private final Map<Object, Object> worlds;
++
++ TimingHistory() {
++ this.endTime = System.currentTimeMillis() / 1000;
++ this.startTime = TimingsManager.historyStart / 1000;
++ if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) {
++ this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]);
++ this.minuteReports[this.minuteReports.length - 1] = new MinuteReport();
++ } else {
++ this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]);
++ }
++ long ticks = 0;
++ for (MinuteReport mp : this.minuteReports) {
++ ticks += mp.ticksRecord.timed;
++ }
++ this.totalTicks = ticks;
++ this.totalTime = FULL_SERVER_TICK.record.getTotalTime();
++ this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()];
++
++ int i = 0;
++ for (TimingHandler handler : TimingsManager.HANDLERS) {
++ entries[i++] = new TimingHistoryEntry(handler);
++ }
++
++ // Information about all loaded chunks/entities
++ //noinspection unchecked
++ this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function<World, JSONPair>() {
++ @NotNull
++ @Override
++ public JSONPair apply(World world) {
++ Map<RegionId, RegionData> regions = LoadingMap.newHashMap(RegionData.LOADER);
++
++ for (Chunk chunk : world.getLoadedChunks()) {
++ RegionData data = regions.get(new RegionId(chunk.getX(), chunk.getZ()));
++
++ for (Entity entity : chunk.getEntities()) {
++ if (entity == null) {
++ Bukkit.getLogger().warning("Null entity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ());
++ continue;
++ }
++
++ data.entityCounts.get(entity.getType()).increment();
++ }
++
++ for (BlockState tileEntity : chunk.getTileEntities()) {
++ if (tileEntity == null) {
++ Bukkit.getLogger().warning("Null tileentity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ());
++ continue;
++ }
++
++ data.tileEntityCounts.get(tileEntity.getBlock().getType()).increment();
++ }
++ }
++ return pair(
++ worldMap.get(world.getName()),
++ toArrayMapper(regions.values(),new Function<RegionData, Object>() {
++ @NotNull
++ @Override
++ public Object apply(RegionData input) {
++ return toArray(
++ input.regionId.x,
++ input.regionId.z,
++ toObjectMapper(input.entityCounts.entrySet(),
++ new Function<Map.Entry<EntityType, Counter>, JSONPair>() {
++ @NotNull
++ @Override
++ public JSONPair apply(Map.Entry<EntityType, Counter> entry) {
++ entityTypeSet.add(entry.getKey());
++ return pair(
++ String.valueOf(entry.getKey().ordinal()),
++ entry.getValue().count()
++ );
++ }
++ }
++ ),
++ toObjectMapper(input.tileEntityCounts.entrySet(),
++ new Function<Map.Entry<Material, Counter>, JSONPair>() {
++ @NotNull
++ @Override
++ public JSONPair apply(Map.Entry<Material, Counter> entry) {
++ tileEntityTypeSet.add(entry.getKey());
++ return pair(
++ String.valueOf(entry.getKey().ordinal()),
++ entry.getValue().count()
++ );
++ }
++ }
++ )
++ );
++ }
++ })
++ );
++ }
++ });
++ }
++ static class RegionData {
++ final RegionId regionId;
++ @SuppressWarnings("Guava")
++ static Function<RegionId, RegionData> LOADER = new Function<RegionId, RegionData>() {
++ @NotNull
++ @Override
++ public RegionData apply(@NotNull RegionId id) {
++ return new RegionData(id);
++ }
++ };
++ RegionData(@NotNull RegionId id) {
++ this.regionId = id;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (this == o) {
++ return true;
++ }
++ if (o == null || getClass() != o.getClass()) {
++ return false;
++ }
++
++ RegionData that = (RegionData) o;
++
++ return regionId.equals(that.regionId);
++
++ }
++
++ @Override
++ public int hashCode() {
++ return regionId.hashCode();
++ }
++
++ @SuppressWarnings("unchecked")
++ final Map<EntityType, Counter> entityCounts = MRUMapCache.of(LoadingMap.of(
++ new EnumMap<EntityType, Counter>(EntityType.class), k -> new Counter()
++ ));
++ @SuppressWarnings("unchecked")
++ final Map<Material, Counter> tileEntityCounts = MRUMapCache.of(LoadingMap.of(
++ new EnumMap<Material, Counter>(Material.class), k -> new Counter()
++ ));
++
++ static class RegionId {
++ final int x, z;
++ final long regionId;
++ RegionId(int x, int z) {
++ this.x = x >> 5 << 5;
++ this.z = z >> 5 << 5;
++ this.regionId = ((long) (this.x) << 32) + (this.z >> 5 << 5) - Integer.MIN_VALUE;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (this == o) return true;
++ if (o == null || getClass() != o.getClass()) return false;
++
++ RegionId regionId1 = (RegionId) o;
++
++ return regionId == regionId1.regionId;
++
++ }
++
++ @Override
++ public int hashCode() {
++ return (int) (regionId ^ (regionId >>> 32));
++ }
++ }
++ }
++ static void resetTicks(boolean fullReset) {
++ if (fullReset) {
++ // Non full is simply for 1 minute reports
++ timedTicks = 0;
++ }
++ lastMinuteTime = System.nanoTime();
++ playerTicks = 0;
++ tileEntityTicks = 0;
++ entityTicks = 0;
++ activatedEntityTicks = 0;
++ }
++
++ @NotNull
++ Object export() {
++ return createObject(
++ pair("s", startTime),
++ pair("e", endTime),
++ pair("tk", totalTicks),
++ pair("tm", totalTime),
++ pair("w", worlds),
++ pair("h", toArrayMapper(entries, new Function<TimingHistoryEntry, Object>() {
++ @Nullable
++ @Override
++ public Object apply(TimingHistoryEntry entry) {
++ TimingData record = entry.data;
++ if (!record.hasData()) {
++ return null;
++ }
++ return entry.export();
++ }
++ })),
++ pair("mp", toArrayMapper(minuteReports, new Function<MinuteReport, Object>() {
++ @NotNull
++ @Override
++ public Object apply(MinuteReport input) {
++ return input.export();
++ }
++ }))
++ );
++ }
++
++ static class MinuteReport {
++ final long time = System.currentTimeMillis() / 1000;
++
++ final TicksRecord ticksRecord = new TicksRecord();
++ final PingRecord pingRecord = new PingRecord();
++ final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone();
++ final double tps = 1E9 / ( System.nanoTime() - lastMinuteTime ) * ticksRecord.timed;
++ final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory;
++ final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory;
++ final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
++
++ @NotNull
++ List<Object> export() {
++ return toArray(
++ time,
++ Math.round(tps * 100D) / 100D,
++ Math.round(pingRecord.avg * 100D) / 100D,
++ fst.export(),
++ toArray(ticksRecord.timed,
++ ticksRecord.player,
++ ticksRecord.entity,
++ ticksRecord.activatedEntity,
++ ticksRecord.tileEntity
++ ),
++ usedMemory,
++ freeMemory,
++ loadAvg
++ );
++ }
++ }
++
++ private static class TicksRecord {
++ final long timed;
++ final long player;
++ final long entity;
++ final long tileEntity;
++ final long activatedEntity;
++
++ TicksRecord() {
++ timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200);
++ player = playerTicks;
++ entity = entityTicks;
++ tileEntity = tileEntityTicks;
++ activatedEntity = activatedEntityTicks;
++ }
++
++ }
++
++ private static class PingRecord {
++ final double avg;
++
++ PingRecord() {
++ final Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
++ int totalPing = 0;
++ for (Player player : onlinePlayers) {
++ totalPing += player.spigot().getPing();
++ }
++ avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size();
++ }
++ }
++
++
++ private static class Counter {
++ private int count = 0;
++ public int increment() {
++ return ++count;
++ }
++ public int count() {
++ return count;
++ }
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingHistoryEntry.java b/src/main/java/co/aikar/timings/TimingHistoryEntry.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..86d5ac6bd0d7d0003688761aceb3f3343575319f
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingHistoryEntry.java
+@@ -0,0 +1,58 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import com.google.common.base.Function;
++
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++
++import static co.aikar.util.JSONUtil.toArrayMapper;
++
++class TimingHistoryEntry {
++ final TimingData data;
++ private final TimingData[] children;
++
++ TimingHistoryEntry(@NotNull TimingHandler handler) {
++ this.data = handler.record.clone();
++ children = handler.cloneChildren();
++ }
++
++ @NotNull
++ List<Object> export() {
++ List<Object> result = data.export();
++ if (children.length > 0) {
++ result.add(
++ toArrayMapper(children, new Function<TimingData, Object>() {
++ @NotNull
++ @Override
++ public Object apply(TimingData child) {
++ return child.export();
++ }
++ })
++ );
++ }
++ return result;
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingIdentifier.java b/src/main/java/co/aikar/timings/TimingIdentifier.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..df142a89b8c43acb81eb383eac0ef048a1f49a6e
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingIdentifier.java
+@@ -0,0 +1,116 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import co.aikar.util.LoadingMap;
++
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
++import java.util.Map;
++import java.util.Objects;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.atomic.AtomicInteger;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * <p>Used as a basis for fast HashMap key comparisons for the Timing Map.</p>
++ *
++ * This class uses interned strings giving us the ability to do an identity check instead of equals() on the strings
++ */
++final class TimingIdentifier {
++ /**
++ * Holds all groups. Autoloads on request for a group by name.
++ */
++ static final Map<String, TimingGroup> GROUP_MAP = LoadingMap.of(new ConcurrentHashMap<>(64, .5F), TimingGroup::new);
++ private static final TimingGroup DEFAULT_GROUP = getGroup("Minecraft");
++ final String group;
++ final String name;
++ final TimingHandler groupHandler;
++ private final int hashCode;
++
++ TimingIdentifier(@Nullable String group, @NotNull String name, @Nullable Timing groupHandler) {
++ this.group = group != null ? group: DEFAULT_GROUP.name;
++ this.name = name;
++ this.groupHandler = groupHandler != null ? groupHandler.getTimingHandler() : null;
++ this.hashCode = (31 * this.group.hashCode()) + this.name.hashCode();
++ }
++
++ @NotNull
++ static TimingGroup getGroup(@Nullable String groupName) {
++ if (groupName == null) {
++ //noinspection ConstantConditions
++ return DEFAULT_GROUP;
++ }
++
++ return GROUP_MAP.get(groupName);
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (o == null) {
++ return false;
++ }
++
++ TimingIdentifier that = (TimingIdentifier) o;
++ return Objects.equals(group, that.group) && Objects.equals(name, that.name);
++ }
++
++ @Override
++ public int hashCode() {
++ return hashCode;
++ }
++
++ @Override
++ public String toString() {
++ return "TimingIdentifier{id=" + group + ":" + name +'}';
++ }
++
++ static class TimingGroup {
++
++ private static AtomicInteger idPool = new AtomicInteger(1);
++ final int id = idPool.getAndIncrement();
++
++ final String name;
++ final List<TimingHandler> handlers = Collections.synchronizedList(new ArrayList<>(64));
++
++ private TimingGroup(String name) {
++ this.name = name;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (this == o) return true;
++ if (o == null || getClass() != o.getClass()) return false;
++ TimingGroup that = (TimingGroup) o;
++ return id == that.id;
++ }
++
++ @Override
++ public int hashCode() {
++ return id;
++ }
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..da76e1aaee1dee794e38ddd4e0a28e0071e90bbf
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/Timings.java
+@@ -0,0 +1,296 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import com.google.common.base.Preconditions;
++import com.google.common.collect.EvictingQueue;
++import com.google.common.collect.Lists;
++import org.apache.commons.lang.Validate;
++import org.bukkit.Bukkit;
++import org.bukkit.command.CommandSender;
++import org.bukkit.plugin.Plugin;
++
++import java.util.List;
++import java.util.Queue;
++import java.util.logging.Level;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++@SuppressWarnings({"UnusedDeclaration", "WeakerAccess", "SameParameterValue"})
++public final class Timings {
++
++ final static List<CommandSender> requestingReport = Lists.newArrayList();
++ private static final int MAX_HISTORY_FRAMES = 12;
++ public static final Timing NULL_HANDLER = new NullTimingHandler();
++ static boolean timingsEnabled = false;
++ static boolean verboseEnabled = false;
++ private static int historyInterval = -1;
++ private static int historyLength = -1;
++
++ private Timings() {}
++
++ /**
++ * Returns a Timing for a plugin corresponding to a name.
++ *
++ * @param plugin Plugin to own the Timing
++ * @param name Name of Timing
++ * @return Handler
++ */
++ @NotNull
++ public static Timing of(@NotNull Plugin plugin, @NotNull String name) {
++ Timing pluginHandler = null;
++ if (plugin != null) {
++ pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER);
++ }
++ return of(plugin, name, pluginHandler);
++ }
++
++ /**
++ * <p>Returns a handler that has a groupHandler timer handler. Parent timers should not have their
++ * start/stop methods called directly, as the children will call it for you.</p>
++ *
++ * Parent Timers are used to group multiple subsections together and get a summary of them combined
++ * Parent Handler can not be changed after first call
++ *
++ * @param plugin Plugin to own the Timing
++ * @param name Name of Timing
++ * @param groupHandler Parent handler to mirror .start/stop calls to
++ * @return Timing Handler
++ */
++ @NotNull
++ public static Timing of(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) {
++ Preconditions.checkNotNull(plugin, "Plugin can not be null");
++ return TimingsManager.getHandler(plugin.getName(), name, groupHandler);
++ }
++
++ /**
++ * Returns a Timing object after starting it, useful for Java7 try-with-resources.
++ *
++ * try (Timing ignored = Timings.ofStart(plugin, someName)) {
++ * // timed section
++ * }
++ *
++ * @param plugin Plugin to own the Timing
++ * @param name Name of Timing
++ * @return Timing Handler
++ */
++ @NotNull
++ public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name) {
++ return ofStart(plugin, name, null);
++ }
++
++ /**
++ * Returns a Timing object after starting it, useful for Java7 try-with-resources.
++ *
++ * try (Timing ignored = Timings.ofStart(plugin, someName, groupHandler)) {
++ * // timed section
++ * }
++ *
++ * @param plugin Plugin to own the Timing
++ * @param name Name of Timing
++ * @param groupHandler Parent handler to mirror .start/stop calls to
++ * @return Timing Handler
++ */
++ @NotNull
++ public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) {
++ Timing timing = of(plugin, name, groupHandler);
++ timing.startTiming();
++ return timing;
++ }
++
++ /**
++ * Gets whether or not the Spigot Timings system is enabled
++ *
++ * @return Enabled or not
++ */
++ public static boolean isTimingsEnabled() {
++ return timingsEnabled;
++ }
++
++ /**
++ * <p>Sets whether or not the Spigot Timings system should be enabled</p>
++ *
++ * Calling this will reset timing data.
++ *
++ * @param enabled Should timings be reported
++ */
++ public static void setTimingsEnabled(boolean enabled) {
++ timingsEnabled = enabled;
++ reset();
++ }
++
++ /**
++ * <p>Sets whether or not the Timings should monitor at Verbose level.</p>
++ *
++ * <p>When Verbose is disabled, high-frequency timings will not be available.</p>
++ *
++ * @return Enabled or not
++ */
++ public static boolean isVerboseTimingsEnabled() {
++ return verboseEnabled;
++ }
++
++ /**
++ * <p>Sets whether or not the Timings should monitor at Verbose level.</p>
++ *
++ * When Verbose is disabled, high-frequency timings will not be available.
++ * Calling this will reset timing data.
++ *
++ * @param enabled Should high-frequency timings be reported
++ */
++ public static void setVerboseTimingsEnabled(boolean enabled) {
++ verboseEnabled = enabled;
++ TimingsManager.needsRecheckEnabled = true;
++ }
++
++ /**
++ * <p>Gets the interval between Timing History report generation.</p>
++ *
++ * Defaults to 5 minutes (6000 ticks)
++ *
++ * @return Interval in ticks
++ */
++ public static int getHistoryInterval() {
++ return historyInterval;
++ }
++
++ /**
++ * <p>Sets the interval between Timing History report generations.</p>
++ *
++ * <p>Defaults to 5 minutes (6000 ticks)</p>
++ *
++ * This will recheck your history length, so lowering this value will lower your
++ * history length if you need more than 60 history windows.
++ *
++ * @param interval Interval in ticks
++ */
++ public static void setHistoryInterval(int interval) {
++ historyInterval = Math.max(20*60, interval);
++ // Recheck the history length with the new Interval
++ if (historyLength != -1) {
++ setHistoryLength(historyLength);
++ }
++ }
++
++ /**
++ * Gets how long in ticks Timings history is kept for the server.
++ *
++ * Defaults to 1 hour (72000 ticks)
++ *
++ * @return Duration in Ticks
++ */
++ public static int getHistoryLength() {
++ return historyLength;
++ }
++
++ /**
++ * Sets how long Timing History reports are kept for the server.
++ *
++ * Defaults to 1 hours(72000 ticks)
++ *
++ * This value is capped at a maximum of getHistoryInterval() * MAX_HISTORY_FRAMES (12)
++ *
++ * Will not reset Timing Data but may truncate old history if the new length is less than old length.
++ *
++ * @param length Duration in ticks
++ */
++ public static void setHistoryLength(int length) {
++ // Cap at 12 History Frames, 1 hour at 5 minute frames.
++ int maxLength = historyInterval * MAX_HISTORY_FRAMES;
++ // For special cases of servers with special permission to bypass the max.
++ // This max helps keep data file sizes reasonable for processing on Aikar's Timing parser side.
++ // Setting this will not help you bypass the max unless Aikar has added an exception on the API side.
++ if (System.getProperty("timings.bypassMax") != null) {
++ maxLength = Integer.MAX_VALUE;
++ }
++ historyLength = Math.max(Math.min(maxLength, length), historyInterval);
++ Queue<TimingHistory> oldQueue = TimingsManager.HISTORY;
++ int frames = (getHistoryLength() / getHistoryInterval());
++ if (length > maxLength) {
++ Bukkit.getLogger().log(Level.WARNING, "Timings Length too high. Requested " + length + ", max is " + maxLength + ". To get longer history, you must increase your interval. Set Interval to " + Math.ceil(length / MAX_HISTORY_FRAMES) + " to achieve this length.");
++ }
++ TimingsManager.HISTORY = EvictingQueue.create(frames);
++ TimingsManager.HISTORY.addAll(oldQueue);
++ }
++
++ /**
++ * Resets all Timing Data
++ */
++ public static void reset() {
++ TimingsManager.reset();
++ }
++
++ /**
++ * Generates a report and sends it to the specified command sender.
++ *
++ * If sender is null, ConsoleCommandSender will be used.
++ * @param sender The sender to send to, or null to use the ConsoleCommandSender
++ */
++ public static void generateReport(@Nullable CommandSender sender) {
++ if (sender == null) {
++ sender = Bukkit.getConsoleSender();
++ }
++ requestingReport.add(sender);
++ }
++
++ /**
++ * Generates a report and sends it to the specified listener.
++ * Use with {@link org.bukkit.command.BufferedCommandSender} to get full response when done!
++ * @param sender The listener to send responses too.
++ */
++ public static void generateReport(@NotNull TimingsReportListener sender) {
++ Validate.notNull(sender);
++ requestingReport.add(sender);
++ }
++
++ /*
++ =================
++ Protected API: These are for internal use only in Bukkit/CraftBukkit
++ These do not have isPrimaryThread() checks in the startTiming/stopTiming
++ =================
++ */
++ @NotNull
++ static TimingHandler ofSafe(@NotNull String name) {
++ return ofSafe(null, name, null);
++ }
++
++ @NotNull
++ static Timing ofSafe(@Nullable Plugin plugin, @NotNull String name) {
++ Timing pluginHandler = null;
++ if (plugin != null) {
++ pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER);
++ }
++ return ofSafe(plugin != null ? plugin.getName() : "Minecraft - Invalid Plugin", name, pluginHandler);
++ }
++
++ @NotNull
++ static TimingHandler ofSafe(@NotNull String name, @Nullable Timing groupHandler) {
++ return ofSafe(null, name, groupHandler);
++ }
++
++ @NotNull
++ static TimingHandler ofSafe(@Nullable String groupName, @NotNull String name, @Nullable Timing groupHandler) {
++ return TimingsManager.getHandler(groupName, name, groupHandler);
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f7c2245a310a084367ff25db539b3c967d5cb141
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingsCommand.java
+@@ -0,0 +1,119 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import com.google.common.collect.ImmutableList;
++import org.apache.commons.lang.Validate;
++import org.bukkit.ChatColor;
++import org.bukkit.command.CommandSender;
++import org.bukkit.command.defaults.BukkitCommand;
++import org.bukkit.util.StringUtil;
++
++import java.util.ArrayList;
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++
++public class TimingsCommand extends BukkitCommand {
++ private static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste", "verbon", "verboff");
++ private long lastResetAttempt = 0;
++
++ public TimingsCommand(@NotNull String name) {
++ super(name);
++ this.description = "Manages Spigot Timings data to see performance of the server.";
++ this.usageMessage = "/timings <reset|report|on|off|verbon|verboff>";
++ this.setPermission("bukkit.command.timings");
++ }
++
++ @Override
++ public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
++ if (!testPermission(sender)) {
++ return true;
++ }
++ if (args.length < 1) {
++ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
++ return true;
++ }
++ final String arg = args[0];
++ if ("on".equalsIgnoreCase(arg)) {
++ Timings.setTimingsEnabled(true);
++ sender.sendMessage("Enabled Timings & Reset");
++ return true;
++ } else if ("off".equalsIgnoreCase(arg)) {
++ Timings.setTimingsEnabled(false);
++ sender.sendMessage("Disabled Timings");
++ return true;
++ }
++
++ if (!Timings.isTimingsEnabled()) {
++ sender.sendMessage("Please enable timings by typing /timings on");
++ return true;
++ }
++
++ long now = System.currentTimeMillis();
++ if ("verbon".equalsIgnoreCase(arg)) {
++ Timings.setVerboseTimingsEnabled(true);
++ sender.sendMessage("Enabled Verbose Timings");
++ return true;
++ } else if ("verboff".equalsIgnoreCase(arg)) {
++ Timings.setVerboseTimingsEnabled(false);
++ sender.sendMessage("Disabled Verbose Timings");
++ return true;
++ } else if ("reset".equalsIgnoreCase(arg)) {
++ if (now - lastResetAttempt < 30000) {
++ TimingsManager.reset();
++ sender.sendMessage(ChatColor.RED + "Timings reset. Please wait 5-10 minutes before using /timings report.");
++ } else {
++ lastResetAttempt = now;
++ sender.sendMessage(ChatColor.RED + "WARNING: Timings v2 should not be reset. If you are encountering lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds.");
++ }
++ } else if (
++ "paste".equalsIgnoreCase(arg) ||
++ "report".equalsIgnoreCase(arg) ||
++ "get".equalsIgnoreCase(arg) ||
++ "merged".equalsIgnoreCase(arg) ||
++ "separate".equalsIgnoreCase(arg)
++ ) {
++ Timings.generateReport(sender);
++ } else {
++ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
++ }
++ return true;
++ }
++
++ @NotNull
++ @Override
++ public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) {
++ Validate.notNull(sender, "Sender cannot be null");
++ Validate.notNull(args, "Arguments cannot be null");
++ Validate.notNull(alias, "Alias cannot be null");
++
++ if (args.length == 1) {
++ return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS,
++ new ArrayList<String>(TIMINGS_SUBCOMMANDS.size()));
++ }
++ return ImmutableList.of();
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingsManager.java b/src/main/java/co/aikar/timings/TimingsManager.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ef824d701c97cad8b31e76ad98c94fc4367a7eda
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingsManager.java
+@@ -0,0 +1,188 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import co.aikar.util.LoadingMap;
++import com.google.common.collect.EvictingQueue;
++import org.bukkit.Bukkit;
++import org.bukkit.Server;
++import org.bukkit.command.Command;
++import org.bukkit.plugin.Plugin;
++import org.bukkit.plugin.java.PluginClassLoader;
++
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
++import java.util.Map;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.logging.Level;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public final class TimingsManager {
++ static final Map<TimingIdentifier, TimingHandler> TIMING_MAP = LoadingMap.of(
++ new ConcurrentHashMap<>(4096, .5F), TimingHandler::new
++ );
++ public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler();
++ public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK);
++ public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins");
++ public static List<String> hiddenConfigs = new ArrayList<String>();
++ public static boolean privacy = false;
++
++ static final List<TimingHandler> HANDLERS = new ArrayList<>(1024);
++ static final List<TimingHistory.MinuteReport> MINUTE_REPORTS = new ArrayList<>(64);
++
++ static EvictingQueue<TimingHistory> HISTORY = EvictingQueue.create(12);
++ static long timingStart = 0;
++ static long historyStart = 0;
++ static boolean needsFullReset = false;
++ static boolean needsRecheckEnabled = false;
++
++ private TimingsManager() {}
++
++ /**
++ * Resets all timing data on the next tick
++ */
++ static void reset() {
++ needsFullReset = true;
++ }
++
++ /**
++ * Ticked every tick by CraftBukkit to count the number of times a timer
++ * caused TPS loss.
++ */
++ static void tick() {
++ if (Timings.timingsEnabled) {
++ boolean violated = FULL_SERVER_TICK.isViolated();
++
++ for (TimingHandler handler : HANDLERS) {
++ if (handler.isSpecial()) {
++ // We manually call this
++ continue;
++ }
++ handler.processTick(violated);
++ }
++
++ TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size();
++ TimingHistory.timedTicks++;
++ // Generate TPS/Ping/Tick reports every minute
++ }
++ }
++ static void stopServer() {
++ Timings.timingsEnabled = false;
++ recheckEnabled();
++ }
++ static void recheckEnabled() {
++ synchronized (TIMING_MAP) {
++ for (TimingHandler timings : TIMING_MAP.values()) {
++ timings.checkEnabled();
++ }
++ }
++ needsRecheckEnabled = false;
++ }
++ static void resetTimings() {
++ if (needsFullReset) {
++ // Full resets need to re-check every handlers enabled state
++ // Timing map can be modified from async so we must sync on it.
++ synchronized (TIMING_MAP) {
++ for (TimingHandler timings : TIMING_MAP.values()) {
++ timings.reset(true);
++ }
++ }
++ Bukkit.getLogger().log(Level.INFO, "Timings Reset");
++ HISTORY.clear();
++ needsFullReset = false;
++ needsRecheckEnabled = false;
++ timingStart = System.currentTimeMillis();
++ } else {
++ // Soft resets only need to act on timings that have done something
++ // Handlers can only be modified on main thread.
++ for (TimingHandler timings : HANDLERS) {
++ timings.reset(false);
++ }
++ }
++
++ HANDLERS.clear();
++ MINUTE_REPORTS.clear();
++
++ TimingHistory.resetTicks(true);
++ historyStart = System.currentTimeMillis();
++ }
++
++ @NotNull
++ static TimingHandler getHandler(@Nullable String group, @NotNull String name, @Nullable Timing parent) {
++ return TIMING_MAP.get(new TimingIdentifier(group, name, parent));
++ }
++
++
++ /**
++ * <p>Due to access restrictions, we need a helper method to get a Command TimingHandler with String group</p>
++ *
++ * Plugins should never call this
++ *
++ * @param pluginName Plugin this command is associated with
++ * @param command Command to get timings for
++ * @return TimingHandler
++ */
++ @NotNull
++ public static Timing getCommandTiming(@Nullable String pluginName, @NotNull Command command) {
++ Plugin plugin = null;
++ final Server server = Bukkit.getServer();
++ if (!( server == null || pluginName == null ||
++ "minecraft".equals(pluginName) || "bukkit".equals(pluginName) ||
++ "spigot".equalsIgnoreCase(pluginName) || "paper".equals(pluginName)
++ )) {
++ plugin = server.getPluginManager().getPlugin(pluginName);
++ }
++ if (plugin == null) {
++ // Plugin is passing custom fallback prefix, try to look up by class loader
++ plugin = getPluginByClassloader(command.getClass());
++ }
++ if (plugin == null) {
++ return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName());
++ }
++
++ return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName());
++ }
++
++ /**
++ * Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the
++ * Plugin that created this class.
++ *
++ * @param clazz Class to check
++ * @return Plugin if created by a plugin
++ */
++ @Nullable
++ public static Plugin getPluginByClassloader(@Nullable Class<?> clazz) {
++ if (clazz == null) {
++ return null;
++ }
++ final ClassLoader classLoader = clazz.getClassLoader();
++ if (classLoader instanceof PluginClassLoader) {
++ PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader;
++ return pluginClassLoader.getPlugin();
++ }
++ return null;
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/TimingsReportListener.java b/src/main/java/co/aikar/timings/TimingsReportListener.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ef58a6c00f444bd498a2d8fc4e457236f393954f
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/TimingsReportListener.java
+@@ -0,0 +1,77 @@
++package co.aikar.timings;
++
++import com.google.common.collect.Lists;
++import org.apache.commons.lang.Validate;
++import org.bukkit.Bukkit;
++import org.bukkit.command.CommandSender;
++import org.bukkit.command.ConsoleCommandSender;
++import org.bukkit.command.MessageCommandSender;
++import org.bukkit.command.RemoteConsoleCommandSender;
++
++import java.util.List;
++import java.util.UUID;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++@SuppressWarnings("WeakerAccess")
++public class TimingsReportListener implements MessageCommandSender {
++ private final List<CommandSender> senders;
++ private final Runnable onDone;
++ private String timingsURL;
++
++ public TimingsReportListener(@NotNull CommandSender senders) {
++ this(senders, null);
++ }
++ public TimingsReportListener(@NotNull CommandSender sender, @Nullable Runnable onDone) {
++ this(Lists.newArrayList(sender), onDone);
++ }
++ public TimingsReportListener(@NotNull List<CommandSender> senders) {
++ this(senders, null);
++ }
++ public TimingsReportListener(@NotNull List<CommandSender> senders, @Nullable Runnable onDone) {
++ Validate.notNull(senders);
++ Validate.notEmpty(senders);
++
++ this.senders = Lists.newArrayList(senders);
++ this.onDone = onDone;
++ }
++
++ @Nullable
++ public String getTimingsURL() {
++ return timingsURL;
++ }
++
++ public void done() {
++ done(null);
++ }
++
++ public void done(@Nullable String url) {
++ this.timingsURL = url;
++ if (onDone != null) {
++ onDone.run();
++ }
++ for (CommandSender sender : senders) {
++ if (sender instanceof TimingsReportListener) {
++ ((TimingsReportListener) sender).done();
++ }
++ }
++ }
++
++ @Override
++ public void sendMessage(@NotNull String message) {
++ senders.forEach((sender) -> sender.sendMessage(message));
++ }
++
++ public void addConsoleIfNeeded() {
++ boolean hasConsole = false;
++ for (CommandSender sender : this.senders) {
++ if (sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
++ hasConsole = true;
++ }
++ }
++ if (!hasConsole) {
++ this.senders.add(Bukkit.getConsoleSender());
++ }
++ }
++}
+diff --git a/src/main/java/co/aikar/timings/UnsafeTimingHandler.java b/src/main/java/co/aikar/timings/UnsafeTimingHandler.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..632c4961515f5052551f841cfa840e60bba7a257
+--- /dev/null
++++ b/src/main/java/co/aikar/timings/UnsafeTimingHandler.java
+@@ -0,0 +1,53 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.timings;
++
++import org.bukkit.Bukkit;
++import org.jetbrains.annotations.NotNull;
++
++class UnsafeTimingHandler extends TimingHandler {
++
++ UnsafeTimingHandler(@NotNull TimingIdentifier id) {
++ super(id);
++ }
++
++ private static void checkThread() {
++ if (!Bukkit.isPrimaryThread()) {
++ throw new IllegalStateException("Calling Timings from Async Operation");
++ }
++ }
++
++ @NotNull
++ @Override
++ public Timing startTiming() {
++ checkThread();
++ return super.startTiming();
++ }
++
++ @Override
++ public void stopTiming() {
++ checkThread();
++ super.stopTiming();
++ }
++}
+diff --git a/src/main/java/co/aikar/util/Counter.java b/src/main/java/co/aikar/util/Counter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..80155072d1004e34e04342d434cf7d75f0b7e29d
+--- /dev/null
++++ b/src/main/java/co/aikar/util/Counter.java
+@@ -0,0 +1,38 @@
++package co.aikar.util;
++
++import com.google.common.collect.ForwardingMap;
++
++import java.util.HashMap;
++import java.util.Map;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class Counter <T> extends ForwardingMap<T, Long> {
++ private final Map<T, Long> counts = new HashMap<>();
++
++ public long decrement(@Nullable T key) {
++ return increment(key, -1);
++ }
++ public long increment(@Nullable T key) {
++ return increment(key, 1);
++ }
++ public long decrement(@Nullable T key, long amount) {
++ return decrement(key, -amount);
++ }
++ public long increment(@Nullable T key, long amount) {
++ Long count = this.getCount(key);
++ count += amount;
++ this.counts.put(key, count);
++ return count;
++ }
++
++ public long getCount(@Nullable T key) {
++ return this.counts.getOrDefault(key, 0L);
++ }
++
++ @NotNull
++ @Override
++ protected Map<T, Long> delegate() {
++ return this.counts;
++ }
++}
+diff --git a/src/main/java/co/aikar/util/JSONUtil.java b/src/main/java/co/aikar/util/JSONUtil.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..190bf0598442c89c2a1c93ad7c8c1a29797304ae
+--- /dev/null
++++ b/src/main/java/co/aikar/util/JSONUtil.java
+@@ -0,0 +1,140 @@
++package co.aikar.util;
++
++import com.google.common.base.Function;
++import com.google.common.collect.Lists;
++import com.google.common.collect.Maps;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++import org.json.simple.JSONArray;
++import org.json.simple.JSONObject;
++
++import java.util.ArrayList;
++import java.util.LinkedHashMap;
++import java.util.List;
++import java.util.Map;
++
++/**
++ * Provides Utility methods that assist with generating JSON Objects
++ */
++@SuppressWarnings({"rawtypes", "SuppressionAnnotation"})
++public final class JSONUtil {
++ private JSONUtil() {}
++
++ /**
++ * Creates a key/value "JSONPair" object
++ *
++ * @param key Key to use
++ * @param obj Value to use
++ * @return JSONPair
++ */
++ @NotNull
++ public static JSONPair pair(@NotNull String key, @Nullable Object obj) {
++ return new JSONPair(key, obj);
++ }
++
++ @NotNull
++ public static JSONPair pair(long key, @Nullable Object obj) {
++ return new JSONPair(String.valueOf(key), obj);
++ }
++
++ /**
++ * Creates a new JSON object from multiple JSONPair key/value pairs
++ *
++ * @param data JSONPairs
++ * @return Map
++ */
++ @NotNull
++ public static Map<String, Object> createObject(@NotNull JSONPair... data) {
++ return appendObjectData(new LinkedHashMap(), data);
++ }
++
++ /**
++ * This appends multiple key/value Obj pairs into a JSON Object
++ *
++ * @param parent Map to be appended to
++ * @param data Data to append
++ * @return Map
++ */
++ @NotNull
++ public static Map<String, Object> appendObjectData(@NotNull Map parent, @NotNull JSONPair... data) {
++ for (JSONPair JSONPair : data) {
++ parent.put(JSONPair.key, JSONPair.val);
++ }
++ return parent;
++ }
++
++ /**
++ * This builds a JSON array from a set of data
++ *
++ * @param data Data to build JSON array from
++ * @return List
++ */
++ @NotNull
++ public static List toArray(@NotNull Object... data) {
++ return Lists.newArrayList(data);
++ }
++
++ /**
++ * These help build a single JSON array using a mapper function
++ *
++ * @param collection Collection to apply to
++ * @param mapper Mapper to apply
++ * @param <E> Element Type
++ * @return List
++ */
++ @NotNull
++ public static <E> List toArrayMapper(@NotNull E[] collection, @NotNull Function<E, Object> mapper) {
++ return toArrayMapper(Lists.newArrayList(collection), mapper);
++ }
++
++ @NotNull
++ public static <E> List toArrayMapper(@NotNull Iterable<E> collection, @NotNull Function<E, Object> mapper) {
++ List array = Lists.newArrayList();
++ for (E e : collection) {
++ Object object = mapper.apply(e);
++ if (object != null) {
++ array.add(object);
++ }
++ }
++ return array;
++ }
++
++ /**
++ * These help build a single JSON Object from a collection, using a mapper function
++ *
++ * @param collection Collection to apply to
++ * @param mapper Mapper to apply
++ * @param <E> Element Type
++ * @return Map
++ */
++ @NotNull
++ public static <E> Map toObjectMapper(@NotNull E[] collection, @NotNull Function<E, JSONPair> mapper) {
++ return toObjectMapper(Lists.newArrayList(collection), mapper);
++ }
++
++ @NotNull
++ public static <E> Map toObjectMapper(@NotNull Iterable<E> collection, @NotNull Function<E, JSONPair> mapper) {
++ Map object = Maps.newLinkedHashMap();
++ for (E e : collection) {
++ JSONPair JSONPair = mapper.apply(e);
++ if (JSONPair != null) {
++ object.put(JSONPair.key, JSONPair.val);
++ }
++ }
++ return object;
++ }
++
++ /**
++ * Simply stores a key and a value, used internally by many methods below.
++ */
++ @SuppressWarnings("PublicInnerClass")
++ public static class JSONPair {
++ final String key;
++ final Object val;
++
++ JSONPair(@NotNull String key, @NotNull Object val) {
++ this.key = key;
++ this.val = val;
++ }
++ }
++}
+diff --git a/src/main/java/co/aikar/util/LoadingIntMap.java b/src/main/java/co/aikar/util/LoadingIntMap.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..63a899c7dbdb69daa4876a2ce2a7dfb734b5af9d
+--- /dev/null
++++ b/src/main/java/co/aikar/util/LoadingIntMap.java
+@@ -0,0 +1,76 @@
++/*
++ * Copyright (c) 2015. Starlis LLC / dba Empire Minecraft
++ *
++ * This source code is proprietary software and must not be redistributed without Starlis LLC's approval
++ *
++ */
++package co.aikar.util;
++
++
++import com.google.common.base.Function;
++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Allows you to pass a Loader function that when a key is accessed that doesn't exist,
++ * automatically loads the entry into the map by calling the loader Function.
++ *
++ * .get() Will only return null if the Loader can return null.
++ *
++ * You may pass any backing Map to use.
++ *
++ * This class is not thread safe and should be wrapped with Collections.synchronizedMap on the OUTSIDE of the LoadingMap if needed.
++ *
++ * Do not wrap the backing map with Collections.synchronizedMap.
++ *
++ * @param <V> Value
++ */
++public class LoadingIntMap<V> extends Int2ObjectOpenHashMap<V> {
++ private final Function<Integer, V> loader;
++
++ public LoadingIntMap(@NotNull Function<Integer, V> loader) {
++ super();
++ this.loader = loader;
++ }
++
++ public LoadingIntMap(int expectedSize, @NotNull Function<Integer, V> loader) {
++ super(expectedSize);
++ this.loader = loader;
++ }
++
++ public LoadingIntMap(int expectedSize, float loadFactor, @NotNull Function<Integer, V> loader) {
++ super(expectedSize, loadFactor);
++ this.loader = loader;
++ }
++
++
++ @Nullable
++ @Override
++ public V get(int key) {
++ V res = super.get(key);
++ if (res == null) {
++ res = loader.apply(key);
++ if (res != null) {
++ put(key, res);
++ }
++ }
++ return res;
++ }
++
++ /**
++ * Due to java stuff, you will need to cast it to (Function) for some cases
++ *
++ * @param <T> Type
++ */
++ public abstract static class Feeder <T> implements Function<T, T> {
++ @Nullable
++ @Override
++ public T apply(@Nullable Object input) {
++ return apply();
++ }
++
++ @Nullable
++ public abstract T apply();
++ }
++}
+diff --git a/src/main/java/co/aikar/util/LoadingMap.java b/src/main/java/co/aikar/util/LoadingMap.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..aedbb03321886cb267879d7994653e447b485f6a
+--- /dev/null
++++ b/src/main/java/co/aikar/util/LoadingMap.java
+@@ -0,0 +1,368 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.util;
++
++import com.google.common.base.Preconditions;
++import java.lang.reflect.Constructor;
++import java.util.AbstractMap;
++import java.util.Collection;
++import java.util.HashMap;
++import java.util.IdentityHashMap;
++import java.util.Map;
++import java.util.Set;
++import java.util.function.Function;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Allows you to pass a Loader function that when a key is accessed that doesn't exists,
++ * automatically loads the entry into the map by calling the loader Function.
++ *
++ * .get() Will only return null if the Loader can return null.
++ *
++ * You may pass any backing Map to use.
++ *
++ * This class is not thread safe and should be wrapped with Collections.synchronizedMap on the OUTSIDE of the LoadingMap if needed.
++ *
++ * Do not wrap the backing map with Collections.synchronizedMap.
++ *
++ * @param <K> Key
++ * @param <V> Value
++ */
++public class LoadingMap <K, V> extends AbstractMap<K, V> {
++ private final Map<K, V> backingMap;
++ private final java.util.function.Function<K, V> loader;
++
++ /**
++ * Initializes an auto loading map using specified loader and backing map
++ * @param backingMap Map to wrap
++ * @param loader Loader
++ */
++ public LoadingMap(@NotNull Map<K, V> backingMap, @NotNull java.util.function.Function<K, V> loader) {
++ this.backingMap = backingMap;
++ this.loader = loader;
++ }
++
++ /**
++ * Creates a new LoadingMap with the specified map and loader
++ *
++ * @param backingMap Actual map being used.
++ * @param loader Loader to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> of(@NotNull Map<K, V> backingMap, @NotNull Function<K, V> loader) {
++ return new LoadingMap<>(backingMap, loader);
++ }
++
++ /**
++ * Creates a LoadingMap with an auto instantiating loader.
++ *
++ * Will auto construct class of of Value when not found
++ *
++ * Since this uses Reflection, It is more effecient to define your own static loader
++ * than using this helper, but if performance is not critical, this is easier.
++ *
++ * @param backingMap Actual map being used.
++ * @param keyClass Class used for the K generic
++ * @param valueClass Class used for the V generic
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newAutoMap(@NotNull Map<K, V> backingMap, @Nullable final Class<? extends K> keyClass,
++ @NotNull final Class<? extends V> valueClass) {
++ return new LoadingMap<>(backingMap, new AutoInstantiatingLoader<>(keyClass, valueClass));
++ }
++ /**
++ * Creates a LoadingMap with an auto instantiating loader.
++ *
++ * Will auto construct class of of Value when not found
++ *
++ * Since this uses Reflection, It is more effecient to define your own static loader
++ * than using this helper, but if performance is not critical, this is easier.
++ *
++ * @param backingMap Actual map being used.
++ * @param valueClass Class used for the V generic
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newAutoMap(@NotNull Map<K, V> backingMap,
++ @NotNull final Class<? extends V> valueClass) {
++ return newAutoMap(backingMap, null, valueClass);
++ }
++
++ /**
++ * @see #newAutoMap
++ *
++ * new Auto initializing map using a HashMap.
++ *
++ * @param keyClass Class used for the K generic
++ * @param valueClass Class used for the V generic
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashAutoMap(@Nullable final Class<? extends K> keyClass, @NotNull final Class<? extends V> valueClass) {
++ return newAutoMap(new HashMap<>(), keyClass, valueClass);
++ }
++
++ /**
++ * @see #newAutoMap
++ *
++ * new Auto initializing map using a HashMap.
++ *
++ * @param valueClass Class used for the V generic
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashAutoMap(@NotNull final Class<? extends V> valueClass) {
++ return newHashAutoMap(null, valueClass);
++ }
++
++ /**
++ * @see #newAutoMap
++ *
++ * new Auto initializing map using a HashMap.
++ *
++ * @param keyClass Class used for the K generic
++ * @param valueClass Class used for the V generic
++ * @param initialCapacity Initial capacity to use
++ * @param loadFactor Load factor to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashAutoMap(@Nullable final Class<? extends K> keyClass, @NotNull final Class<? extends V> valueClass, int initialCapacity, float loadFactor) {
++ return newAutoMap(new HashMap<>(initialCapacity, loadFactor), keyClass, valueClass);
++ }
++
++ /**
++ * @see #newAutoMap
++ *
++ * new Auto initializing map using a HashMap.
++ *
++ * @param valueClass Class used for the V generic
++ * @param initialCapacity Initial capacity to use
++ * @param loadFactor Load factor to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map that auto instantiates on .get()
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashAutoMap(@NotNull final Class<? extends V> valueClass, int initialCapacity, float loadFactor) {
++ return newHashAutoMap(null, valueClass, initialCapacity, loadFactor);
++ }
++
++ /**
++ * Initializes an auto loading map using a HashMap
++ *
++ * @param loader Loader to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashMap(@NotNull Function<K, V> loader) {
++ return new LoadingMap<>(new HashMap<>(), loader);
++ }
++
++ /**
++ * Initializes an auto loading map using a HashMap
++ *
++ * @param loader Loader to use
++ * @param initialCapacity Initial capacity to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashMap(@NotNull Function<K, V> loader, int initialCapacity) {
++ return new LoadingMap<>(new HashMap<>(initialCapacity), loader);
++ }
++ /**
++ * Initializes an auto loading map using a HashMap
++ *
++ * @param loader Loader to use
++ * @param initialCapacity Initial capacity to use
++ * @param loadFactor Load factor to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newHashMap(@NotNull Function<K, V> loader, int initialCapacity, float loadFactor) {
++ return new LoadingMap<>(new HashMap<>(initialCapacity, loadFactor), loader);
++ }
++
++ /**
++ * Initializes an auto loading map using an Identity HashMap
++ *
++ * @param loader Loader to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newIdentityHashMap(@NotNull Function<K, V> loader) {
++ return new LoadingMap<>(new IdentityHashMap<>(), loader);
++ }
++
++ /**
++ * Initializes an auto loading map using an Identity HashMap
++ *
++ * @param loader Loader to use
++ * @param initialCapacity Initial capacity to use
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> newIdentityHashMap(@NotNull Function<K, V> loader, int initialCapacity) {
++ return new LoadingMap<>(new IdentityHashMap<>(initialCapacity), loader);
++ }
++
++ @Override
++ public int size() {return backingMap.size();}
++
++ @Override
++ public boolean isEmpty() {return backingMap.isEmpty();}
++
++ @Override
++ public boolean containsKey(@Nullable Object key) {return backingMap.containsKey(key);}
++
++ @Override
++ public boolean containsValue(@Nullable Object value) {return backingMap.containsValue(value);}
++
++ @Nullable
++ @Override
++ public V get(@Nullable Object key) {
++ V v = backingMap.get(key);
++ if (v != null) {
++ return v;
++ }
++ return backingMap.computeIfAbsent((K) key, loader);
++ }
++
++ @Nullable
++ public V put(@Nullable K key, @Nullable V value) {return backingMap.put(key, value);}
++
++ @Nullable
++ @Override
++ public V remove(@Nullable Object key) {return backingMap.remove(key);}
++
++ public void putAll(@NotNull Map<? extends K, ? extends V> m) {backingMap.putAll(m);}
++
++ @Override
++ public void clear() {backingMap.clear();}
++
++ @NotNull
++ @Override
++ public Set<K> keySet() {return backingMap.keySet();}
++
++ @NotNull
++ @Override
++ public Collection<V> values() {return backingMap.values();}
++
++ @Override
++ public boolean equals(@Nullable Object o) {return backingMap.equals(o);}
++
++ @Override
++ public int hashCode() {return backingMap.hashCode();}
++
++ @NotNull
++ @Override
++ public Set<Entry<K, V>> entrySet() {
++ return backingMap.entrySet();
++ }
++
++ @NotNull
++ public LoadingMap<K, V> clone() {
++ return new LoadingMap<>(backingMap, loader);
++ }
++
++ private static class AutoInstantiatingLoader<K, V> implements Function<K, V> {
++ final Constructor<? extends V> constructor;
++ private final Class<? extends V> valueClass;
++
++ AutoInstantiatingLoader(@Nullable Class<? extends K> keyClass, @NotNull Class<? extends V> valueClass) {
++ try {
++ this.valueClass = valueClass;
++ if (keyClass != null) {
++ constructor = valueClass.getConstructor(keyClass);
++ } else {
++ constructor = null;
++ }
++ } catch (NoSuchMethodException e) {
++ throw new IllegalStateException(
++ valueClass.getName() + " does not have a constructor for " + (keyClass != null ? keyClass.getName() : null));
++ }
++ }
++
++ @NotNull
++ @Override
++ public V apply(@Nullable K input) {
++ try {
++ return (constructor != null ? constructor.newInstance(input) : valueClass.newInstance());
++ } catch (Exception e) {
++ throw new ExceptionInInitializerError(e);
++ }
++ }
++
++ @Override
++ public int hashCode() {
++ return super.hashCode();
++ }
++
++ @Override
++ public boolean equals(Object object) {
++ return false;
++ }
++ }
++
++ /**
++ * Due to java stuff, you will need to cast it to (Function) for some cases
++ *
++ * @param <T> Type
++ */
++ public abstract static class Feeder <T> implements Function<T, T> {
++ @Nullable
++ @Override
++ public T apply(@Nullable Object input) {
++ return apply();
++ }
++
++ @Nullable
++ public abstract T apply();
++ }
++}
+diff --git a/src/main/java/co/aikar/util/MRUMapCache.java b/src/main/java/co/aikar/util/MRUMapCache.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5989ee21297935651b0edd44b8239e655eaef1d9
+--- /dev/null
++++ b/src/main/java/co/aikar/util/MRUMapCache.java
+@@ -0,0 +1,111 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++package co.aikar.util;
++
++import java.util.AbstractMap;
++import java.util.Collection;
++import java.util.Map;
++import java.util.Set;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Implements a Most Recently Used cache in front of a backing map, to quickly access the last accessed result.
++ *
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ */
++public class MRUMapCache<K, V> extends AbstractMap<K, V> {
++ final Map<K, V> backingMap;
++ Object cacheKey;
++ V cacheValue;
++ public MRUMapCache(@NotNull final Map<K, V> backingMap) {
++ this.backingMap = backingMap;
++ }
++
++ public int size() {return backingMap.size();}
++
++ public boolean isEmpty() {return backingMap.isEmpty();}
++
++ public boolean containsKey(@Nullable Object key) {
++ return key != null && key.equals(cacheKey) || backingMap.containsKey(key);
++ }
++
++ public boolean containsValue(@Nullable Object value) {
++ return value != null && value == cacheValue || backingMap.containsValue(value);
++ }
++
++ @Nullable
++ public V get(@Nullable Object key) {
++ if (cacheKey != null && cacheKey.equals(key)) {
++ return cacheValue;
++ }
++ cacheKey = key;
++ return cacheValue = backingMap.get(key);
++ }
++
++ @Nullable
++ public V put(@Nullable K key, @Nullable V value) {
++ cacheKey = key;
++ return cacheValue = backingMap.put(key, value);
++ }
++
++ @Nullable
++ public V remove(@Nullable Object key) {
++ if (key != null && key.equals(cacheKey)) {
++ cacheKey = null;
++ }
++ return backingMap.remove(key);
++ }
++
++ public void putAll(@NotNull Map<? extends K, ? extends V> m) {backingMap.putAll(m);}
++
++ public void clear() {
++ cacheKey = null;
++ cacheValue = null;
++ backingMap.clear();
++ }
++
++ @NotNull
++ public Set<K> keySet() {return backingMap.keySet();}
++
++ @NotNull
++ public Collection<V> values() {return backingMap.values();}
++
++ @NotNull
++ public Set<Map.Entry<K, V>> entrySet() {return backingMap.entrySet();}
++
++ /**
++ * Wraps the specified map with a most recently used cache
++ *
++ * @param map Map to be wrapped
++ * @param <K> Key Type of the Map
++ * @param <V> Value Type of the Map
++ * @return Map
++ */
++ @NotNull
++ public static <K, V> Map<K, V> of(@NotNull Map<K, V> map) {
++ return new MRUMapCache<K, V>(map);
++ }
++}
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 7168dd083ee30a47b104ab32cabb3215815f7470..7c715fdc11ab7837552b1fe3ffd08b31cec0a63b 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -649,7 +649,6 @@ public final class Bukkit {
+ */
+ public static void reload() {
+ server.reload();
+- org.spigotmc.CustomTimingsHandler.reload(); // Spigot
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 4ba8572f1beb3b9ad46620946eb4ee89ac91818e..a6b9e4f158583e5932bf8ca210d531857e9f5360 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1344,6 +1344,26 @@ public interface Server extends PluginMessageRecipient {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
++ // Paper start
++ @NotNull
++ public org.bukkit.configuration.file.YamlConfiguration getBukkitConfig()
++ {
++ throw new UnsupportedOperationException( "Not supported yet." );
++ }
++
++ @NotNull
++ public org.bukkit.configuration.file.YamlConfiguration getSpigotConfig()
++ {
++ throw new UnsupportedOperationException("Not supported yet.");
++ }
++
++ @NotNull
++ public org.bukkit.configuration.file.YamlConfiguration getPaperConfig()
++ {
++ throw new UnsupportedOperationException("Not supported yet.");
++ }
++ // Paper end
++
+ /**
+ * Sends the component to the player
+ *
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 247d194f86c00db11acbc58e7d163b2606db4f07..945b8b030d1b2a13afc0c4efad76997eb7bf00ba 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -18,6 +18,7 @@ import org.bukkit.plugin.PluginDescriptionFile;
+ @Deprecated
+ public interface UnsafeValues {
+
++ void reportTimings(); // Paper
+ Material toLegacy(Material material);
+
+ Material fromLegacy(Material material);
+@@ -69,4 +70,12 @@ public interface UnsafeValues {
+ * @return true if a file matching this key was found and deleted
+ */
+ boolean removeAdvancement(NamespacedKey key);
++
++ // Paper start
++ /**
++ * Server name to report to timings v2
++ * @return name
++ */
++ String getTimingsServerName();
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/BufferedCommandSender.java b/src/main/java/org/bukkit/command/BufferedCommandSender.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f9a00aecca5ec41b460bf41dfe1c69694768cf98
+--- /dev/null
++++ b/src/main/java/org/bukkit/command/BufferedCommandSender.java
+@@ -0,0 +1,21 @@
++package org.bukkit.command;
++
++import org.jetbrains.annotations.NotNull;
++
++public class BufferedCommandSender implements MessageCommandSender {
++ private final StringBuffer buffer = new StringBuffer();
++ @Override
++ public void sendMessage(@NotNull String message) {
++ buffer.append(message);
++ buffer.append("\n");
++ }
++
++ @NotNull
++ public String getBuffer() {
++ return buffer.toString();
++ }
++
++ public void reset() {
++ this.buffer.setLength(0);
++ }
++}
+diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java
+index 4bfc214685164a38ba4261b2bae7faa8a3bd297e..03bdc1622791e1206406c87065978688d602e39e 100644
+--- a/src/main/java/org/bukkit/command/Command.java
++++ b/src/main/java/org/bukkit/command/Command.java
+@@ -33,7 +33,8 @@ public abstract class Command {
+ protected String usageMessage;
+ private String permission;
+ private String permissionMessage;
+- public org.spigotmc.CustomTimingsHandler timings; // Spigot
++ public co.aikar.timings.Timing timings; // Paper
++ @NotNull public String getTimingName() {return getName();} // Paper
+
+ protected Command(@NotNull String name) {
+ this(name, "", "/" + name, new ArrayList<String>());
+@@ -47,7 +48,6 @@ public abstract class Command {
+ this.usageMessage = (usageMessage == null) ? "/" + name : usageMessage;
+ this.aliases = aliases;
+ this.activeAliases = new ArrayList<String>(aliases);
+- this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot
+ }
+
+ /**
+@@ -245,7 +245,6 @@ public abstract class Command {
+ }
+ this.nextLabel = name;
+ if (!isRegistered()) {
+- this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot
+ this.label = name;
+ return true;
+ }
+diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java
+index d6c8938b1e13b63116b7b0e074ea8ef5997f8dc3..a6ad94ef98a1df1d2842635d850bc990b0137849 100644
+--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java
++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java
+@@ -9,6 +9,7 @@ public class FormattedCommandAlias extends Command {
+
+ public FormattedCommandAlias(@NotNull String alias, @NotNull String[] formatStrings) {
+ super(alias);
++ timings = co.aikar.timings.TimingsManager.getCommandTiming("minecraft", this); // Spigot
+ this.formatStrings = formatStrings;
+ }
+
+@@ -113,6 +114,10 @@ public class FormattedCommandAlias extends Command {
+ return formatString;
+ }
+
++ @NotNull
++ @Override // Paper
++ public String getTimingName() {return "Command Forwarder - " + super.getTimingName();} // Paper
++
+ private static boolean inRange(int i, int j, int k) {
+ return i >= j && i <= k;
+ }
+diff --git a/src/main/java/org/bukkit/command/MessageCommandSender.java b/src/main/java/org/bukkit/command/MessageCommandSender.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a7ef1f51c2b96617a32e6e7b1723e8770ba8a6a8
+--- /dev/null
++++ b/src/main/java/org/bukkit/command/MessageCommandSender.java
+@@ -0,0 +1,129 @@
++package org.bukkit.command;
++
++import org.apache.commons.lang.NotImplementedException;
++import org.bukkit.Bukkit;
++import org.bukkit.Server;
++import org.bukkit.permissions.Permission;
++import org.bukkit.permissions.PermissionAttachment;
++import org.bukkit.permissions.PermissionAttachmentInfo;
++import org.bukkit.plugin.Plugin;
++
++import java.util.Set;
++import java.util.UUID;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * For when all you care about is just messaging
++ */
++public interface MessageCommandSender extends CommandSender {
++
++ @Override
++ default void sendMessage(@NotNull String[] messages) {
++ for (String message : messages) {
++ sendMessage(message);
++ }
++ }
++
++ @Override
++ default void sendMessage(@Nullable UUID sender, @NotNull String message) {
++ sendMessage(message);
++ }
++
++ @Override
++ default void sendMessage(@Nullable UUID sender, @NotNull String[] messages) {
++ for (String message : messages) {
++ sendMessage(message);
++ }
++ }
++
++ @NotNull
++ @Override
++ default Server getServer() {
++ return Bukkit.getServer();
++ }
++
++ @NotNull
++ @Override
++ default String getName() {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default boolean isOp() {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default void setOp(boolean value) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default boolean isPermissionSet(@NotNull String name) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default boolean isPermissionSet(@NotNull Permission perm) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default boolean hasPermission(@NotNull String name) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default boolean hasPermission(@NotNull Permission perm) {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default PermissionAttachment addAttachment(@NotNull Plugin plugin) {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default void removeAttachment(@NotNull PermissionAttachment attachment) {
++ throw new NotImplementedException();
++ }
++
++ @Override
++ default void recalculatePermissions() {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default Set<PermissionAttachmentInfo> getEffectivePermissions() {
++ throw new NotImplementedException();
++ }
++
++ @NotNull
++ @Override
++ default Spigot spigot() {
++ throw new NotImplementedException();
++ }
++
++}
+diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+index 81e4fa57337f5a40c4b673136dd5eb595cce4629..f020cb04eba27a2e70fc7cf799ebbfb434b9d974 100644
+--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+@@ -15,7 +15,6 @@ import org.bukkit.command.defaults.BukkitCommand;
+ import org.bukkit.command.defaults.HelpCommand;
+ import org.bukkit.command.defaults.PluginsCommand;
+ import org.bukkit.command.defaults.ReloadCommand;
+-import org.bukkit.command.defaults.TimingsCommand;
+ import org.bukkit.command.defaults.VersionCommand;
+ import org.bukkit.entity.Player;
+ import org.bukkit.util.StringUtil;
+@@ -35,7 +34,7 @@ public class SimpleCommandMap implements CommandMap {
+ register("bukkit", new VersionCommand("version"));
+ register("bukkit", new ReloadCommand("reload"));
+ register("bukkit", new PluginsCommand("plugins"));
+- register("bukkit", new TimingsCommand("timings"));
++ register("bukkit", new co.aikar.timings.TimingsCommand("timings")); // Paper
+ }
+
+ public void setFallbackCommands() {
+@@ -67,6 +66,7 @@ public class SimpleCommandMap implements CommandMap {
+ */
+ @Override
+ public boolean register(@NotNull String label, @NotNull String fallbackPrefix, @NotNull Command command) {
++ command.timings = co.aikar.timings.TimingsManager.getCommandTiming(fallbackPrefix, command); // Paper
+ label = label.toLowerCase(java.util.Locale.ENGLISH).trim();
+ fallbackPrefix = fallbackPrefix.toLowerCase(java.util.Locale.ENGLISH).trim();
+ boolean registered = register(label, command, false, fallbackPrefix);
+@@ -143,16 +143,22 @@ public class SimpleCommandMap implements CommandMap {
+ return false;
+ }
+
++ // Paper start - Plugins do weird things to workaround normal registration
++ if (target.timings == null) {
++ target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target);
++ }
++ // Paper end
++
+ try {
+- target.timings.startTiming(); // Spigot
++ try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources
+ // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
+ target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length));
+- target.timings.stopTiming(); // Spigot
++ } // target.timings.stopTiming(); // Spigot // Paper
+ } catch (CommandException ex) {
+- target.timings.stopTiming(); // Spigot
++ //target.timings.stopTiming(); // Spigot // Paper
+ throw ex;
+ } catch (Throwable ex) {
+- target.timings.stopTiming(); // Spigot
++ //target.timings.stopTiming(); // Spigot // Paper
+ throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex);
+ }
+
+diff --git a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java b/src/main/java/org/bukkit/command/defaults/TimingsCommand.java
+deleted file mode 100644
+index 2a145d851ce30360aa39549745bd87590c034584..0000000000000000000000000000000000000000
+--- a/src/main/java/org/bukkit/command/defaults/TimingsCommand.java
++++ /dev/null
+@@ -1,250 +0,0 @@
+-package org.bukkit.command.defaults;
+-
+-import com.google.common.collect.ImmutableList;
+-import java.io.File;
+-import java.io.IOException;
+-import java.io.PrintStream;
+-import java.util.ArrayList;
+-import java.util.List;
+-import org.apache.commons.lang.Validate;
+-import org.bukkit.Bukkit;
+-import org.bukkit.ChatColor;
+-import org.bukkit.command.CommandSender;
+-import org.bukkit.event.Event;
+-import org.bukkit.event.HandlerList;
+-import org.bukkit.plugin.Plugin;
+-import org.bukkit.plugin.RegisteredListener;
+-import org.bukkit.plugin.TimedRegisteredListener;
+-import org.bukkit.util.StringUtil;
+-import org.jetbrains.annotations.NotNull;
+-
+-// Spigot start
+-// CHECKSTYLE:OFF
+-import java.io.ByteArrayOutputStream;
+-import java.io.OutputStream;
+-import java.net.HttpURLConnection;
+-import java.net.URL;
+-import java.util.logging.Level;
+-import org.bukkit.command.RemoteConsoleCommandSender;
+-import org.bukkit.plugin.SimplePluginManager;
+-import org.spigotmc.CustomTimingsHandler;
+-// CHECKSTYLE:ON
+-// Spigot end
+-
+-public class TimingsCommand extends BukkitCommand {
+- private static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste"); // Spigot
+- public static long timingStart = 0; // Spigot
+-
+- public TimingsCommand(@NotNull String name) {
+- super(name);
+- this.description = "Manages Spigot Timings data to see performance of the server."; // Spigot
+- this.usageMessage = "/timings <reset|report|on|off|paste>"; // Spigot
+- this.setPermission("bukkit.command.timings");
+- }
+-
+- // Spigot start - redesigned Timings Command
+- public void executeSpigotTimings(@NotNull CommandSender sender, @NotNull String[] args) {
+- if ("on".equals(args[0])) {
+- ((SimplePluginManager) Bukkit.getPluginManager()).useTimings(true);
+- CustomTimingsHandler.reload();
+- sender.sendMessage("Enabled Timings & Reset");
+- return;
+- } else if ("off".equals(args[0])) {
+- ((SimplePluginManager) Bukkit.getPluginManager()).useTimings(false);
+- sender.sendMessage("Disabled Timings");
+- return;
+- }
+-
+- if (!Bukkit.getPluginManager().useTimings()) {
+- sender.sendMessage("Please enable timings by typing /timings on");
+- return;
+- }
+-
+- boolean paste = "paste".equals(args[0]);
+- if ("reset".equals(args[0])) {
+- CustomTimingsHandler.reload();
+- sender.sendMessage("Timings reset");
+- } else if ("merged".equals(args[0]) || "report".equals(args[0]) || paste) {
+- long sampleTime = System.nanoTime() - timingStart;
+- int index = 0;
+- File timingFolder = new File("timings");
+- timingFolder.mkdirs();
+- File timings = new File(timingFolder, "timings.txt");
+- ByteArrayOutputStream bout = (paste) ? new ByteArrayOutputStream() : null;
+- while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt");
+- PrintStream fileTimings = null;
+- try {
+- fileTimings = (paste) ? new PrintStream(bout) : new PrintStream(timings);
+-
+- CustomTimingsHandler.printTimings(fileTimings);
+- fileTimings.println("Sample time " + sampleTime + " (" + sampleTime / 1E9 + "s)");
+-
+- fileTimings.println("<spigotConfig>");
+- fileTimings.println(Bukkit.spigot().getConfig().saveToString());
+- fileTimings.println("</spigotConfig>");
+-
+- if (paste) {
+- new PasteThread(sender, bout).start();
+- return;
+- }
+-
+- sender.sendMessage("Timings written to " + timings.getPath());
+- sender.sendMessage("Paste contents of file into form at http://www.spigotmc.org/go/timings to read results.");
+-
+- } catch (IOException e) {
+- } finally {
+- if (fileTimings != null) {
+- fileTimings.close();
+- }
+- }
+- }
+- }
+- // Spigot end
+-
+- @Override
+- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
+- if (!testPermission(sender)) return true;
+- if (args.length < 1) { // Spigot
+- sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
+- return false;
+- }
+- // Spigot start
+- if (true) {
+- executeSpigotTimings(sender, args);
+- return true;
+- }
+- // Spigot end
+- if (!sender.getServer().getPluginManager().useTimings()) {
+- sender.sendMessage("Please enable timings by setting \"settings.plugin-profiling\" to true in bukkit.yml");
+- return true;
+- }
+-
+- boolean separate = "separate".equalsIgnoreCase(args[0]);
+- if ("reset".equalsIgnoreCase(args[0])) {
+- for (HandlerList handlerList : HandlerList.getHandlerLists()) {
+- for (RegisteredListener listener : handlerList.getRegisteredListeners()) {
+- if (listener instanceof TimedRegisteredListener) {
+- ((TimedRegisteredListener) listener).reset();
+- }
+- }
+- }
+- sender.sendMessage("Timings reset");
+- } else if ("merged".equalsIgnoreCase(args[0]) || separate) {
+-
+- int index = 0;
+- int pluginIdx = 0;
+- File timingFolder = new File("timings");
+- timingFolder.mkdirs();
+- File timings = new File(timingFolder, "timings.txt");
+- File names = null;
+- while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt");
+- PrintStream fileTimings = null;
+- PrintStream fileNames = null;
+- try {
+- fileTimings = new PrintStream(timings);
+- if (separate) {
+- names = new File(timingFolder, "names" + index + ".txt");
+- fileNames = new PrintStream(names);
+- }
+- for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
+- pluginIdx++;
+- long totalTime = 0;
+- if (separate) {
+- fileNames.println(pluginIdx + " " + plugin.getDescription().getFullName());
+- fileTimings.println("Plugin " + pluginIdx);
+- } else {
+- fileTimings.println(plugin.getDescription().getFullName());
+- }
+- for (RegisteredListener listener : HandlerList.getRegisteredListeners(plugin)) {
+- if (listener instanceof TimedRegisteredListener) {
+- TimedRegisteredListener trl = (TimedRegisteredListener) listener;
+- long time = trl.getTotalTime();
+- int count = trl.getCount();
+- if (count == 0) continue;
+- long avg = time / count;
+- totalTime += time;
+- Class<? extends Event> eventClass = trl.getEventClass();
+- if (count > 0 && eventClass != null) {
+- fileTimings.println(" " + eventClass.getSimpleName() + (trl.hasMultiple() ? " (and sub-classes)" : "") + " Time: " + time + " Count: " + count + " Avg: " + avg);
+- }
+- }
+- }
+- fileTimings.println(" Total time " + totalTime + " (" + totalTime / 1000000000 + "s)");
+- }
+- sender.sendMessage("Timings written to " + timings.getPath());
+- if (separate) sender.sendMessage("Names written to " + names.getPath());
+- } catch (IOException e) {
+- } finally {
+- if (fileTimings != null) {
+- fileTimings.close();
+- }
+- if (fileNames != null) {
+- fileNames.close();
+- }
+- }
+- } else {
+- sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
+- return false;
+- }
+- return true;
+- }
+-
+- @NotNull
+- @Override
+- public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) {
+- Validate.notNull(sender, "Sender cannot be null");
+- Validate.notNull(args, "Arguments cannot be null");
+- Validate.notNull(alias, "Alias cannot be null");
+-
+- if (args.length == 1) {
+- return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, new ArrayList<String>(TIMINGS_SUBCOMMANDS.size()));
+- }
+- return ImmutableList.of();
+- }
+-
+- // Spigot start
+- private static class PasteThread extends Thread {
+-
+- private final CommandSender sender;
+- private final ByteArrayOutputStream bout;
+-
+- public PasteThread(@NotNull CommandSender sender, @NotNull ByteArrayOutputStream bout) {
+- super("Timings paste thread");
+- this.sender = sender;
+- this.bout = bout;
+- }
+-
+- @Override
+- public synchronized void start() {
+- if (sender instanceof RemoteConsoleCommandSender) {
+- run();
+- } else {
+- super.start();
+- }
+- }
+-
+- @Override
+- public void run() {
+- try {
+- HttpURLConnection con = (HttpURLConnection) new URL("https://timings.spigotmc.org/paste").openConnection();
+- con.setDoOutput(true);
+- con.setRequestMethod("POST");
+- con.setInstanceFollowRedirects(false);
+-
+- OutputStream out = con.getOutputStream();
+- out.write(bout.toByteArray());
+- out.close();
+-
+- com.google.gson.JsonObject location = new com.google.gson.Gson().fromJson(new java.io.InputStreamReader(con.getInputStream()), com.google.gson.JsonObject.class);
+- con.getInputStream().close();
+-
+- String pasteID = location.get("key").getAsString();
+- sender.sendMessage(ChatColor.GREEN + "Timings results can be viewed at https://www.spigotmc.org/go/timings?url=" + pasteID);
+- } catch (IOException ex) {
+- sender.sendMessage(ChatColor.RED + "Error pasting timings, check your console for more information");
+- Bukkit.getServer().getLogger().log(Level.WARNING, "Could not paste timings", ex);
+- }
+- }
+- }
+- // Spigot end
+-}
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 90c8c3eb8bc3285bf7a51da89015304e49e6602c..344aecc097c5e12476d2be53019145812b028914 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1353,7 +1353,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
++
++ }
++
++ // Paper start
++ public int getPing() {
++ throw new UnsupportedOperationException( "Not supported yet." );
+ }
++ // Paper end
+ }
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index 62d0017362204070465c8ff72e5c2ca07501f558..745eaa8f2f2ff83536301db8ca47a8af30df7a73 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -358,7 +358,6 @@ public final class SimplePluginManager implements PluginManager {
+ }
+ }
+
+- org.bukkit.command.defaults.TimingsCommand.timingStart = System.nanoTime(); // Spigot
+ return result.toArray(new Plugin[result.size()]);
+ }
+
+@@ -397,9 +396,9 @@ public final class SimplePluginManager implements PluginManager {
+
+ if (result != null) {
+ plugins.add(result);
+- lookupNames.put(result.getDescription().getName(), result);
++ lookupNames.put(result.getDescription().getName().toLowerCase(java.util.Locale.ENGLISH), result); // Paper
+ for (String provided : result.getDescription().getProvides()) {
+- lookupNames.putIfAbsent(provided, result);
++ lookupNames.putIfAbsent(provided.toLowerCase(java.util.Locale.ENGLISH), result); // Paper
+ }
+ }
+
+@@ -428,7 +427,7 @@ public final class SimplePluginManager implements PluginManager {
+ @Override
+ @Nullable
+ public synchronized Plugin getPlugin(@NotNull String name) {
+- return lookupNames.get(name.replace(' ', '_'));
++ return lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Paper
+ }
+
+ @Override
+@@ -646,7 +645,8 @@ public final class SimplePluginManager implements PluginManager {
+ throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
+ }
+
+- if (useTimings) {
++ executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Paper
++ if (false) { // Spigot - RL handles useTimings check now // Paper
+ getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
+ } else {
+ getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
+@@ -860,7 +860,7 @@ public final class SimplePluginManager implements PluginManager {
+
+ @Override
+ public boolean useTimings() {
+- return useTimings;
++ return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot
+ }
+
+ /**
+@@ -869,6 +869,6 @@ public final class SimplePluginManager implements PluginManager {
+ * @param use True if per event timing code should be used
+ */
+ public void useTimings(boolean use) {
+- useTimings = use;
++ co.aikar.timings.Timings.setTimingsEnabled(use); // Paper
+ }
+ }
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index 7b9ad3834c9c81220c74a16f1e66fb4da512e9f6..b6d739ca8ad8ebd4b1be7ebd129f9a7ae16b2a2a 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -53,7 +53,6 @@ public final class JavaPluginLoader implements PluginLoader {
+ private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
+ private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
+ private final List<PluginClassLoader> loaders = new CopyOnWriteArrayList<PluginClassLoader>();
+- public static final CustomTimingsHandler pluginParentTimer = new CustomTimingsHandler("** Plugins"); // Spigot
+
+ /**
+ * This class was not meant to be constructed explicitly
+@@ -301,27 +300,21 @@ public final class JavaPluginLoader implements PluginLoader {
+ }
+ }
+
+- final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName() + "(" + eventClass.getSimpleName() + ")", pluginParentTimer); // Spigot
+- EventExecutor executor = new EventExecutor() {
++ EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper
+ @Override
+- public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper
+ try {
+ if (!eventClass.isAssignableFrom(event.getClass())) {
+ return;
+ }
+- // Spigot start
+- boolean isAsync = event.isAsynchronous();
+- if (!isAsync) timings.startTiming();
+ method.invoke(listener, event);
+- if (!isAsync) timings.stopTiming();
+- // Spigot end
+ } catch (InvocationTargetException ex) {
+ throw new EventException(ex.getCause());
+ } catch (Throwable t) {
+ throw new EventException(t);
+ }
+ }
+- };
++ }, plugin, method, eventClass); // Paper
+ if (false) { // Spigot - RL handles useTimings check now
+ eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
+ } else {
+diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+index 5830e8b9b74d6107e54b6e19e03ab0e8c0da2f19..36f542a85e0f16e97c65c0ca64ec660ddf75d63e 100644
+--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+@@ -28,7 +28,8 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * A ClassLoader for plugins, to allow shared classes across multiple plugins
+ */
+-final class PluginClassLoader extends URLClassLoader {
++public final class PluginClassLoader extends URLClassLoader { // Spigot
++ public JavaPlugin getPlugin() { return plugin; } // Spigot
+ private final JavaPluginLoader loader;
+ private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
+ private final PluginDescriptionFile description;
+diff --git a/src/main/java/org/bukkit/util/CachedServerIcon.java b/src/main/java/org/bukkit/util/CachedServerIcon.java
+index 5ca863b3692b2e1b58e7fb4d82f554a92cc4f01e..612958a331575d1da2715531ebdf6b1168f2e860 100644
+--- a/src/main/java/org/bukkit/util/CachedServerIcon.java
++++ b/src/main/java/org/bukkit/util/CachedServerIcon.java
+@@ -2,6 +2,7 @@ package org.bukkit.util;
+
+ import org.bukkit.Server;
+ import org.bukkit.event.server.ServerListPingEvent;
++import org.jetbrains.annotations.Nullable;
+
+ /**
+ * This is a cached version of a server-icon. It's internal representation
+@@ -12,4 +13,9 @@ import org.bukkit.event.server.ServerListPingEvent;
+ * @see Server#loadServerIcon(java.io.File)
+ * @see ServerListPingEvent#setServerIcon(CachedServerIcon)
+ */
+-public interface CachedServerIcon {}
++public interface CachedServerIcon {
++
++ @Nullable
++ public String getData(); // Paper
++
++}
+diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java
+index 44badfedcc3fdc26bdc293b85d8c781d6f659faa..3cbe5c2bb55dead7968a6f165ef267e3e2931061 100644
+--- a/src/main/java/org/spigotmc/CustomTimingsHandler.java
++++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java
+@@ -1,3 +1,26 @@
++/*
++ * This file is licensed under the MIT License (MIT).
++ *
++ * Copyright (c) 2014 Daniel Ennis <http://aikar.co>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
+ package org.spigotmc;
+
+ import java.io.PrintStream;
+@@ -5,133 +28,84 @@ import java.util.Queue;
+ import java.util.concurrent.ConcurrentLinkedQueue;
+ import org.bukkit.Bukkit;
+ import org.bukkit.World;
+-import org.bukkit.command.defaults.TimingsCommand;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
++import org.bukkit.plugin.AuthorNagException;
++import org.bukkit.plugin.Plugin;
++import co.aikar.timings.Timing;
++import co.aikar.timings.Timings;
++import co.aikar.timings.TimingsManager;
++
++import java.lang.reflect.InvocationTargetException;
++import java.lang.reflect.Method;
++import java.util.logging.Level;
+
+ /**
+- * Provides custom timing sections for /timings merged.
++ * This is here for legacy purposes incase any plugin used it.
++ *
++ * If you use this, migrate ASAP as this will be removed in the future!
++ *
++ * @deprecated
++ * @see co.aikar.timings.Timings#of
+ */
+-public class CustomTimingsHandler {
+-
+- private static Queue<CustomTimingsHandler> HANDLERS = new ConcurrentLinkedQueue<CustomTimingsHandler>();
+- /*========================================================================*/
+- private final String name;
+- private final CustomTimingsHandler parent;
+- private long count = 0;
+- private long start = 0;
+- private long timingDepth = 0;
+- private long totalTime = 0;
+- private long curTickTotal = 0;
+- private long violations = 0;
++@Deprecated
++public final class CustomTimingsHandler {
++ private final Timing handler;
++ private static Boolean sunReflectAvailable;
++ private static Method getCallerClass;
+
+ public CustomTimingsHandler(@NotNull String name) {
+- this(name, null);
+- }
++ if (sunReflectAvailable == null) {
++ String javaVer = System.getProperty("java.version");
++ String[] elements = javaVer.split("\\.");
+
+- public CustomTimingsHandler(@NotNull String name, @Nullable CustomTimingsHandler parent) {
+- this.name = name;
+- this.parent = parent;
+- HANDLERS.add(this);
+- }
++ int major = Integer.parseInt(elements.length >= 2 ? elements[1] : javaVer);
++ if (major <= 8) {
++ sunReflectAvailable = true;
+
+- /**
+- * Prints the timings and extra data to the given stream.
+- *
+- * @param printStream output stream
+- */
+- public static void printTimings(@NotNull PrintStream printStream) {
+- printStream.println("Minecraft");
+- for (CustomTimingsHandler timings : HANDLERS) {
+- long time = timings.totalTime;
+- long count = timings.count;
+- if (count == 0) {
+- continue;
++ try {
++ Class<?> reflection = Class.forName("sun.reflect.Reflection");
++ getCallerClass = reflection.getMethod("getCallerClass", int.class);
++ } catch (ClassNotFoundException | NoSuchMethodException ignored) {
++ }
++ } else {
++ sunReflectAvailable = false;
+ }
+- long avg = time / count;
+-
+- printStream.println(" " + timings.name + " Time: " + time + " Count: " + count + " Avg: " + avg + " Violations: " + timings.violations);
+- }
+- printStream.println("# Version " + Bukkit.getVersion());
+- int entities = 0;
+- int livingEntities = 0;
+- for (World world : Bukkit.getWorlds()) {
+- entities += world.getEntities().size();
+- livingEntities += world.getLivingEntities().size();
+ }
+- printStream.println("# Entities " + entities);
+- printStream.println("# LivingEntities " + livingEntities);
+- }
+
+- /**
+- * Resets all timings.
+- */
+- public static void reload() {
+- if (Bukkit.getPluginManager().useTimings()) {
+- for (CustomTimingsHandler timings : HANDLERS) {
+- timings.reset();
++ Class calling = null;
++ if (sunReflectAvailable) {
++ try {
++ calling = (Class) getCallerClass.invoke(null, 2);
++ } catch (IllegalAccessException | InvocationTargetException ignored) {
+ }
+ }
+- TimingsCommand.timingStart = System.nanoTime();
+- }
+
+- /**
+- * Ticked every tick by CraftBukkit to count the number of times a timer
+- * caused TPS loss.
+- */
+- public static void tick() {
+- if (Bukkit.getPluginManager().useTimings()) {
+- for (CustomTimingsHandler timings : HANDLERS) {
+- if (timings.curTickTotal > 50000000) {
+- timings.violations += Math.ceil(timings.curTickTotal / 50000000);
+- }
+- timings.curTickTotal = 0;
+- timings.timingDepth = 0; // incase reset messes this up
+- }
+- }
+- }
++ Timing timing;
+
+- /**
+- * Starts timing to track a section of code.
+- */
+- public void startTiming() {
+- // If second condtion fails we are already timing
+- if (Bukkit.getPluginManager().useTimings() && ++timingDepth == 1) {
+- start = System.nanoTime();
+- if (parent != null && ++parent.timingDepth == 1) {
+- parent.start = start;
+- }
+- }
+- }
++ Plugin plugin = null;
++ try {
++ plugin = TimingsManager.getPluginByClassloader(calling);
++ } catch (Exception ignored) {}
+
+- /**
+- * Stops timing a section of code.
+- */
+- public void stopTiming() {
+- if (Bukkit.getPluginManager().useTimings()) {
+- if (--timingDepth != 0 || start == 0) {
+- return;
+- }
+- long diff = System.nanoTime() - start;
+- totalTime += diff;
+- curTickTotal += diff;
+- count++;
+- start = 0;
+- if (parent != null) {
+- parent.stopTiming();
++ new AuthorNagException("Deprecated use of CustomTimingsHandler. Please Switch to Timings.of ASAP").printStackTrace();
++ if (plugin != null) {
++ timing = Timings.of(plugin, "(Deprecated API) " + name);
++ } else {
++ try {
++ final Method ofSafe = TimingsManager.class.getDeclaredMethod("getHandler", String.class, String.class, Timing.class);
++ ofSafe.setAccessible(true);
++ timing = (Timing) ofSafe.invoke(null,"Minecraft", "(Deprecated API) " + name, null);
++ } catch (Exception e) {
++ e.printStackTrace();
++ Bukkit.getLogger().log(Level.SEVERE, "This handler could not be registered");
++ timing = Timings.NULL_HANDLER;
+ }
+ }
++ handler = timing;
+ }
+
+- /**
+- * Reset this timer, setting all values to zero.
+- */
+- public void reset() {
+- count = 0;
+- violations = 0;
+- curTickTotal = 0;
+- totalTime = 0;
+- start = 0;
+- timingDepth = 0;
+- }
++ public void startTiming() { handler.startTiming(); }
++ public void stopTiming() { handler.stopTiming(); }
++
+ }
diff --git a/Spigot-API-Patches-Unmapped/0005-Adventure.patch b/Spigot-API-Patches-Unmapped/0005-Adventure.patch
new file mode 100644
index 0000000000..c52a15827a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0005-Adventure.patch
@@ -0,0 +1,3734 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Riley Park <[email protected]>
+Date: Fri, 29 Jan 2021 17:21:55 +0100
+Subject: [PATCH] Adventure
+
+Co-authored-by: zml <[email protected]>
+Co-authored-by: Jake Potrebic <[email protected]>
+
+diff --git a/pom.xml b/pom.xml
+index e3744ee042e0426a513c4fdf4abedafe85e31cd2..75b2830340051deb0fa39149e80872d2b88ed6f0 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -31,7 +31,39 @@
+ </repository>
+ </repositories>
+
++ <!-- Paper start -->
++ <dependencyManagement>
++ <dependencies>
++ <dependency>
++ <groupId>net.kyori</groupId>
++ <artifactId>adventure-bom</artifactId>
++ <version>4.7.0</version> <!-- keep this version in synch with javadoc section below! -->
++ <type>pom</type>
++ <scope>import</scope>
++ </dependency>
++ </dependencies>
++ </dependencyManagement>
++ <!-- Paper end -->
++
+ <dependencies>
++ <!-- Paper start -->
++ <dependency>
++ <groupId>net.kyori</groupId>
++ <artifactId>adventure-api</artifactId>
++ </dependency>
++ <dependency>
++ <groupId>net.kyori</groupId>
++ <artifactId>adventure-text-serializer-gson</artifactId>
++ </dependency>
++ <dependency>
++ <groupId>net.kyori</groupId>
++ <artifactId>adventure-text-serializer-legacy</artifactId>
++ </dependency>
++ <dependency>
++ <groupId>net.kyori</groupId>
++ <artifactId>adventure-text-serializer-plain</artifactId>
++ </dependency>
++ <!-- Paper end -->
+ <dependency>
+ <groupId>it.unimi.dsi</groupId>
+ <artifactId>fastutil</artifactId>
+@@ -197,6 +229,12 @@
+ <link>https://javadoc.io/doc/org.yaml/snakeyaml/1.27/</link>
+ <link>https://javadoc.io/doc/org.jetbrains/annotations-java5/20.1.0/</link>
+ <link>https://javadoc.io/doc/net.md-5/bungeecord-chat/1.16-R0.4/</link>
++ <!-- Paper start -->
++ <link>https://jd.adventure.kyori.net/api/4.7.0/</link>
++ <link>https://jd.adventure.kyori.net/text-serializer-gson/4.7.0/</link>
++ <link>https://jd.adventure.kyori.net/text-serializer-legacy/4.7.0/</link>
++ <link>https://jd.adventure.kyori.net/text-serializer-plain/4.7.0/</link>
++ <!-- Paper end -->
+ </links>
+ </configuration>
+ </plugin>
+diff --git a/src/main/java/co/aikar/timings/TimingsReportListener.java b/src/main/java/co/aikar/timings/TimingsReportListener.java
+index ef58a6c00f444bd498a2d8fc4e457236f393954f..ecd149157d4fb80444f34bf5633d74bcdb63dec5 100644
+--- a/src/main/java/co/aikar/timings/TimingsReportListener.java
++++ b/src/main/java/co/aikar/timings/TimingsReportListener.java
+@@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
+ @SuppressWarnings("WeakerAccess")
+-public class TimingsReportListener implements MessageCommandSender {
++public class TimingsReportListener implements net.kyori.adventure.audience.ForwardingAudience, MessageCommandSender { // Paper
+ private final List<CommandSender> senders;
+ private final Runnable onDone;
+ private String timingsURL;
+@@ -74,4 +74,17 @@ public class TimingsReportListener implements MessageCommandSender {
+ this.senders.add(Bukkit.getConsoleSender());
+ }
+ }
++
++ // Paper start
++ @Override
++ public void sendMessage(final @NotNull net.kyori.adventure.identity.Identity source, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
++ net.kyori.adventure.audience.ForwardingAudience.super.sendMessage(source, message, type);
++ }
++
++ @NotNull
++ @Override
++ public Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
++ return this.senders;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/io/papermc/paper/chat/ChatComposer.java b/src/main/java/io/papermc/paper/chat/ChatComposer.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..1f03ce9ff40ed12a1825c8e24dabddbbef44d6af
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chat/ChatComposer.java
+@@ -0,0 +1,24 @@
++package io.papermc.paper.chat;
++
++import net.kyori.adventure.text.Component;
++import org.bukkit.entity.Player;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A chat composer is responsible for composing chat messages sent by {@link Player}s to the server.
++ */
++@FunctionalInterface
++public interface ChatComposer {
++ ChatComposer DEFAULT = (player, displayName, message) -> Component.translatable("chat.type.text", displayName, message);
++
++ /**
++ * Composes a chat message.
++ *
++ * @param source the message source
++ * @param displayName the display name of the {@link Player} sending the message
++ * @param message the chat message
++ * @return a composed chat message
++ */
++ @NotNull
++ Component composeChat(final @NotNull Player source, final @NotNull Component displayName, final @NotNull Component message);
++}
+diff --git a/src/main/java/io/papermc/paper/chat/ChatFormatter.java b/src/main/java/io/papermc/paper/chat/ChatFormatter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ae811175089009be6b1db6941e9c5a24b2b1f027
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chat/ChatFormatter.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.chat;
++
++import net.kyori.adventure.text.Component;
++import org.bukkit.entity.Player;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A chat formatter is responsible for the formatting of chat messages sent by {@link Player}s to the server.
++ *
++ * @deprecated in favour of {@link ChatComposer}
++ */
++@Deprecated
++@FunctionalInterface
++public interface ChatFormatter {
++ @Deprecated
++ ChatFormatter DEFAULT = (displayName, message) -> Component.translatable("chat.type.text", displayName, message);
++
++ /**
++ * Formats a chat message.
++ *
++ * @param displayName the display name of the {@link Player} sending the message
++ * @param message the chat message
++ * @return a formatted chat message
++ */
++ @Deprecated
++ @NotNull
++ Component chat(final @NotNull Component displayName, final @NotNull Component message);
++}
+diff --git a/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f77597786f201b57ac18e14099f7b84f1e4e4cf3
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
+@@ -0,0 +1,140 @@
++package io.papermc.paper.event.player;
++
++import io.papermc.paper.chat.ChatComposer;
++import io.papermc.paper.chat.ChatFormatter;
++import java.util.Set;
++import net.kyori.adventure.text.Component;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.player.PlayerEvent;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.jetbrains.annotations.NotNull;
++
++import static java.util.Objects.requireNonNull;
++
++/**
++ * An abstract implementation of a chat event, handling shared logic.
++ */
++public abstract class AbstractChatEvent extends PlayerEvent implements Cancellable {
++ private final Set<Player> recipients;
++ private boolean cancelled = false;
++ private ChatComposer composer;
++ @Deprecated private @Nullable ChatFormatter formatter;
++ private Component message;
++
++ AbstractChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatComposer composer, final @NotNull Component message) {
++ super(player, async);
++ this.recipients = recipients;
++ this.composer = composer;
++ this.message = message;
++ }
++
++ @Deprecated
++ AbstractChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) {
++ super(player, async);
++ this.recipients = recipients;
++ this.formatter = formatter;
++ this.message = message;
++ }
++
++ /**
++ * Gets a set of recipients that this chat message will be displayed to.
++ *
++ * <p>The set returned is not guaranteed to be mutable and may auto-populate
++ * on access. Any listener accessing the returned set should be aware that
++ * it may reduce performance for a lazy set implementation.</p>
++ *
++ * <p>Listeners should be aware that modifying the list may throw {@link
++ * UnsupportedOperationException} if the event caller provides an
++ * unmodifiable set.</p>
++ *
++ * @return a set of players who will receive the chat message
++ */
++ @NotNull
++ public final Set<Player> recipients() {
++ return this.recipients;
++ }
++
++ /**
++ * Gets the chat composer.
++ *
++ * @return the chat composer
++ */
++ @NotNull
++ public final ChatComposer composer() {
++ if(this.composer == null) {
++ requireNonNull(this.formatter, "composer and formatter");
++ this.composer = (source, displayName, message) -> this.formatter.chat(displayName, message);
++ }
++ return this.composer;
++ }
++
++ /**
++ * Sets the chat composer.
++ *
++ * @param composer the chat composer
++ * @throws NullPointerException if {@code composer} is {@code null}
++ */
++ public final void composer(final @NotNull ChatComposer composer) {
++ this.composer = requireNonNull(composer, "composer");
++ this.formatter = null;
++ }
++
++ /**
++ * Gets the chat formatter.
++ *
++ * @return the chat formatter
++ * @deprecated in favour of {@link #composer()}
++ */
++ @Deprecated
++ @NotNull
++ public final ChatFormatter formatter() {
++ if(this.formatter == null) {
++ this.formatter = (displayName, message) -> this.composer.composeChat(this.player, displayName, message);
++ }
++ return this.formatter;
++ }
++
++ /**
++ * Sets the chat formatter.
++ *
++ * @param formatter the chat formatter
++ * @throws NullPointerException if {@code formatter} is {@code null}
++ * @deprecated in favour of {@link #composer(ChatComposer)}
++ */
++ @Deprecated
++ public final void formatter(final @NotNull ChatFormatter formatter) {
++ this.formatter = requireNonNull(formatter, "formatter");
++ this.composer = (source, displayName, message) -> formatter.chat(displayName, message);
++ }
++
++ /**
++ * Gets the user-supplied message.
++ *
++ * @return the user-supplied message
++ */
++ @NotNull
++ public final Component message() {
++ return this.message;
++ }
++
++ /**
++ * Sets the user-supplied message.
++ *
++ * @param message the user-supplied message
++ * @throws NullPointerException if {@code message} is {@code null}
++ */
++ public final void message(final @NotNull Component message) {
++ this.message = requireNonNull(message, "message");
++ }
++
++ @Override
++ public final boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ @Override
++ public final void setCancelled(final boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a0f748957f4472103dd27fc95a711a42de7fae89
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
+@@ -0,0 +1,39 @@
++package io.papermc.paper.event.player;
++
++import io.papermc.paper.chat.ChatComposer;
++import io.papermc.paper.chat.ChatFormatter;
++import java.util.Set;
++import net.kyori.adventure.text.Component;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * An event fired when a {@link Player} sends a chat message to the server.
++ */
++public final class AsyncChatEvent extends AbstractChatEvent {
++ private static final HandlerList HANDLERS = new HandlerList();
++
++ public AsyncChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatComposer composer, final @NotNull Component message) {
++ super(async, player, recipients, composer, message);
++ }
++
++ /**
++ * @deprecated use {@link #AsyncChatEvent(boolean, Player, Set, ChatComposer, Component)}
++ */
++ @Deprecated
++ public AsyncChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) {
++ super(async, player, recipients, formatter, message);
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLERS;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLERS;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/player/ChatEvent.java b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..13c5df5fb8ce1d0203d99e88dd691019146a8f52
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
+@@ -0,0 +1,44 @@
++package io.papermc.paper.event.player;
++
++import io.papermc.paper.chat.ChatComposer;
++import io.papermc.paper.chat.ChatFormatter;
++import java.util.Set;
++import net.kyori.adventure.text.Component;
++import org.bukkit.Warning;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * An event fired when a {@link Player} sends a chat message to the server.
++ *
++ * @deprecated Listening to this event forces chat to wait for the main thread, delaying chat messages. It is recommended to use {@link AsyncChatEvent} instead, wherever possible.
++ */
++@Deprecated
++@Warning(reason = "Listening to this event forces chat to wait for the main thread, delaying chat messages.")
++public final class ChatEvent extends AbstractChatEvent {
++ private static final HandlerList HANDLERS = new HandlerList();
++
++ public ChatEvent(final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatComposer composer, final @NotNull Component message) {
++ super(false, player, recipients, composer, message);
++ }
++
++ /**
++ * @deprecated use {@link #ChatEvent(Player, Set, ChatComposer, Component)}
++ */
++ @Deprecated
++ public ChatEvent(final @NotNull Player player, final @NotNull Set<Player> recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) {
++ super(false, player, recipients, formatter, message);
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLERS;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLERS;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/text/PaperComponents.java b/src/main/java/io/papermc/paper/text/PaperComponents.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..239f41e32dd94c75a8a549816465417bb4c63d71
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/text/PaperComponents.java
+@@ -0,0 +1,88 @@
++package io.papermc.paper.text;
++
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.flattener.ComponentFlattener;
++import net.kyori.adventure.text.format.NamedTextColor;
++import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
++import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
++import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
++import org.bukkit.Bukkit;
++import org.checkerframework.checker.nullness.qual.NonNull;
++
++/**
++ * Paper API-specific methods for working with {@link Component}s and related.
++ */
++public final class PaperComponents {
++ private PaperComponents() {
++ throw new RuntimeException("PaperComponents is not to be instantiated!");
++ }
++
++ /**
++ * Return a component flattener that can use game data to resolve extra information about components.
++ *
++ * @return a component flattener
++ */
++ public static @NonNull ComponentFlattener flattener() {
++ return Bukkit.getUnsafe().componentFlattener();
++ }
++
++ /**
++ * Get a serializer for {@link Component}s that will convert components to
++ * a plain-text string.
++ *
++ * <p>Implementations may provide a serializer capable of processing any
++ * information that requires access to implementation details.</p>
++ *
++ * @return a serializer to plain text
++ */
++ public static @NonNull PlainComponentSerializer plainSerializer() {
++ return Bukkit.getUnsafe().plainComponentSerializer();
++ }
++
++ /**
++ * Get a serializer for {@link Component}s that will convert to and from the
++ * standard JSON serialization format using Gson.
++ *
++ * <p>Implementations may provide a serializer capable of processing any
++ * information that requires implementation details, such as legacy
++ * (pre-1.16) hover events.</p>
++ *
++ * @return a json component serializer
++ */
++ public static @NonNull GsonComponentSerializer gsonSerializer() {
++ return Bukkit.getUnsafe().gsonComponentSerializer();
++ }
++
++ /**
++ * Get a serializer for {@link Component}s that will convert to and from the
++ * standard JSON serialization format using Gson, downsampling any RGB colors
++ * to their nearest {@link NamedTextColor} counterpart.
++ *
++ * <p>Implementations may provide a serializer capable of processing any
++ * information that requires implementation details, such as legacy
++ * (pre-1.16) hover events.</p>
++ *
++ * @return a json component serializer
++ */
++ public static @NonNull GsonComponentSerializer colorDownsamplingGsonSerializer() {
++ return Bukkit.getUnsafe().colorDownsamplingGsonComponentSerializer();
++ }
++
++ /**
++ * Get a serializer for {@link Component}s that will convert to and from the
++ * legacy component format used by Bukkit. This serializer uses the
++ * {@link LegacyComponentSerializer.Builder#useUnusualXRepeatedCharacterHexFormat()}
++ * option to match upstream behavior.
++ *
++ * <p>This legacy serializer uses the standard section symbol to mark
++ * formatting characters.</p>
++ *
++ * <p>Implementations may provide a serializer capable of processing any
++ * information that requires access to implementation details.</p>
++ *
++ * @return a section serializer
++ */
++ public static @NonNull LegacyComponentSerializer legacySectionSerializer() {
++ return Bukkit.getUnsafe().legacyComponentSerializer();
++ }
++}
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 7c715fdc11ab7837552b1fe3ffd08b31cec0a63b..426b1e83226e674ee4bf3ec05ddcd3ac4376b06d 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -278,7 +278,9 @@ public final class Bukkit {
+ *
+ * @param message the message
+ * @return the number of players
++ * @deprecated in favour of {@link Server#sendMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public static int broadcastMessage(@NotNull String message) {
+ return server.broadcastMessage(message);
+ }
+@@ -836,6 +838,7 @@ public final class Bukkit {
+ server.shutdown();
+ }
+
++ // Paper start
+ /**
+ * Broadcasts the specified message to every user with the given
+ * permission name.
+@@ -845,6 +848,21 @@ public final class Bukkit {
+ * permissibles} must have to receive the broadcast
+ * @return number of message recipients
+ */
++ public static int broadcast(@NotNull net.kyori.adventure.text.Component message, @NotNull String permission) {
++ return server.broadcast(message, permission);
++ }
++ // Paper end
++ /**
++ * Broadcasts the specified message to every user with the given
++ * permission name.
++ *
++ * @param message message to broadcast
++ * @param permission the required permission {@link Permissible
++ * permissibles} must have to receive the broadcast
++ * @return number of message recipients
++ * @deprecated in favour of {@link #broadcast(net.kyori.adventure.text.Component, String)}
++ */
++ @Deprecated // Paper
+ public static int broadcast(@NotNull String message, @NotNull String permission) {
+ return server.broadcast(message, permission);
+ }
+@@ -1044,6 +1062,7 @@ public final class Bukkit {
+ return server.createInventory(owner, type);
+ }
+
++ // Paper start
+ /**
+ * Creates an empty inventory with the specified type and title. If the type
+ * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
+@@ -1069,6 +1088,38 @@ public final class Bukkit {
+ * @see InventoryType#isCreatable()
+ */
+ @NotNull
++ public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull net.kyori.adventure.text.Component title) {
++ return server.createInventory(owner, type, title);
++ }
++ // Paper end
++
++ /**
++ * Creates an empty inventory with the specified type and title. If the type
++ * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
++ * otherwise the new inventory has the normal size for its type.<br>
++ * It should be noted that some inventory types do not support titles and
++ * may not render with said titles on the Minecraft client.
++ * <br>
++ * {@link InventoryType#WORKBENCH} will not process crafting recipes if
++ * created with this method. Use
++ * {@link Player#openWorkbench(Location, boolean)} instead.
++ * <br>
++ * {@link InventoryType#ENCHANTING} will not process {@link ItemStack}s
++ * for possible enchanting results. Use
++ * {@link Player#openEnchanting(Location, boolean)} instead.
++ *
++ * @param owner The holder of the inventory; can be null if there's no holder.
++ * @param type The type of inventory to create.
++ * @param title The title of the inventory, to be displayed when it is viewed.
++ * @return The new inventory.
++ * @throws IllegalArgumentException if the {@link InventoryType} cannot be
++ * viewed.
++ * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
++ *
++ * @see InventoryType#isCreatable()
++ */
++ @Deprecated // Paper
++ @NotNull
+ public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull String title) {
+ return server.createInventory(owner, type, title);
+ }
+@@ -1087,6 +1138,7 @@ public final class Bukkit {
+ return server.createInventory(owner, size);
+ }
+
++ // Paper start
+ /**
+ * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+ * specified size and title.
+@@ -1099,10 +1151,30 @@ public final class Bukkit {
+ * @throws IllegalArgumentException if the size is not a multiple of 9
+ */
+ @NotNull
++ public static Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull net.kyori.adventure.text.Component title) throws IllegalArgumentException {
++ return server.createInventory(owner, size, title);
++ }
++ // Paper end
++
++ /**
++ * Creates an empty inventory of type {@link InventoryType#CHEST} with the
++ * specified size and title.
++ *
++ * @param owner the holder of the inventory, or null to indicate no holder
++ * @param size a multiple of 9 as the size of inventory to create
++ * @param title the title of the inventory, displayed when inventory is
++ * viewed
++ * @return a new inventory
++ * @throws IllegalArgumentException if the size is not a multiple of 9
++ * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
++ */
++ @Deprecated // Paper
++ @NotNull
+ public static Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException {
+ return server.createInventory(owner, size, title);
+ }
+
++ // Paper start
+ /**
+ * Creates an empty merchant.
+ *
+@@ -1110,7 +1182,20 @@ public final class Bukkit {
+ * when the merchant inventory is viewed
+ * @return a new merchant
+ */
++ public static @NotNull Merchant createMerchant(@Nullable net.kyori.adventure.text.Component title) {
++ return server.createMerchant(title);
++ }
++ // Paper start
++ /**
++ * Creates an empty merchant.
++ *
++ * @param title the title of the corresponding merchant inventory, displayed
++ * when the merchant inventory is viewed
++ * @return a new merchant
++ * @deprecated in favour of {@link #createMerchant(net.kyori.adventure.text.Component)}
++ */
+ @NotNull
++ @Deprecated // Paper
+ public static Merchant createMerchant(@Nullable String title) {
+ return server.createMerchant(title);
+ }
+@@ -1181,22 +1266,47 @@ public final class Bukkit {
+ return server.isPrimaryThread();
+ }
+
++ // Paper start
++ /**
++ * Gets the message that is displayed on the server list.
++ *
++ * @return the server's MOTD
++ */
++ @NotNull public static net.kyori.adventure.text.Component motd() {
++ return server.motd();
++ }
++ // Paper end
++
+ /**
+ * Gets the message that is displayed on the server list.
+ *
+ * @return the servers MOTD
++ * @deprecated in favour of {@link #motd()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public static String getMotd() {
+ return server.getMotd();
+ }
+
++ // Paper start
++ /**
++ * Gets the default message that is displayed when the server is stopped.
++ *
++ * @return the shutdown message
++ */
++ public static @Nullable net.kyori.adventure.text.Component shutdownMessage() {
++ return server.shutdownMessage();
++ }
++ // Paper end
+ /**
+ * Gets the default message that is displayed when the server is stopped.
+ *
+ * @return the shutdown message
++ * @deprecated in favour of {@link #shutdownMessage()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public static String getShutdownMessage() {
+ return server.getShutdownMessage();
+ }
+diff --git a/src/main/java/org/bukkit/Nameable.java b/src/main/java/org/bukkit/Nameable.java
+index fee814e01a653d2b53c56e8b566383ca44aa5346..2acdf2a6d3955923c721222b9da784f3278f6418 100644
+--- a/src/main/java/org/bukkit/Nameable.java
++++ b/src/main/java/org/bukkit/Nameable.java
+@@ -4,6 +4,30 @@ import org.jetbrains.annotations.Nullable;
+
+ public interface Nameable {
+
++ // Paper start
++ /**
++ * Gets the custom name.
++ *
++ * <p>This value has no effect on players, they will always use their real name.</p>
++ *
++ * @return the custom name
++ */
++ @Nullable net.kyori.adventure.text.Component customName();
++
++ /**
++ * Sets the custom name.
++ *
++ * <p>This name will be used in death messages and can be sent to the client as a nameplate over the mob.</p>
++ *
++ * <p>Setting the name to {@code null} will clear it.</p>
++ *
++ * <p>This value has no effect on players, they will always use their real name.</p>
++ *
++ * @param customName the custom name to set
++ */
++ void customName(final @Nullable net.kyori.adventure.text.Component customName);
++ // Paper end
++
+ /**
+ * Gets the custom name on a mob or block. If there is no name this method
+ * will return null.
+diff --git a/src/main/java/org/bukkit/NamespacedKey.java b/src/main/java/org/bukkit/NamespacedKey.java
+index 803fa0019869127ee8c7e4fb1777a59c43e66f8a..c65f0d6569c130b4920a9e71ad24af6427f1f030 100644
+--- a/src/main/java/org/bukkit/NamespacedKey.java
++++ b/src/main/java/org/bukkit/NamespacedKey.java
+@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
+ * underscores, hyphens, and forward slashes.
+ *
+ */
+-public final class NamespacedKey {
++public final class NamespacedKey implements net.kyori.adventure.key.Key { // Paper - implement Key
+
+ /**
+ * The namespace representing all inbuilt keys.
+@@ -212,4 +212,24 @@ public final class NamespacedKey {
+ public static NamespacedKey fromString(@NotNull String key) {
+ return fromString(key, null);
+ }
++
++ // Paper start
++ @NotNull
++ @Override
++ public String namespace() {
++ return this.getNamespace();
++ }
++
++ @NotNull
++ @Override
++ public String value() {
++ return this.getKey();
++ }
++
++ @NotNull
++ @Override
++ public String asString() {
++ return this.namespace + ':' + this.key;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index a6b9e4f158583e5932bf8ca210d531857e9f5360..d9515a79dc7ed60c66960cd6c6bb4c108f206f3c 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -55,7 +55,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a server implementation.
+ */
+-public interface Server extends PluginMessageRecipient {
++public interface Server extends PluginMessageRecipient, net.kyori.adventure.audience.ForwardingAudience { // Paper
+
+ /**
+ * Used for all administrative messages, such as an operator using a
+@@ -229,7 +229,9 @@ public interface Server extends PluginMessageRecipient {
+ *
+ * @param message the message
+ * @return the number of players
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public int broadcastMessage(@NotNull String message);
+
+ /**
+@@ -703,8 +705,22 @@ public interface Server extends PluginMessageRecipient {
+ * @param permission the required permission {@link Permissible
+ * permissibles} must have to receive the broadcast
+ * @return number of message recipients
++ * @deprecated in favour of {@link #broadcast(net.kyori.adventure.text.Component, String)}
+ */
++ @Deprecated // Paper
+ public int broadcast(@NotNull String message, @NotNull String permission);
++ // Paper start
++ /**
++ * Broadcasts the specified message to every user with the given
++ * permission name.
++ *
++ * @param message message to broadcast
++ * @param permission the required permission {@link Permissible
++ * permissibles} must have to receive the broadcast
++ * @return number of message recipients
++ */
++ int broadcast(@NotNull net.kyori.adventure.text.Component message, @NotNull String permission);
++ // Paper end
+
+ /**
+ * Gets the player by the given name, regardless if they are offline or
+@@ -869,6 +885,7 @@ public interface Server extends PluginMessageRecipient {
+ @NotNull
+ Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type);
+
++ // Paper start
+ /**
+ * Creates an empty inventory with the specified type and title. If the type
+ * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
+@@ -894,6 +911,36 @@ public interface Server extends PluginMessageRecipient {
+ * @see InventoryType#isCreatable()
+ */
+ @NotNull
++ Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull net.kyori.adventure.text.Component title);
++ // Paper end
++
++ /**
++ * Creates an empty inventory with the specified type and title. If the type
++ * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
++ * otherwise the new inventory has the normal size for its type.<br>
++ * It should be noted that some inventory types do not support titles and
++ * may not render with said titles on the Minecraft client.
++ * <br>
++ * {@link InventoryType#WORKBENCH} will not process crafting recipes if
++ * created with this method. Use
++ * {@link Player#openWorkbench(Location, boolean)} instead.
++ * <br>
++ * {@link InventoryType#ENCHANTING} will not process {@link ItemStack}s
++ * for possible enchanting results. Use
++ * {@link Player#openEnchanting(Location, boolean)} instead.
++ *
++ * @param owner The holder of the inventory; can be null if there's no holder.
++ * @param type The type of inventory to create.
++ * @param title The title of the inventory, to be displayed when it is viewed.
++ * @return The new inventory.
++ * @throws IllegalArgumentException if the {@link InventoryType} cannot be
++ * viewed.
++ * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
++ *
++ * @see InventoryType#isCreatable()
++ */
++ @Deprecated // Paper
++ @NotNull
+ Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull String title);
+
+ /**
+@@ -908,6 +955,22 @@ public interface Server extends PluginMessageRecipient {
+ @NotNull
+ Inventory createInventory(@Nullable InventoryHolder owner, int size) throws IllegalArgumentException;
+
++ // Paper start
++ /**
++ * Creates an empty inventory of type {@link InventoryType#CHEST} with the
++ * specified size and title.
++ *
++ * @param owner the holder of the inventory, or null to indicate no holder
++ * @param size a multiple of 9 as the size of inventory to create
++ * @param title the title of the inventory, displayed when inventory is
++ * viewed
++ * @return a new inventory
++ * @throws IllegalArgumentException if the size is not a multiple of 9
++ */
++ @NotNull
++ Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull net.kyori.adventure.text.Component title) throws IllegalArgumentException;
++ // Paper end
++
+ /**
+ * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+ * specified size and title.
+@@ -918,18 +981,32 @@ public interface Server extends PluginMessageRecipient {
+ * viewed
+ * @return a new inventory
+ * @throws IllegalArgumentException if the size is not a multiple of 9
++ * @deprecated in favour of {@link #createInventory(InventoryHolder, int, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ @NotNull
+ Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException;
+
++ // Paper start
++ /**
++ * Creates an empty merchant.
++ *
++ * @param title the title of the corresponding merchant inventory, displayed
++ * when the merchant inventory is viewed
++ * @return a new merchant
++ */
++ @NotNull Merchant createMerchant(@Nullable net.kyori.adventure.text.Component title);
++ // Paper start
+ /**
+ * Creates an empty merchant.
+ *
+ * @param title the title of the corresponding merchant inventory, displayed
+ * when the merchant inventory is viewed
+ * @return a new merchant
++ * @deprecated in favour of {@link #createMerchant(net.kyori.adventure.text.Component)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ Merchant createMerchant(@Nullable String title);
+
+ /**
+@@ -986,20 +1063,41 @@ public interface Server extends PluginMessageRecipient {
+ */
+ boolean isPrimaryThread();
+
++ // Paper start
++ /**
++ * Gets the message that is displayed on the server list.
++ *
++ * @return the server's MOTD
++ */
++ @NotNull net.kyori.adventure.text.Component motd();
++ // Paper end
++
+ /**
+ * Gets the message that is displayed on the server list.
+ *
+ * @return the servers MOTD
++ * @deprecated in favour of {@link #motd()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getMotd();
+
++ // Paper start
++ /**
++ * Gets the default message that is displayed when the server is stopped.
++ *
++ * @return the shutdown message
++ */
++ @Nullable net.kyori.adventure.text.Component shutdownMessage();
++ // Paper end
+ /**
+ * Gets the default message that is displayed when the server is stopped.
+ *
+ * @return the shutdown message
++ * @deprecated in favour of {@link #shutdownMessage()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ String getShutdownMessage();
+
+ /**
+@@ -1368,7 +1466,9 @@ public interface Server extends PluginMessageRecipient {
+ * Sends the component to the player
+ *
+ * @param component the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -1377,7 +1477,9 @@ public interface Server extends PluginMessageRecipient {
+ * Sends an array of components as a single message to the player
+ *
+ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+diff --git a/src/main/java/org/bukkit/Sound.java b/src/main/java/org/bukkit/Sound.java
+index 768f35c19c4557236bded5f4a85f48a2b2b2a9e6..d0ce64412276512cde133937a85a3340a70eea6d 100644
+--- a/src/main/java/org/bukkit/Sound.java
++++ b/src/main/java/org/bukkit/Sound.java
+@@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
+ * guarantee values will not be removed from this Enum. As such, you should not
+ * depend on the ordinal values of this class.
+ */
+-public enum Sound implements Keyed {
++public enum Sound implements Keyed, net.kyori.adventure.sound.Sound.Type { // Paper - implement Sound.Type
+
+ AMBIENT_BASALT_DELTAS_ADDITIONS("ambient.basalt_deltas.additions"),
+ AMBIENT_BASALT_DELTAS_LOOP("ambient.basalt_deltas.loop"),
+@@ -1016,4 +1016,12 @@ public enum Sound implements Keyed {
+ public NamespacedKey getKey() {
+ return key;
+ }
++
++ // Paper start
++ @NotNull
++ @Override
++ public net.kyori.adventure.key.@org.checkerframework.checker.nullness.qual.NonNull Key key() {
++ return this.key;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 945b8b030d1b2a13afc0c4efad76997eb7bf00ba..207c656c0a11a3a630bc70491efcf433b2681e18 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -17,6 +17,13 @@ import org.bukkit.plugin.PluginDescriptionFile;
+ */
+ @Deprecated
+ public interface UnsafeValues {
++ // Paper start
++ net.kyori.adventure.text.flattener.ComponentFlattener componentFlattener();
++ net.kyori.adventure.text.serializer.plain.PlainComponentSerializer plainComponentSerializer();
++ net.kyori.adventure.text.serializer.gson.GsonComponentSerializer gsonComponentSerializer();
++ net.kyori.adventure.text.serializer.gson.GsonComponentSerializer colorDownsamplingGsonComponentSerializer();
++ net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer legacyComponentSerializer();
++ // Paper end
+
+ void reportTimings(); // Paper
+ Material toLegacy(Material material);
+diff --git a/src/main/java/org/bukkit/Warning.java b/src/main/java/org/bukkit/Warning.java
+index efb97712cc9dc7c1e12a59f5b94e4f2ad7c6b7d8..3024468af4c073324e536c1cb26beffb1e09f3f4 100644
+--- a/src/main/java/org/bukkit/Warning.java
++++ b/src/main/java/org/bukkit/Warning.java
+@@ -67,6 +67,7 @@ public @interface Warning {
+ * </ul>
+ */
+ public boolean printFor(@Nullable Warning warning) {
++ if (Boolean.getBoolean("paper.alwaysPrintWarningState")) return true; // Paper
+ if (this == DEFAULT) {
+ return warning == null || warning.value();
+ }
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 0fcf1dbb7b9cd2cfcb803b1a430d6131de87c92d..f9dea4ed6c57c88fa2f5b710440a35f4d310dd5f 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -38,7 +38,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a world, which may contain entities, chunks and blocks
+ */
+-public interface World extends PluginMessageRecipient, Metadatable {
++public interface World extends PluginMessageRecipient, Metadatable, net.kyori.adventure.audience.ForwardingAudience { // Paper
+
+ /**
+ * Gets the {@link Block} at the given coordinates
+@@ -640,6 +640,14 @@ public interface World extends PluginMessageRecipient, Metadatable {
+ @NotNull
+ public List<Player> getPlayers();
+
++ // Paper start
++ @NotNull
++ @Override
++ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
++ return this.getPlayers();
++ }
++ // Paper end
++
+ /**
+ * Returns a list of entities within a bounding box centered around a
+ * Location.
+diff --git a/src/main/java/org/bukkit/block/Sign.java b/src/main/java/org/bukkit/block/Sign.java
+index 7e3cf00e49c66023bf46c298ef46c00e8c3c2caf..6ea9b54d95d80070c01a612c0ce2ab37f0b4ad41 100644
+--- a/src/main/java/org/bukkit/block/Sign.java
++++ b/src/main/java/org/bukkit/block/Sign.java
+@@ -7,13 +7,48 @@ import org.jetbrains.annotations.NotNull;
+ * Represents a captured state of either a SignPost or a WallSign.
+ */
+ public interface Sign extends TileState, Colorable {
++ // Paper start
++ /**
++ * Gets all the lines of text currently on this sign.
++ *
++ * @return Array of Strings containing each line of text
++ */
++ @NotNull
++ public java.util.List<net.kyori.adventure.text.Component> lines();
++
++ /**
++ * Gets the line of text at the specified index.
++ * <p>
++ * For example, getLine(0) will return the first line of text.
++ *
++ * @param index Line number to get the text from, starting at 0
++ * @throws IndexOutOfBoundsException Thrown when the line does not exist
++ * @return Text on the given line
++ */
++ @NotNull
++ public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
++
++ /**
++ * Sets the line of text at the specified index.
++ * <p>
++ * For example, setLine(0, "Line One") will set the first line of text to
++ * "Line One".
++ *
++ * @param index Line number to set the text at, starting from 0
++ * @param line New text to set at the specified index
++ * @throws IndexOutOfBoundsException If the index is out of the range 0..3
++ */
++ public void line(int index, @NotNull net.kyori.adventure.text.Component line) throws IndexOutOfBoundsException;
++ // Paper end
+
+ /**
+ * Gets all the lines of text currently on this sign.
+ *
+ * @return Array of Strings containing each line of text
++ * @deprecated in favour of {@link #lines()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String[] getLines();
+
+ /**
+@@ -24,8 +59,10 @@ public interface Sign extends TileState, Colorable {
+ * @param index Line number to get the text from, starting at 0
+ * @throws IndexOutOfBoundsException Thrown when the line does not exist
+ * @return Text on the given line
++ * @deprecated in favour of {@link #line(int)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getLine(int index) throws IndexOutOfBoundsException;
+
+ /**
+@@ -37,7 +74,9 @@ public interface Sign extends TileState, Colorable {
+ * @param index Line number to set the text at, starting from 0
+ * @param line New text to set at the specified index
+ * @throws IndexOutOfBoundsException If the index is out of the range 0..3
++ * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
+
+ /**
+diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
+index ac772bf349e0ffe9cab1df165d9460b387f2fe69..c88418c7aa19b4fecdfa9af3d18ff202a5dc5763 100644
+--- a/src/main/java/org/bukkit/command/CommandSender.java
++++ b/src/main/java/org/bukkit/command/CommandSender.java
+@@ -6,12 +6,13 @@ import org.bukkit.permissions.Permissible;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
+-public interface CommandSender extends Permissible {
++public interface CommandSender extends net.kyori.adventure.audience.Audience, Permissible { // Paper
+
+ /**
+ * Sends this sender a message
+ *
+ * @param message Message to be displayed
++ * @see #sendMessage(net.kyori.adventure.text.Component)
+ */
+ public void sendMessage(@NotNull String message);
+
+@@ -19,6 +20,7 @@ public interface CommandSender extends Permissible {
+ * Sends this sender multiple messages
+ *
+ * @param messages An array of messages to be displayed
++ * @see #sendMessage(net.kyori.adventure.text.Component)
+ */
+ public void sendMessage(@NotNull String[] messages);
+
+@@ -27,6 +29,7 @@ public interface CommandSender extends Permissible {
+ *
+ * @param message Message to be displayed
+ * @param sender The sender of this message
++ * @see #sendMessage(net.kyori.adventure.identity.Identified, net.kyori.adventure.text.Component)
+ */
+ public void sendMessage(@Nullable UUID sender, @NotNull String message);
+
+@@ -35,6 +38,7 @@ public interface CommandSender extends Permissible {
+ *
+ * @param messages An array of messages to be displayed
+ * @param sender The sender of this message
++ * @see #sendMessage(net.kyori.adventure.identity.Identified, net.kyori.adventure.text.Component)
+ */
+ public void sendMessage(@Nullable UUID sender, @NotNull String[] messages);
+
+@@ -61,7 +65,9 @@ public interface CommandSender extends Permissible {
+ * Sends this sender a chat component.
+ *
+ * @param component the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -70,7 +76,9 @@ public interface CommandSender extends Permissible {
+ * Sends an array of components as a single message to the sender.
+ *
+ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -80,7 +88,9 @@ public interface CommandSender extends Permissible {
+ *
+ * @param component the components to send
+ * @param sender the sender of the message
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -90,7 +100,9 @@ public interface CommandSender extends Permissible {
+ *
+ * @param components the components to send
+ * @param sender the sender of the message
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -99,4 +111,11 @@ public interface CommandSender extends Permissible {
+ @NotNull
+ Spigot spigot();
+ // Spigot end
++
++ // Paper start
++ @Override
++ default void sendMessage(final @NotNull net.kyori.adventure.identity.Identity identity, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
++ this.sendMessage(org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(message));
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/ProxiedCommandSender.java b/src/main/java/org/bukkit/command/ProxiedCommandSender.java
+index fcc34b640265f4dccb46b9f09466ab8e1d96043e..74599b4ee0518481c0e3a5f6ab2f5302837f1ae3 100644
+--- a/src/main/java/org/bukkit/command/ProxiedCommandSender.java
++++ b/src/main/java/org/bukkit/command/ProxiedCommandSender.java
+@@ -3,7 +3,7 @@ package org.bukkit.command;
+
+ import org.jetbrains.annotations.NotNull;
+
+-public interface ProxiedCommandSender extends CommandSender {
++public interface ProxiedCommandSender extends CommandSender, net.kyori.adventure.audience.ForwardingAudience.Single { // Paper
+
+ /**
+ * Returns the CommandSender which triggered this proxied command
+@@ -21,4 +21,16 @@ public interface ProxiedCommandSender extends CommandSender {
+ @NotNull
+ CommandSender getCallee();
+
++ // Paper start
++ @Override
++ default void sendMessage(final @NotNull net.kyori.adventure.identity.Identity source, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
++ net.kyori.adventure.audience.ForwardingAudience.Single.super.sendMessage(source, message, type);
++ }
++
++ @NotNull
++ @Override
++ default net.kyori.adventure.audience.Audience audience() {
++ return this.getCaller();
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/enchantments/Enchantment.java b/src/main/java/org/bukkit/enchantments/Enchantment.java
+index b833ef63fbe01271ceb2bd83a9eb4a84c9912761..8eb0497c81744874809ebc4bc2e28b128e66a926 100644
+--- a/src/main/java/org/bukkit/enchantments/Enchantment.java
++++ b/src/main/java/org/bukkit/enchantments/Enchantment.java
+@@ -294,6 +294,19 @@ public abstract class Enchantment implements Keyed {
+ * @return True if the enchantment may be applied, otherwise False
+ */
+ public abstract boolean canEnchantItem(@NotNull ItemStack item);
++ // Paper start
++ /**
++ * Get the name of the enchantment with its applied level.
++ * <p>
++ * If the given {@code level} is either less than the {@link #getStartLevel()} or greater than the {@link #getMaxLevel()},
++ * the level may not be shown in the numeral format one may otherwise expect.
++ * </p>
++ *
++ * @param level the level of the enchantment to show
++ * @return the name of the enchantment with {@code level} applied
++ */
++ public abstract @NotNull net.kyori.adventure.text.Component displayName(int level);
++ // Paper end
+
+ @Override
+ public boolean equals(Object obj) {
+diff --git a/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java b/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
+index 9566e4306ada5e82dede0f002aa06da12c44996b..4d5f0837bd0e02a30c943d8969fb6b13452322e0 100644
+--- a/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
++++ b/src/main/java/org/bukkit/enchantments/EnchantmentWrapper.java
+@@ -63,4 +63,11 @@ public class EnchantmentWrapper extends Enchantment {
+ public boolean conflictsWith(@NotNull Enchantment other) {
+ return getEnchantment().conflictsWith(other);
+ }
++ // Paper start
++ @NotNull
++ @Override
++ public net.kyori.adventure.text.Component displayName(int level) {
++ return getEnchantment().displayName(level);
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 344c14a5ed86e9ebe401bfb5ba3aedc0c0ed0b04..41a1bc45cc5eb7f19374115ade7f5328c7fc1dae 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a base entity in the world
+ */
+-public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder {
++public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowEntity> { // Paper
+
+ /**
+ * Gets the entity's current position
+@@ -602,4 +602,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ @Override
+ Spigot spigot();
+ // Spigot end
++
++ // Paper start
++ @NotNull
++ @Override
++ default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
++ return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.customName())));
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 344aecc097c5e12476d2be53019145812b028914..c5e7e037493ffe1f1764c3ad5d3e12bfac498cb2 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -30,7 +30,28 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a player, connected or not
+ */
+-public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient {
++public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified { // Paper
++
++ // Paper start
++ @Override
++ default @NotNull net.kyori.adventure.identity.Identity identity() {
++ return net.kyori.adventure.identity.Identity.identity(this.getUniqueId());
++ }
++
++ /**
++ * Gets the "friendly" name to display of this player.
++ *
++ * @return the display name
++ */
++ @NotNull net.kyori.adventure.text.Component displayName();
++
++ /**
++ * Sets the "friendly" name to display of this player.
++ *
++ * @param displayName the display name to set
++ */
++ void displayName(final @Nullable net.kyori.adventure.text.Component displayName);
++ // Paper end
+
+ /**
+ * Gets the "friendly" name to display of this player. This may include
+@@ -40,7 +61,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * places defined by plugins.
+ *
+ * @return the friendly name
++ * @deprecated in favour of {@link #displayName()}
+ */
++ @Deprecated // Paper
+ @NotNull
+ public String getDisplayName();
+
+@@ -52,15 +75,50 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * places defined by plugins.
+ *
+ * @param name The new display name.
++ * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setDisplayName(@Nullable String name);
+
++ // Paper start
++ /**
++ * Sets the name that is shown on the in-game player list.
++ * <p>
++ * If the value is null, the name will be identical to {@link #getName()}.
++ *
++ * @param name new player list name
++ */
++ void playerListName(@Nullable net.kyori.adventure.text.Component name);
++
++ /**
++ * Gets the name that is shown on the in-game player list.
++ *
++ * @return the player list name
++ */
++ @Nullable net.kyori.adventure.text.Component playerListName();
++
++ /**
++ * Gets the currently displayed player list header for this player.
++ *
++ * @return player list header or null
++ */
++ @Nullable net.kyori.adventure.text.Component playerListHeader();
++
++ /**
++ * Gets the currently displayed player list footer for this player.
++ *
++ * @return player list footer or null
++ */
++ @Nullable net.kyori.adventure.text.Component playerListFooter();
++ // Paper end
+ /**
+ * Gets the name that is shown on the player list.
+ *
+ * @return the player list name
++ * @deprecated in favour of {@link #playerListName()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getPlayerListName();
+
+ /**
+@@ -69,14 +127,18 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * If the value is null, the name will be identical to {@link #getName()}.
+ *
+ * @param name new player list name
++ * @deprecated in favour of {@link #playerListName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setPlayerListName(@Nullable String name);
+
+ /**
+ * Gets the currently displayed player list header for this player.
+ *
+ * @return player list header or null
++ * @deprecated in favour of {@link #playerListHeader()}
+ */
++ @Deprecated // Paper
+ @Nullable
+ public String getPlayerListHeader();
+
+@@ -84,7 +146,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * Gets the currently displayed player list footer for this player.
+ *
+ * @return player list header or null
++ * @deprecated in favour of {@link #playerListFooter()}
+ */
++ @Deprecated // Paper
+ @Nullable
+ public String getPlayerListFooter();
+
+@@ -92,14 +156,18 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * Sets the currently displayed player list header for this player.
+ *
+ * @param header player list header, null for empty
++ * @deprecated in favour of {@link #sendPlayerListHeader(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setPlayerListHeader(@Nullable String header);
+
+ /**
+ * Sets the currently displayed player list footer for this player.
+ *
+ * @param footer player list footer, null for empty
++ * @deprecated in favour of {@link #sendPlayerListFooter(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setPlayerListFooter(@Nullable String footer);
+
+ /**
+@@ -108,7 +176,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ *
+ * @param header player list header, null for empty
+ * @param footer player list footer, null for empty
++ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setPlayerListHeaderFooter(@Nullable String header, @Nullable String footer);
+
+ /**
+@@ -146,9 +216,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * Kicks player with custom kick message.
+ *
+ * @param message kick message
++ * @deprecated in favour of {@link #kick(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void kickPlayer(@Nullable String message);
+
++ // Paper start
++ /**
++ * Kicks player with custom kick message.
++ *
++ * @param message kick message
++ */
++ void kick(final @Nullable net.kyori.adventure.text.Component message);
++ // Paper end
++
+ /**
+ * Says a message (or runs a command).
+ *
+@@ -448,6 +529,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ @Deprecated
+ public boolean sendChunkChange(@NotNull Location loc, int sx, int sy, int sz, @NotNull byte[] data);
+
++ // Paper start
+ /**
+ * Send a sign change. This fakes a sign change packet for a user at
+ * a certain location. This will not actually change the world in any way.
+@@ -463,6 +545,43 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @throws IllegalArgumentException if location is null
+ * @throws IllegalArgumentException if lines is non-null and has a length less than 4
+ */
++ void sendSignChange(@NotNull Location loc, @Nullable java.util.List<net.kyori.adventure.text.Component> lines) throws IllegalArgumentException;
++ /**
++ * Send a sign change. This fakes a sign change packet for a user at
++ * a certain location. This will not actually change the world in any way.
++ * This method will use a sign at the location's block or a faked sign
++ * sent via
++ * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
++ * <p>
++ * If the client does not have a sign at the given location it will
++ * display an error message to the user.
++ *
++ * @param loc the location of the sign
++ * @param lines the new text on the sign or null to clear it
++ * @param dyeColor the color of the sign
++ * @throws IllegalArgumentException if location is null
++ * @throws IllegalArgumentException if dyeColor is null
++ * @throws IllegalArgumentException if lines is non-null and has a length less than 4
++ */
++ void sendSignChange(@NotNull Location loc, @Nullable java.util.List<net.kyori.adventure.text.Component> lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException;
++ // Paper end
++ /**
++ * Send a sign change. This fakes a sign change packet for a user at
++ * a certain location. This will not actually change the world in any way.
++ * This method will use a sign at the location's block or a faked sign
++ * sent via
++ * {@link #sendBlockChange(org.bukkit.Location, org.bukkit.Material, byte)}.
++ * <p>
++ * If the client does not have a sign at the given location it will
++ * display an error message to the user.
++ *
++ * @param loc the location of the sign
++ * @param lines the new text on the sign or null to clear it
++ * @throws IllegalArgumentException if location is null
++ * @throws IllegalArgumentException if lines is non-null and has a length less than 4
++ * @deprecated in favour of {@link #sendSignChange(org.bukkit.Location, java.util.List)}
++ */
++ @Deprecated // Paper
+ public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) throws IllegalArgumentException;
+
+
+@@ -482,7 +601,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @throws IllegalArgumentException if location is null
+ * @throws IllegalArgumentException if dyeColor is null
+ * @throws IllegalArgumentException if lines is non-null and has a length less than 4
++ * @deprecated in favour of {@link #sendSignChange(org.bukkit.Location, java.util.List, org.bukkit.DyeColor)}
+ */
++ @Deprecated // Paper
+ public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor) throws IllegalArgumentException;
+
+ /**
+@@ -1220,6 +1341,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ public int getClientViewDistance();
+
++ // Paper start
++ /**
++ * Gets the player's current locale.
++ *
++ * @return the player's locale
++ */
++ @NotNull java.util.Locale locale();
++ // Paper end
+ /**
+ * Gets the player's current locale.
+ *
+@@ -1230,8 +1359,10 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * they wish.
+ *
+ * @return the player's locale
++ * @deprecated in favour of {@link #locale()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getLocale();
+
+ /**
+@@ -1249,6 +1380,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ public void openBook(@NotNull ItemStack book);
+
++ // Paper start
++ @NotNull
++ @Override
++ default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
++ return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
++ }
++ // Paper end
++
+ // Spigot start
+ public class Spigot extends Entity.Spigot {
+
+@@ -1303,11 +1442,13 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
++ @Deprecated // Paper
+ @Override
+ public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
++ @Deprecated // Paper
+ @Override
+ public void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+@@ -1318,7 +1459,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ *
+ * @param position the screen position
+ * @param component the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -1328,7 +1471,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ *
+ * @param position the screen position
+ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -1339,7 +1484,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @param position the screen position
+ * @param sender the sender of the message
+ * @param component the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -1350,7 +1497,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @param position the screen position
+ * @param sender the sender of the message
+ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
++ @Deprecated // Paper
+ public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @Nullable UUID sender, @NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ throw new UnsupportedOperationException("Not supported yet.");
+
+diff --git a/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java b/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
+index 63c80b4ee1f7adc8a9efc3b607993104b1991f90..91cab8b13d5bba34007f124838b32a1df58c5ac7 100644
+--- a/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
++++ b/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java
+@@ -32,7 +32,9 @@ public interface CommandMinecart extends Minecart {
+ * same as setting it to "@".
+ *
+ * @param name New name for this CommandMinecart.
++ * @deprecated in favour of {@link #customName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setName(@Nullable String name);
+
+ }
+diff --git a/src/main/java/org/bukkit/event/block/SignChangeEvent.java b/src/main/java/org/bukkit/event/block/SignChangeEvent.java
+index 7190db11eff7d48df8a99f405a9dbaefdfa76e3d..1f79f704abf339150df08900b8ea7da4cefef258 100644
+--- a/src/main/java/org/bukkit/event/block/SignChangeEvent.java
++++ b/src/main/java/org/bukkit/event/block/SignChangeEvent.java
+@@ -16,12 +16,25 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancel = false;
+ private final Player player;
+- private final String[] lines;
++ // Paper start
++ private final java.util.List<net.kyori.adventure.text.Component> adventure$lines;
+
++ public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player player, @NotNull final java.util.List<net.kyori.adventure.text.Component> adventure$lines) {
++ super(theBlock);
++ this.player = player;
++ this.adventure$lines = adventure$lines;
++ }
++
++ @Deprecated // Paper end
+ public SignChangeEvent(@NotNull final Block theBlock, @NotNull final Player thePlayer, @NotNull final String[] theLines) {
+ super(theBlock);
+ this.player = thePlayer;
+- this.lines = theLines;
++ // Paper start
++ this.adventure$lines = new java.util.ArrayList<>();
++ for (String theLine : theLines) {
++ this.adventure$lines.add(org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(theLine));
++ }
++ // Paper end
+ }
+
+ /**
+@@ -34,14 +47,52 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+ return player;
+ }
+
++ // Paper start
++ /**
++ * Gets all of the lines of text from the sign involved in this event.
++ *
++ * @return the String array for the sign's lines new text
++ */
++ public @NotNull java.util.List<net.kyori.adventure.text.Component> lines() {
++ return this.adventure$lines;
++ }
++
++ /**
++ * Gets a single line of text from the sign involved in this event.
++ *
++ * @param index index of the line to get
++ * @return the String containing the line of text associated with the
++ * provided index
++ * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
++ * or < 0}
++ */
++ public @Nullable net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException {
++ return this.adventure$lines.get(index);
++ }
++
++ /**
++ * Sets a single line for the sign involved in this event
++ *
++ * @param index index of the line to set
++ * @param line text to set
++ * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
++ * or < 0}
++ */
++ public void line(int index, @Nullable net.kyori.adventure.text.Component line) throws IndexOutOfBoundsException {
++ this.adventure$lines.set(index, line);
++ }
++ // Paper end
++
+ /**
+ * Gets all of the lines of text from the sign involved in this event.
+ *
+ * @return the String array for the sign's lines new text
++ * @deprecated in favour of {@link #lines()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String[] getLines() {
+- return lines;
++ return adventure$lines.stream().map(org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer()::serialize).toArray(String[]::new); // Paper
+ }
+
+ /**
+@@ -52,10 +103,12 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+ * provided index
+ * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+ * or < 0}
++ * @deprecated in favour of {@link #line(int)}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public String getLine(int index) throws IndexOutOfBoundsException {
+- return lines[index];
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.adventure$lines.get(index)); // Paper
+ }
+
+ /**
+@@ -65,9 +118,11 @@ public class SignChangeEvent extends BlockEvent implements Cancellable {
+ * @param line text to set
+ * @throws IndexOutOfBoundsException thrown when the provided index is {@literal > 3
+ * or < 0}
++ * @deprecated in favour of {@link #line(int, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setLine(int index, @Nullable String line) throws IndexOutOfBoundsException {
+- lines[index] = line;
++ adventure$lines.set(index, line != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(line) : null); // Paper
+ }
+
+ @Override
+diff --git a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+index 3c2ea8fec3a748cab7f5ad9100d12bd8213ec6c9..a01d4c21bedc7f1a54f5a330bb4c2909ce3a18e4 100644
+--- a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
++++ b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+@@ -12,25 +12,48 @@ import org.jetbrains.annotations.Nullable;
+ public class PlayerDeathEvent extends EntityDeathEvent {
+ private int newExp = 0;
+ private String deathMessage = "";
++ private net.kyori.adventure.text.Component adventure$deathMessage; // Paper
+ private int newLevel = 0;
+ private int newTotalExp = 0;
+ private boolean keepLevel = false;
+ private boolean keepInventory = false;
++ // Paper start
++ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage) {
++ this(player, drops, droppedExp, 0, adventure$deathMessage, null);
++ }
++
++ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage) {
++ this(player, drops, droppedExp, newExp, 0, 0, adventure$deathMessage, deathMessage);
++ }
+
++ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage) {
++ super(player, drops, droppedExp);
++ this.newExp = newExp;
++ this.newTotalExp = newTotalExp;
++ this.newLevel = newLevel;
++ this.deathMessage = deathMessage;
++ this.adventure$deathMessage = adventure$deathMessage;
++ }
++ // Paper end
++
++ @Deprecated // Paper
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final String deathMessage) {
+ this(player, drops, droppedExp, 0, deathMessage);
+ }
+
++ @Deprecated // Paper
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, @Nullable final String deathMessage) {
+ this(player, drops, droppedExp, newExp, 0, 0, deathMessage);
+ }
+
++ @Deprecated // Paper
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final String deathMessage) {
+ super(player, drops, droppedExp);
+ this.newExp = newExp;
+ this.newTotalExp = newTotalExp;
+ this.newLevel = newLevel;
+ this.deathMessage = deathMessage;
++ this.adventure$deathMessage = deathMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(deathMessage) : net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
+ @NotNull
+@@ -39,25 +62,55 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ return (Player) entity;
+ }
+
++ // Paper start
++ /**
++ * Set the death message that will appear to everyone on the server.
++ *
++ * @param deathMessage Message to appear to other players on the server.
++ */
++ public void deathMessage(@Nullable net.kyori.adventure.text.Component deathMessage) {
++ this.deathMessage = null;
++ this.adventure$deathMessage = deathMessage;
++ }
++
++ /**
++ * Get the death message that will appear to everyone on the server.
++ *
++ * @return Message to appear to other players on the server.
++ */
++ public @Nullable net.kyori.adventure.text.Component deathMessage() {
++ return this.adventure$deathMessage;
++ }
++ // Paper end
++
+ /**
+ * Set the death message that will appear to everyone on the server.
+ *
+ * @param deathMessage Message to appear to other players on the server.
++ * @deprecated in favour of {@link #deathMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setDeathMessage(@Nullable String deathMessage) {
+ this.deathMessage = deathMessage;
++ this.adventure$deathMessage = deathMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(deathMessage) : net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
+ /**
+ * Get the death message that will appear to everyone on the server.
+ *
+ * @return Message to appear to other players on the server.
++ * @deprecated in favour of {@link #deathMessage()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public String getDeathMessage() {
+- return deathMessage;
++ return this.deathMessage != null ? this.deathMessage : (this.adventure$deathMessage != null ? getDeathMessageString(this.adventure$deathMessage) : null); // Paper
+ }
+-
++ // Paper start //TODO: add translation API to drop String deathMessage in favor of just Adventure
++ private static String getDeathMessageString(net.kyori.adventure.text.Component component) {
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(component);
++ }
++ // Paper end
+ /**
+ * Gets how much EXP the Player should have at respawn.
+ * <p>
+diff --git a/src/main/java/org/bukkit/event/inventory/InventoryType.java b/src/main/java/org/bukkit/event/inventory/InventoryType.java
+index f1e9bc9bc797b7216336d3470e3c696a06f2b21a..090d22bd30f7947103771aaaf09a2398970ac337 100644
+--- a/src/main/java/org/bukkit/event/inventory/InventoryType.java
++++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java
+@@ -136,6 +136,18 @@ public enum InventoryType {
+ private final String title;
+ private final boolean isCreatable;
+
++ // Paper start
++ private final net.kyori.adventure.text.Component defaultTitleComponent;
++
++ /**
++ * Gets the inventory's default title.
++ *
++ * @return the inventory's default title
++ */
++ public @NotNull net.kyori.adventure.text.Component defaultTitle() {
++ return defaultTitleComponent;
++ }
++ // Paper end
+ private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle) {
+ this(defaultSize, defaultTitle, true);
+ }
+@@ -144,6 +156,7 @@ public enum InventoryType {
+ size = defaultSize;
+ title = defaultTitle;
+ this.isCreatable = isCreatable;
++ this.defaultTitleComponent = net.kyori.adventure.text.Component.text(defaultTitle); // Paper - Adventure
+ }
+
+ public int getDefaultSize() {
+@@ -151,6 +164,7 @@ public enum InventoryType {
+ }
+
+ @NotNull
++ @Deprecated // Paper
+ public String getDefaultTitle() {
+ return title;
+ }
+diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
+index 9c68c3f2d61500479f48b80264f625aaae2f3204..399afcd19fcb6acd24857ed6ab48cf0d105a01a3 100644
+--- a/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
++++ b/src/main/java/org/bukkit/event/player/AsyncPlayerChatEvent.java
+@@ -22,7 +22,11 @@ import org.jetbrains.annotations.NotNull;
+ * <p>
+ * Care should be taken to check {@link #isAsynchronous()} and treat the event
+ * appropriately.
++ *
++ * @deprecated use {@link io.papermc.paper.event.player.AsyncChatEvent} instead
+ */
++@Deprecated // Paper
[email protected](value = false, reason = "Don't nag on old event yet") // Paper
+ public class AsyncPlayerChatEvent extends PlayerEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancel = false;
+diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+index c8384da69af61e1970f254a3a9c206ee81d7a989..d3b4219a57fff4519ef8d803c333c854fafa7859 100644
+--- a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
++++ b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+@@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull;
+ public class AsyncPlayerPreLoginEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ private Result result;
+- private String message;
++ private net.kyori.adventure.text.Component message; // Paper
+ private final String name;
+ private final InetAddress ipAddress;
+ private final UUID uniqueId;
+@@ -27,7 +27,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId) {
+ super(true);
+ this.result = Result.ALLOWED;
+- this.message = "";
++ this.message = net.kyori.adventure.text.Component.empty(); // Paper
+ this.name = name;
+ this.ipAddress = ipAddress;
+ this.uniqueId = uniqueId;
+@@ -79,6 +79,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ this.result = result == null ? null : Result.valueOf(result.name());
+ }
+
++ // Paper start
+ /**
+ * Gets the current kick message that will be used if getResult() !=
+ * Result.ALLOWED
+@@ -86,7 +87,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ * @return Current kick message
+ */
+ @NotNull
+- public String getKickMessage() {
++ public net.kyori.adventure.text.Component kickMessage() {
+ return message;
+ }
+
+@@ -95,16 +96,66 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ *
+ * @param message New kick message
+ */
+- public void setKickMessage(@NotNull final String message) {
++ public void kickMessage(@NotNull final net.kyori.adventure.text.Component message) {
++ this.message = message;
++ }
++
++ /**
++ * Disallows the player from logging in, with the given reason
++ *
++ * @param result New result for disallowing the player
++ * @param message Kick message to display to the user
++ */
++ public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
++ this.result = result;
+ this.message = message;
+ }
+
++ /**
++ * Disallows the player from logging in, with the given reason
++ *
++ * @param result New result for disallowing the player
++ * @param message Kick message to display to the user
++ * @deprecated This method uses a deprecated enum from {@link
++ * PlayerPreLoginEvent}
++ * @see #disallow(Result, String)
++ */
++ @Deprecated
++ public void disallow(@NotNull final PlayerPreLoginEvent.Result result, @NotNull final net.kyori.adventure.text.Component message) {
++ this.result = result == null ? null : Result.valueOf(result.name());
++ this.message = message;
++ }
++ // Paper end
++ /**
++ * Gets the current kick message that will be used if getResult() !=
++ * Result.ALLOWED
++ *
++ * @return Current kick message
++ * @deprecated in favour of {@link #kickMessage()}
++ */
++ @NotNull
++ @Deprecated // Paper
++ public String getKickMessage() {
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.message); // Paper
++ }
++
++ /**
++ * Sets the kick message to display if getResult() != Result.ALLOWED
++ *
++ * @param message New kick message
++ * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
++ */
++ @Deprecated // Paper
++ public void setKickMessage(@NotNull final String message) {
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
++ }
++
+ /**
+ * Allows the player to log in
+ */
+ public void allow() {
+ result = Result.ALLOWED;
+- message = "";
++ message = net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
+ /**
+@@ -112,10 +163,12 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ *
+ * @param result New result for disallowing the player
+ * @param message Kick message to display to the user
++ * @deprecated in favour of {@link #disallow(org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void disallow(@NotNull final Result result, @NotNull final String message) {
+ this.result = result;
+- this.message = message;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
+ }
+
+ /**
+@@ -130,7 +183,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ @Deprecated
+ public void disallow(@NotNull final PlayerPreLoginEvent.Result result, @NotNull final String message) {
+ this.result = result == null ? null : Result.valueOf(result.name());
+- this.message = message;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/event/player/PlayerChatEvent.java b/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
+index 8ea56aac752544f798728b429e7152afbee497e4..213837794c603cb9f152f917941b912326a08030 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerChatEvent.java
+@@ -18,6 +18,7 @@ import org.jetbrains.annotations.NotNull;
+ * Listening to this event forces chat to wait for the main thread which
+ * causes delays for chat. {@link AsyncPlayerChatEvent} is the encouraged
+ * alternative for thread safe implementations.
++ * @deprecated use {@link io.papermc.paper.event.player.ChatEvent} instead
+ */
+ @Deprecated
+ @Warning(reason = "Listening to this event forces chat to wait for the main thread, delaying chat messages.")
+diff --git a/src/main/java/org/bukkit/event/player/PlayerEvent.java b/src/main/java/org/bukkit/event/player/PlayerEvent.java
+index 793b661b6d2d05de3d7f4fc26a4c018a2af58e62..f6d3b817de3001f04ea4554c7c39a1290af3fd6d 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerEvent.java
+@@ -14,7 +14,7 @@ public abstract class PlayerEvent extends Event {
+ player = who;
+ }
+
+- PlayerEvent(@NotNull final Player who, boolean async) {
++ public PlayerEvent(@NotNull final Player who, boolean async) { // Paper - public
+ super(async);
+ player = who;
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java b/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
+index d06684aba7688ce06777dbd837a46856a9d7767f..851a189d42e271679abc78f95049d8badf7a2b64 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerJoinEvent.java
+@@ -10,30 +10,60 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public class PlayerJoinEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+- private String joinMessage;
++ // Paper start
++ private net.kyori.adventure.text.Component joinMessage;
++ public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final net.kyori.adventure.text.Component joinMessage) {
++ super(playerJoined);
++ this.joinMessage = joinMessage;
++ }
+
++ @Deprecated // Paper end
+ public PlayerJoinEvent(@NotNull final Player playerJoined, @Nullable final String joinMessage) {
+ super(playerJoined);
++ this.joinMessage = joinMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(joinMessage) : null; // Paper end
++ }
++
++ // Paper start
++ /**
++ * Gets the join message to send to all online players
++ *
++ * @return string join message. Can be null
++ */
++ public @Nullable net.kyori.adventure.text.Component joinMessage() {
++ return this.joinMessage;
++ }
++
++ /**
++ * Sets the join message to send to all online players
++ *
++ * @param joinMessage join message. If null, no message will be sent
++ */
++ public void joinMessage(@Nullable net.kyori.adventure.text.Component joinMessage) {
+ this.joinMessage = joinMessage;
+ }
++ // Paper end
+
+ /**
+ * Gets the join message to send to all online players
+ *
+ * @return string join message. Can be null
++ * @deprecated in favour of {@link #joinMessage()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public String getJoinMessage() {
+- return joinMessage;
++ return this.joinMessage == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.joinMessage); // Paper
+ }
+
+ /**
+ * Sets the join message to send to all online players
+ *
+ * @param joinMessage join message. If null, no message will be sent
++ * @deprecated in favour of {@link #joinMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setJoinMessage(@Nullable String joinMessage) {
+- this.joinMessage = joinMessage;
++ this.joinMessage = joinMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(joinMessage) : null; // Paper
+ }
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java b/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
+index 14c337f15fc804f52e52cb0a185aad38d89303a8..5c0efe74237dbe6803ce023fde99682ff70d1a92 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerKickEvent.java
+@@ -10,35 +10,84 @@ import org.jetbrains.annotations.NotNull;
+ */
+ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+- private String leaveMessage;
+- private String kickReason;
++ private net.kyori.adventure.text.Component leaveMessage; // Paper
++ private net.kyori.adventure.text.Component kickReason; // Paper
+ private Boolean cancel;
+
++ @Deprecated // Paper
+ public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final String kickReason, @NotNull final String leaveMessage) {
++ super(playerKicked);
++ this.kickReason = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(kickReason); // Paper
++ this.leaveMessage = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(leaveMessage); // Paper
++ this.cancel = false;
++ }
++ // Paper start
++ public PlayerKickEvent(@NotNull final Player playerKicked, @NotNull final net.kyori.adventure.text.Component kickReason, @NotNull final net.kyori.adventure.text.Component leaveMessage) {
+ super(playerKicked);
+ this.kickReason = kickReason;
+ this.leaveMessage = leaveMessage;
+ this.cancel = false;
+ }
+
++ /**
++ * Gets the leave message send to all online players
++ *
++ * @return string kick reason
++ */
++ public @NotNull net.kyori.adventure.text.Component leaveMessage() {
++ return this.leaveMessage;
++ }
++
++ /**
++ * Sets the leave message send to all online players
++ *
++ * @param leaveMessage leave message
++ */
++ public void leaveMessage(@NotNull net.kyori.adventure.text.Component leaveMessage) {
++ this.leaveMessage = leaveMessage;
++ }
++
+ /**
+ * Gets the reason why the player is getting kicked
+ *
+ * @return string kick reason
+ */
++ public @NotNull net.kyori.adventure.text.Component reason() {
++ return this.kickReason;
++ }
++
++ /**
++ * Sets the reason why the player is getting kicked
++ *
++ * @param kickReason kick reason
++ */
++ public void reason(@NotNull net.kyori.adventure.text.Component kickReason) {
++ this.kickReason = kickReason;
++ }
++ // Paper end
++
++ /**
++ * Gets the reason why the player is getting kicked
++ *
++ * @return string kick reason
++ * @deprecated in favour of {@link #reason()}
++ */
+ @NotNull
++ @Deprecated // Paper
+ public String getReason() {
+- return kickReason;
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.kickReason); // Paper
+ }
+
+ /**
+ * Gets the leave message send to all online players
+ *
+ * @return string kick reason
++ * @deprecated in favour of {@link #leaveMessage()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getLeaveMessage() {
+- return leaveMessage;
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.leaveMessage); // Paper
+ }
+
+ @Override
+@@ -55,18 +104,22 @@ public class PlayerKickEvent extends PlayerEvent implements Cancellable {
+ * Sets the reason why the player is getting kicked
+ *
+ * @param kickReason kick reason
++ * @deprecated in favour of {@link #reason(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setReason(@NotNull String kickReason) {
+- this.kickReason = kickReason;
++ this.kickReason = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(kickReason); // Paper
+ }
+
+ /**
+ * Sets the leave message send to all online players
+ *
+ * @param leaveMessage leave message
++ * @deprecated in favour of {@link #leaveMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setLeaveMessage(@NotNull String leaveMessage) {
+- this.leaveMessage = leaveMessage;
++ this.leaveMessage = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(leaveMessage); // Paper
+ }
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java b/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
+index 1db386bb701cb6974daedc6bb5b93a3afbc42100..84521186404b8e43c81a2f9513dce2be40d27840 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerLocaleChangeEvent.java
+@@ -12,18 +12,32 @@ public class PlayerLocaleChangeEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+ //
+ private final String locale;
++ // Paper start
++ private final java.util.Locale adventure$locale;
++ /**
++ * @see Player#getLocale()
++ *
++ * @return the player's new locale
++ */
++ public @NotNull java.util.Locale locale() {
++ return this.adventure$locale;
++ }
++ // Paper end
+
+ public PlayerLocaleChangeEvent(@NotNull Player who, @NotNull String locale) {
+ super(who);
+ this.locale = locale;
++ this.adventure$locale = net.kyori.adventure.translation.Translator.parseLocale(locale); // Paper
+ }
+
+ /**
+ * @see Player#getLocale()
+ *
+ * @return the player's new locale
++ * @deprecated in favour of {@link #locale()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getLocale() {
+ return locale;
+ }
+diff --git a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
+index 084ca8cfcb7381bfa4fa6280748cf9b81210a9c1..75cc54739ef841cd90568d74927d6002d4cfa7e0 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerLoginEvent.java
+@@ -17,7 +17,7 @@ public class PlayerLoginEvent extends PlayerEvent {
+ private final InetAddress address;
+ private final String hostname;
+ private Result result = Result.ALLOWED;
+- private String message = "";
++ private net.kyori.adventure.text.Component message = net.kyori.adventure.text.Component.empty();
+ private final InetAddress realAddress; // Spigot
+
+ /**
+@@ -53,12 +53,52 @@ public class PlayerLoginEvent extends PlayerEvent {
+ * @param result The result status for this event
+ * @param message The message to be displayed if result denies login
+ * @param realAddress the actual, unspoofed connecting address
++ * @deprecated in favour of {@link #PlayerLoginEvent(Player, String, InetAddress, Result, net.kyori.adventure.text.Component, InetAddress)}
+ */
++ @Deprecated // Paper
+ public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final String message, @NotNull final InetAddress realAddress) { // Spigot
+ this(player, hostname, address, realAddress); // Spigot
+ this.result = result;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
++ }
++
++ // Paper start
++ /**
++ * This constructor pre-configures the event with a result and message
++ *
++ * @param player The {@link Player} for this event
++ * @param hostname The hostname that was used to connect to the server
++ * @param address The address the player used to connect, provided for
++ * timing issues
++ * @param result The result status for this event
++ * @param message The message to be displayed if result denies login
++ * @param realAddress the actual, unspoofed connecting address
++ */
++ public PlayerLoginEvent(@NotNull final Player player, @NotNull String hostname, @NotNull final InetAddress address, @NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message, @NotNull final InetAddress realAddress) { // Spigot
++ this(player, hostname, address, realAddress); // Spigot
++ this.result = result;
++ this.message = message;
++ }
++
++ /**
++ * Gets the current kick message that will be used if getResult() !=
++ * Result.ALLOWED
++ *
++ * @return Current kick message
++ */
++ public @NotNull net.kyori.adventure.text.Component kickMessage() {
++ return this.message;
++ }
++
++ /**
++ * Sets the kick message to display if getResult() != Result.ALLOWED
++ *
++ * @param message New kick message
++ */
++ public void kickMessage(@NotNull net.kyori.adventure.text.Component message) {
+ this.message = message;
+ }
++ // Paper end
+
+ // Spigot start
+ /**
+@@ -96,19 +136,23 @@ public class PlayerLoginEvent extends PlayerEvent {
+ * Result.ALLOWED
+ *
+ * @return Current kick message
++ * @deprecated in favour of {@link #kickMessage()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getKickMessage() {
+- return message;
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.message); // Paper
+ }
+
+ /**
+ * Sets the kick message to display if getResult() != Result.ALLOWED
+ *
+ * @param message New kick message
++ * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setKickMessage(@NotNull final String message) {
+- this.message = message;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
+ }
+
+ /**
+@@ -127,7 +171,7 @@ public class PlayerLoginEvent extends PlayerEvent {
+ */
+ public void allow() {
+ result = Result.ALLOWED;
+- message = "";
++ message = net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
+ /**
+@@ -135,8 +179,21 @@ public class PlayerLoginEvent extends PlayerEvent {
+ *
+ * @param result New result for disallowing the player
+ * @param message Kick message to display to the user
++ * @deprecated in favour of {@link #disallow(Result, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper start
+ public void disallow(@NotNull final Result result, @NotNull final String message) {
++ this.result = result;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message);
++ }
++ /**
++ * Disallows the player from logging in, with the given reason
++ *
++ * @param result New result for disallowing the player
++ * @param message Kick message to display to the user
++ */
++ public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
++ // Paper end
+ this.result = result;
+ this.message = message;
+ }
+diff --git a/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
+index fb066251f793ec3b41bfc075b9478901b15ee549..123979ed64939d615b061f91c19c630e1e1db8c7 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
+@@ -19,7 +19,7 @@ import org.jetbrains.annotations.NotNull;
+ public class PlayerPreLoginEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ private Result result;
+- private String message;
++ private net.kyori.adventure.text.Component message; // Paper
+ private final String name;
+ private final InetAddress ipAddress;
+ private final UUID uniqueId;
+@@ -31,7 +31,7 @@ public class PlayerPreLoginEvent extends Event {
+
+ public PlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId) {
+ this.result = Result.ALLOWED;
+- this.message = "";
++ this.message = net.kyori.adventure.text.Component.empty(); // Paper
+ this.name = name;
+ this.ipAddress = ipAddress;
+ this.uniqueId = uniqueId;
+@@ -56,6 +56,7 @@ public class PlayerPreLoginEvent extends Event {
+ this.result = result;
+ }
+
++ // Paper start
+ /**
+ * Gets the current kick message that will be used if getResult() !=
+ * Result.ALLOWED
+@@ -63,7 +64,7 @@ public class PlayerPreLoginEvent extends Event {
+ * @return Current kick message
+ */
+ @NotNull
+- public String getKickMessage() {
++ public net.kyori.adventure.text.Component kickMessage() {
+ return message;
+ }
+
+@@ -72,16 +73,51 @@ public class PlayerPreLoginEvent extends Event {
+ *
+ * @param message New kick message
+ */
+- public void setKickMessage(@NotNull final String message) {
++ public void kickMessage(@NotNull final net.kyori.adventure.text.Component message) {
+ this.message = message;
+ }
+
++ /**
++ * Disallows the player from logging in, with the given reason
++ *
++ * @param result New result for disallowing the player
++ * @param message Kick message to display to the user
++ */
++ public void disallow(@NotNull final Result result, @NotNull final net.kyori.adventure.text.Component message) {
++ this.result = result;
++ this.message = message;
++ }
++ // Paper end
++ /**
++ * Gets the current kick message that will be used if getResult() !=
++ * Result.ALLOWED
++ *
++ * @return Current kick message
++ * @deprecated in favour of {@link #kickMessage()}
++ */
++ @Deprecated // Paper
++ @NotNull
++ public String getKickMessage() {
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.message); // Paper
++ }
++
++ /**
++ * Sets the kick message to display if getResult() != Result.ALLOWED
++ *
++ * @param message New kick message
++ * @deprecated in favour of {@link #kickMessage(net.kyori.adventure.text.Component)}
++ */
++ @Deprecated // Paper
++ public void setKickMessage(@NotNull final String message) {
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
++ }
++
+ /**
+ * Allows the player to log in
+ */
+ public void allow() {
+ result = Result.ALLOWED;
+- message = "";
++ message = net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
+ /**
+@@ -89,10 +125,12 @@ public class PlayerPreLoginEvent extends Event {
+ *
+ * @param result New result for disallowing the player
+ * @param message Kick message to display to the user
++ * @deprecated in favour of {@link #disallow(org.bukkit.event.player.PlayerPreLoginEvent.Result, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void disallow(@NotNull final Result result, @NotNull final String message) {
+ this.result = result;
+- this.message = message;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
+index d70c25f404e994766a9ebce89a917c8d0719777c..849e8f10dd77e9fb46aab17752b8f1ff79e9d42e 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
+@@ -10,30 +10,59 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public class PlayerQuitEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+- private String quitMessage;
++ private net.kyori.adventure.text.Component quitMessage; // Paper
+
++ @Deprecated // Paper
+ public PlayerQuitEvent(@NotNull final Player who, @Nullable final String quitMessage) {
+ super(who);
++ this.quitMessage = quitMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(quitMessage) : null; // Paper
++ }
++ // Paper start
++ public PlayerQuitEvent(@NotNull final Player who, @Nullable final net.kyori.adventure.text.Component quitMessage) {
++ super(who);
++ this.quitMessage = quitMessage;
++ }
++
++ /**
++ * Gets the quit message to send to all online players
++ *
++ * @return string quit message
++ */
++ public @Nullable net.kyori.adventure.text.Component quitMessage() {
++ return quitMessage;
++ }
++
++ /**
++ * Sets the quit message to send to all online players
++ *
++ * @param quitMessage quit message
++ */
++ public void quitMessage(@Nullable net.kyori.adventure.text.Component quitMessage) {
+ this.quitMessage = quitMessage;
+ }
++ // Paper end
+
+ /**
+ * Gets the quit message to send to all online players
+ *
+ * @return string quit message
++ * @deprecated in favour of {@link #quitMessage()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public String getQuitMessage() {
+- return quitMessage;
++ return this.quitMessage == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.quitMessage); // Paper
+ }
+
+ /**
+ * Sets the quit message to send to all online players
+ *
+ * @param quitMessage quit message
++ * @deprecated in favour of {@link #quitMessage(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setQuitMessage(@Nullable String quitMessage) {
+- this.quitMessage = quitMessage;
++ this.quitMessage = quitMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(quitMessage) : null; // Paper
+ }
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java b/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
+index 03bfca9d368bbe4b7c1353d52c883e756bf69bda..4f8c85222c7bd33217c7db0ff5f47bf397f8f3e5 100644
+--- a/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
++++ b/src/main/java/org/bukkit/event/server/BroadcastMessageEvent.java
+@@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
+ public class BroadcastMessageEvent extends ServerEvent implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+- private String message;
++ private net.kyori.adventure.text.Component message; // Paper
+ private final Set<CommandSender> recipients;
+ private boolean cancelled = false;
+
+@@ -27,29 +27,66 @@ public class BroadcastMessageEvent extends ServerEvent implements Cancellable {
+ this(false, message, recipients);
+ }
+
++ @Deprecated // Paper
+ public BroadcastMessageEvent(boolean isAsync, @NotNull String message, @NotNull Set<CommandSender> recipients) {
++ // Paper start
++ super(isAsync);
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message);
++ this.recipients = recipients;
++ }
++
++ @Deprecated
++ public BroadcastMessageEvent(@NotNull net.kyori.adventure.text.Component message, @NotNull Set<CommandSender> recipients) {
++ this(false, message, recipients);
++ }
++
++ public BroadcastMessageEvent(boolean isAsync, @NotNull net.kyori.adventure.text.Component message, @NotNull Set<CommandSender> recipients) {
++ // Paper end
+ super(isAsync);
+ this.message = message;
+ this.recipients = recipients;
+ }
++ // Paper start
++ /**
++ * Get the broadcast message.
++ *
++ * @return Message to broadcast
++ */
++ public @NotNull net.kyori.adventure.text.Component message() {
++ return this.message;
++ }
++
++ /**
++ * Set the broadcast message.
++ *
++ * @param message New message to broadcast
++ */
++ public void message(@NotNull net.kyori.adventure.text.Component message) {
++ this.message = message;
++ }
++ // Paper end
+
+ /**
+ * Get the message to broadcast.
+ *
+ * @return Message to broadcast
++ * @deprecated in favour of {@link #message()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getMessage() {
+- return message;
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.message); // Paper
+ }
+
+ /**
+ * Set the message to broadcast.
+ *
+ * @param message New message to broadcast
++ * @deprecated in favour of {@link #message(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setMessage(@NotNull String message) {
+- this.message = message;
++ this.message = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message); // Paper
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
+index 7a2a58bac8e721c3f0c64f69f77be07a51f76d58..ede5a41bc071a9c9cea369b227b37a50222f295d 100644
+--- a/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
++++ b/src/main/java/org/bukkit/event/server/ServerListPingEvent.java
+@@ -17,15 +17,16 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
+ private static final int MAGIC_PLAYER_COUNT = Integer.MIN_VALUE;
+ private static final HandlerList handlers = new HandlerList();
+ private final InetAddress address;
+- private String motd;
++ private net.kyori.adventure.text.Component motd; // Paper
+ private final int numPlayers;
+ private int maxPlayers;
+
++ @Deprecated // Paper
+ public ServerListPingEvent(@NotNull final InetAddress address, @NotNull final String motd, final int numPlayers, final int maxPlayers) {
+ super(true);
+ Validate.isTrue(numPlayers >= 0, "Cannot have negative number of players online", numPlayers);
+ this.address = address;
+- this.motd = motd;
++ this.motd = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(motd); // Paper
+ this.numPlayers = numPlayers;
+ this.maxPlayers = maxPlayers;
+ }
+@@ -38,14 +39,58 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
+ * @param address the address of the pinger
+ * @param motd the message of the day
+ * @param maxPlayers the max number of players
++ * @deprecated in favour of {@link #ServerListPingEvent(java.net.InetAddress, net.kyori.adventure.text.Component, int)}
+ */
++ @Deprecated // Paper
+ protected ServerListPingEvent(@NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) {
++ super(true);
++ this.numPlayers = MAGIC_PLAYER_COUNT;
++ this.address = address;
++ this.motd = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(motd); // Paper
++ this.maxPlayers = maxPlayers;
++ }
++ // Paper start
++ public ServerListPingEvent(@NotNull final InetAddress address, @NotNull final net.kyori.adventure.text.Component motd, final int numPlayers, final int maxPlayers) {
++ super(true);
++ Validate.isTrue(numPlayers >= 0, "Cannot have negative number of players online", numPlayers);
++ this.address = address;
++ this.motd = motd;
++ this.numPlayers = numPlayers;
++ this.maxPlayers = maxPlayers;
++ }
++ /**
++ * This constructor is intended for implementations that provide the
++ * {@link #iterator()} method, thus provided the {@link #getNumPlayers()}
++ * count.
++ *
++ * @param address the address of the pinger
++ * @param motd the message of the day
++ * @param maxPlayers the max number of players
++ */
++ protected ServerListPingEvent(@NotNull final InetAddress address, @NotNull final net.kyori.adventure.text.Component motd, final int maxPlayers) {
+ super(true);
+ this.numPlayers = MAGIC_PLAYER_COUNT;
+ this.address = address;
+ this.motd = motd;
+ this.maxPlayers = maxPlayers;
+ }
++ /**
++ * Get the message of the day message.
++ *
++ * @return the message of the day
++ */
++ public @NotNull net.kyori.adventure.text.Component motd() {
++ return motd;
++ }
++ /**
++ * Change the message of the day message.
++ *
++ * @param motd the message of the day
++ */
++ public void motd(@NotNull net.kyori.adventure.text.Component motd) {
++ this.motd = motd;
++ }
++ // Paper end
+
+ /**
+ * Get the address the ping is coming from.
+@@ -61,19 +106,23 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
+ * Get the message of the day message.
+ *
+ * @return the message of the day
++ * @deprecated in favour of {@link #motd()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public String getMotd() {
+- return motd;
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.motd); // Paper
+ }
+
+ /**
+ * Change the message of the day message.
+ *
+ * @param motd the message of the day
++ * @deprecated in favour of {@link #motd(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setMotd(@NotNull String motd) {
+- this.motd = motd;
++ this.motd = org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(motd); // Paper
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/inventory/InventoryView.java b/src/main/java/org/bukkit/inventory/InventoryView.java
+index 14346d83bc99581b18e53d19af03708c0bf22cf7..664de64b020cf9090a2fbee0afe2bfaf150adc3c 100644
+--- a/src/main/java/org/bukkit/inventory/InventoryView.java
++++ b/src/main/java/org/bukkit/inventory/InventoryView.java
+@@ -446,11 +446,25 @@ public abstract class InventoryView {
+ return getPlayer().setWindowProperty(prop, value);
+ }
+
++ // Paper start
+ /**
+ * Get the title of this inventory window.
+ *
+ * @return The title.
+ */
+ @NotNull
++ public /*abstract*/ net.kyori.adventure.text.Component title() {
++ return org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(this.getTitle());
++ }
++ // Paper end
++
++ /**
++ * Get the title of this inventory window.
++ *
++ * @return The title.
++ * @deprecated in favour of {@link #title()}
++ */
++ @Deprecated // Paper
++ @NotNull
+ public abstract String getTitle();
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
+index 4ff149fd98895dd8ba45939a37c223b1f8d7281f..3dba4c361993e143e511b4f108ac0b444a84d964 100644
+--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
+@@ -141,4 +141,15 @@ public interface ItemFactory {
+ @Deprecated
+ @NotNull
+ Material updateMaterial(@NotNull final ItemMeta meta, @NotNull final Material material) throws IllegalArgumentException;
++
++ // Paper start
++ /**
++ * Creates a hover event for the given item.
++ *
++ * @param item The item
++ * @return A hover event
++ */
++ @NotNull
++ net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull ItemStack item, final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op);
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index f70a6a22b85ff0da76e67e9b223ad4e0b020b5c4..4b20b557eaa958cf1ad1baf8d6cc17f38b180ff1 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
+ * use this class to encapsulate Materials for which {@link Material#isItem()}
+ * returns false.</b>
+ */
+-public class ItemStack implements Cloneable, ConfigurationSerializable {
++public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowItem> { // Paper
+ private Material type = Material.AIR;
+ private int amount = 0;
+ private MaterialData data = null;
+@@ -595,4 +595,12 @@ public class ItemStack implements Cloneable, ConfigurationSerializable {
+
+ return true;
+ }
++
++ // Paper start
++ @NotNull
++ @Override
++ public net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op) {
++ return org.bukkit.Bukkit.getServer().getItemFactory().asHoverEvent(this, op);
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/meta/BookMeta.java b/src/main/java/org/bukkit/inventory/meta/BookMeta.java
+index 94852d50e88d0594b84b581cd627174043629995..0cfd4b02cc2095da56b4dc8d4ea4e9b4a95f513c 100644
+--- a/src/main/java/org/bukkit/inventory/meta/BookMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/BookMeta.java
+@@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
+ * Represents a book ({@link Material#WRITABLE_BOOK} or {@link
+ * Material#WRITTEN_BOOK}) that can have a title, an author, and pages.
+ */
+-public interface BookMeta extends ItemMeta {
++public interface BookMeta extends ItemMeta, net.kyori.adventure.inventory.Book { // Paper
+
+ /**
+ * Represents the generation (or level of copying) of a written book
+@@ -119,6 +119,111 @@ public interface BookMeta extends ItemMeta {
+ */
+ boolean hasPages();
+
++ // Paper start
++ /**
++ * Gets the title of the book.
++ * <p>
++ * Plugins should check that hasTitle() returns true before calling this
++ * method.
++ *
++ * @return the title of the book
++ */
++ @Nullable
++ @Override
++ net.kyori.adventure.text.Component title();
++
++ /**
++ * Sets the title of the book.
++ * <p>
++ * Limited to 32 characters. Removes title when given null.
++ *
++ * @param title the title to set
++ * @return true if the title was successfully set
++ */
++ @NotNull
++ @Override
++ BookMeta title(@Nullable net.kyori.adventure.text.Component title);
++
++ /**
++ * Gets the author of the book.
++ * <p>
++ * Plugins should check that hasAuthor() returns true before calling this
++ * method.
++ *
++ * @return the author of the book
++ */
++ @Nullable
++ @Override
++ net.kyori.adventure.text.Component author();
++
++ /**
++ * Sets the author of the book. Removes author when given null.
++ *
++ * @param author the author to set
++ */
++ @NotNull
++ @Override
++ BookMeta author(@Nullable net.kyori.adventure.text.Component author);
++ /**
++ * Gets the specified page in the book. The page must exist.
++ * <p>
++ * Pages are 1-indexed.
++ *
++ * @param page the page number to get, in range [1, getPageCount()]
++ * @return the page from the book
++ */
++ @NotNull net.kyori.adventure.text.Component page(int page);
++
++ /**
++ * Sets the specified page in the book. Pages of the book must be
++ * contiguous.
++ * <p>
++ * The data can be up to 256 characters in length, additional characters
++ * are truncated.
++ * <p>
++ * Pages are 1-indexed.
++ *
++ * @param page the page number to set, in range [1, getPageCount()]
++ * @param data the data to set for that page
++ */
++ void page(int page, @NotNull net.kyori.adventure.text.Component data);
++
++ /**
++ * Adds new pages to the end of the book. Up to a maximum of 50 pages with
++ * 256 characters per page.
++ *
++ * @param pages A list of strings, each being a page
++ */
++ void addPages(@NotNull net.kyori.adventure.text.Component... pages);
++
++ interface BookMetaBuilder extends Builder {
++
++ @NotNull
++ @Override
++ BookMetaBuilder title(@Nullable net.kyori.adventure.text.Component title);
++
++ @NotNull
++ @Override
++ BookMetaBuilder author(@Nullable net.kyori.adventure.text.Component author);
++
++ @NotNull
++ @Override
++ BookMetaBuilder addPage(@NotNull net.kyori.adventure.text.Component page);
++
++ @NotNull
++ @Override
++ BookMetaBuilder pages(@NotNull net.kyori.adventure.text.Component... pages);
++
++ @NotNull
++ @Override
++ BookMetaBuilder pages(@NotNull java.util.Collection<net.kyori.adventure.text.Component> pages);
++
++ @NotNull
++ @Override
++ BookMeta build();
++ }
++ // Paper end
++
+ /**
+ * Gets the specified page in the book. The given page must exist.
+ * <p>
+@@ -126,8 +231,10 @@ public interface BookMeta extends ItemMeta {
+ *
+ * @param page the page number to get, in range [1, getPageCount()]
+ * @return the page from the book
++ * @deprecated in favour of {@link #page(int)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getPage(int page);
+
+ /**
+@@ -141,15 +248,19 @@ public interface BookMeta extends ItemMeta {
+ *
+ * @param page the page number to set, in range [1, getPageCount()]
+ * @param data the data to set for that page
++ * @deprecated in favour of {@link #page(int, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setPage(int page, @NotNull String data);
+
+ /**
+ * Gets all the pages in the book.
+ *
+ * @return list of all the pages in the book
++ * @deprecated in favour of {@link #pages()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ List<String> getPages();
+
+ /**
+@@ -157,7 +268,9 @@ public interface BookMeta extends ItemMeta {
+ * pages. Maximum 100 pages with 256 characters per page.
+ *
+ * @param pages A list of pages to set the book to use
++ * @deprecated in favour of {@link #pages(List)}
+ */
++ @Deprecated // Paper
+ void setPages(@NotNull List<String> pages);
+
+ /**
+@@ -165,7 +278,9 @@ public interface BookMeta extends ItemMeta {
+ * pages. Maximum 50 pages with 256 characters per page.
+ *
+ * @param pages A list of strings, each being a page
++ * @deprecated in favour of {@link #pages(net.kyori.adventure.text.Component...)}
+ */
++ @Deprecated // Paper
+ void setPages(@NotNull String... pages);
+
+ /**
+@@ -173,7 +288,9 @@ public interface BookMeta extends ItemMeta {
+ * 256 characters per page.
+ *
+ * @param pages A list of strings, each being a page
++ * @deprecated in favour of {@link #addPages(net.kyori.adventure.text.Component...)}
+ */
++ @Deprecated // Paper
+ void addPage(@NotNull String... pages);
+
+ /**
+@@ -195,8 +312,10 @@ public interface BookMeta extends ItemMeta {
+ *
+ * @param page the page number to get
+ * @return the page from the book
++ * @deprecated in favour of {@link #page(int)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public BaseComponent[] getPage(int page) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -210,7 +329,9 @@ public interface BookMeta extends ItemMeta {
+ *
+ * @param page the page number to set
+ * @param data the data to set for that page
++ * @deprecated in favour of {@link #page(int, net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setPage(int page, @Nullable BaseComponent... data) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -219,8 +340,10 @@ public interface BookMeta extends ItemMeta {
+ * Gets all the pages in the book.
+ *
+ * @return list of all the pages in the book
++ * @deprecated in favour of {@link #pages()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ public List<BaseComponent[]> getPages() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -230,7 +353,9 @@ public interface BookMeta extends ItemMeta {
+ * pages. Maximum 50 pages with 256 characters per page.
+ *
+ * @param pages A list of pages to set the book to use
++ * @deprecated in favour of {@link #pages(java.util.List)}
+ */
++ @Deprecated // Paper
+ public void setPages(@NotNull List<BaseComponent[]> pages) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -240,7 +365,9 @@ public interface BookMeta extends ItemMeta {
+ * pages. Maximum 50 pages with 256 characters per page.
+ *
+ * @param pages A list of component arrays, each being a page
++ * @deprecated in favour of {@link #pages(net.kyori.adventure.text.Component...)}
+ */
++ @Deprecated // Paper
+ public void setPages(@NotNull BaseComponent[]... pages) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+@@ -250,7 +377,9 @@ public interface BookMeta extends ItemMeta {
+ * with 256 characters per page.
+ *
+ * @param pages A list of component arrays, each being a page
++ * @deprecated in favour of {@link #addPages(net.kyori.adventure.text.Component...)}
+ */
++ @Deprecated // Paper
+ public void addPage(@NotNull BaseComponent[]... pages) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+index f2e9f2753ec92aa4a3e3f06ca6053bd70b9091d7..1c362636c56db0e6c118171ba367c43c4f7cff33 100644
+--- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+@@ -31,6 +31,24 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ */
+ boolean hasDisplayName();
+
++ // Paper start
++ /**
++ * Gets the display name.
++ *
++ * <p>Plugins should check that {@link #hasDisplayName()} returns <code>true</code> before calling this method.</p>
++ *
++ * @return the display name
++ */
++ @Nullable net.kyori.adventure.text.Component displayName();
++
++ /**
++ * Sets the display name.
++ *
++ * @param displayName the display name to set
++ */
++ void displayName(final @Nullable net.kyori.adventure.text.Component displayName);
++ // Paper end
++
+ /**
+ * Gets the display name that is set.
+ * <p>
+@@ -38,7 +56,9 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ * before calling this method.
+ *
+ * @return the display name that is set
++ * @deprecated in favour of {@link #displayName()}
+ */
++ @Deprecated // Paper
+ @NotNull
+ String getDisplayName();
+
+@@ -46,7 +66,9 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ * Sets the display name.
+ *
+ * @param name the name to set
++ * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setDisplayName(@Nullable String name);
+
+ /**
+@@ -81,6 +103,24 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ */
+ boolean hasLore();
+
++ // Paper start
++ /**
++ * Gets the lore.
++ *
++ * <p>Plugins should check that {@link #hasLore()} returns <code>true</code> before calling this method.</p>
++ *
++ * @return the display name
++ */
++ @Nullable List<net.kyori.adventure.text.Component> lore();
++
++ /**
++ * Sets the lore.
++ *
++ * @param lore the lore to set
++ */
++ void lore(final @Nullable List<net.kyori.adventure.text.Component> lore);
++ // Paper end
++
+ /**
+ * Gets the lore that is set.
+ * <p>
+@@ -88,7 +128,9 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ * calling this method.
+ *
+ * @return a list of lore that is set
++ * @deprecated in favour of {@link #lore()}
+ */
++ @Deprecated // Paper
+ @Nullable
+ List<String> getLore();
+
+@@ -97,7 +139,9 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ * Removes lore when given null.
+ *
+ * @param lore the lore that will be set
++ * @deprecated in favour of {@link #lore(List)}
+ */
++ @Deprecated // Paper
+ void setLore(@Nullable List<String> lore);
+
+ /**
+diff --git a/src/main/java/org/bukkit/map/MapCursor.java b/src/main/java/org/bukkit/map/MapCursor.java
+index 83354b2a38b6261b172b91c1008dcf3313cc4a8f..ed0bc2024a0bb85837e25f75ae89d1fe257b2e60 100644
+--- a/src/main/java/org/bukkit/map/MapCursor.java
++++ b/src/main/java/org/bukkit/map/MapCursor.java
+@@ -10,7 +10,7 @@ public final class MapCursor {
+ private byte x, y;
+ private byte direction, type;
+ private boolean visible;
+- private String caption;
++ private net.kyori.adventure.text.Component caption; // Paper
+
+ /**
+ * Initialize the map cursor.
+@@ -24,7 +24,7 @@ public final class MapCursor {
+ */
+ @Deprecated
+ public MapCursor(byte x, byte y, byte direction, byte type, boolean visible) {
+- this(x, y, direction, type, visible, null);
++ this(x, y, direction, type, visible, (String) null); // Paper
+ }
+
+ /**
+@@ -37,7 +37,7 @@ public final class MapCursor {
+ * @param visible Whether the cursor is visible by default.
+ */
+ public MapCursor(byte x, byte y, byte direction, @NotNull Type type, boolean visible) {
+- this(x, y, direction, type, visible, null);
++ this(x, y, direction, type, visible, (String) null); // Paper
+ }
+
+ /**
+@@ -49,7 +49,7 @@ public final class MapCursor {
+ * @param type The type (color/style) of the map cursor.
+ * @param visible Whether the cursor is visible by default.
+ * @param caption cursor caption
+- * @deprecated Magic value
++ * @deprecated Magic value. Use {@link #MapCursor(byte, byte, byte, byte, boolean, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated
+ public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable String caption) {
+@@ -58,8 +58,42 @@ public final class MapCursor {
+ setDirection(direction);
+ setRawType(type);
+ this.visible = visible;
+- this.caption = caption;
++ this.caption = caption == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(caption); // Paper
+ }
++ // Paper start
++ /**
++ * Initialize the map cursor.
++ *
++ * @param x The x coordinate, from -128 to 127.
++ * @param y The y coordinate, from -128 to 127.
++ * @param direction The facing of the cursor, from 0 to 15.
++ * @param type The type (color/style) of the map cursor.
++ * @param visible Whether the cursor is visible by default.
++ * @param caption cursor caption
++ * @deprecated Magic value
++ */
++ @Deprecated
++ public MapCursor(byte x, byte y, byte direction, byte type, boolean visible, @Nullable net.kyori.adventure.text.Component caption) {
++ this.x = x; this.y = y; this.visible = visible; this.caption = caption;
++ setDirection(direction);
++ setRawType(type);
++ }
++ /**
++ * Initialize the map cursor.
++ *
++ * @param x The x coordinate, from -128 to 127.
++ * @param y The y coordinate, from -128 to 127.
++ * @param direction The facing of the cursor, from 0 to 15.
++ * @param type The type (color/style) of the map cursor.
++ * @param visible Whether the cursor is visible by default.
++ * @param caption cursor caption
++ */
++ public MapCursor(byte x, byte y, byte direction, @NotNull Type type, boolean visible, @Nullable net.kyori.adventure.text.Component caption) {
++ this.x = x; this.y = y; this.visible = visible; this.caption = caption;
++ setDirection(direction);
++ setType(type);
++ }
++ // Paper end
+
+ /**
+ * Initialize the map cursor.
+@@ -77,7 +111,7 @@ public final class MapCursor {
+ setDirection(direction);
+ setType(type);
+ this.visible = visible;
+- this.caption = caption;
++ this.caption = caption == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(caption); // Paper
+ }
+
+ /**
+@@ -200,23 +234,45 @@ public final class MapCursor {
+ this.visible = visible;
+ }
+
++ // Paper start
++ /**
++ * Gets the caption on this cursor.
++ *
++ * @return caption
++ */
++ public @Nullable net.kyori.adventure.text.Component caption() {
++ return this.caption;
++ }
++ /**
++ * Sets the caption on this cursor.
++ *
++ * @param caption new caption
++ */
++ public void caption(@Nullable net.kyori.adventure.text.Component caption) {
++ this.caption = caption;
++ }
++ // Paper end
+ /**
+ * Gets the caption on this cursor.
+ *
+ * @return caption
++ * @deprecated in favour of {@link #caption()}
+ */
+ @Nullable
++ @Deprecated // Paper
+ public String getCaption() {
+- return caption;
++ return this.caption == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.caption); // Paper
+ }
+
+ /**
+ * Sets the caption on this cursor.
+ *
+ * @param caption new caption
++ * @deprecated in favour of {@link #caption(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ public void setCaption(@Nullable String caption) {
+- this.caption = caption;
++ this.caption = caption == null ? null : org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(caption); // Paper
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/map/MapCursorCollection.java b/src/main/java/org/bukkit/map/MapCursorCollection.java
+index 4dba721aefe4fc6699b3b4bfa7ecb0b19c2a2a1a..01dec2c877df58c9dc22445e8b1f9ce2e53066da 100644
+--- a/src/main/java/org/bukkit/map/MapCursorCollection.java
++++ b/src/main/java/org/bukkit/map/MapCursorCollection.java
+@@ -117,4 +117,22 @@ public final class MapCursorCollection {
+ public MapCursor addCursor(int x, int y, byte direction, byte type, boolean visible, @Nullable String caption) {
+ return addCursor(new MapCursor((byte) x, (byte) y, direction, type, visible, caption));
+ }
++ // Paper start
++ /**
++ * Add a cursor to the collection.
++ *
++ * @param x The x coordinate, from -128 to 127.
++ * @param y The y coordinate, from -128 to 127.
++ * @param direction The facing of the cursor, from 0 to 15.
++ * @param type The type (color/style) of the map cursor.
++ * @param visible Whether the cursor is visible.
++ * @param caption banner caption
++ * @return The newly added MapCursor.
++ * @deprecated Magic value
++ */
++ @Deprecated
++ public @NotNull MapCursor addCursor(int x, int y, byte direction, byte type, boolean visible, @Nullable net.kyori.adventure.text.Component caption) {
++ return addCursor(new MapCursor((byte) x, (byte) y, direction, type, visible, caption));
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/scoreboard/Objective.java b/src/main/java/org/bukkit/scoreboard/Objective.java
+index f5cbf6df32ef169cf0f2266f7c6e9c4f771ccb7d..58bddb11fd534e7c33a4ffd7b72b055ba92c767a 100644
+--- a/src/main/java/org/bukkit/scoreboard/Objective.java
++++ b/src/main/java/org/bukkit/scoreboard/Objective.java
+@@ -19,14 +19,35 @@ public interface Objective {
+ */
+ @NotNull
+ String getName() throws IllegalStateException;
++ // Paper start
++ /**
++ * Gets the name displayed to players for this objective
++ *
++ * @return this objective's display name
++ * @throws IllegalStateException if this objective has been unregistered
++ */
++ @NotNull net.kyori.adventure.text.Component displayName() throws IllegalStateException;
++ /**
++ * Sets the name displayed to players for this objective.
++ *
++ * @param displayName Display name to set
++ * @throws IllegalStateException if this objective has been unregistered
++ * @throws IllegalArgumentException if displayName is null
++ * @throws IllegalArgumentException if displayName is longer than 128
++ * characters.
++ */
++ void displayName(@Nullable net.kyori.adventure.text.Component displayName) throws IllegalStateException, IllegalArgumentException;
++ // Paper end
+
+ /**
+ * Gets the name displayed to players for this objective
+ *
+ * @return this objective's display name
+ * @throws IllegalStateException if this objective has been unregistered
++ * @deprecated in favour of {@link #displayName()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getDisplayName() throws IllegalStateException;
+
+ /**
+@@ -37,7 +58,9 @@ public interface Objective {
+ * @throws IllegalArgumentException if displayName is null
+ * @throws IllegalArgumentException if displayName is longer than 128
+ * characters.
++ * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setDisplayName(@NotNull String displayName) throws IllegalStateException, IllegalArgumentException;
+
+ /**
+diff --git a/src/main/java/org/bukkit/scoreboard/Scoreboard.java b/src/main/java/org/bukkit/scoreboard/Scoreboard.java
+index 4bfaaea78c9b6aa5d392629aa943d26dbe6a7d4a..f09ff32cc3ffc16af379a378b1948991435393e8 100644
+--- a/src/main/java/org/bukkit/scoreboard/Scoreboard.java
++++ b/src/main/java/org/bukkit/scoreboard/Scoreboard.java
+@@ -27,6 +27,48 @@ public interface Scoreboard {
+ @Deprecated
+ @NotNull
+ Objective registerNewObjective(@NotNull String name, @NotNull String criteria) throws IllegalArgumentException;
++ // Paper start
++ /**
++ * Registers an Objective on this Scoreboard
++ *
++ * @param name Name of the Objective
++ * @param criteria Criteria for the Objective
++ * @param displayName Name displayed to players for the Objective.
++ * @return The registered Objective
++ * @throws IllegalArgumentException if name is null
++ * @throws IllegalArgumentException if name is longer than 16
++ * characters.
++ * @throws IllegalArgumentException if criteria is null
++ * @throws IllegalArgumentException if displayName is null
++ * @throws IllegalArgumentException if displayName is longer than 128
++ * characters.
++ * @throws IllegalArgumentException if an objective by that name already
++ * exists
++ */
++ @NotNull
++ Objective registerNewObjective(@NotNull String name, @NotNull String criteria, @Nullable net.kyori.adventure.text.Component displayName) throws IllegalArgumentException;
++ /**
++ * Registers an Objective on this Scoreboard
++ *
++ * @param name Name of the Objective
++ * @param criteria Criteria for the Objective
++ * @param displayName Name displayed to players for the Objective.
++ * @param renderType Manner of rendering the Objective
++ * @return The registered Objective
++ * @throws IllegalArgumentException if name is null
++ * @throws IllegalArgumentException if name is longer than 16
++ * characters.
++ * @throws IllegalArgumentException if criteria is null
++ * @throws IllegalArgumentException if displayName is null
++ * @throws IllegalArgumentException if displayName is longer than 128
++ * characters.
++ * @throws IllegalArgumentException if renderType is null
++ * @throws IllegalArgumentException if an objective by that name already
++ * exists
++ */
++ @NotNull
++ Objective registerNewObjective(@NotNull String name, @NotNull String criteria, @Nullable net.kyori.adventure.text.Component displayName, @NotNull RenderType renderType) throws IllegalArgumentException;
++ // Paper end
+
+ /**
+ * Registers an Objective on this Scoreboard
+@@ -44,8 +86,10 @@ public interface Scoreboard {
+ * characters.
+ * @throws IllegalArgumentException if an objective by that name already
+ * exists
++ * @deprecated in favour of {@link #registerNewObjective(String, String, net.kyori.adventure.text.Component)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ Objective registerNewObjective(@NotNull String name, @NotNull String criteria, @NotNull String displayName) throws IllegalArgumentException;
+
+ /**
+@@ -66,8 +110,10 @@ public interface Scoreboard {
+ * @throws IllegalArgumentException if renderType is null
+ * @throws IllegalArgumentException if an objective by that name already
+ * exists
++ * @deprecated in favour of {@link #registerNewObjective(String, String, net.kyori.adventure.text.Component, RenderType)}
+ */
+ @NotNull
++ @Deprecated // Paper
+ Objective registerNewObjective(@NotNull String name, @NotNull String criteria, @NotNull String displayName, @NotNull RenderType renderType) throws IllegalArgumentException;
+
+ /**
+diff --git a/src/main/java/org/bukkit/scoreboard/Team.java b/src/main/java/org/bukkit/scoreboard/Team.java
+index da01d2926cc8a2485a3349ac1ebb32cad20e287c..f0af10a5b9ad048be197ed5ec6c8ed2672eb3dd5 100644
+--- a/src/main/java/org/bukkit/scoreboard/Team.java
++++ b/src/main/java/org/bukkit/scoreboard/Team.java
+@@ -22,14 +22,95 @@ public interface Team {
+ */
+ @NotNull
+ String getName() throws IllegalStateException;
++ // Paper start
++ /**
++ * Gets the name displayed to entries for this team
++ *
++ * @return Team display name
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ @NotNull net.kyori.adventure.text.Component displayName() throws IllegalStateException;
++
++ /**
++ * Sets the name displayed to entries for this team
++ *
++ * @param displayName New display name
++ * @throws IllegalArgumentException if displayName is longer than 128
++ * characters.
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ void displayName(@Nullable net.kyori.adventure.text.Component displayName) throws IllegalStateException, IllegalArgumentException;
++
++ /**
++ * Gets the prefix prepended to the display of entries on this team.
++ *
++ * @return Team prefix
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ @NotNull net.kyori.adventure.text.Component prefix() throws IllegalStateException;
++
++ /**
++ * Sets the prefix prepended to the display of entries on this team.
++ *
++ * @param prefix New prefix
++ * @throws IllegalArgumentException if prefix is null
++ * @throws IllegalArgumentException if prefix is longer than 64
++ * characters
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ void prefix(@Nullable net.kyori.adventure.text.Component prefix) throws IllegalStateException, IllegalArgumentException;
++
++ /**
++ * Gets the suffix appended to the display of entries on this team.
++ *
++ * @return the team's current suffix
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ @NotNull net.kyori.adventure.text.Component suffix() throws IllegalStateException;
++
++ /**
++ * Sets the suffix appended to the display of entries on this team.
++ *
++ * @param suffix the new suffix for this team.
++ * @throws IllegalArgumentException if suffix is null
++ * @throws IllegalArgumentException if suffix is longer than 64
++ * characters
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ void suffix(@Nullable net.kyori.adventure.text.Component suffix) throws IllegalStateException, IllegalArgumentException;
++
++ /**
++ * Gets the color of the team.
++ * <br>
++ * This only sets the team outline, other occurrences of colors such as in
++ * names are handled by prefixes / suffixes.
++ *
++ * @return team color, defaults to {@link ChatColor#RESET}
++ * @throws IllegalStateException if this team has been unregistered
++ */
++ @NotNull net.kyori.adventure.text.format.TextColor color() throws IllegalStateException;
++
++ /**
++ * Sets the color of the team.
++ * <br>
++ * This only sets the team outline, other occurrences of colors such as in
++ * names are handled by prefixes / suffixes.
++ *
++ * @param color new color, must be non-null. Use {@link ChatColor#RESET} for
++ * no color
++ */
++ void color(@Nullable net.kyori.adventure.text.format.NamedTextColor color);
++ // Paper end
+
+ /**
+ * Gets the name displayed to entries for this team
+ *
+ * @return Team display name
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #displayName()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getDisplayName() throws IllegalStateException;
+
+ /**
+@@ -39,7 +120,9 @@ public interface Team {
+ * @throws IllegalArgumentException if displayName is longer than 128
+ * characters.
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #displayName(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setDisplayName(@NotNull String displayName) throws IllegalStateException, IllegalArgumentException;
+
+ /**
+@@ -47,8 +130,10 @@ public interface Team {
+ *
+ * @return Team prefix
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #prefix()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getPrefix() throws IllegalStateException;
+
+ /**
+@@ -59,7 +144,9 @@ public interface Team {
+ * @throws IllegalArgumentException if prefix is longer than 64
+ * characters
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #prefix(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setPrefix(@NotNull String prefix) throws IllegalStateException, IllegalArgumentException;
+
+ /**
+@@ -67,8 +154,10 @@ public interface Team {
+ *
+ * @return the team's current suffix
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #suffix()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ String getSuffix() throws IllegalStateException;
+
+ /**
+@@ -79,7 +168,9 @@ public interface Team {
+ * @throws IllegalArgumentException if suffix is longer than 64
+ * characters
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #suffix(net.kyori.adventure.text.Component)}
+ */
++ @Deprecated // Paper
+ void setSuffix(@NotNull String suffix) throws IllegalStateException, IllegalArgumentException;
+
+ /**
+@@ -90,8 +181,10 @@ public interface Team {
+ *
+ * @return team color, defaults to {@link ChatColor#RESET}
+ * @throws IllegalStateException if this team has been unregistered
++ * @deprecated in favour of {@link #color()}
+ */
+ @NotNull
++ @Deprecated // Paper
+ ChatColor getColor() throws IllegalStateException;
+
+ /**
+@@ -102,7 +195,9 @@ public interface Team {
+ *
+ * @param color new color, must be non-null. Use {@link ChatColor#RESET} for
+ * no color
++ * @deprecated in favour of {@link #color(net.kyori.adventure.text.format.NamedTextColor)}
+ */
++ @Deprecated // Paper
+ void setColor(@NotNull ChatColor color);
+
+ /**
+diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
+index 0c7377247ad9251c9e498039511e7220370aba2d..c62919f18f318fec15a6c364d8b6d562c2b04762 100644
+--- a/src/test/java/org/bukkit/AnnotationTest.java
++++ b/src/test/java/org/bukkit/AnnotationTest.java
+@@ -26,6 +26,12 @@ import org.objectweb.asm.tree.ParameterNode;
+ public class AnnotationTest {
+
+ private static final String[] ACCEPTED_ANNOTATIONS = {
++ // Paper start
++ "Lorg/checkerframework/checker/nullness/qual/Nullable;",
++ "Lorg/checkerframework/checker/nullness/qual/NonNull;",
++ "Lorg/checkerframework/checker/nullness/qual/PolyNull;",
++ "Lorg/checkerframework/checker/nullness/qual/MonotonicNonNull;",
++ // Paper end
+ "Lorg/jetbrains/annotations/Nullable;",
+ "Lorg/jetbrains/annotations/NotNull;",
+ "Lorg/jetbrains/annotations/Contract;",
+@@ -66,7 +72,7 @@ public class AnnotationTest {
+ continue;
+ }
+
+- if (mustBeAnnotated(Type.getReturnType(method.desc)) && !isWellAnnotated(method.invisibleAnnotations)) {
++ if (mustBeAnnotated(Type.getReturnType(method.desc)) && !isWellAnnotated(method.invisibleAnnotations) && !isWellAnnotated(method.visibleTypeAnnotations)) { // Paper - also check visible type annotations
+ warn(errors, clazz, method, "return value");
+ }
+
+@@ -174,7 +180,7 @@ public class AnnotationTest {
+ return true;
+ }
+
+- private static boolean isWellAnnotated(@Nullable List<AnnotationNode> annotations) {
++ private static boolean isWellAnnotated(@Nullable List<? extends AnnotationNode> annotations) { // Paper - allow children of AnnotationNode
+ if (annotations == null) {
+ return false;
+ }
diff --git a/Spigot-API-Patches-Unmapped/0006-Player-affects-spawning-API.patch b/Spigot-API-Patches-Unmapped/0006-Player-affects-spawning-API.patch
new file mode 100644
index 0000000000..55a407450e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0006-Player-affects-spawning-API.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jedediah Smith <[email protected]>
+Date: Mon, 29 Feb 2016 17:22:34 -0600
+Subject: [PATCH] Player affects spawning API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index c5e7e037493ffe1f1764c3ad5d3e12bfac498cb2..e3a23cef93df2bbb2f27f87150bc1dbf7fd051b5 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1365,6 +1365,22 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ @Deprecated // Paper
+ public String getLocale();
+
++ // Paper start
++ /**
++ * Get whether the player can affect mob spawning
++ *
++ * @return if the player can affect mob spawning
++ */
++ public boolean getAffectsSpawning();
++
++ /**
++ * Set whether the player can affect mob spawning
++ *
++ * @param affects Whether the player can affect mob spawning
++ */
++ public void setAffectsSpawning(boolean affects);
++ // Paper end
++
+ /**
+ * Update the list of commands sent to the client.
+ * <br>
diff --git a/Spigot-API-Patches-Unmapped/0007-Add-getTPS-method.patch b/Spigot-API-Patches-Unmapped/0007-Add-getTPS-method.patch
new file mode 100644
index 0000000000..b4bcd8fbba
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0007-Add-getTPS-method.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 29 Feb 2016 17:24:57 -0600
+Subject: [PATCH] Add getTPS method
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 426b1e83226e674ee4bf3ec05ddcd3ac4376b06d..cd1b4be422a3a4290579d5daed9466084f18ed60 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1531,6 +1531,17 @@ public final class Bukkit {
+ return server.getEntity(uuid);
+ }
+
++ // Paper start
++ /**
++ * Gets the current server TPS
++ * @return current server TPS (1m, 5m, 15m in Paper-Server)
++ */
++ @NotNull
++ public static double[] getTPS() {
++ return server.getTPS();
++ }
++ // Paper end
++
+ /**
+ * Get the advancement specified by this key.
+ *
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index d9515a79dc7ed60c66960cd6c6bb4c108f206f3c..9ec374570e135a4ba14db7efdbb5c1bd213226eb 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1290,6 +1290,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @Nullable
+ Entity getEntity(@NotNull UUID uuid);
+
++ // Paper start
++ /**
++ * Gets the current server TPS
++ *
++ * @return current server TPS (1m, 5m, 15m in Paper-Server)
++ */
++ @NotNull
++ public double[] getTPS();
++ // Paper end
++
+ /**
+ * Get the advancement specified by this key.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0008-Entity-Origin-API.patch b/Spigot-API-Patches-Unmapped/0008-Entity-Origin-API.patch
new file mode 100644
index 0000000000..b26d5933f8
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0008-Entity-Origin-API.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Byteflux <[email protected]>
+Date: Mon, 29 Feb 2016 17:50:31 -0600
+Subject: [PATCH] Entity Origin API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 41a1bc45cc5eb7f19374115ade7f5328c7fc1dae..e9d0d507b47b0347b975b1a83f5ae70dca5587b8 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -609,5 +609,15 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
+ return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.customName())));
+ }
++
++ /**
++ * Gets the location where this entity originates from.
++ * <p>
++ * This value can be null if the entity hasn't yet been added to the world.
++ *
++ * @return Location where entity originates or null if not yet added
++ */
++ @Nullable
++ Location getOrigin();
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/entity/FallingBlock.java b/src/main/java/org/bukkit/entity/FallingBlock.java
+index 64f9d3fd870d65afd2ee9a85625b149163eee144..14cb0d770561151570ab4399ca5facff43076819 100644
+--- a/src/main/java/org/bukkit/entity/FallingBlock.java
++++ b/src/main/java/org/bukkit/entity/FallingBlock.java
+@@ -54,4 +54,15 @@ public interface FallingBlock extends Entity {
+ * @param hurtEntities whether entities will be damaged by this block.
+ */
+ void setHurtEntities(boolean hurtEntities);
++
++ /**
++ * Gets the source block location of the FallingBlock
++ *
++ * @return the source block location the FallingBlock was spawned from
++ * @deprecated replaced by {@link Entity#getOrigin()}
++ */
++ @Deprecated
++ default org.bukkit.Location getSourceLoc() {
++ return this.getOrigin();
++ }
+ }
+diff --git a/src/main/java/org/bukkit/entity/TNTPrimed.java b/src/main/java/org/bukkit/entity/TNTPrimed.java
+index a23cfdf66877f0a61eae700de084c76e6ee7b431..0813bd913c8fdb2001963ce3e82c07c2af105418 100644
+--- a/src/main/java/org/bukkit/entity/TNTPrimed.java
++++ b/src/main/java/org/bukkit/entity/TNTPrimed.java
+@@ -53,4 +53,15 @@ public interface TNTPrimed extends Explosive {
+ * @param source the source of this primed TNT
+ */
+ public void setSource(@Nullable Entity source);
++
++ /**
++ * Gets the source block location of the TNTPrimed
++ *
++ * @return the source block location the TNTPrimed was spawned from
++ * @deprecated replaced by {@link Entity#getOrigin()}
++ */
++ @Deprecated
++ default org.bukkit.Location getSourceLoc() {
++ return this.getOrigin();
++ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0009-Version-Command-2.0.patch b/Spigot-API-Patches-Unmapped/0009-Version-Command-2.0.patch
new file mode 100644
index 0000000000..c7cc974e1a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0009-Version-Command-2.0.patch
@@ -0,0 +1,185 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Mon, 27 May 2019 01:10:06 -0500
+Subject: [PATCH] Version Command 2.0
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a736d7bcdc5861a01b66ba36158db1c716339346
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
+@@ -0,0 +1,45 @@
++package com.destroystokyo.paper.util;
++
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.format.NamedTextColor;
++import org.bukkit.Bukkit;
++import org.jetbrains.annotations.NotNull;
++
++public interface VersionFetcher {
++ /**
++ * Amount of time to cache results for in milliseconds
++ * <p>
++ * Negative values will never cache.
++ *
++ * @return cache time
++ */
++ long getCacheTime();
++
++ /**
++ * Gets the version message to cache and show to command senders.
++ *
++ * <p>NOTE: This is run in a new thread separate from that of the command processing thread</p>
++ *
++ * @param serverVersion the current version of the server (will match {@link Bukkit#getVersion()})
++ * @return the message to show when requesting a version
++ */
++ @NotNull
++ Component getVersionMessage(@NotNull String serverVersion);
++
++ class DummyVersionFetcher implements VersionFetcher {
++
++ @Override
++ public long getCacheTime() {
++ return -1;
++ }
++
++ @NotNull
++ @Override
++ public Component getVersionMessage(@NotNull String serverVersion) {
++ Bukkit.getLogger().warning("Version provider has not been set, cannot check for updates!");
++ Bukkit.getLogger().info("Override the default implementation of org.bukkit.UnsafeValues#getVersionFetcher()");
++ new Throwable().printStackTrace();
++ return Component.text("Unable to check for updates. No version provider set.", NamedTextColor.RED);
++ }
++ }
++}
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 207c656c0a11a3a630bc70491efcf433b2681e18..195b6bb328de92c4d17d1cd14e13578226b1ac3c 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -84,5 +84,12 @@ public interface UnsafeValues {
+ * @return name
+ */
+ String getTimingsServerName();
++
++ /**
++ * Called once by the version command on first use, then cached.
++ */
++ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
++ return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher();
++ }
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
+index 2305eb40832a82159cd89162934870cf57e1aa0e..4c2ddc722a9dc4011906ad9530b13fa9be1d3ff9 100644
+--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java
++++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.command.defaults;
+
++import com.destroystokyo.paper.util.VersionFetcher; // Paper - version supplier
+ import com.google.common.base.Charsets;
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.io.Resources;
+@@ -16,6 +17,7 @@ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Set;
+ import java.util.concurrent.locks.ReentrantLock;
++import net.kyori.adventure.text.Component; // Paper
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.Bukkit;
+ import org.bukkit.ChatColor;
+@@ -26,6 +28,15 @@ import org.bukkit.util.StringUtil;
+ import org.jetbrains.annotations.NotNull;
+
+ public class VersionCommand extends BukkitCommand {
++ private VersionFetcher versionFetcher;
++ private VersionFetcher getVersionFetcher() { // lazy load because unsafe isn't available at command registration
++ if (versionFetcher == null) {
++ versionFetcher = Bukkit.getUnsafe().getVersionFetcher();
++ }
++
++ return versionFetcher;
++ }
++
+ public VersionCommand(@NotNull String name) {
+ super(name);
+
+@@ -40,7 +51,7 @@ public class VersionCommand extends BukkitCommand {
+ if (!testPermission(sender)) return true;
+
+ if (args.length == 0) {
+- sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")");
++ //sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); // Paper - moved to setVersionMessage
+ sendVersion(sender);
+ } else {
+ StringBuilder name = new StringBuilder();
+@@ -146,14 +157,14 @@ public class VersionCommand extends BukkitCommand {
+
+ private final ReentrantLock versionLock = new ReentrantLock();
+ private boolean hasVersion = false;
+- private String versionMessage = null;
++ private Component versionMessage = null; // Paper
+ private final Set<CommandSender> versionWaiters = new HashSet<CommandSender>();
+ private boolean versionTaskStarted = false;
+ private long lastCheck = 0;
+
+ private void sendVersion(@NotNull CommandSender sender) {
+ if (hasVersion) {
+- if (System.currentTimeMillis() - lastCheck > 21600000) {
++ if (System.currentTimeMillis() - lastCheck > getVersionFetcher().getCacheTime()) { // Paper - use version supplier
+ lastCheck = System.currentTimeMillis();
+ hasVersion = false;
+ } else {
+@@ -168,7 +179,7 @@ public class VersionCommand extends BukkitCommand {
+ return;
+ }
+ versionWaiters.add(sender);
+- sender.sendMessage("Checking version, please wait...");
++ sender.sendMessage(Component.text("Checking version, please wait...", net.kyori.adventure.text.format.NamedTextColor.WHITE, net.kyori.adventure.text.format.TextDecoration.ITALIC)); // Paper
+ if (!versionTaskStarted) {
+ versionTaskStarted = true;
+ new Thread(new Runnable() {
+@@ -186,6 +197,13 @@ public class VersionCommand extends BukkitCommand {
+
+ private void obtainVersion() {
+ String version = Bukkit.getVersion();
++ // Paper start
++ if (version.startsWith("null")) { // running from ide?
++ setVersionMessage(Component.text("Unknown version, custom build?", net.kyori.adventure.text.format.NamedTextColor.YELLOW));
++ return;
++ }
++ setVersionMessage(getVersionFetcher().getVersionMessage(version));
++ /*
+ if (version == null) version = "Custom";
+ String[] parts = version.substring(0, version.indexOf(' ')).split("-");
+ if (parts.length == 4) {
+@@ -215,11 +233,24 @@ public class VersionCommand extends BukkitCommand {
+ } else {
+ setVersionMessage("Unknown version, custom build?");
+ }
++ */
++ // Paper end
+ }
+
+- private void setVersionMessage(@NotNull String msg) {
++ // Paper start
++ private void setVersionMessage(final @NotNull Component msg) {
+ lastCheck = System.currentTimeMillis();
+- versionMessage = msg;
++ final Component message = net.kyori.adventure.text.TextComponent.ofChildren(
++ Component.text("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")", net.kyori.adventure.text.format.NamedTextColor.WHITE),
++ Component.newline(),
++ msg
++ );
++ this.versionMessage = Component.text()
++ .append(message)
++ .hoverEvent(Component.text("Click to copy to clipboard", net.kyori.adventure.text.format.NamedTextColor.WHITE))
++ .clickEvent(net.kyori.adventure.text.event.ClickEvent.copyToClipboard(net.kyori.adventure.text.serializer.plain.PlainComponentSerializer.plain().serialize(message)))
++ .build();
++ // Paper end
+ versionLock.lock();
+ try {
+ hasVersion = true;
diff --git a/Spigot-API-Patches-Unmapped/0010-Add-PlayerLocaleChangeEvent.patch b/Spigot-API-Patches-Unmapped/0010-Add-PlayerLocaleChangeEvent.patch
new file mode 100644
index 0000000000..10593168ec
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0010-Add-PlayerLocaleChangeEvent.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Isaac Moore <[email protected]>
+Date: Mon, 29 Feb 2016 18:02:25 -0600
+Subject: [PATCH] Add PlayerLocaleChangeEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..29dd763a99ce7c6ecb176b9fb346a400369d48a0
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
+@@ -0,0 +1,50 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++
++/**
++ * Called when the locale of the player is changed.
++ *
++ * @deprecated Replaced by {@link org.bukkit.event.player.PlayerLocaleChangeEvent} upstream
++ */
++@Deprecated
++public class PlayerLocaleChangeEvent extends PlayerEvent {
++ private static final HandlerList handlers = new HandlerList();
++ private final String oldLocale;
++ private final String newLocale;
++
++ public PlayerLocaleChangeEvent(final Player player, final String oldLocale, final String newLocale) {
++ super(player);
++ this.oldLocale = oldLocale;
++ this.newLocale = newLocale;
++ }
++
++ /**
++ * Gets the locale the player switched from.
++ *
++ * @return player's old locale
++ */
++ public String getOldLocale() {
++ return oldLocale;
++ }
++
++ /**
++ * Gets the locale the player is changed to.
++ *
++ * @return player's new locale
++ */
++ public String getNewLocale() {
++ return newLocale;
++ }
++
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0011-Add-player-view-distance-API.patch b/Spigot-API-Patches-Unmapped/0011-Add-player-view-distance-API.patch
new file mode 100644
index 0000000000..03fa8d1f5f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0011-Add-player-view-distance-API.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Byteflux <[email protected]>
+Date: Mon, 29 Feb 2016 18:05:37 -0600
+Subject: [PATCH] Add player view distance API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index e3a23cef93df2bbb2f27f87150bc1dbf7fd051b5..1ce07f925acb550c379e4deb1a013bcbe5701a8a 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1379,6 +1379,28 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @param affects Whether the player can affect mob spawning
+ */
+ public void setAffectsSpawning(boolean affects);
++
++ /**
++ * Gets the view distance for this player
++ *
++ * @return the player's view distance
++ * @deprecated This is unimplemented and <i>will</i> throw an exception at runtime. The {@link org.bukkit.World World}-based methods still work.
++ * @see org.bukkit.World#getViewDistance()
++ * @see org.bukkit.World#getNoTickViewDistance()
++ */
++ @Deprecated
++ public int getViewDistance();
++
++ /**
++ * Sets the view distance for this player
++ *
++ * @param viewDistance the player's view distance
++ * @deprecated This is unimplemented and <i>will</i> throw an exception at runtime. The {@link org.bukkit.World World}-based methods still work.
++ * @see org.bukkit.World#setViewDistance(int)
++ * @see org.bukkit.World#setNoTickViewDistance(int)
++ */
++ @Deprecated
++ public void setViewDistance(int viewDistance);
+ // Paper end
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0012-Add-BeaconEffectEvent.patch b/Spigot-API-Patches-Unmapped/0012-Add-BeaconEffectEvent.patch
new file mode 100644
index 0000000000..43af3c3828
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0012-Add-BeaconEffectEvent.patch
@@ -0,0 +1,98 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Byteflux <[email protected]>
+Date: Mon, 29 Feb 2016 18:09:40 -0600
+Subject: [PATCH] Add BeaconEffectEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/block/BeaconEffectEvent.java b/src/main/java/com/destroystokyo/paper/event/block/BeaconEffectEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..978813b94a5eae0afccbd3b38b463091a46b56ac
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/block/BeaconEffectEvent.java
+@@ -0,0 +1,86 @@
++package com.destroystokyo.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.bukkit.potion.PotionEffect;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a beacon effect is being applied to a player.
++ */
++public class BeaconEffectEvent extends BlockEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ private PotionEffect effect;
++ private Player player;
++ private boolean primary;
++
++ public BeaconEffectEvent(@NotNull Block block, @NotNull PotionEffect effect, @NotNull Player player, boolean primary) {
++ super(block);
++ this.effect = effect;
++ this.player = player;
++ this.primary = primary;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ /**
++ * Gets the potion effect being applied.
++ *
++ * @return Potion effect
++ */
++ @NotNull
++ public PotionEffect getEffect() {
++ return effect;
++ }
++
++ /**
++ * Sets the potion effect that will be applied.
++ *
++ * @param effect Potion effect
++ */
++ public void setEffect(@NotNull PotionEffect effect) {
++ this.effect = effect;
++ }
++
++ /**
++ * Gets the player who the potion effect is being applied to.
++ *
++ * @return Affected player
++ */
++ @NotNull
++ public Player getPlayer() {
++ return player;
++ }
++
++ /**
++ * Gets whether the effect is a primary beacon effect.
++ *
++ * @return true if this event represents a primary effect
++ */
++ public boolean isPrimary() {
++ return primary;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0013-Add-PlayerInitialSpawnEvent.patch b/Spigot-API-Patches-Unmapped/0013-Add-PlayerInitialSpawnEvent.patch
new file mode 100644
index 0000000000..c113777bf5
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0013-Add-PlayerInitialSpawnEvent.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Steve Anton <[email protected]>
+Date: Mon, 29 Feb 2016 18:13:58 -0600
+Subject: [PATCH] Add PlayerInitialSpawnEvent
+
+For modifying a player's initial spawn location as they join the server
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8b1fdb9d2869d4c1862d557c91bf8a1d8c537507
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
+@@ -0,0 +1,16 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Player;
++import org.jetbrains.annotations.NotNull;
++import org.spigotmc.event.player.PlayerSpawnLocationEvent;
++
++/**
++ * @deprecated Use {@link PlayerSpawnLocationEvent}, Duplicate API
++ */
++public class PlayerInitialSpawnEvent extends PlayerSpawnLocationEvent {
++
++ public PlayerInitialSpawnEvent(@NotNull Player who, @NotNull Location spawnLocation) {
++ super(who, spawnLocation);
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0014-Automatically-disable-plugins-that-fail-to-load.patch b/Spigot-API-Patches-Unmapped/0014-Automatically-disable-plugins-that-fail-to-load.patch
new file mode 100644
index 0000000000..9d44d1360d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0014-Automatically-disable-plugins-that-fail-to-load.patch
@@ -0,0 +1,21 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 29 Feb 2016 19:45:21 -0600
+Subject: [PATCH] Automatically disable plugins that fail to load
+
+
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index b6d739ca8ad8ebd4b1be7ebd129f9a7ae16b2a2a..c8497cb3021f584a885f4cb21c3be576ce0935a7 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -344,6 +344,10 @@ public final class JavaPluginLoader implements PluginLoader {
+ jPlugin.setEnabled(true);
+ } catch (Throwable ex) {
+ server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ // Paper start - Disable plugins that fail to load
++ disablePlugin(jPlugin);
++ return;
++ // Paper end
+ }
+
+ // Perhaps abort here, rather than continue going, but as it stands,
diff --git a/Spigot-API-Patches-Unmapped/0015-Expose-server-CommandMap.patch b/Spigot-API-Patches-Unmapped/0015-Expose-server-CommandMap.patch
new file mode 100644
index 0000000000..aa4d6d7b13
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0015-Expose-server-CommandMap.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Mon, 29 Feb 2016 19:48:59 -0600
+Subject: [PATCH] Expose server CommandMap
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index cd1b4be422a3a4290579d5daed9466084f18ed60..10274053320f1ec690a65d3794abb44b58658059 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1705,6 +1705,19 @@ public final class Bukkit {
+ return server.getUnsafe();
+ }
+
++
++ // Paper start
++ /**
++ * Gets the active {@link org.bukkit.command.CommandMap}
++ *
++ * @return the active command map
++ */
++ @NotNull
++ public static org.bukkit.command.CommandMap getCommandMap() {
++ return server.getCommandMap();
++ }
++ // Paper end
++
+ @NotNull
+ public static Server.Spigot spigot() {
+ return server.spigot();
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 9ec374570e135a4ba14db7efdbb5c1bd213226eb..7e4a728ceb943b6a32b9ba9b84bada34e71c0980 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1300,6 +1300,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ public double[] getTPS();
+ // Paper end
+
++ // Paper start
++ /**
++ * Gets the active {@link org.bukkit.command.CommandMap}
++ *
++ * @return the active command map
++ */
++ @NotNull
++ org.bukkit.command.CommandMap getCommandMap();
++
+ /**
+ * Get the advancement specified by this key.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0016-Graduate-bungeecord-chat-API-from-spigot-subclasses.patch b/Spigot-API-Patches-Unmapped/0016-Graduate-bungeecord-chat-API-from-spigot-subclasses.patch
new file mode 100644
index 0000000000..28128cfb69
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0016-Graduate-bungeecord-chat-API-from-spigot-subclasses.patch
@@ -0,0 +1,124 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Mon, 29 Feb 2016 19:54:32 -0600
+Subject: [PATCH] Graduate bungeecord chat API from spigot subclasses
+
+Change Javadoc to be accurate
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 10274053320f1ec690a65d3794abb44b58658059..22b83b142de97dcba28fa9a49730de7880d0b5d2 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -285,6 +285,30 @@ public final class Bukkit {
+ return server.broadcastMessage(message);
+ }
+
++ // Paper start
++ /**
++ * Sends the component to all online players.
++ *
++ * @param component the component to send
++ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Deprecated
++ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
++ server.broadcast(component);
++ }
++
++ /**
++ * Sends an array of components as a single message to all online players.
++ *
++ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Deprecated
++ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
++ server.broadcast(components);
++ }
++ // Paper end
++
+ /**
+ * Gets the name of the update folder. The update folder is used to safely
+ * update plugins at the right moment on a plugin load.
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 7e4a728ceb943b6a32b9ba9b84bada34e71c0980..9ce9b4ce2da6c57c62607502ae2042e30fc26d88 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -234,6 +234,30 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @Deprecated // Paper
+ public int broadcastMessage(@NotNull String message);
+
++ // Paper start
++ /**
++ * Sends the component to all online players.
++ *
++ * @param component the component to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Deprecated
++ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
++ spigot().broadcast(component);
++ }
++
++ /**
++ * Sends an array of components as a single message to all online players.
++ *
++ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Deprecated
++ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
++ spigot().broadcast(components);
++ }
++ // Paper end
++
+ /**
+ * Gets the name of the update folder. The update folder is used to safely
+ * update plugins at the right moment on a plugin load.
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 68795a48cb15d322906ce0569b7701231c1f94c2..88853cc165c67fd60a0a8f87e4ce356e4ca045f9 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -614,6 +614,42 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ public void sendMap(@NotNull MapView map);
+
++ // Paper start
++ /**
++ * Sends the component to the player
++ *
++ * @param component the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Override
++ @Deprecated
++ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
++ spigot().sendMessage(component);
++ }
++
++ /**
++ * Sends an array of components as a single message to the player
++ *
++ * @param components the components to send
++ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
++ */
++ @Override
++ @Deprecated
++ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
++ spigot().sendMessage(components);
++ }
++
++ /**
++ * Sends an array of components as a single message to the specified screen position of this player
++ *
++ * @param position the screen position
++ * @param components the components to send
++ */
++ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
++ spigot().sendMessage(position, components);
++ }
++ // Paper end
++
+ /**
+ * Forces an update of the player's entire inventory.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0017-Player-Tab-List-and-Title-APIs.patch b/Spigot-API-Patches-Unmapped/0017-Player-Tab-List-and-Title-APIs.patch
new file mode 100644
index 0000000000..90316cae1d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0017-Player-Tab-List-and-Title-APIs.patch
@@ -0,0 +1,577 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Techcable <[email protected]>
+Date: Mon, 29 Feb 2016 20:02:40 -0600
+Subject: [PATCH] Player Tab List and Title APIs
+
+Co-authored-by: Fruxz <[email protected]>
+
+diff --git a/src/main/java/com/destroystokyo/paper/Title.java b/src/main/java/com/destroystokyo/paper/Title.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9e90c3df567a65b48a0b9341f784eb902cb35d8c
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/Title.java
+@@ -0,0 +1,420 @@
++package com.destroystokyo.paper;
++
++import net.md_5.bungee.api.chat.BaseComponent;
++import net.md_5.bungee.api.chat.TextComponent;
++
++import org.bukkit.Bukkit;
++import org.bukkit.entity.Player;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Collection;
++import java.util.List;
++
++import static com.google.common.base.Preconditions.checkArgument;
++import static com.google.common.base.Preconditions.checkNotNull;
++import static com.google.common.base.Preconditions.checkState;
++
++/**
++ * Represents a title to may be sent to a {@link Player}.
++ *
++ * <p>A title can be sent without subtitle text.</p>
++ *
++ * @deprecated use {@link net.kyori.adventure.title.Title}
++ */
++@Deprecated
++public final class Title {
++
++ /**
++ * The default number of ticks for the title to fade in.
++ */
++ public static final int DEFAULT_FADE_IN = 20;
++ /**
++ * The default number of ticks for the title to stay.
++ */
++ public static final int DEFAULT_STAY = 200;
++ /**
++ * The default number of ticks for the title to fade out.
++ */
++ public static final int DEFAULT_FADE_OUT = 20;
++
++ private final BaseComponent[] title;
++ private final BaseComponent[] subtitle;
++ private final int fadeIn;
++ private final int stay;
++ private final int fadeOut;
++
++ /**
++ * Create a title with the default time values and no subtitle.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @throws NullPointerException if the title is null
++ */
++ public Title(@NotNull BaseComponent title) {
++ this(title, null);
++ }
++
++ /**
++ * Create a title with the default time values and no subtitle.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @throws NullPointerException if the title is null
++ */
++ public Title(@NotNull BaseComponent[] title) {
++ this(title, null);
++ }
++
++ /**
++ * Create a title with the default time values and no subtitle.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @throws NullPointerException if the title is null
++ */
++ public Title(@NotNull String title) {
++ this(title, null);
++ }
++
++ /**
++ * Create a title with the default time values.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ */
++ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle) {
++ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
++ }
++
++ /**
++ * Create a title with the default time values.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ */
++ public Title(@NotNull BaseComponent[] title, @Nullable BaseComponent[] subtitle) {
++ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
++ }
++
++ /**
++ * Create a title with the default time values.
++ *
++ * <p>Times use default values.</p>
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ */
++ public Title(@NotNull String title, @Nullable String subtitle) {
++ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
++ }
++
++ /**
++ * Creates a new title.
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ * @param fadeIn the number of ticks for the title to fade in
++ * @param stay the number of ticks for the title to stay on screen
++ * @param fadeOut the number of ticks for the title to fade out
++ * @throws IllegalArgumentException if any of the times are negative
++ */
++ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle, int fadeIn, int stay, int fadeOut) {
++ this(
++ new BaseComponent[]{checkNotNull(title, "title")},
++ subtitle == null ? null : new BaseComponent[]{subtitle},
++ fadeIn,
++ stay,
++ fadeOut
++ );
++ }
++
++ /**
++ * Creates a new title.
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ * @param fadeIn the number of ticks for the title to fade in
++ * @param stay the number of ticks for the title to stay on screen
++ * @param fadeOut the number of ticks for the title to fade out
++ * @throws IllegalArgumentException if any of the times are negative
++ */
++ public Title(@Nullable BaseComponent[] title, @NotNull BaseComponent[] subtitle, int fadeIn, int stay, int fadeOut) {
++ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
++ checkArgument(stay >= 0, "Negative stay: %s", stay);
++ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
++ this.title = checkNotNull(title, "title");
++ this.subtitle = subtitle;
++ this.fadeIn = fadeIn;
++ this.stay = stay;
++ this.fadeOut = fadeOut;
++ }
++
++ /**
++ * Creates a new title.
++ *
++ * <p>It is recommended to the {@link BaseComponent} constrctors.</p>
++ *
++ * @param title the main text of the title
++ * @param subtitle the secondary text of the title
++ * @param fadeIn the number of ticks for the title to fade in
++ * @param stay the number of ticks for the title to stay on screen
++ * @param fadeOut the number of ticks for the title to fade out
++ */
++ public Title(@NotNull String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) {
++ this(
++ TextComponent.fromLegacyText(checkNotNull(title, "title")),
++ subtitle == null ? null : TextComponent.fromLegacyText(subtitle),
++ fadeIn,
++ stay,
++ fadeOut
++ );
++ }
++
++ /**
++ * Gets the text of this title
++ *
++ * @return the text
++ */
++ @NotNull
++ public BaseComponent[] getTitle() {
++ return this.title;
++ }
++
++ /**
++ * Gets the text of this title's subtitle
++ *
++ * @return the text
++ */
++ @Nullable
++ public BaseComponent[] getSubtitle() {
++ return this.subtitle;
++ }
++
++ /**
++ * Gets the number of ticks to fade in.
++ *
++ * <p>The returned value is never negative.</p>
++ *
++ * @return the number of ticks to fade in
++ */
++ public int getFadeIn() {
++ return this.fadeIn;
++ }
++
++ /**
++ * Gets the number of ticks to stay.
++ *
++ * <p>The returned value is never negative.</p>
++ *
++ * @return the number of ticks to stay
++ */
++ public int getStay() {
++ return this.stay;
++ }
++
++ /**
++ * Gets the number of ticks to fade out.
++ *
++ * <p>The returned value is never negative.</p>
++ *
++ * @return the number of ticks to fade out
++ */
++ public int getFadeOut() {
++ return this.fadeOut;
++ }
++
++ /**
++ * Sends the title directly to an player
++ *
++ * @param player the receiver of the title
++ */
++ public void send(@NotNull Player player) {
++ player.sendTitle(this);
++ }
++
++ /**
++ * Sends the title directly to the defined players
++ *
++ * @param players the receivers of the title
++ */
++ public void send(@NotNull Collection<? extends Player> players) {
++ for (Player player : players) {
++ player.sendTitle(this);
++ }
++ }
++
++ /**
++ * Sends the title directly to the defined players
++ *
++ * @param players the receivers of the title
++ */
++ public void send(@NotNull Player[] players) {
++ for (Player player : players) {
++ player.sendTitle(this);
++ }
++ }
++
++ /**
++ * Sends the title directly to all online players
++ */
++ public void broadcast() {
++ send(Bukkit.getOnlinePlayers());
++ }
++
++ @NotNull
++ public static Builder builder() {
++ return new Builder();
++ }
++
++ /**
++ * A builder for creating titles
++ */
++ public static final class Builder {
++
++ private BaseComponent[] title;
++ private BaseComponent[] subtitle;
++ private int fadeIn = DEFAULT_FADE_IN;
++ private int stay = DEFAULT_STAY;
++ private int fadeOut = DEFAULT_FADE_OUT;
++
++ /**
++ * Sets the title to the given text.
++ *
++ * @param title the title text
++ * @return this builder instance
++ * @throws NullPointerException if the title is null
++ */
++ @NotNull
++ public Builder title(@NotNull BaseComponent title) {
++ return this.title(new BaseComponent[]{checkNotNull(title, "title")});
++ }
++
++ /**
++ * Sets the title to the given text.
++ *
++ * @param title the title text
++ * @return this builder instance
++ * @throws NullPointerException if the title is null
++ */
++ @NotNull
++ public Builder title(@NotNull BaseComponent[] title) {
++ this.title = checkNotNull(title, "title");
++ return this;
++ }
++
++ /**
++ * Sets the title to the given text.
++ *
++ * <p>It is recommended to the {@link BaseComponent} methods.</p>
++ *
++ * @param title the title text
++ * @return this builder instance
++ * @throws NullPointerException if the title is null
++ */
++ @NotNull
++ public Builder title(@NotNull String title) {
++ return this.title(TextComponent.fromLegacyText(checkNotNull(title, "title")));
++ }
++
++ /**
++ * Sets the subtitle to the given text.
++ *
++ * @param subtitle the title text
++ * @return this builder instance
++ */
++ @NotNull
++ public Builder subtitle(@Nullable BaseComponent subtitle) {
++ return this.subtitle(subtitle == null ? null : new BaseComponent[]{subtitle});
++ }
++
++ /**
++ * Sets the subtitle to the given text.
++ *
++ * @param subtitle the title text
++ * @return this builder instance
++ */
++ @NotNull
++ public Builder subtitle(@Nullable BaseComponent[] subtitle) {
++ this.subtitle = subtitle;
++ return this;
++ }
++
++ /**
++ * Sets the subtitle to the given text.
++ *
++ * <p>It is recommended to the {@link BaseComponent} methods.</p>
++ *
++ * @param subtitle the title text
++ * @return this builder instance
++ */
++ @NotNull
++ public Builder subtitle(@Nullable String subtitle) {
++ return this.subtitle(subtitle == null ? null : TextComponent.fromLegacyText(subtitle));
++ }
++
++ /**
++ * Sets the number of ticks for the title to fade in
++ *
++ * @param fadeIn the number of ticks to fade in
++ * @return this builder instance
++ * @throws IllegalArgumentException if it is negative
++ */
++ @NotNull
++ public Builder fadeIn(int fadeIn) {
++ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
++ this.fadeIn = fadeIn;
++ return this;
++ }
++
++
++ /**
++ * Sets the number of ticks for the title to stay.
++ *
++ * @param stay the number of ticks to stay
++ * @return this builder instance
++ * @throws IllegalArgumentException if it is negative
++ */
++ @NotNull
++ public Builder stay(int stay) {
++ checkArgument(stay >= 0, "Negative stay: %s", stay);
++ this.stay = stay;
++ return this;
++ }
++
++ /**
++ * Sets the number of ticks for the title to fade out.
++ *
++ * @param fadeOut the number of ticks to fade out
++ * @return this builder instance
++ * @throws IllegalArgumentException if it is negative
++ */
++ @NotNull
++ public Builder fadeOut(int fadeOut) {
++ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
++ this.fadeOut = fadeOut;
++ return this;
++ }
++
++ /**
++ * Create a title based on the values in the builder.
++ *
++ * @return a title from the values in this builder
++ * @throws IllegalStateException if title isn't specified
++ */
++ @NotNull
++ public Title build() {
++ checkState(title != null, "Title not specified");
++ return new Title(this.title, this.subtitle, this.fadeIn, this.stay, this.fadeOut);
++ }
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 88853cc165c67fd60a0a8f87e4ce356e4ca045f9..c9e4cb2d153fc0c6853fe520263a0073e7504e38 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -2,6 +2,7 @@ package org.bukkit.entity;
+
+ import java.net.InetSocketAddress;
+ import java.util.UUID;
++import com.destroystokyo.paper.Title; // Paper
+ import org.bukkit.DyeColor;
+ import org.bukkit.Effect;
+ import org.bukkit.GameMode;
+@@ -648,6 +649,131 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(position, components);
+ }
++
++ /**
++ * Set the text displayed in the player list header and footer for this player
++ *
++ * @param header content for the top of the player list
++ * @param footer content for the bottom of the player list
++ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
++ */
++ @Deprecated
++ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent[] header, @Nullable net.md_5.bungee.api.chat.BaseComponent[] footer);
++
++ /**
++ * Set the text displayed in the player list header and footer for this player
++ *
++ * @param header content for the top of the player list
++ * @param footer content for the bottom of the player list
++ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
++ */
++ @Deprecated
++ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent header, @Nullable net.md_5.bungee.api.chat.BaseComponent footer);
++
++ /**
++ * Update the times for titles displayed to the player
++ *
++ * @param fadeInTicks ticks to fade-in
++ * @param stayTicks ticks to stay visible
++ * @param fadeOutTicks ticks to fade-out
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks);
++
++ /**
++ * Update the subtitle of titles displayed to the player
++ *
++ * @param subtitle Subtitle to set
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent[] subtitle);
++
++ /**
++ * Update the subtitle of titles displayed to the player
++ *
++ * @param subtitle Subtitle to set
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent subtitle);
++
++ /**
++ * Show the given title to the player, along with the last subtitle set, using the last set times
++ *
++ * @param title Title to set
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title);
++
++ /**
++ * Show the given title to the player, along with the last subtitle set, using the last set times
++ *
++ * @param title Title to set
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title);
++
++ /**
++ * Show the given title and subtitle to the player using the given times
++ *
++ * @param title big text
++ * @param subtitle little text under it
++ * @param fadeInTicks ticks to fade-in
++ * @param stayTicks ticks to stay visible
++ * @param fadeOutTicks ticks to fade-out
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title, @Nullable net.md_5.bungee.api.chat.BaseComponent[] subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
++
++ /**
++ * Show the given title and subtitle to the player using the given times
++ *
++ * @param title big text
++ * @param subtitle little text under it
++ * @param fadeInTicks ticks to fade-in
++ * @param stayTicks ticks to stay visible
++ * @param fadeOutTicks ticks to fade-out
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title, @Nullable net.md_5.bungee.api.chat.BaseComponent subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
++
++ /**
++ * Show the title to the player, overriding any previously displayed title.
++ *
++ * <p>This method overrides any previous title, use {@link #updateTitle(Title)} to change the existing one.</p>
++ *
++ * @param title the title to send
++ * @throws NullPointerException if the title is null
++ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ void sendTitle(@NotNull Title title);
++
++ /**
++ * Show the title to the player, overriding any previously displayed title.
++ *
++ * <p>This method doesn't override previous titles, but changes their values.</p>
++ *
++ * @param title the title to send
++ * @throws NullPointerException if title is null
++ * @deprecated use {@link #showTitle(net.kyori.adventure.title.Title)}
++ */
++ @Deprecated
++ void updateTitle(@NotNull Title title);
++
++ /**
++ * Hide any title that is currently visible to the player
++ *
++ * @deprecated use {@link #clearTitle()}
++ */
++ @Deprecated
++ public void hideTitle();
+ // Paper end
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0018-Add-exception-reporting-event.patch b/Spigot-API-Patches-Unmapped/0018-Add-exception-reporting-event.patch
new file mode 100644
index 0000000000..b295b466fc
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0018-Add-exception-reporting-event.patch
@@ -0,0 +1,604 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Mon, 29 Feb 2016 20:24:35 -0600
+Subject: [PATCH] Add exception reporting event
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2f573299a9a817a98372817a1de8bf641aaca956
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
+@@ -0,0 +1,43 @@
++package com.destroystokyo.paper.event.server;
++
++import com.google.common.base.Preconditions;
++import org.apache.commons.lang.Validate;
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import com.destroystokyo.paper.exception.ServerException;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called whenever an exception is thrown in a recoverable section of the server.
++ */
++public class ServerExceptionEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private ServerException exception;
++
++ public ServerExceptionEvent(@NotNull ServerException exception) {
++ super(!Bukkit.isPrimaryThread());
++ this.exception = Preconditions.checkNotNull(exception, "exception");
++ }
++
++ /**
++ * Gets the wrapped exception that was thrown.
++ *
++ * @return Exception thrown
++ */
++ @NotNull
++ public ServerException getException() {
++ return exception;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6fb39af0479a818f7f1465bcdfe505ab4ff7da1a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
+@@ -0,0 +1,64 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.command.Command;
++import org.bukkit.command.CommandSender;
++
++import static com.google.common.base.Preconditions.checkNotNull;
++
++/**
++ * Thrown when a command throws an exception
++ */
++public class ServerCommandException extends ServerException {
++
++ private final Command command;
++ private final CommandSender commandSender;
++ private final String[] arguments;
++
++ public ServerCommandException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
++ super(message, cause);
++ this.commandSender = checkNotNull(commandSender, "commandSender");
++ this.arguments = checkNotNull(arguments, "arguments");
++ this.command = checkNotNull(command, "command");
++ }
++
++ public ServerCommandException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
++ super(cause);
++ this.commandSender = checkNotNull(commandSender, "commandSender");
++ this.arguments = checkNotNull(arguments, "arguments");
++ this.command = checkNotNull(command, "command");
++ }
++
++ protected ServerCommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
++ super(message, cause, enableSuppression, writableStackTrace);
++ this.commandSender = checkNotNull(commandSender, "commandSender");
++ this.arguments = checkNotNull(arguments, "arguments");
++ this.command = checkNotNull(command, "command");
++ }
++
++ /**
++ * Gets the command which threw the exception
++ *
++ * @return exception throwing command
++ */
++ public Command getCommand() {
++ return command;
++ }
++
++ /**
++ * Gets the command sender which executed the command request
++ *
++ * @return command sender of exception thrown command request
++ */
++ public CommandSender getCommandSender() {
++ return commandSender;
++ }
++
++ /**
++ * Gets the arguments which threw the exception for the command
++ *
++ * @return arguments of exception thrown command request
++ */
++ public String[] getArguments() {
++ return arguments;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..410b24139535cd5d8439ad581c43c61b5757fbf6
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
+@@ -0,0 +1,52 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.event.Event;
++import org.bukkit.event.Listener;
++import org.bukkit.plugin.Plugin;
++
++import static com.google.common.base.Preconditions.*;
++
++/**
++ * Exception thrown when a server event listener throws an exception
++ */
++public class ServerEventException extends ServerPluginException {
++
++ private final Listener listener;
++ private final Event event;
++
++ public ServerEventException(String message, Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
++ super(message, cause, responsiblePlugin);
++ this.listener = checkNotNull(listener, "listener");
++ this.event = checkNotNull(event, "event");
++ }
++
++ public ServerEventException(Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
++ super(cause, responsiblePlugin);
++ this.listener = checkNotNull(listener, "listener");
++ this.event = checkNotNull(event, "event");
++ }
++
++ protected ServerEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Listener listener, Event event) {
++ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
++ this.listener = checkNotNull(listener, "listener");
++ this.event = checkNotNull(event, "event");
++ }
++
++ /**
++ * Gets the listener which threw the exception
++ *
++ * @return event listener
++ */
++ public Listener getListener() {
++ return listener;
++ }
++
++ /**
++ * Gets the event which caused the exception
++ *
++ * @return event
++ */
++ public Event getEvent() {
++ return event;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c06ea3942447d4824b83ff839cb449fb818dede1
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
+@@ -0,0 +1,23 @@
++package com.destroystokyo.paper.exception;
++
++/**
++ * Wrapper exception for all exceptions that are thrown by the server.
++ */
++public class ServerException extends Exception {
++
++ public ServerException(String message) {
++ super(message);
++ }
++
++ public ServerException(String message, Throwable cause) {
++ super(message, cause);
++ }
++
++ public ServerException(Throwable cause) {
++ super(cause);
++ }
++
++ protected ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
++ super(message, cause, enableSuppression, writableStackTrace);
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e762ed0dbad51625e65fef2e1898679108459a36
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
+@@ -0,0 +1,35 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.Bukkit;
++import com.destroystokyo.paper.event.server.ServerExceptionEvent;
++
++/**
++ * Thrown when the internal server throws a recoverable exception.
++ */
++public class ServerInternalException extends ServerException {
++
++ public ServerInternalException(String message) {
++ super(message);
++ }
++
++ public ServerInternalException(String message, Throwable cause) {
++ super(message, cause);
++ }
++
++ public ServerInternalException(Throwable cause) {
++ super(cause);
++ }
++
++ protected ServerInternalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
++ super(message, cause, enableSuppression, writableStackTrace);
++ }
++
++ public static void reportInternalException(Throwable cause) {
++ try {
++ Bukkit.getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(cause)));
++ ;
++ } catch (Throwable t) {
++ t.printStackTrace(); // Don't want to rethrow!
++ }
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f016ba3b1b62e554a9bacbb9635f2dbe441b3c4e
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
+@@ -0,0 +1,20 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.plugin.Plugin;
++
++/**
++ * Thrown whenever there is an exception with any enabling or disabling of plugins.
++ */
++public class ServerPluginEnableDisableException extends ServerPluginException {
++ public ServerPluginEnableDisableException(String message, Throwable cause, Plugin responsiblePlugin) {
++ super(message, cause, responsiblePlugin);
++ }
++
++ public ServerPluginEnableDisableException(Throwable cause, Plugin responsiblePlugin) {
++ super(cause, responsiblePlugin);
++ }
++
++ protected ServerPluginEnableDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
++ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
++ }
++}
+\ No newline at end of file
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6defac287d0214fdf99418d979144050cc1e53bc
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
+@@ -0,0 +1,38 @@
++package com.destroystokyo.paper.exception;
++
++import com.google.common.base.Preconditions;
++import org.apache.commons.lang.Validate;
++import org.bukkit.plugin.Plugin;
++
++import static com.google.common.base.Preconditions.*;
++
++/**
++ * Wrapper exception for all cases to which a plugin can be immediately blamed for
++ */
++public class ServerPluginException extends ServerException {
++ public ServerPluginException(String message, Throwable cause, Plugin responsiblePlugin) {
++ super(message, cause);
++ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
++ }
++
++ public ServerPluginException(Throwable cause, Plugin responsiblePlugin) {
++ super(cause);
++ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
++ }
++
++ protected ServerPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
++ super(message, cause, enableSuppression, writableStackTrace);
++ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
++ }
++
++ private final Plugin responsiblePlugin;
++
++ /**
++ * Gets the plugin which is directly responsible for the exception being thrown
++ *
++ * @return plugin which is responsible for the exception throw
++ */
++ public Plugin getResponsiblePlugin() {
++ return responsiblePlugin;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..89e132525cfae0ce979e37b3e2793df781e47227
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
+@@ -0,0 +1,64 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.entity.Player;
++import org.bukkit.plugin.Plugin;
++
++import static com.google.common.base.Preconditions.*;
++
++/**
++ * Thrown when an incoming plugin message channel throws an exception
++ */
++public class ServerPluginMessageException extends ServerPluginException {
++
++ private final Player player;
++ private final String channel;
++ private final byte[] data;
++
++ public ServerPluginMessageException(String message, Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
++ super(message, cause, responsiblePlugin);
++ this.player = checkNotNull(player, "player");
++ this.channel = checkNotNull(channel, "channel");
++ this.data = checkNotNull(data, "data");
++ }
++
++ public ServerPluginMessageException(Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
++ super(cause, responsiblePlugin);
++ this.player = checkNotNull(player, "player");
++ this.channel = checkNotNull(channel, "channel");
++ this.data = checkNotNull(data, "data");
++ }
++
++ protected ServerPluginMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
++ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
++ this.player = checkNotNull(player, "player");
++ this.channel = checkNotNull(channel, "channel");
++ this.data = checkNotNull(data, "data");
++ }
++
++ /**
++ * Gets the channel to which the error occurred from recieving data from
++ *
++ * @return exception channel
++ */
++ public String getChannel() {
++ return channel;
++ }
++
++ /**
++ * Gets the data to which the error occurred from
++ *
++ * @return exception data
++ */
++ public byte[] getData() {
++ return data;
++ }
++
++ /**
++ * Gets the player which the plugin message causing the exception originated from
++ *
++ * @return exception player
++ */
++ public Player getPlayer() {
++ return player;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2d0b2d4a9b3e5bdeec0e4ea7ab69858d86aa3715
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
+@@ -0,0 +1,37 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.scheduler.BukkitTask;
++
++import static com.google.common.base.Preconditions.checkNotNull;
++
++/**
++ * Thrown when a plugin's scheduler fails with an exception
++ */
++public class ServerSchedulerException extends ServerPluginException {
++
++ private final BukkitTask task;
++
++ public ServerSchedulerException(String message, Throwable cause, BukkitTask task) {
++ super(message, cause, task.getOwner());
++ this.task = checkNotNull(task, "task");
++ }
++
++ public ServerSchedulerException(Throwable cause, BukkitTask task) {
++ super(cause, task.getOwner());
++ this.task = checkNotNull(task, "task");
++ }
++
++ protected ServerSchedulerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, BukkitTask task) {
++ super(message, cause, enableSuppression, writableStackTrace, task.getOwner());
++ this.task = checkNotNull(task, "task");
++ }
++
++ /**
++ * Gets the task which threw the exception
++ *
++ * @return exception throwing task
++ */
++ public BukkitTask getTask() {
++ return task;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5582999fe94c7a3dac655044ccc6d078cd9521a1
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
+@@ -0,0 +1,22 @@
++package com.destroystokyo.paper.exception;
++
++import org.bukkit.command.Command;
++import org.bukkit.command.CommandSender;
++
++/**
++ * Called when a tab-complete request throws an exception
++ */
++public class ServerTabCompleteException extends ServerCommandException {
++
++ public ServerTabCompleteException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
++ super(message, cause, command, commandSender, arguments);
++ }
++
++ public ServerTabCompleteException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
++ super(cause, command, commandSender, arguments);
++ }
++
++ protected ServerTabCompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
++ super(message, cause, enableSuppression, writableStackTrace, command, commandSender, arguments);
++ }
++}
+diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+index f020cb04eba27a2e70fc7cf799ebbfb434b9d974..adfc7aae2c0f49bbcdd358e83b04a0cf078a7d52 100644
+--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+@@ -8,6 +8,10 @@ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
++
++import com.destroystokyo.paper.event.server.ServerExceptionEvent;
++import com.destroystokyo.paper.exception.ServerCommandException;
++import com.destroystokyo.paper.exception.ServerTabCompleteException;
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.Location;
+ import org.bukkit.Server;
+@@ -155,11 +159,14 @@ public class SimpleCommandMap implements CommandMap {
+ target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length));
+ } // target.timings.stopTiming(); // Spigot // Paper
+ } catch (CommandException ex) {
++ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper
+ //target.timings.stopTiming(); // Spigot // Paper
+ throw ex;
+ } catch (Throwable ex) {
+ //target.timings.stopTiming(); // Spigot // Paper
+- throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex);
++ String msg = "Unhandled exception executing '" + commandLine + "' in " + target;
++ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper
++ throw new CommandException(msg, ex);
+ }
+
+ // return true as command was handled
+@@ -238,7 +245,9 @@ public class SimpleCommandMap implements CommandMap {
+ } catch (CommandException ex) {
+ throw ex;
+ } catch (Throwable ex) {
+- throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex);
++ String msg = "Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target;
++ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerTabCompleteException(msg, ex, target, sender, args))); // Paper
++ throw new CommandException(msg, ex);
+ }
+ }
+
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index 745eaa8f2f2ff83536301db8ca47a8af30df7a73..d0fec44d2546290091649879450761ce08514fcb 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -23,6 +23,10 @@ import java.util.WeakHashMap;
+ import java.util.logging.Level;
+ import java.util.regex.Matcher;
+ import java.util.regex.Pattern;
++
++import com.destroystokyo.paper.event.server.ServerExceptionEvent;
++import com.destroystokyo.paper.exception.ServerEventException;
++import com.destroystokyo.paper.exception.ServerPluginEnableDisableException;
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.Server;
+ import org.bukkit.World;
+@@ -478,7 +482,8 @@ public final class SimplePluginManager implements PluginManager {
+ try {
+ plugin.getPluginLoader().enablePlugin(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while enabling "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
+ }
+
+ HandlerList.bakeAll();
+@@ -499,32 +504,37 @@ public final class SimplePluginManager implements PluginManager {
+ try {
+ plugin.getPluginLoader().disablePlugin(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while disabling "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
+ try {
+ server.getScheduler().cancelTasks(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
+ try {
+ server.getServicesManager().unregisterAll(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
+ try {
+ HandlerList.unregisterAll(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
+ try {
+ server.getMessenger().unregisterIncomingPluginChannel(plugin);
+ server.getMessenger().unregisterOutgoingPluginChannel(plugin);
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
++ handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
++ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+ }
+
+ try {
+@@ -537,6 +547,13 @@ public final class SimplePluginManager implements PluginManager {
+ }
+ }
+
++ // Paper start
++ private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
++ server.getLogger().log(Level.SEVERE, msg, ex);
++ callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
++ }
++ // Paper end
++
+ @Override
+ public void clearPlugins() {
+ synchronized (this) {
+@@ -600,7 +617,13 @@ public final class SimplePluginManager implements PluginManager {
+ ));
+ }
+ } catch (Throwable ex) {
+- server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex);
++ // Paper start - error reporting
++ String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
++ server.getLogger().log(Level.SEVERE, msg, ex);
++ if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
++ callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
++ }
++ // Paper end
+ }
+ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0019-Fix-ServerListPingEvent-flagging-as-Async.patch b/Spigot-API-Patches-Unmapped/0019-Fix-ServerListPingEvent-flagging-as-Async.patch
new file mode 100644
index 0000000000..87d3369578
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0019-Fix-ServerListPingEvent-flagging-as-Async.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 29 Feb 2016 20:26:39 -0600
+Subject: [PATCH] Fix ServerListPingEvent flagging as Async
+
+This event can sometimes fire Async, set the proper boolean
+
+diff --git a/src/main/java/org/bukkit/event/server/ServerEvent.java b/src/main/java/org/bukkit/event/server/ServerEvent.java
+index 46b119017a1e3dfcd9ae5fb91b4fe8c20b0d6b86..05167fb34e4c42edc67af6e6700a2a3cc0f92769 100644
+--- a/src/main/java/org/bukkit/event/server/ServerEvent.java
++++ b/src/main/java/org/bukkit/event/server/ServerEvent.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.event.server;
+
++import org.bukkit.Bukkit;
+ import org.bukkit.event.Event;
+
+ /**
+@@ -8,7 +9,7 @@ import org.bukkit.event.Event;
+ public abstract class ServerEvent extends Event {
+
+ public ServerEvent() {
+- super();
++ super(!Bukkit.isPrimaryThread()); // Paper
+ }
+
+ public ServerEvent(boolean isAsync) {
diff --git a/Spigot-API-Patches-Unmapped/0020-Add-BaseComponent-sendMessage-methods-to-CommandSend.patch b/Spigot-API-Patches-Unmapped/0020-Add-BaseComponent-sendMessage-methods-to-CommandSend.patch
new file mode 100644
index 0000000000..b8376a9108
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0020-Add-BaseComponent-sendMessage-methods-to-CommandSend.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Tue, 8 Mar 2016 13:05:59 -0800
+Subject: [PATCH] Add BaseComponent sendMessage methods to CommandSender
+
+
+diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
+index c88418c7aa19b4fecdfa9af3d18ff202a5dc5763..fb0e608fa92dae99b9eee8fc1cbdf4b91a33e620 100644
+--- a/src/main/java/org/bukkit/command/CommandSender.java
++++ b/src/main/java/org/bukkit/command/CommandSender.java
+@@ -1,6 +1,9 @@
+ package org.bukkit.command;
+
+ import java.util.UUID;
++import net.kyori.adventure.audience.MessageType;
++import net.kyori.adventure.identity.Identity;
++import net.kyori.adventure.text.Component;
+ import org.bukkit.Server;
+ import org.bukkit.permissions.Permissible;
+ import org.jetbrains.annotations.NotNull;
+@@ -117,5 +120,33 @@ public interface CommandSender extends net.kyori.adventure.audience.Audience, Pe
+ default void sendMessage(final @NotNull net.kyori.adventure.identity.Identity identity, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
+ this.sendMessage(org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(message));
+ }
++
++ /**
++ * Sends the component to the sender
++ *
++ * <p>If this sender does not support sending full components then
++ * the component will be sent as legacy text.</p>
++ *
++ * @param component the component to send
++ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
++ */
++ @Deprecated
++ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
++ this.sendMessage(component.toLegacyText());
++ }
++
++ /**
++ * Sends an array of components as a single message to the sender
++ *
++ * <p>If this sender does not support sending full components then
++ * the components will be sent as legacy text.</p>
++ *
++ * @param components the components to send
++ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
++ */
++ @Deprecated
++ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
++ this.sendMessage(new net.md_5.bungee.api.chat.TextComponent(components).toLegacyText());
++ }
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0021-Add-methods-for-working-with-arrows-stuck-in-living-.patch b/Spigot-API-Patches-Unmapped/0021-Add-methods-for-working-with-arrows-stuck-in-living-.patch
new file mode 100644
index 0000000000..515b56ff03
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0021-Add-methods-for-working-with-arrows-stuck-in-living-.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: mrapple <[email protected]>
+Date: Sun, 25 Nov 2012 13:47:27 -0600
+Subject: [PATCH] Add methods for working with arrows stuck in living entities
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 24c858182f25496cc7254f7cf9e996b3bea1f9ec..45e9f585c3e522ecf94a6bc42cdc190e1a191a5c 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -605,4 +605,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @return Whether the entity is invisible
+ */
+ public boolean isInvisible();
++
++ // Paper start
++ /**
++ * Get the number of arrows stuck in this entity
++ * @return Number of arrows stuck
++ */
++ int getArrowsStuck();
++
++ /**
++ * Set the number of arrows stuck in this entity
++ *
++ * @param arrows Number of arrows to stick in this entity
++ */
++ void setArrowsStuck(int arrows);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0022-Complete-resource-pack-API.patch b/Spigot-API-Patches-Unmapped/0022-Complete-resource-pack-API.patch
new file mode 100644
index 0000000000..de972d33b6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0022-Complete-resource-pack-API.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jedediah Smith <[email protected]>
+Date: Sat, 4 Apr 2015 22:59:54 -0400
+Subject: [PATCH] Complete resource pack API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index a3d3cefb3739f3cb787befb03a5ddc38fb343849..38f47517d5391696c52d8848c21dee40033458e6 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1124,7 +1124,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
++ * @deprecated use {@link #setResourcePack(String, String)}
+ */
++ @Deprecated // Paper
+ public void setResourcePack(@NotNull String url);
+
+ /**
+@@ -1586,6 +1588,60 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
+ return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
+ }
++
++ /**
++ * Request that the player's client download and switch resource packs.
++ * <p>
++ * The player's client will download the new resource pack asynchronously
++ * in the background, and will automatically switch to it once the
++ * download is complete. If the client has downloaded and cached the same
++ * resource pack in the past, it will perform a quick timestamp check
++ * over the network to determine if the resource pack has changed and
++ * needs to be downloaded again. When this request is sent for the very
++ * first time from a given server, the client will first display a
++ * confirmation GUI to the player before proceeding with the download.
++ * <p>
++ * Notes:
++ * <ul>
++ * <li>Players can disable server resources on their client, in which
++ * case this method will have no affect on them.
++ * <li>There is no concept of resetting resource packs back to default
++ * within Minecraft, so players will have to relog to do so.
++ * </ul>
++ *
++ * @param url The URL from which the client will download the resource
++ * pack. The string must contain only US-ASCII characters and should
++ * be encoded as per RFC 1738.
++ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
++ * the resource pack file.
++ * @throws IllegalArgumentException Thrown if the URL is null.
++ * @throws IllegalArgumentException Thrown if the URL is too long. The
++ * length restriction is an implementation specific arbitrary value.
++ */
++ void setResourcePack(@NotNull String url, @NotNull String hash);
++
++ /**
++ * @return the most recent resource pack status received from the player,
++ * or null if no status has ever been received from this player.
++ */
++ @Nullable
++ org.bukkit.event.player.PlayerResourcePackStatusEvent.Status getResourcePackStatus();
++
++ /**
++ * @return the most recent resource pack hash received from the player,
++ * or null if no hash has ever been received from this player.
++ *
++ * @deprecated This is no longer sent from the client and will always be null
++ */
++ @Nullable
++ @Deprecated
++ String getResourcePackHash();
++
++ /**
++ * @return true if the last resource pack status received from this player
++ * was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED}
++ */
++ boolean hasResourcePack();
+ // Paper end
+
+ // Spigot start
+diff --git a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
+index b98195650d49d78ec35970ca0376b6289b861e4b..4c2102a11c3d682d98f0db4ccafa35231e66bcdd 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
+@@ -11,13 +11,32 @@ import org.jetbrains.annotations.NotNull;
+ public class PlayerResourcePackStatusEvent extends PlayerEvent {
+
+ private static final HandlerList handlers = new HandlerList();
++ @Deprecated
++ private final String hash; // Paper
+ private final Status status;
+
+ public PlayerResourcePackStatusEvent(@NotNull final Player who, @NotNull Status resourcePackStatus) {
+ super(who);
++ this.hash = null; // Paper
+ this.status = resourcePackStatus;
+ }
+
++ @Deprecated // Paper
++ public PlayerResourcePackStatusEvent(final Player who, Status resourcePackStatus, String hash) {
++ super(who);
++ this.hash = hash; // Paper
++ this.status = resourcePackStatus;
++ }
++
++ @Deprecated
++ /**
++ * @deprecated Hash does not seem to ever be set
++ */
++ public String getHash() {
++ return this.hash;
++ }
++ // Paper end
++
+ /**
+ * Gets the status of this pack.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0023-Use-ASM-for-event-executors.patch b/Spigot-API-Patches-Unmapped/0023-Use-ASM-for-event-executors.patch
new file mode 100644
index 0000000000..e54e92b274
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0023-Use-ASM-for-event-executors.patch
@@ -0,0 +1,398 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Techcable <[email protected]>
+Date: Thu, 3 Mar 2016 13:20:33 -0700
+Subject: [PATCH] Use ASM for event executors.
+
+Uses method handles for private or static methods.
+
+diff --git a/pom.xml b/pom.xml
+index 75b2830340051deb0fa39149e80872d2b88ed6f0..c3d65e441f5c26b6c6b10f4924504d8f3837e674 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -143,6 +143,17 @@
+ <version>9.1</version>
+ <scope>test</scope>
+ </dependency>
++ <!-- ASM -->
++ <dependency>
++ <groupId>org.ow2.asm</groupId>
++ <artifactId>asm</artifactId>
++ <version>9.0</version>
++ </dependency>
++ <dependency>
++ <groupId>org.ow2.asm</groupId>
++ <artifactId>asm-commons</artifactId>
++ <version>9.0</version>
++ </dependency>
+ </dependencies>
+
+ <build>
+diff --git a/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5b28e9b1daba7834af67dbc193dd656bedd9a994
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
+@@ -0,0 +1,42 @@
++package com.destroystokyo.paper.event.executor;
++
++import java.lang.invoke.MethodHandle;
++import java.lang.invoke.MethodHandles;
++import java.lang.reflect.Method;
++
++import com.destroystokyo.paper.util.SneakyThrow;
++import org.bukkit.event.Event;
++import org.bukkit.event.EventException;
++import org.bukkit.event.Listener;
++import org.bukkit.plugin.EventExecutor;
++import org.jetbrains.annotations.NotNull;
++
++public class MethodHandleEventExecutor implements EventExecutor {
++ private final Class<? extends Event> eventClass;
++ private final MethodHandle handle;
++
++ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull MethodHandle handle) {
++ this.eventClass = eventClass;
++ this.handle = handle;
++ }
++
++ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
++ this.eventClass = eventClass;
++ try {
++ m.setAccessible(true);
++ this.handle = MethodHandles.lookup().unreflect(m);
++ } catch (IllegalAccessException e) {
++ throw new AssertionError("Unable to set accessible", e);
++ }
++ }
++
++ @Override
++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
++ if (!eventClass.isInstance(event)) return;
++ try {
++ handle.invoke(listener, event);
++ } catch (Throwable t) {
++ SneakyThrow.sneaky(t);
++ }
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c83672427324bd068ed52916f700b68446a226f6
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
+@@ -0,0 +1,43 @@
++package com.destroystokyo.paper.event.executor;
++
++import java.lang.invoke.MethodHandle;
++import java.lang.invoke.MethodHandles;
++import java.lang.reflect.Method;
++import java.lang.reflect.Modifier;
++
++import com.destroystokyo.paper.util.SneakyThrow;
++import com.google.common.base.Preconditions;
++
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.EventException;
++import org.bukkit.event.Listener;
++import org.bukkit.plugin.EventExecutor;
++import org.jetbrains.annotations.NotNull;
++
++public class StaticMethodHandleEventExecutor implements EventExecutor {
++ private final Class<? extends Event> eventClass;
++ private final MethodHandle handle;
++
++ public StaticMethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
++ Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m);
++ Preconditions.checkArgument(eventClass != null, "eventClass is null");
++ this.eventClass = eventClass;
++ try {
++ m.setAccessible(true);
++ this.handle = MethodHandles.lookup().unreflect(m);
++ } catch (IllegalAccessException e) {
++ throw new AssertionError("Unable to set accessible", e);
++ }
++ }
++
++ @Override
++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
++ if (!eventClass.isInstance(event)) return;
++ try {
++ handle.invoke(event);
++ } catch (Throwable throwable) {
++ SneakyThrow.sneaky(throwable);
++ }
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b6e7d8ee8d903ebf975d60bec0e08603d9a49fdb
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
+@@ -0,0 +1,47 @@
++package com.destroystokyo.paper.event.executor.asm;
++
++import java.lang.reflect.Method;
++import java.util.concurrent.atomic.AtomicInteger;
++
++import org.bukkit.plugin.EventExecutor;
++import org.jetbrains.annotations.NotNull;
++import org.objectweb.asm.ClassWriter;
++import org.objectweb.asm.Type;
++import org.objectweb.asm.commons.GeneratorAdapter;
++
++import static org.objectweb.asm.Opcodes.*;
++
++public class ASMEventExecutorGenerator {
++ @NotNull
++ public static byte[] generateEventExecutor(@NotNull Method m, @NotNull String name) {
++ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
++ writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(Object.class), new String[] {Type.getInternalName(EventExecutor.class)});
++ // Generate constructor
++ GeneratorAdapter methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null), ACC_PUBLIC, "<init>", "()V");
++ methodGenerator.loadThis();
++ methodGenerator.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false); // Invoke the super class (Object) constructor
++ methodGenerator.returnValue();
++ methodGenerator.endMethod();
++ // Generate the execute method
++ methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "execute", "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Event;)V", null, null), ACC_PUBLIC, "execute", "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Listener;)V");;
++ methodGenerator.loadArg(0);
++ methodGenerator.checkCast(Type.getType(m.getDeclaringClass()));
++ methodGenerator.loadArg(1);
++ methodGenerator.checkCast(Type.getType(m.getParameterTypes()[0]));
++ methodGenerator.visitMethodInsn(m.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m), m.getDeclaringClass().isInterface());
++ if (m.getReturnType() != void.class) {
++ methodGenerator.pop();
++ }
++ methodGenerator.returnValue();
++ methodGenerator.endMethod();
++ writer.visitEnd();
++ return writer.toByteArray();
++ }
++
++ public static AtomicInteger NEXT_ID = new AtomicInteger(1);
++ @NotNull
++ public static String generateName() {
++ int id = NEXT_ID.getAndIncrement();
++ return "com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor" + id;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f79685b48bb581277a6891927988b6f7a4389dc4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
+@@ -0,0 +1,34 @@
++package com.destroystokyo.paper.event.executor.asm;
++
++import org.jetbrains.annotations.NotNull;
++
++public interface ClassDefiner {
++
++ /**
++ * Returns if the defined classes can bypass access checks
++ *
++ * @return if classes bypass access checks
++ */
++ public default boolean isBypassAccessChecks() {
++ return false;
++ }
++
++ /**
++ * Define a class
++ *
++ * @param parentLoader the parent classloader
++ * @param name the name of the class
++ * @param data the class data to load
++ * @return the defined class
++ * @throws ClassFormatError if the class data is invalid
++ * @throws NullPointerException if any of the arguments are null
++ */
++ @NotNull
++ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data);
++
++ @NotNull
++ public static ClassDefiner getInstance() {
++ return SafeClassDefiner.INSTANCE;
++ }
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ac99477e9f2c08041aeff31abc1d1edee58d0a67
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
+@@ -0,0 +1,66 @@
++package com.destroystokyo.paper.event.executor.asm;
++
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.ConcurrentMap;
++
++import com.google.common.base.Preconditions;
++
++import com.google.common.collect.MapMaker;
++import org.jetbrains.annotations.NotNull;
++import org.objectweb.asm.Type;
++
++public class SafeClassDefiner implements ClassDefiner {
++ /* default */ static final SafeClassDefiner INSTANCE = new SafeClassDefiner();
++
++ private SafeClassDefiner() {}
++
++ private final ConcurrentMap<ClassLoader, GeneratedClassLoader> loaders = new MapMaker().weakKeys().makeMap();
++
++ @NotNull
++ @Override
++ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data) {
++ GeneratedClassLoader loader = loaders.computeIfAbsent(parentLoader, GeneratedClassLoader::new);
++ synchronized (loader.getClassLoadingLock(name)) {
++ Preconditions.checkState(!loader.hasClass(name), "%s already defined", name);
++ Class<?> c = loader.define(name, data);
++ assert c.getName().equals(name);
++ return c;
++ }
++ }
++
++ private static class GeneratedClassLoader extends ClassLoader {
++ static {
++ ClassLoader.registerAsParallelCapable();
++ }
++
++ protected GeneratedClassLoader(@NotNull ClassLoader parent) {
++ super(parent);
++ }
++
++ private Class<?> define(@NotNull String name, byte[] data) {
++ synchronized (getClassLoadingLock(name)) {
++ assert !hasClass(name);
++ Class<?> c = defineClass(name, data, 0, data.length);
++ resolveClass(c);
++ return c;
++ }
++ }
++
++ @Override
++ @NotNull
++ public Object getClassLoadingLock(@NotNull String name) {
++ return super.getClassLoadingLock(name);
++ }
++
++ public boolean hasClass(@NotNull String name) {
++ synchronized (getClassLoadingLock(name)) {
++ try {
++ Class.forName(name);
++ return true;
++ } catch (ClassNotFoundException e) {
++ return false;
++ }
++ }
++ }
++ }
++}
+diff --git a/src/main/java/org/bukkit/plugin/EventExecutor.java b/src/main/java/org/bukkit/plugin/EventExecutor.java
+index a850f0780de05463fc0d3f9e15ff7f19d88b2aed..9026e108ccd3a88aee1267ee275137befa646455 100644
+--- a/src/main/java/org/bukkit/plugin/EventExecutor.java
++++ b/src/main/java/org/bukkit/plugin/EventExecutor.java
+@@ -5,9 +5,75 @@ import org.bukkit.event.EventException;
+ import org.bukkit.event.Listener;
+ import org.jetbrains.annotations.NotNull;
+
++// Paper start
++import java.lang.reflect.Method;
++import java.lang.reflect.Modifier;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.ConcurrentMap;
++import java.util.function.Function;
++
++import com.destroystokyo.paper.event.executor.MethodHandleEventExecutor;
++import com.destroystokyo.paper.event.executor.StaticMethodHandleEventExecutor;
++import com.destroystokyo.paper.event.executor.asm.ASMEventExecutorGenerator;
++import com.destroystokyo.paper.event.executor.asm.ClassDefiner;
++import com.google.common.base.Preconditions;
++// Paper end
++
+ /**
+ * Interface which defines the class for event call backs to plugins
+ */
+ public interface EventExecutor {
+ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException;
++
++ // Paper start
++ ConcurrentMap<Method, Class<? extends EventExecutor>> eventExecutorMap = new ConcurrentHashMap<Method, Class<? extends EventExecutor>>() {
++ @NotNull
++ @Override
++ public Class<? extends EventExecutor> computeIfAbsent(@NotNull Method key, @NotNull Function<? super Method, ? extends Class<? extends EventExecutor>> mappingFunction) {
++ Class<? extends EventExecutor> executorClass = get(key);
++ if (executorClass != null)
++ return executorClass;
++
++ //noinspection SynchronizationOnLocalVariableOrMethodParameter
++ synchronized (key) {
++ executorClass = get(key);
++ if (executorClass != null)
++ return executorClass;
++
++ return super.computeIfAbsent(key, mappingFunction);
++ }
++ }
++ };
++
++ @NotNull
++ public static EventExecutor create(@NotNull Method m, @NotNull Class<? extends Event> eventClass) {
++ Preconditions.checkNotNull(m, "Null method");
++ Preconditions.checkArgument(m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount());
++ Preconditions.checkArgument(m.getParameterTypes()[0] == eventClass, "First parameter %s doesn't match event class %s", m.getParameterTypes()[0], eventClass);
++ ClassDefiner definer = ClassDefiner.getInstance();
++ if (Modifier.isStatic(m.getModifiers())) {
++ return new StaticMethodHandleEventExecutor(eventClass, m);
++ } else if (definer.isBypassAccessChecks() || Modifier.isPublic(m.getDeclaringClass().getModifiers()) && Modifier.isPublic(m.getModifiers())) {
++ // get the existing generated EventExecutor class for the Method or generate one
++ Class<? extends EventExecutor> executorClass = eventExecutorMap.computeIfAbsent(m, (__) -> {
++ String name = ASMEventExecutorGenerator.generateName();
++ byte[] classData = ASMEventExecutorGenerator.generateEventExecutor(m, name);
++ return definer.defineClass(m.getDeclaringClass().getClassLoader(), name, classData).asSubclass(EventExecutor.class);
++ });
++
++ try {
++ EventExecutor asmExecutor = executorClass.newInstance();
++ // Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception)
++ return (listener, event) -> {
++ if (!eventClass.isInstance(event)) return;
++ asmExecutor.execute(listener, event);
++ };
++ } catch (InstantiationException | IllegalAccessException e) {
++ throw new AssertionError("Unable to initialize generated event executor", e);
++ }
++ } else {
++ return new MethodHandleEventExecutor(eventClass, m);
++ }
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index c8497cb3021f584a885f4cb21c3be576ce0935a7..5be6460e8eb81381c7e305cb7ab6b77c0c7a8fe5 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -300,21 +300,7 @@ public final class JavaPluginLoader implements PluginLoader {
+ }
+ }
+
+- EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper
+- @Override
+- public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper
+- try {
+- if (!eventClass.isAssignableFrom(event.getClass())) {
+- return;
+- }
+- method.invoke(listener, event);
+- } catch (InvocationTargetException ex) {
+- throw new EventException(ex.getCause());
+- } catch (Throwable t) {
+- throw new EventException(t);
+- }
+- }
+- }, plugin, method, eventClass); // Paper
++ EventExecutor executor = new co.aikar.timings.TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass); // Paper // Paper (Yes.) - Use factory method `EventExecutor.create()`
+ if (false) { // Spigot - RL handles useTimings check now
+ eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
+ } else {
diff --git a/Spigot-API-Patches-Unmapped/0024-Add-a-call-helper-to-Event.patch b/Spigot-API-Patches-Unmapped/0024-Add-a-call-helper-to-Event.patch
new file mode 100644
index 0000000000..4b287bfa56
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0024-Add-a-call-helper-to-Event.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 19 May 2013 20:36:58 -0400
+Subject: [PATCH] Add a call helper to Event
+
+Reduces diff in Server patches
+
+diff --git a/src/main/java/org/bukkit/event/Event.java b/src/main/java/org/bukkit/event/Event.java
+index 18d0636b749913bfdcea8eebc7d0840d192fb071..8ec56cd6b8e0f5c5dd8c7c88b4671e18dcf109d0 100644
+--- a/src/main/java/org/bukkit/event/Event.java
++++ b/src/main/java/org/bukkit/event/Event.java
+@@ -35,6 +35,22 @@ public abstract class Event {
+ this.async = isAsync;
+ }
+
++ // Paper start
++ /**
++ * Calls the event and tests if cancelled.
++ *
++ * @return false if event was cancelled, if cancellable. otherwise true.
++ */
++ public boolean callEvent() {
++ org.bukkit.Bukkit.getPluginManager().callEvent(this);
++ if (this instanceof Cancellable) {
++ return !((Cancellable) this).isCancelled();
++ } else {
++ return true;
++ }
++ }
++ // Paper end
++
+ /**
+ * Convenience method for providing a user-friendly identifier. By
+ * default, it is the event's class's {@linkplain Class#getSimpleName()
diff --git a/Spigot-API-Patches-Unmapped/0025-Add-sender-name-to-commands.yml-replacement.patch b/Spigot-API-Patches-Unmapped/0025-Add-sender-name-to-commands.yml-replacement.patch
new file mode 100644
index 0000000000..7122fed4a2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0025-Add-sender-name-to-commands.yml-replacement.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 22 Jul 2015 18:50:41 -0400
+Subject: [PATCH] Add sender name to commands.yml replacement
+
+This allows you to use $sender in commands.yml definitions to make
+commands that auto target self.
+
+diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java
+index a6ad94ef98a1df1d2842635d850bc990b0137849..9d4f553c04784cca63901a56a7aea62a5cae1d72 100644
+--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java
++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java
+@@ -1,6 +1,9 @@
+ package org.bukkit.command;
+
+ import java.util.ArrayList;
++import java.util.regex.Matcher; // Paper
++import java.util.regex.Pattern; // Paper
++
+ import org.bukkit.Bukkit;
+ import org.jetbrains.annotations.NotNull;
+
+@@ -19,7 +22,7 @@ public class FormattedCommandAlias extends Command {
+ ArrayList<String> commands = new ArrayList<String>();
+ for (String formatString : formatStrings) {
+ try {
+- commands.add(buildCommand(formatString, args));
++ commands.add(buildCommand(sender, formatString, args)); // Paper
+ } catch (Throwable throwable) {
+ if (throwable instanceof IllegalArgumentException) {
+ sender.sendMessage(throwable.getMessage());
+@@ -37,7 +40,10 @@ public class FormattedCommandAlias extends Command {
+ return result;
+ }
+
+- private String buildCommand(@NotNull String formatString, @NotNull String[] args) {
++ private String buildCommand(@NotNull CommandSender sender, @NotNull String formatString, @NotNull String[] args) { // Paper
++ if (formatString.contains("$sender")) { // Paper
++ formatString = formatString.replaceAll(Pattern.quote("$sender"), Matcher.quoteReplacement(sender.getName())); // Paper
++ } // Paper
+ int index = formatString.indexOf('$');
+ while (index != -1) {
+ int start = index;
diff --git a/Spigot-API-Patches-Unmapped/0026-Add-command-to-reload-permissions.yml-and-require-co.patch b/Spigot-API-Patches-Unmapped/0026-Add-command-to-reload-permissions.yml-and-require-co.patch
new file mode 100644
index 0000000000..ebb524d781
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0026-Add-command-to-reload-permissions.yml-and-require-co.patch
@@ -0,0 +1,104 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William <[email protected]>
+Date: Fri, 18 Mar 2016 03:28:07 -0400
+Subject: [PATCH] Add command to reload permissions.yml and require confirm to
+ reload
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 22b83b142de97dcba28fa9a49730de7880d0b5d2..945d75525465739dd30610dff985ea0fb0f1c8df 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1740,6 +1740,13 @@ public final class Bukkit {
+ public static org.bukkit.command.CommandMap getCommandMap() {
+ return server.getCommandMap();
+ }
++
++ /**
++ * Reload the Permissions in permissions.yml
++ */
++ public static void reloadPermissions() {
++ server.reloadPermissions();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 9ce9b4ce2da6c57c62607502ae2042e30fc26d88..f69c30ebea9c9e9add0b6c97994be0ce64a80ad3 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1538,4 +1538,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @NotNull
+ Spigot spigot();
+ // Spigot end
++
++ void reloadPermissions(); // Paper
+ }
+diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+index 50cc311be7904cc8fc6070a21c8e4de3a489fd20..c62da4131b17e66892678e8b618fb9ba3de93b56 100644
+--- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
++++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+@@ -13,15 +13,35 @@ public class ReloadCommand extends BukkitCommand {
+ public ReloadCommand(@NotNull String name) {
+ super(name);
+ this.description = "Reloads the server configuration and plugins";
+- this.usageMessage = "/reload";
++ this.usageMessage = "/reload [permissions]"; // Paper
+ this.setPermission("bukkit.command.reload");
+ this.setAliases(Arrays.asList("rl"));
+ }
+
+ @Override
+- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
++ public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { // Paper
+ if (!testPermission(sender)) return true;
+
++ // Paper start - Reload permissions.yml & require confirm
++ boolean confirmed = System.getProperty("LetMeReload") != null;
++ if (args.length == 1) {
++ if (args[0].equalsIgnoreCase("permissions")) {
++ Bukkit.getServer().reloadPermissions();
++ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Permissions successfully reloaded.");
++ return true;
++ } else if ("confirm".equalsIgnoreCase(args[0])) {
++ confirmed = true;
++ } else {
++ Command.broadcastCommandMessage(sender, ChatColor.RED + "Usage: " + usageMessage);
++ return true;
++ }
++ }
++ if (!confirmed) {
++ Command.broadcastCommandMessage(sender, ChatColor.RED + "Are you sure you wish to reload your server? Doing so may cause bugs and memory leaks. It is recommended to restart instead of using /reload. To confirm, please type " + ChatColor.YELLOW + "/reload confirm");
++ return true;
++ }
++ // Paper end
++
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues when using some plugins.");
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
+ Bukkit.reload();
+@@ -33,6 +53,6 @@ public class ReloadCommand extends BukkitCommand {
+ @NotNull
+ @Override
+ public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
+- return Collections.emptyList();
++ return java.util.Collections.singletonList("permissions"); // Paper
+ }
+ }
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index d0fec44d2546290091649879450761ce08514fcb..8b33d914d29897c0276f9e2e7ce83bd2c316d5e2 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -894,4 +894,13 @@ public final class SimplePluginManager implements PluginManager {
+ public void useTimings(boolean use) {
+ co.aikar.timings.Timings.setTimingsEnabled(use); // Paper
+ }
++
++ // Paper start
++ public void clearPermissions() {
++ permissions.clear();
++ defaultPerms.get(true).clear();
++ defaultPerms.get(false).clear();
++ }
++ // Paper end
++
+ }
diff --git a/Spigot-API-Patches-Unmapped/0027-Custom-replacement-for-eaten-items.patch b/Spigot-API-Patches-Unmapped/0027-Custom-replacement-for-eaten-items.patch
new file mode 100644
index 0000000000..94ff9d88e2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0027-Custom-replacement-for-eaten-items.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jedediah Smith <[email protected]>
+Date: Sun, 21 Jun 2015 15:05:21 -0400
+Subject: [PATCH] Custom replacement for eaten items
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
+index c2793f3ef01c1246c130971c17e1c2bf8f551435..373f4b5b5185aa81ff728da89c9cc4e0ccf87889 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
+@@ -22,6 +22,7 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean isCancelled = false;
+ private ItemStack item;
++ @Nullable private ItemStack replacement; // Paper
+
+ /**
+ * @param player the player consuming
+@@ -58,6 +59,29 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
+ }
+ }
+
++ // Paper start
++ /**
++ * Return the custom item stack that will replace the consumed item, or null if no
++ * custom replacement has been set (which means the default replacement will be used).
++ *
++ * @return The custom item stack that will replace the consumed item or null
++ */
++ @Nullable
++ public ItemStack getReplacement() {
++ return this.replacement;
++ }
++
++ /**
++ * Set a custom item stack to replace the consumed item. Pass null to clear any custom
++ * stack that has been set and use the default replacement.
++ *
++ * @param replacement Replacement item to set, null to clear any custom stack and use default
++ */
++ public void setReplacement(@Nullable ItemStack replacement) {
++ this.replacement = replacement;
++ }
++ // Paper end
++
+ @Override
+ public boolean isCancelled() {
+ return this.isCancelled;
diff --git a/Spigot-API-Patches-Unmapped/0028-Entity-AddTo-RemoveFrom-World-Events.patch b/Spigot-API-Patches-Unmapped/0028-Entity-AddTo-RemoveFrom-World-Events.patch
new file mode 100644
index 0000000000..5dc11c88f8
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0028-Entity-AddTo-RemoveFrom-World-Events.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 28 Mar 2016 20:26:34 -0400
+Subject: [PATCH] Entity AddTo/RemoveFrom World Events
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityAddToWorldEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityAddToWorldEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..07660202e41ee86f1b66bad3335cf6fe126e7f9c
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityAddToWorldEvent.java
+@@ -0,0 +1,32 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired any time an entity is being added to the world for any reason.
++ *
++ * Not to be confused with {@link org.bukkit.event.entity.CreatureSpawnEvent}
++ * This will fire anytime a chunk is reloaded too.
++ */
++public class EntityAddToWorldEvent extends EntityEvent {
++
++ public EntityAddToWorldEvent(@NotNull Entity entity) {
++ super(entity);
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityRemoveFromWorldEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityRemoveFromWorldEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e5dbbd660409bae0d3b96e83390511d3a423a52e
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityRemoveFromWorldEvent.java
+@@ -0,0 +1,29 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired any time an entity is being removed from a world for any reason
++ */
++public class EntityRemoveFromWorldEvent extends EntityEvent {
++
++ public EntityRemoveFromWorldEvent(@NotNull Entity entity) {
++ super(entity);
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0029-EntityPathfindEvent.patch b/Spigot-API-Patches-Unmapped/0029-EntityPathfindEvent.patch
new file mode 100644
index 0000000000..697b76661a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0029-EntityPathfindEvent.patch
@@ -0,0 +1,95 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 28 Mar 2016 21:15:34 -0400
+Subject: [PATCH] EntityPathfindEvent
+
+Fires when an Entity decides to start moving to a location.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityPathfindEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityPathfindEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..63e46b2fb1b12b36fcb1e98b178cf29dd2e3d1b5
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityPathfindEvent.java
+@@ -0,0 +1,82 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Fired when an Entity decides to start moving towards a location.
++ *
++ * This event does not fire for the entities actual movement. Only when it
++ * is choosing to start moving to a location.
++ */
++public class EntityPathfindEvent extends EntityEvent implements Cancellable {
++ @Nullable private final Entity targetEntity;
++ @NotNull private final Location loc;
++
++ public EntityPathfindEvent(@NotNull Entity entity, @NotNull Location loc, @Nullable Entity targetEntity) {
++ super(entity);
++ this.targetEntity = targetEntity;
++ this.loc = loc;
++ }
++
++ /**
++ * The Entity that is pathfinding.
++ * @return The Entity that is pathfinding.
++ */
++ @NotNull
++ public Entity getEntity() {
++ return entity;
++ }
++
++ /**
++ * If the Entity is trying to pathfind to an entity, this is the entity in relation.
++ *
++ * Otherwise this will return null.
++ *
++ * @return The entity target or null
++ */
++ @Nullable
++ public Entity getTargetEntity() {
++ return targetEntity;
++ }
++
++ /**
++ * The Location of where the entity is about to move to.
++ *
++ * Note that if the target happened to of been an entity
++ * @return Location of where the entity is trying to pathfind to.
++ */
++ @NotNull
++ public Location getLoc() {
++ return loc;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0030-Reduce-thread-synchronization-in-MetadataStoreBase.patch b/Spigot-API-Patches-Unmapped/0030-Reduce-thread-synchronization-in-MetadataStoreBase.patch
new file mode 100644
index 0000000000..d4674ca2f4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0030-Reduce-thread-synchronization-in-MetadataStoreBase.patch
@@ -0,0 +1,90 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: crast <[email protected]>
+Date: Sat, 1 Jun 2013 13:52:30 -0600
+Subject: [PATCH] Reduce thread synchronization in MetadataStoreBase
+
+Use ConcurrentHashMap to allow thread-safe access methods and very
+limited synchronized portions to allow much higher concurrency in
+MetadataStore as well as far less locking, especially on reads
+
+diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+index baf850226aed8545a5794deba6dff9603953b4b2..d363d517c05b3335101d829ce4ec22d049059c24 100644
+--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
++++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+@@ -12,7 +12,7 @@ import org.bukkit.plugin.Plugin;
+ import org.jetbrains.annotations.NotNull;
+
+ public abstract class MetadataStoreBase<T> {
+- private Map<String, Map<Plugin, MetadataValue>> metadataMap = new HashMap<String, Map<Plugin, MetadataValue>>();
++ private Map<String, Map<Plugin, MetadataValue>> metadataMap = new java.util.concurrent.ConcurrentHashMap<String, Map<Plugin, MetadataValue>>(); // Paper
+
+ /**
+ * Adds a metadata value to an object. Each metadata value is owned by a
+@@ -46,7 +46,9 @@ public abstract class MetadataStoreBase<T> {
+ entry = new WeakHashMap<Plugin, MetadataValue>(1);
+ metadataMap.put(key, entry);
+ }
+- entry.put(owningPlugin, newMetadataValue);
++ synchronized (entry) {
++ entry.put(owningPlugin, newMetadataValue);
++ }
+ }
+
+ /**
+@@ -60,10 +62,11 @@ public abstract class MetadataStoreBase<T> {
+ * @see MetadataStore#getMetadata(Object, String)
+ */
+ @NotNull
+- public synchronized List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) {
++ public List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
+ String key = disambiguate(subject, metadataKey);
+- if (metadataMap.containsKey(key)) {
+- Collection<MetadataValue> values = metadataMap.get(key).values();
++ Map<Plugin, MetadataValue> entry = metadataMap.get(key);
++ if (entry != null) {
++ Collection<MetadataValue> values = entry.values();
+ return Collections.unmodifiableList(new ArrayList<MetadataValue>(values));
+ } else {
+ return Collections.emptyList();
+@@ -78,7 +81,7 @@ public abstract class MetadataStoreBase<T> {
+ * @param metadataKey the unique metadata key being queried.
+ * @return the existence of the metadataKey within subject.
+ */
+- public synchronized boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) {
++ public boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
+ String key = disambiguate(subject, metadataKey);
+ return metadataMap.containsKey(key);
+ }
+@@ -94,17 +97,18 @@ public abstract class MetadataStoreBase<T> {
+ * @see MetadataStore#removeMetadata(Object, String,
+ * org.bukkit.plugin.Plugin)
+ */
+- public synchronized void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) {
++ public void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) { // Paper
+ Validate.notNull(owningPlugin, "Plugin cannot be null");
+ String key = disambiguate(subject, metadataKey);
+ Map<Plugin, MetadataValue> entry = metadataMap.get(key);
+ if (entry == null) {
+ return;
+ }
+-
+- entry.remove(owningPlugin);
+- if (entry.isEmpty()) {
+- metadataMap.remove(key);
++ synchronized (entry) {
++ entry.remove(owningPlugin);
++ if (entry.isEmpty()) {
++ metadataMap.remove(key);
++ }
+ }
+ }
+
+@@ -117,7 +121,7 @@ public abstract class MetadataStoreBase<T> {
+ * @throws IllegalArgumentException If plugin is null
+ * @see MetadataStore#invalidateAll(org.bukkit.plugin.Plugin)
+ */
+- public synchronized void invalidateAll(@NotNull Plugin owningPlugin) {
++ public void invalidateAll(@NotNull Plugin owningPlugin) { // Paper
+ Validate.notNull(owningPlugin, "Plugin cannot be null");
+ for (Map<Plugin, MetadataValue> values : metadataMap.values()) {
+ if (values.containsKey(owningPlugin)) {
diff --git a/Spigot-API-Patches-Unmapped/0031-Add-MetadataStoreBase.removeAll-Plugin.patch b/Spigot-API-Patches-Unmapped/0031-Add-MetadataStoreBase.removeAll-Plugin.patch
new file mode 100644
index 0000000000..d15d19b316
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0031-Add-MetadataStoreBase.removeAll-Plugin.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 16 Jul 2013 21:26:50 -0400
+Subject: [PATCH] Add MetadataStoreBase.removeAll(Plugin)
+
+So that on reload, metadata will be cleared
+
+diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+index d363d517c05b3335101d829ce4ec22d049059c24..abbe545af572687a0399c2387434863cd2b70f68 100644
+--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
++++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+@@ -4,6 +4,7 @@ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.HashMap;
++import java.util.Iterator; // Paper
+ import java.util.List;
+ import java.util.Map;
+ import java.util.WeakHashMap;
+@@ -130,6 +131,26 @@ public abstract class MetadataStoreBase<T> {
+ }
+ }
+
++ /**
++ * Removes all metadata in the metadata store that originates from the
++ * given plugin.
++ *
++ * @param owningPlugin the plugin requesting the invalidation.
++ * @throws IllegalArgumentException If plugin is null
++ */
++ public void removeAll(@NotNull Plugin owningPlugin) {
++ Validate.notNull(owningPlugin, "Plugin cannot be null");
++ for (Iterator<Map<Plugin, MetadataValue>> iterator = metadataMap.values().iterator(); iterator.hasNext(); ) {
++ Map<Plugin, MetadataValue> values = iterator.next();
++ if (values.containsKey(owningPlugin)) {
++ values.remove(owningPlugin);
++ }
++ if (values.isEmpty()) {
++ iterator.remove();
++ }
++ }
++ }
++
+ /**
+ * Creates a unique name for the object receiving metadata by combining
+ * unique data from the subject with a metadataKey.
diff --git a/Spigot-API-Patches-Unmapped/0032-Add-PlayerUseUnknownEntityEvent.patch b/Spigot-API-Patches-Unmapped/0032-Add-PlayerUseUnknownEntityEvent.patch
new file mode 100644
index 0000000000..8fa59475c2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0032-Add-PlayerUseUnknownEntityEvent.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jedediah Smith <[email protected]>
+Date: Sat, 2 Apr 2016 05:08:36 -0400
+Subject: [PATCH] Add PlayerUseUnknownEntityEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..09cfdf48ead8f03f3497646537292174241b0868
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
+@@ -0,0 +1,46 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.EquipmentSlot;
++import org.jetbrains.annotations.NotNull;
++
++public class PlayerUseUnknownEntityEvent extends PlayerEvent {
++
++ private static final HandlerList handlers = new HandlerList();
++ private final int entityId;
++ private final boolean attack;
++ @NotNull private final EquipmentSlot hand;
++
++ public PlayerUseUnknownEntityEvent(@NotNull Player who, int entityId, boolean attack, @NotNull EquipmentSlot hand) {
++ super(who);
++ this.entityId = entityId;
++ this.attack = attack;
++ this.hand = hand;
++ }
++
++ public int getEntityId() {
++ return this.entityId;
++ }
++
++ public boolean isAttack() {
++ return this.attack;
++ }
++
++ @NotNull
++ public EquipmentSlot getHand() {
++ return this.hand;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0033-Add-handshake-event-to-allow-plugins-to-handle-clien.patch b/Spigot-API-Patches-Unmapped/0033-Add-handshake-event-to-allow-plugins-to-handle-clien.patch
new file mode 100644
index 0000000000..eb1703e485
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0033-Add-handshake-event-to-allow-plugins-to-handle-clien.patch
@@ -0,0 +1,290 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Wed, 13 Apr 2016 20:20:18 -0700
+Subject: [PATCH] Add handshake event to allow plugins to handle client
+ handshaking logic themselves
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a077962fa786a3291849abfa823c7f0ec4664fce
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
+@@ -0,0 +1,277 @@
++package com.destroystokyo.paper.event.player;
++
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.format.NamedTextColor;
++import org.apache.commons.lang.Validate;
++import org.bukkit.Bukkit;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.util.UUID;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * This event is fired during a player handshake.
++ *
++ * <p>If there are no listeners listening to this event, the logic default
++ * to your server platform will be ran.</p>
++ *
++ * <p>WARNING: TAMPERING WITH THIS EVENT CAN BE DANGEROUS</p>
++ */
++public class PlayerHandshakeEvent extends Event implements Cancellable {
++
++ private static final HandlerList HANDLERS = new HandlerList();
++ @NotNull private final String originalHandshake;
++ @NotNull private final String originalSocketAddressHostname;
++ private boolean cancelled;
++ @Nullable private String serverHostname;
++ @Nullable private String socketAddressHostname;
++ @Nullable private UUID uniqueId;
++ @Nullable private String propertiesJson;
++ private boolean failed;
++ private Component failMessage = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.YELLOW);
++
++ /**
++ * Creates a new {@link PlayerHandshakeEvent}.
++ *
++ * @param originalHandshake the original handshake string
++ * @param cancelled if this event is enabled
++ *
++ * @deprecated in favour of {@link PlayerHandshakeEvent(String, String, boolean)}
++ */
++ @Deprecated
++ public PlayerHandshakeEvent(@NotNull String originalHandshake, boolean cancelled) {
++ this(originalHandshake, "127.0.0.1", cancelled);
++ }
++
++ /**
++ * Creates a new {@link PlayerHandshakeEvent}.
++ *
++ * @param originalHandshake the original handshake string
++ * @param originalSocketAddressHostname the original socket address hostname
++ * @param cancelled if this event is enabled
++ */
++ public PlayerHandshakeEvent(@NotNull String originalHandshake, @NotNull String originalSocketAddressHostname, boolean cancelled) {
++ super(true);
++ this.originalHandshake = originalHandshake;
++ this.originalSocketAddressHostname = originalSocketAddressHostname;
++ this.cancelled = cancelled;
++ }
++
++ /**
++ * Determines if this event is cancelled.
++ *
++ * <p>When this event is cancelled, custom handshake logic will not
++ * be processed.</p>
++ *
++ * @return {@code true} if this event is cancelled, {@code false} otherwise
++ */
++ @Override
++ public boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ /**
++ * Sets if this event is cancelled.
++ *
++ * <p>When this event is cancelled, custom handshake logic will not
++ * be processed.</p>
++ *
++ * @param cancelled {@code true} if this event is cancelled, {@code false} otherwise
++ */
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ /**
++ * Gets the original handshake string.
++ *
++ * @return the original handshake string
++ */
++ @NotNull
++ public String getOriginalHandshake() {
++ return this.originalHandshake;
++ }
++
++ /**
++ * Gets the original socket address hostname.
++ *
++ * <p>This does not include the port.</p>
++ * <p>In cases where this event is manually fired and the plugin wasn't updated yet, the default is {@code "127.0.0.1"}.</p>
++ *
++ * @return the original socket address hostname
++ */
++ @NotNull
++ public String getOriginalSocketAddressHostname() {
++ return this.originalSocketAddressHostname;
++ }
++
++ /**
++ * Gets the server hostname string.
++ *
++ * <p>This should not include the port.</p>
++ *
++ * @return the server hostname string
++ */
++ @Nullable
++ public String getServerHostname() {
++ return this.serverHostname;
++ }
++
++ /**
++ * Sets the server hostname string.
++ *
++ * <p>This should not include the port.</p>
++ *
++ * @param serverHostname the server hostname string
++ */
++ public void setServerHostname(@NotNull String serverHostname) {
++ this.serverHostname = serverHostname;
++ }
++
++ /**
++ * Gets the socket address hostname string.
++ *
++ * <p>This should not include the port.</p>
++ *
++ * @return the socket address hostname string
++ */
++ @Nullable
++ public String getSocketAddressHostname() {
++ return this.socketAddressHostname;
++ }
++
++ /**
++ * Sets the socket address hostname string.
++ *
++ * <p>This should not include the port.</p>
++ *
++ * @param socketAddressHostname the socket address hostname string
++ */
++ public void setSocketAddressHostname(@NotNull String socketAddressHostname) {
++ this.socketAddressHostname = socketAddressHostname;
++ }
++
++ /**
++ * Gets the unique id.
++ *
++ * @return the unique id
++ */
++ @Nullable
++ public UUID getUniqueId() {
++ return this.uniqueId;
++ }
++
++ /**
++ * Sets the unique id.
++ *
++ * @param uniqueId the unique id
++ */
++ public void setUniqueId(@NotNull UUID uniqueId) {
++ this.uniqueId = uniqueId;
++ }
++
++ /**
++ * Gets the profile properties.
++ *
++ * <p>This should be a valid JSON string.</p>
++ *
++ * @return the profile properties, as JSON
++ */
++ @Nullable
++ public String getPropertiesJson() {
++ return this.propertiesJson;
++ }
++
++ /**
++ * Determines if authentication failed.
++ *
++ * <p>When {@code true}, the client connecting will be disconnected
++ * with the {@link #getFailMessage() fail message}.</p>
++ *
++ * @return {@code true} if authentication failed, {@code false} otherwise
++ */
++ public boolean isFailed() {
++ return this.failed;
++ }
++
++ /**
++ * Sets if authentication failed and the client should be disconnected.
++ *
++ * <p>When {@code true}, the client connecting will be disconnected
++ * with the {@link #getFailMessage() fail message}.</p>
++ *
++ * @param failed {@code true} if authentication failed, {@code false} otherwise
++ */
++ public void setFailed(boolean failed) {
++ this.failed = failed;
++ }
++
++ /**
++ * Sets the profile properties.
++ *
++ * <p>This should be a valid JSON string.</p>
++ *
++ * @param propertiesJson the profile properties, as JSON
++ */
++ public void setPropertiesJson(@NotNull String propertiesJson) {
++ this.propertiesJson = propertiesJson;
++ }
++
++ /**
++ * Gets the message to display to the client when authentication fails.
++ *
++ * @return the message to display to the client
++ */
++ @NotNull
++ public Component failMessage() {
++ return this.failMessage;
++ }
++
++ /**
++ * Sets the message to display to the client when authentication fails.
++ *
++ * @param failMessage the message to display to the client
++ */
++ public void failMessage(@NotNull Component failMessage) {
++ this.failMessage = failMessage;
++ }
++
++ /**
++ * Gets the message to display to the client when authentication fails.
++ *
++ * @return the message to display to the client
++ * @deprecated use {@link #failMessage()}
++ */
++ @NotNull
++ @Deprecated
++ public String getFailMessage() {
++ return Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.failMessage());
++ }
++
++ /**
++ * Sets the message to display to the client when authentication fails.
++ *
++ * @param failMessage the message to display to the client
++ * @deprecated use {@link #failMessage(Component)}
++ */
++ @Deprecated
++ public void setFailMessage(@NotNull String failMessage) {
++ Validate.notEmpty(failMessage, "fail message cannot be null or empty");
++ this.failMessage(Bukkit.getUnsafe().legacyComponentSerializer().deserialize(failMessage));
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLERS;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLERS;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0034-Arrow-pickup-rule-API.patch b/Spigot-API-Patches-Unmapped/0034-Arrow-pickup-rule-API.patch
new file mode 100644
index 0000000000..fb3753f0e1
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0034-Arrow-pickup-rule-API.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jedediah Smith <[email protected]>
+Date: Fri, 4 Mar 2016 03:13:18 -0500
+Subject: [PATCH] Arrow pickup rule API
+
+
+diff --git a/src/main/java/org/bukkit/entity/AbstractArrow.java b/src/main/java/org/bukkit/entity/AbstractArrow.java
+index 5b50a4e10e8ace8cc53ad3c8d7c3185f88d5c8db..e8e56e89e32d84af0639fe2e9b0eeabd747b6007 100644
+--- a/src/main/java/org/bukkit/entity/AbstractArrow.java
++++ b/src/main/java/org/bukkit/entity/AbstractArrow.java
+@@ -141,4 +141,38 @@ public interface AbstractArrow extends Projectile {
+ */
+ CREATIVE_ONLY
+ }
++
++ // Paper start
++ /**
++ * Gets the {@link PickupRule} for this arrow.
++ *
++ * <p>This is generally {@link PickupRule#ALLOWED} only if the arrow was
++ * <b>not</b> fired from a bow with the infinity enchantment.</p>
++ *
++ * @return The pickup rule
++ * @deprecated Use {@link Arrow#getPickupStatus()} as an upstream compatible replacement for this function
++ */
++ @Deprecated
++ default PickupRule getPickupRule() {
++ return PickupRule.valueOf(this.getPickupStatus().name());
++ }
++
++ /**
++ * Set the rule for which players can pickup this arrow as an item.
++ *
++ * @param rule The pickup rule
++ * @deprecated Use {@link Arrow#setPickupStatus(PickupStatus)} with {@link PickupStatus} as an upstream compatible replacement for this function
++ */
++ @Deprecated
++ default void setPickupRule(PickupRule rule) {
++ this.setPickupStatus(PickupStatus.valueOf(rule.name()));
++ }
++
++ @Deprecated
++ enum PickupRule {
++ DISALLOWED,
++ ALLOWED,
++ CREATIVE_ONLY;
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0035-EntityRegainHealthEvent-isFastRegen-API.patch b/Spigot-API-Patches-Unmapped/0035-EntityRegainHealthEvent-isFastRegen-API.patch
new file mode 100644
index 0000000000..f06ee13d15
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0035-EntityRegainHealthEvent-isFastRegen-API.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Fri, 22 Apr 2016 01:43:11 -0500
+Subject: [PATCH] EntityRegainHealthEvent isFastRegen API
+
+
+diff --git a/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java b/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java
+index 8feb6698f9630f099be99e52d1149cd6bc615197..d51d2ec1d04d9ea8a25a70d0d856f2355ebfcb4a 100644
+--- a/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java
++++ b/src/main/java/org/bukkit/event/entity/EntityRegainHealthEvent.java
+@@ -13,12 +13,31 @@ public class EntityRegainHealthEvent extends EntityEvent implements Cancellable
+ private boolean cancelled;
+ private double amount;
+ private final RegainReason regainReason;
++ private final boolean isFastRegen; // Paper
+
+ public EntityRegainHealthEvent(@NotNull final Entity entity, final double amount, @NotNull final RegainReason regainReason) {
++ // Paper start - Forward
++ this(entity, amount, regainReason, false);
++ }
++
++ public EntityRegainHealthEvent(@NotNull final Entity entity, final double amount, @NotNull final RegainReason regainReason, boolean isFastRegen) {
++ // Paper end
+ super(entity);
+ this.amount = amount;
+ this.regainReason = regainReason;
++ this.isFastRegen = isFastRegen; // Paper
++ }
++
++ // Paper start - Add getter for isFastRegen
++ /**
++ * Is this event a result of the fast regeneration mechanic
++ *
++ * @return Whether the event is the result of a fast regeneration mechanic
++ */
++ public boolean isFastRegen() {
++ return isFastRegen;
+ }
++ // Paper end
+
+ /**
+ * Gets the amount of regained health
diff --git a/Spigot-API-Patches-Unmapped/0036-LootTable-API.patch b/Spigot-API-Patches-Unmapped/0036-LootTable-API.patch
new file mode 100644
index 0000000000..8c3b9e1807
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0036-LootTable-API.patch
@@ -0,0 +1,402 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 1 May 2016 15:19:49 -0400
+Subject: [PATCH] LootTable API
+
+Provides API to control what Loot Table an object uses.
+
+Also provides an Event to control if a lootable inventory should
+auto replenish for a player.
+
+Provides methods to determine players looted state for an object
+
+diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..92d7b853a2ccaae5afa8ac141bead840942944ef
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
+@@ -0,0 +1,17 @@
++package com.destroystokyo.paper.loottable;
++
++import org.bukkit.block.Block;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents an Inventory that can generate loot, such as Chests inside of Fortresses and Mineshafts
++ */
++public interface LootableBlockInventory extends LootableInventory {
++
++ /**
++ * Gets the block that is lootable
++ * @return The Block
++ */
++ @NotNull
++ Block getBlock();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b387894fe8001edb41ad2ad2b70ebabe065b682e
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
+@@ -0,0 +1,17 @@
++package com.destroystokyo.paper.loottable;
++
++import org.bukkit.entity.Entity;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents an Inventory that can generate loot, such as Minecarts inside of Mineshafts
++ */
++public interface LootableEntityInventory extends LootableInventory {
++
++ /**
++ * Gets the entity that is lootable
++ * @return The Entity
++ */
++ @NotNull
++ Entity getEntity();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..97815eeb231cf0706b34fa47a4f7d1bb786305b4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
+@@ -0,0 +1,116 @@
++package com.destroystokyo.paper.loottable;
++
++import org.bukkit.entity.Player;
++import org.bukkit.loot.Lootable;
++
++import java.util.UUID;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Represents an Inventory that contains a Loot Table associated to it that will
++ * automatically fill on first open.
++ *
++ * A new feature and API is provided to support automatically refreshing the contents
++ * of the inventory based on that Loot Table after a configurable amount of time has passed.
++ *
++ * The behavior of how the Inventory is filled based on the loot table may vary based
++ * on Minecraft versions and the Loot Table feature.
++ */
++public interface LootableInventory extends Lootable {
++
++ /**
++ * Server owners have to enable whether or not an object in a world should refill
++ *
++ * @return If the world this inventory is currently in has Replenishable Lootables enabled
++ */
++ boolean isRefillEnabled();
++
++ /**
++ * Whether or not this object has ever been filled
++ * @return Has ever been filled
++ */
++ boolean hasBeenFilled();
++
++ /**
++ * Has this player ever looted this block
++ * @param player The player to check
++ * @return Whether or not this player has looted this block
++ */
++ default boolean hasPlayerLooted(@NotNull Player player) {
++ return hasPlayerLooted(player.getUniqueId());
++ }
++
++ /**
++ * Has this player ever looted this block
++ * @param player The player to check
++ * @return Whether or not this player has looted this block
++ */
++ boolean hasPlayerLooted(@NotNull UUID player);
++
++ /**
++ * Gets the timestamp, in milliseconds, of when the player last looted this object
++ *
++ * @param player The player to check
++ * @return Timestamp last looted, or null if player has not looted this object
++ */
++ @Nullable
++ default Long getLastLooted(@NotNull Player player) {
++ return getLastLooted(player.getUniqueId());
++ }
++
++ /**
++ * Gets the timestamp, in milliseconds, of when the player last looted this object
++ *
++ * @param player The player to check
++ * @return Timestamp last looted, or null if player has not looted this object
++ */
++ @Nullable
++ Long getLastLooted(@NotNull UUID player);
++
++ /**
++ * Change the state of whether or not a player has looted this block
++ * @param player The player to change state for
++ * @param looted true to add player to looted list, false to remove
++ * @return The previous state of whether the player had looted this or not
++ */
++ default boolean setHasPlayerLooted(@NotNull Player player, boolean looted) {
++ return setHasPlayerLooted(player.getUniqueId(), looted);
++ }
++
++ /**
++ * Change the state of whether or not a player has looted this block
++ * @param player The player to change state for
++ * @param looted true to add player to looted list, false to remove
++ * @return The previous state of whether the player had looted this or not
++ */
++ boolean setHasPlayerLooted(@NotNull UUID player, boolean looted);
++
++ /**
++ * Returns Whether or not this object has been filled and now has a pending refill
++ * @return Has pending refill
++ */
++ boolean hasPendingRefill();
++
++ /**
++ * Gets the timestamp in milliseconds that the Lootable object was last refilled
++ *
++ * @return -1 if it was never refilled, or timestamp in milliseconds
++ */
++ long getLastFilled();
++
++ /**
++ * Gets the timestamp in milliseconds that the Lootable object will refill
++ *
++ * @return -1 if it is not scheduled for refill, or timestamp in milliseconds
++ */
++ long getNextRefill();
++
++ /**
++ * Sets the timestamp in milliseconds of the next refill for this object
++ *
++ * @param refillAt timestamp in milliseconds. -1 to clear next refill
++ * @return The previous scheduled time to refill, or -1 if was not scheduled
++ */
++ long setNextRefill(long refillAt);
++}
+diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fd184f13f5e8ee5cf829fff4f44696e1f760430b
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
+@@ -0,0 +1,45 @@
++package com.destroystokyo.paper.loottable;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++public class LootableInventoryReplenishEvent extends PlayerEvent implements Cancellable {
++ @NotNull private final LootableInventory inventory;
++
++ public LootableInventoryReplenishEvent(@NotNull Player player, @NotNull LootableInventory inventory) {
++ super(player);
++ this.inventory = inventory;
++ }
++
++ @NotNull
++ public LootableInventory getInventory() {
++ return inventory;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
+diff --git a/src/main/java/org/bukkit/block/Chest.java b/src/main/java/org/bukkit/block/Chest.java
+index b451191312e4fb19f2131c2d0a0c0337953f6c7c..db6affbc78106b2d93b41953b624a0bca0ca1d72 100644
+--- a/src/main/java/org/bukkit/block/Chest.java
++++ b/src/main/java/org/bukkit/block/Chest.java
+@@ -1,5 +1,7 @@
+ package org.bukkit.block;
+
++import com.destroystokyo.paper.loottable.LootableBlockInventory; // Paper
++import org.bukkit.Nameable; // Paper
+ import org.bukkit.inventory.Inventory;
+ import org.bukkit.loot.Lootable;
+ import org.jetbrains.annotations.NotNull;
+@@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
+ /**
+ * Represents a captured state of a chest.
+ */
+-public interface Chest extends Container, Lootable, Lidded {
++public interface Chest extends Container, LootableBlockInventory, Lidded { // Paper
+
+ /**
+ * Gets the inventory of the chest block represented by this block state.
+diff --git a/src/main/java/org/bukkit/block/Dispenser.java b/src/main/java/org/bukkit/block/Dispenser.java
+index 74cd194c9a98245dc52e7e352d7d6c046e1e5cf3..07af1a3f011d4b96275f919d302ac367198e923e 100644
+--- a/src/main/java/org/bukkit/block/Dispenser.java
++++ b/src/main/java/org/bukkit/block/Dispenser.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.block;
+
++import com.destroystokyo.paper.loottable.LootableBlockInventory;
+ import org.bukkit.Nameable;
+ import org.bukkit.loot.Lootable;
+ import org.bukkit.projectiles.BlockProjectileSource;
+@@ -8,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a captured state of a dispenser.
+ */
+-public interface Dispenser extends Container, Nameable, Lootable {
++public interface Dispenser extends Container, Nameable, LootableBlockInventory { // Paper
+
+ /**
+ * Gets the BlockProjectileSource object for the dispenser.
+diff --git a/src/main/java/org/bukkit/block/Dropper.java b/src/main/java/org/bukkit/block/Dropper.java
+index 424392fb5ed4628199b0e73689522aa3c90740cb..c76202321e29ad67597ca3017eb8d9baf6787383 100644
+--- a/src/main/java/org/bukkit/block/Dropper.java
++++ b/src/main/java/org/bukkit/block/Dropper.java
+@@ -1,11 +1,12 @@
+ package org.bukkit.block;
+
++import com.destroystokyo.paper.loottable.LootableBlockInventory;
+ import org.bukkit.loot.Lootable;
+
+ /**
+ * Represents a captured state of a dropper.
+ */
+-public interface Dropper extends Container, Lootable {
++public interface Dropper extends Container, LootableBlockInventory { // Paper
+
+ /**
+ * Tries to drop a randomly selected item from the dropper's inventory,
+diff --git a/src/main/java/org/bukkit/block/Hopper.java b/src/main/java/org/bukkit/block/Hopper.java
+index 58e493099810fb8d4705ecd49b4a5e1e1949b87b..7ade312f180b7e30871d3a3240c76325cc369c26 100644
+--- a/src/main/java/org/bukkit/block/Hopper.java
++++ b/src/main/java/org/bukkit/block/Hopper.java
+@@ -1,8 +1,9 @@
+ package org.bukkit.block;
+
++import com.destroystokyo.paper.loottable.LootableBlockInventory;
+ import org.bukkit.loot.Lootable;
+
+ /**
+ * Represents a captured state of a hopper.
+ */
+-public interface Hopper extends Container, Lootable { }
++public interface Hopper extends Container, LootableBlockInventory { } // Paper
+diff --git a/src/main/java/org/bukkit/block/ShulkerBox.java b/src/main/java/org/bukkit/block/ShulkerBox.java
+index 2ab26605814c0745ddb0836b2b3618a5b9251ab7..172f383fea619127324fec2b043639fd0683f135 100644
+--- a/src/main/java/org/bukkit/block/ShulkerBox.java
++++ b/src/main/java/org/bukkit/block/ShulkerBox.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.block;
+
++import com.destroystokyo.paper.loottable.LootableBlockInventory;
+ import org.bukkit.DyeColor;
+ import org.bukkit.loot.Lootable;
+ import org.jetbrains.annotations.NotNull;
+@@ -7,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
+ /**
+ * Represents a captured state of a ShulkerBox.
+ */
+-public interface ShulkerBox extends Container, Lootable, Lidded {
++public interface ShulkerBox extends Container, LootableBlockInventory, Lidded { // Paper
+
+ /**
+ * Get the {@link DyeColor} corresponding to this ShulkerBox
+diff --git a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
+index 937b99f8734d71b2ad33af142afbc251b81d9745..db69687a7ad4b18d17ab1677cae5d8dd4dcd3678 100644
+--- a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
++++ b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.entity.minecart;
+
++import com.destroystokyo.paper.loottable.LootableEntityInventory;
+ import org.bukkit.entity.Minecart;
+ import org.bukkit.inventory.InventoryHolder;
+ import org.bukkit.loot.Lootable;
+@@ -7,7 +8,7 @@ import org.bukkit.loot.Lootable;
+ /**
+ * Represents a Minecart with a Hopper inside it
+ */
+-public interface HopperMinecart extends Minecart, InventoryHolder, Lootable {
++public interface HopperMinecart extends Minecart, InventoryHolder, LootableEntityInventory {
+
+ /**
+ * Checks whether or not this Minecart will pick up
+diff --git a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
+index 9ea403e6fd8e960d017660e0aec118abeda2c42b..238d118f7788b13cd86b7e9ea3a0fc38e2e09715 100644
+--- a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
++++ b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.entity.minecart;
+
++import com.destroystokyo.paper.loottable.LootableEntityInventory;
+ import org.bukkit.entity.Minecart;
+ import org.bukkit.inventory.InventoryHolder;
+ import org.bukkit.loot.Lootable;
+@@ -9,5 +10,5 @@ import org.bukkit.loot.Lootable;
+ * minecarts} have their own inventory that can be accessed using methods
+ * from the {@link InventoryHolder} interface.
+ */
+-public interface StorageMinecart extends Minecart, InventoryHolder, Lootable {
++public interface StorageMinecart extends Minecart, InventoryHolder, LootableEntityInventory { // Paper
+ }
+diff --git a/src/main/java/org/bukkit/loot/Lootable.java b/src/main/java/org/bukkit/loot/Lootable.java
+index 24a3d989db3bc67e7afe8459a3d4bb132f448ea7..901db852498e0658c79a57582508dab29bf0a798 100644
+--- a/src/main/java/org/bukkit/loot/Lootable.java
++++ b/src/main/java/org/bukkit/loot/Lootable.java
+@@ -36,6 +36,34 @@ public interface Lootable {
+ @Nullable
+ LootTable getLootTable();
+
++ // Paper start
++ /**
++ * Set the loot table and seed for a container or entity at the same time.
++ *
++ * @param table the Loot Table this {@link org.bukkit.block.Container} or {@link org.bukkit.entity.Mob} will have.
++ * @param seed the seed to used to generate loot. Default is 0.
++ */
++ default void setLootTable(@Nullable LootTable table, long seed) {
++ setLootTable(table);
++ setSeed(seed);
++ }
++
++ /**
++ * Returns whether or not this object has a Loot Table
++ * @return Has a loot table
++ */
++ default boolean hasLootTable() {
++ return getLootTable() != null;
++ }
++
++ /**
++ * Clears the associated Loot Table to this object
++ */
++ default void clearLootTable() {
++ setLootTable(null);
++ }
++ // Paper end
++
+ /**
+ * Set the seed used when this Loot Table generates loot.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0037-Add-EntityZapEvent.patch b/Spigot-API-Patches-Unmapped/0037-Add-EntityZapEvent.patch
new file mode 100644
index 0000000000..41d603f073
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0037-Add-EntityZapEvent.patch
@@ -0,0 +1,123 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: AlphaBlend <[email protected]>
+Date: Sun, 16 Oct 2016 23:19:34 -0700
+Subject: [PATCH] Add EntityZapEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3b725a489008d333630af166d2be5fc48168a6b9
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
+@@ -0,0 +1,65 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.apache.commons.lang.Validate;
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.LightningStrike;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.event.entity.EntityTransformEvent;
++
++import java.util.Collections;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when lightning strikes an entity
++ */
++public class EntityZapEvent extends EntityTransformEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ @NotNull private final LightningStrike bolt;
++
++ public EntityZapEvent(@NotNull final Entity entity, @NotNull final LightningStrike bolt, @NotNull final Entity replacementEntity) {
++ super(entity, Collections.singletonList(replacementEntity), TransformReason.LIGHTNING);
++ Validate.notNull(bolt);
++ Validate.notNull(replacementEntity);
++ this.bolt = bolt;
++ }
++
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ /**
++ * Gets the lightning bolt that is striking the entity.
++ * @return The lightning bolt responsible for this event
++ */
++ @NotNull
++ public LightningStrike getBolt() {
++ return bolt;
++ }
++
++ /**
++ * Gets the entity that will replace the struck entity.
++ * @return The entity that will replace the struck entity
++ */
++ @NotNull
++ public Entity getReplacementEntity() {
++ return getTransformedEntity();
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/event/entity/PigZapEvent.java b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
+index 0e0ed93b568fd2c0d8f6e359c31dc29cb0fa71c2..d3949edfc736b3d67a627ef378748b374769e183 100644
+--- a/src/main/java/org/bukkit/event/entity/PigZapEvent.java
++++ b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
+@@ -2,6 +2,7 @@ package org.bukkit.event.entity;
+
+ import java.util.Collections;
+ import org.bukkit.entity.Entity;
++import com.destroystokyo.paper.event.entity.EntityZapEvent;
+ import org.bukkit.entity.LightningStrike;
+ import org.bukkit.entity.Pig;
+ import org.bukkit.entity.PigZombie;
+@@ -12,14 +13,14 @@ import org.jetbrains.annotations.NotNull;
+ /**
+ * Stores data for pigs being zapped
+ */
+-public class PigZapEvent extends EntityTransformEvent implements Cancellable {
+- private static final HandlerList handlers = new HandlerList();
++public class PigZapEvent extends EntityZapEvent implements Cancellable {
++ //private static final HandlerList handlers = new HandlerList();
+ private boolean canceled;
+ private final PigZombie pigzombie;
+ private final LightningStrike bolt;
+
+ public PigZapEvent(@NotNull final Pig pig, @NotNull final LightningStrike bolt, @NotNull final PigZombie pigzombie) {
+- super(pig, Collections.singletonList((Entity) pigzombie), TransformReason.LIGHTNING);
++ super(pig, bolt, pigzombie);
+ this.bolt = bolt;
+ this.pigzombie = pigzombie;
+ }
+@@ -63,6 +64,8 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
+ return pigzombie;
+ }
+
++ // Paper start
++ /*
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+@@ -73,4 +76,6 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
++ */
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0038-Misc-Utils.patch b/Spigot-API-Patches-Unmapped/0038-Misc-Utils.patch
new file mode 100644
index 0000000000..fac858b16c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0038-Misc-Utils.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: vemacs <[email protected]>
+Date: Wed, 23 Nov 2016 12:53:43 -0500
+Subject: [PATCH] Misc Utils
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java b/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5bb677ce585b856b3d3e589e29786a29619c56a7
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java
+@@ -0,0 +1,34 @@
++package com.destroystokyo.paper.utils;
++
++import java.util.concurrent.ConcurrentLinkedQueue;
++import java.util.concurrent.atomic.LongAdder;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class CachedSizeConcurrentLinkedQueue<E> extends ConcurrentLinkedQueue<E> {
++ private final LongAdder cachedSize = new LongAdder();
++
++ @Override
++ public boolean add(@NotNull E e) {
++ boolean result = super.add(e);
++ if (result) {
++ cachedSize.increment();
++ }
++ return result;
++ }
++
++ @Nullable
++ @Override
++ public E poll() {
++ E result = super.poll();
++ if (result != null) {
++ cachedSize.decrement();
++ }
++ return result;
++ }
++
++ @Override
++ public int size() {
++ return cachedSize.intValue();
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0039-Allow-Reloading-of-Command-Aliases.patch b/Spigot-API-Patches-Unmapped/0039-Allow-Reloading-of-Command-Aliases.patch
new file mode 100644
index 0000000000..69796b5859
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0039-Allow-Reloading-of-Command-Aliases.patch
@@ -0,0 +1,108 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: willies952002 <[email protected]>
+Date: Mon, 28 Nov 2016 10:16:39 -0500
+Subject: [PATCH] Allow Reloading of Command Aliases
+
+Reload the aliases stored in commands.yml
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 945d75525465739dd30610dff985ea0fb0f1c8df..555ef4c187ce0c83cc29af145694ec9c448d452e 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1747,6 +1747,15 @@ public final class Bukkit {
+ public static void reloadPermissions() {
+ server.reloadPermissions();
+ }
++
++ /**
++ * Reload the Command Aliases in commands.yml
++ *
++ * @return Whether the reload was successful
++ */
++ public static boolean reloadCommandAliases() {
++ return server.reloadCommandAliases();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index f69c30ebea9c9e9add0b6c97994be0ce64a80ad3..9d81f25e39d345b797f73855a802b186d77f6d12 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1540,4 +1540,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ // Spigot end
+
+ void reloadPermissions(); // Paper
++
++ boolean reloadCommandAliases(); // Paper
+ }
+diff --git a/src/main/java/org/bukkit/command/CommandMap.java b/src/main/java/org/bukkit/command/CommandMap.java
+index bd2c7a6964722412148fae39e1b4951fc0002b9b..864c263bbd4dd6dd7c37a74b39b1a40a884d0731 100644
+--- a/src/main/java/org/bukkit/command/CommandMap.java
++++ b/src/main/java/org/bukkit/command/CommandMap.java
+@@ -128,4 +128,14 @@ public interface CommandMap {
+ */
+ @Nullable
+ public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String cmdLine, @Nullable Location location) throws IllegalArgumentException;
++
++ // Paper start - Expose Known Commands
++ /**
++ * Return a Map of known commands
++ *
++ * @return known commands
++ */
++ @NotNull
++ public java.util.Map<String, Command> getKnownCommands();
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+index adfc7aae2c0f49bbcdd358e83b04a0cf078a7d52..460fda05a62b12db2edcfb7ea8b2a5dd8e4b110d 100644
+--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
+@@ -297,4 +297,11 @@ public class SimpleCommandMap implements CommandMap {
+ }
+ }
+ }
++
++ // Paper start - Expose Known Commands
++ @NotNull
++ public Map<String, Command> getKnownCommands() {
++ return knownCommands;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+index c62da4131b17e66892678e8b618fb9ba3de93b56..0c7ba0718de2b93d013968ca0fec34ffd423990f 100644
+--- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
++++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+@@ -13,7 +13,7 @@ public class ReloadCommand extends BukkitCommand {
+ public ReloadCommand(@NotNull String name) {
+ super(name);
+ this.description = "Reloads the server configuration and plugins";
+- this.usageMessage = "/reload [permissions]"; // Paper
++ this.usageMessage = "/reload [permissions|commands|confirm]"; // Paper
+ this.setPermission("bukkit.command.reload");
+ this.setAliases(Arrays.asList("rl"));
+ }
+@@ -29,6 +29,13 @@ public class ReloadCommand extends BukkitCommand {
+ Bukkit.getServer().reloadPermissions();
+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Permissions successfully reloaded.");
+ return true;
++ } else if ("commands".equalsIgnoreCase(args[0])) {
++ if (Bukkit.getServer().reloadCommandAliases()) {
++ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Command aliases successfully reloaded.");
++ } else {
++ Command.broadcastCommandMessage(sender, ChatColor.RED + "An error occurred while trying to reload command aliases.");
++ }
++ return true;
+ } else if ("confirm".equalsIgnoreCase(args[0])) {
+ confirmed = true;
+ } else {
+@@ -53,6 +60,6 @@ public class ReloadCommand extends BukkitCommand {
+ @NotNull
+ @Override
+ public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
+- return java.util.Collections.singletonList("permissions"); // Paper
++ return com.google.common.collect.Lists.newArrayList("permissions", "commands"); // Paper
+ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0040-Add-source-to-PlayerExpChangeEvent.patch b/Spigot-API-Patches-Unmapped/0040-Add-source-to-PlayerExpChangeEvent.patch
new file mode 100644
index 0000000000..675b2be839
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0040-Add-source-to-PlayerExpChangeEvent.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: AlphaBlend <[email protected]>
+Date: Thu, 8 Sep 2016 08:47:08 -0700
+Subject: [PATCH] Add source to PlayerExpChangeEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java b/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java
+index c99c9281e98e4b510dddb711b8785bcd56b3b92f..7c340f539c31a431d7d9204a8135e0bfc31863a8 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerExpChangeEvent.java
+@@ -1,21 +1,43 @@
+ package org.bukkit.event.player;
+
++import org.bukkit.entity.Entity; // Paper
+ import org.bukkit.entity.Player;
+ import org.bukkit.event.HandlerList;
+ import org.jetbrains.annotations.NotNull;
+
++import org.jetbrains.annotations.Nullable; // Paper
++
+ /**
+ * Called when a players experience changes naturally
+ */
+ public class PlayerExpChangeEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
++ // Paper start
++ @Nullable
++ private final Entity source;
+ private int exp;
+
+ public PlayerExpChangeEvent(@NotNull final Player player, final int expAmount) {
++ this(player, null, expAmount);
++ }
++
++ public PlayerExpChangeEvent(@NotNull final Player player, @Nullable final Entity sourceEntity, final int expAmount) {
+ super(player);
++ source = sourceEntity;
+ exp = expAmount;
+ }
+
++ /**
++ * Get the source that provided the experience.
++ *
++ * @return The source of the experience
++ */
++ @Nullable
++ public Entity getSource() {
++ return source;
++ }
++ // Paper end
++
+ /**
+ * Get the amount of experience the player will receive
+ *
diff --git a/Spigot-API-Patches-Unmapped/0041-Add-ProjectileCollideEvent.patch b/Spigot-API-Patches-Unmapped/0041-Add-ProjectileCollideEvent.patch
new file mode 100644
index 0000000000..a5c9df18df
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0041-Add-ProjectileCollideEvent.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Techcable <[email protected]>
+Date: Fri, 16 Dec 2016 21:25:39 -0600
+Subject: [PATCH] Add ProjectileCollideEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/ProjectileCollideEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/ProjectileCollideEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..453663893021768ae21d4980ce17ffba55d9e129
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/ProjectileCollideEvent.java
+@@ -0,0 +1,67 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.Projectile;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when an projectile collides with an entity
++ * <p>
++ * This event is called <b>before</b> {@link org.bukkit.event.entity.EntityDamageByEntityEvent}, and cancelling it will allow the projectile to continue flying
++ */
++public class ProjectileCollideEvent extends EntityEvent implements Cancellable {
++ @NotNull private final Entity collidedWith;
++
++ /**
++ * Get the entity the projectile collided with
++ *
++ * @return the entity collided with
++ */
++ @NotNull
++ public Entity getCollidedWith() {
++ return collidedWith;
++ }
++
++ public ProjectileCollideEvent(@NotNull Projectile what, @NotNull Entity collidedWith) {
++ super(what);
++ this.collidedWith = collidedWith;
++ }
++
++ /**
++ * Get the projectile that collided
++ *
++ * @return the projectile that collided
++ */
++ @NotNull
++ public Projectile getEntity() {
++ return (Projectile) super.getEntity();
++ }
++
++ private static final HandlerList handlerList = new HandlerList();
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlerList;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlerList;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0042-Add-String-based-Action-Bar-API.patch b/Spigot-API-Patches-Unmapped/0042-Add-String-based-Action-Bar-API.patch
new file mode 100644
index 0000000000..892786baf2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0042-Add-String-based-Action-Bar-API.patch
@@ -0,0 +1,86 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 20 Dec 2016 15:55:55 -0500
+Subject: [PATCH] Add String based Action Bar API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 38f47517d5391696c52d8848c21dee40033458e6..f5d9899ec0788019ba14f15dc0f290abe4b3cfa8 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -3,6 +3,7 @@ package org.bukkit.entity;
+ import java.net.InetSocketAddress;
+ import java.util.UUID;
+ import com.destroystokyo.paper.Title; // Paper
++import net.kyori.adventure.text.Component;
+ import org.bukkit.DyeColor;
+ import org.bukkit.Effect;
+ import org.bukkit.GameMode;
+@@ -616,6 +617,39 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ public void sendMap(@NotNull MapView map);
+
+ // Paper start
++
++ /**
++ * Sends an Action Bar message to the client.
++ *
++ * Use Section symbols for legacy color codes to send formatting.
++ *
++ * @param message The message to send
++ * @deprecated use {@link #sendActionBar(Component)}
++ */
++ @Deprecated
++ public void sendActionBar(@NotNull String message);
++
++ /**
++ * Sends an Action Bar message to the client.
++ *
++ * Use supplied alternative character to the section symbol to represent legacy color codes.
++ *
++ * @param alternateChar Alternate symbol such as '&'
++ * @param message The message to send
++ * @deprecated use {@link #sendActionBar(Component)}
++ */
++ @Deprecated
++ public void sendActionBar(char alternateChar, @NotNull String message);
++
++ /**
++ * Sends an Action Bar message to the client.
++ *
++ * @param message The components to send
++ * @deprecated use {@link #sendActionBar(Component)}
++ */
++ @Deprecated
++ public void sendActionBar(@NotNull net.md_5.bungee.api.chat.BaseComponent... message);
++
+ /**
+ * Sends the component to the player
+ *
+@@ -643,9 +677,11 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ /**
+ * Sends an array of components as a single message to the specified screen position of this player
+ *
++ * @deprecated This is unlikely the API you want to use. See {@link #sendActionBar(String)} for a more proper Action Bar API. This deprecated API may send unsafe items to the client.
+ * @param position the screen position
+ * @param components the components to send
+ */
++ @Deprecated
+ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(position, components);
+ }
+@@ -1713,6 +1749,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ /**
+ * Sends the component to the specified screen position of this player
+ *
++ * @deprecated This is unlikely the API you want to use. See {@link #sendActionBar(String)} for a more proper Action Bar API. This deprecated API may send unsafe items to the client.
+ * @param position the screen position
+ * @param component the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+@@ -1725,6 +1762,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ /**
+ * Sends an array of components as a single message to the specified screen position of this player
+ *
++ * @deprecated This is unlikely the API you want to use. See {@link #sendActionBar(String)} for a more proper Action Bar API. This deprecated API may send unsafe items to the client.
+ * @param position the screen position
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
diff --git a/Spigot-API-Patches-Unmapped/0043-Add-API-methods-to-control-if-armour-stands-can-move.patch b/Spigot-API-Patches-Unmapped/0043-Add-API-methods-to-control-if-armour-stands-can-move.patch
new file mode 100644
index 0000000000..3f2a539353
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0043-Add-API-methods-to-control-if-armour-stands-can-move.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Wed, 21 Dec 2016 11:47:25 -0600
+Subject: [PATCH] Add API methods to control if armour stands can move
+
+
+diff --git a/src/main/java/org/bukkit/entity/ArmorStand.java b/src/main/java/org/bukkit/entity/ArmorStand.java
+index e94ec567fbda93f4f5783f84b4b5136b309d0eab..fddc063798edc8084ca695578a47485204a7f3cd 100644
+--- a/src/main/java/org/bukkit/entity/ArmorStand.java
++++ b/src/main/java/org/bukkit/entity/ArmorStand.java
+@@ -344,4 +344,21 @@ public interface ArmorStand extends LivingEntity {
+ */
+ ADDING;
+ }
++ // Paper start
++ /**
++ * Tests if this armor stand can move.
++ *
++ * <p>The default value is {@code true}.</p>
++ *
++ * @return {@code true} if this armour stand can move, {@code false} otherwise
++ */
++ boolean canMove();
++
++ /**
++ * Sets if this armor stand can move.
++ *
++ * @param move {@code true} if this armour stand can move, {@code false} otherwise
++ */
++ void setCanMove(boolean move);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0044-IllegalPacketEvent.patch b/Spigot-API-Patches-Unmapped/0044-IllegalPacketEvent.patch
new file mode 100644
index 0000000000..7eb8d61aaa
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0044-IllegalPacketEvent.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 4 Dec 2016 01:19:14 -0500
+Subject: [PATCH] IllegalPacketEvent
+
+Fire an event when an illegal packet is received to let plugins handle it
+
+Lets plugins change the kick message and if it should kick or not.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/IllegalPacketEvent.java b/src/main/java/com/destroystokyo/paper/event/player/IllegalPacketEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8d8e9b16f2a6707d2af7567c7682dfc5db51a737
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/IllegalPacketEvent.java
+@@ -0,0 +1,74 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.Bukkit;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * @deprecated Not used
++ */
++@Deprecated
++public class IllegalPacketEvent extends PlayerEvent {
++ @Nullable private final String type;
++ @Nullable private final String ex;
++ @Nullable private String kickMessage;
++ private boolean shouldKick = true;
++
++ public IllegalPacketEvent(@NotNull Player player, @Nullable String type, @Nullable String kickMessage, @NotNull Exception e) {
++ super(player);
++ this.type = type;
++ this.kickMessage = kickMessage;
++ this.ex = e.getMessage();
++ }
++
++ public boolean isShouldKick() {
++ return shouldKick;
++ }
++
++ public void setShouldKick(boolean shouldKick) {
++ this.shouldKick = shouldKick;
++ }
++
++ @Nullable
++ public String getKickMessage() {
++ return kickMessage;
++ }
++
++ public void setKickMessage(@Nullable String kickMessage) {
++ this.kickMessage = kickMessage;
++ }
++
++ @Nullable
++ public String getType() {
++ return type;
++ }
++
++ @Nullable
++ public String getExceptionMessage() {
++ return ex;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ public static void process(@NotNull Player player, @Nullable String type, @Nullable String kickMessage, @NotNull Exception exception) {
++ IllegalPacketEvent event = new IllegalPacketEvent(player, type, kickMessage, exception);
++ event.callEvent();
++ if (event.shouldKick) {
++ player.kickPlayer(kickMessage);
++ }
++ Bukkit.getLogger().severe(player.getName() + "/" + type + ": " + exception.getMessage());
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0045-Fireworks-API-s.patch b/Spigot-API-Patches-Unmapped/0045-Fireworks-API-s.patch
new file mode 100644
index 0000000000..a0ed753ac6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0045-Fireworks-API-s.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 28 Dec 2016 01:18:55 -0500
+Subject: [PATCH] Fireworks API's
+
+Get the Entity being boosted
+Get the firework launcher
+
+diff --git a/src/main/java/org/bukkit/entity/Firework.java b/src/main/java/org/bukkit/entity/Firework.java
+index 05e86cb9d826cdf14490fa649348d46c51adbfdb..d616d5941b3c7b85e350e845901da798601b9a3c 100644
+--- a/src/main/java/org/bukkit/entity/Firework.java
++++ b/src/main/java/org/bukkit/entity/Firework.java
+@@ -43,4 +43,15 @@ public interface Firework extends Projectile {
+ * @param shotAtAngle the new shotAtAngle
+ */
+ void setShotAtAngle(boolean shotAtAngle);
++
++ // Paper start
++ @org.jetbrains.annotations.Nullable
++ public java.util.UUID getSpawningEntity();
++ /**
++ * If this firework is boosting an entity, return it
++ * @return The entity being boosted
++ */
++ @org.jetbrains.annotations.Nullable
++ public LivingEntity getBoostedEntity();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0046-PlayerTeleportEndGatewayEvent.patch b/Spigot-API-Patches-Unmapped/0046-PlayerTeleportEndGatewayEvent.patch
new file mode 100644
index 0000000000..664edd24cd
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0046-PlayerTeleportEndGatewayEvent.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 31 Dec 2016 20:29:33 -0500
+Subject: [PATCH] PlayerTeleportEndGatewayEvent
+
+Allows you to access the Gateway being used in a teleport event
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerTeleportEndGatewayEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerTeleportEndGatewayEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b64ab6eecd8bc4ca9c109b9d83c82861d6260793
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerTeleportEndGatewayEvent.java
+@@ -0,0 +1,29 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.Location;
++import org.bukkit.block.EndGateway;
++import org.bukkit.entity.Player;
++import org.bukkit.event.player.PlayerTeleportEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a teleport is triggered for an End Gateway
++ */
++public class PlayerTeleportEndGatewayEvent extends PlayerTeleportEvent {
++ @NotNull private final EndGateway gateway;
++
++ public PlayerTeleportEndGatewayEvent(@NotNull Player player, @NotNull Location from, @NotNull Location to, @NotNull EndGateway gateway) {
++ super(player, from, to, PlayerTeleportEvent.TeleportCause.END_GATEWAY);
++ this.gateway = gateway;
++ }
++
++ /**
++ * The gateway triggering the teleport
++ *
++ * @return EndGateway used
++ */
++ @NotNull
++ public EndGateway getGateway() {
++ return gateway;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0047-Provide-E-TE-Chunk-count-stat-methods.patch b/Spigot-API-Patches-Unmapped/0047-Provide-E-TE-Chunk-count-stat-methods.patch
new file mode 100644
index 0000000000..fc838a6f02
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0047-Provide-E-TE-Chunk-count-stat-methods.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 7 Jan 2017 15:23:03 -0500
+Subject: [PATCH] Provide E/TE/Chunk count stat methods
+
+Provides counts without the ineffeciency of using .getEntities().size()
+which creates copy of the collections.
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 6b5dbe4ea711807a1944cfe2aae2ce415d4f2638..789e070f6aee83e4b6426def784e05df98e1bc65 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -40,6 +40,33 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.adventure.audience.ForwardingAudience { // Paper
+
++ // Paper start
++ /**
++ * @return The amount of Entities in this world
++ */
++ int getEntityCount();
++
++ /**
++ * @return The amount of Tile Entities in this world
++ */
++ int getTileEntityCount();
++
++ /**
++ * @return The amount of Tickable Tile Entities in this world
++ */
++ int getTickableTileEntityCount();
++
++ /**
++ * @return The amount of Chunks in this world
++ */
++ int getChunkCount();
++
++ /**
++ * @return The amount of Players in this world
++ */
++ int getPlayerCount();
++ // Paper end
++
+ /**
+ * Gets the {@link Block} at the given coordinates
+ *
diff --git a/Spigot-API-Patches-Unmapped/0048-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch b/Spigot-API-Patches-Unmapped/0048-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
new file mode 100644
index 0000000000..5dadaf640f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0048-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
@@ -0,0 +1,122 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 19 Dec 2017 16:28:32 -0500
+Subject: [PATCH] ExperienceOrbs API for Reason/Source/Triggering player
+
+Adds lots of information about why this orb exists.
+
+Replaces isFromBottle() with logic that persists entity reloads too.
+
+diff --git a/src/main/java/org/bukkit/entity/ExperienceOrb.java b/src/main/java/org/bukkit/entity/ExperienceOrb.java
+index c286edfd827e185a76e44c44faace19a80325d7a..dec70bbfaf73a9d525b2c45682b804c684e1645b 100644
+--- a/src/main/java/org/bukkit/entity/ExperienceOrb.java
++++ b/src/main/java/org/bukkit/entity/ExperienceOrb.java
+@@ -1,5 +1,8 @@
+ package org.bukkit.entity;
+
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable; // Paper
++
+ /**
+ * Represents an Experience Orb.
+ */
+@@ -18,4 +21,99 @@ public interface ExperienceOrb extends Entity {
+ * @param value Amount of experience
+ */
+ public void setExperience(int value);
++
++ // Paper start
++ /**
++ * Check if this orb was spawned from a {@link ThrownExpBottle}
++ *
++ * @return if orb was spawned from a bottle
++ * @deprecated Use getSpawnReason() == EXP_BOTTLE
++ */
++ @Deprecated
++ default boolean isFromBottle() {
++ return getSpawnReason() == SpawnReason.EXP_BOTTLE;
++ }
++
++ /**
++ * Reasons for why this Experience Orb was spawned
++ */
++ enum SpawnReason {
++ /**
++ * Spawned by a player dying
++ */
++ PLAYER_DEATH,
++ /**
++ * Spawned by an entity dying after being damaged by a player
++ */
++ ENTITY_DEATH,
++ /**
++ * Spawned by player using a furnace
++ */
++ FURNACE,
++ /**
++ * Spawned by player breeding animals
++ */
++ BREED,
++ /**
++ * Spawned by player trading with a villager
++ */
++ VILLAGER_TRADE,
++ /**
++ * Spawned by player fishing
++ */
++ FISHING,
++ /**
++ * Spawned by player breaking a block that gives experience points such as Diamond Ore
++ */
++ BLOCK_BREAK,
++ /**
++ * Spawned by Bukkit API
++ */
++ CUSTOM,
++ /**
++ * Spawned by a player throwing an experience points bottle
++ */
++ EXP_BOTTLE,
++ /**
++ * Spawned by a player using a grindstone
++ */
++ GRINDSTONE,
++ /**
++ * We do not know why it was spawned
++ */
++ UNKNOWN
++ }
++
++ /**
++ * If this experience orb was triggered to be spawned by
++ * an entity such as a player, due to events such as killing entity,
++ * breaking blocks, smelting in a furnace, etc, this will return the UUID
++ * of the entity that triggered this orb to drop.
++ *
++ * In the case of an entity being killed, this will be the killers UUID.
++ *
++ * @return UUID of the player that triggered this orb to drop, or null if unknown/no triggering entity
++ */
++ @Nullable java.util.UUID getTriggerEntityId();
++
++ /**
++ * If this experience orb was spawned in relation to another
++ * entity, such as a player or other living entity death, or breeding,
++ * return the source entity UUID.
++ *
++ * In the case of breeding, this will be the new baby entities UUID.
++ * In the case of an entity being killed, this will be the dead entities UUID.
++ *
++ * @return The UUID of the entity that sourced this experience orb
++ */
++ @Nullable java.util.UUID getSourceEntityId();
++
++ /**
++ * Gets the reason that this experience orb was spawned. For any case that we
++ * do not know, such as orbs spawned before this API was added, UNKNOWN is returned.
++ * @return The reason for this orb being spawned.
++ */
++ @NotNull
++ SpawnReason getSpawnReason();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0049-Expose-WorldBorder-isInBounds-Location-check.patch b/Spigot-API-Patches-Unmapped/0049-Expose-WorldBorder-isInBounds-Location-check.patch
new file mode 100644
index 0000000000..f700226be3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0049-Expose-WorldBorder-isInBounds-Location-check.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Sat, 21 Jan 2017 17:03:10 -0600
+Subject: [PATCH] Expose WorldBorder#isInBounds(Location) check
+
+
+diff --git a/src/main/java/org/bukkit/WorldBorder.java b/src/main/java/org/bukkit/WorldBorder.java
+index 7e8f5649ce0eb0090899514b1e228601eb644613..afb7b136b461202026290624836446cff9f9e45d 100644
+--- a/src/main/java/org/bukkit/WorldBorder.java
++++ b/src/main/java/org/bukkit/WorldBorder.java
+@@ -117,4 +117,18 @@ public interface WorldBorder {
+ * @return if this location is inside the border or not
+ */
+ public boolean isInside(@NotNull Location location);
++
++ // Paper start
++ /**
++ * Checks if the location is within the boundaries of this border.
++ *
++ * @param location specific location to check
++ * @return true if the location is within the bounds of this border, false otherwise.
++ * @deprecated use {@link #isInside(Location)} for an upstream compatible replacement
++ */
++ @Deprecated
++ public default boolean isInBounds(@NotNull Location location) {
++ return this.isInside(location);
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0050-Add-configuration-option-to-prevent-player-names-fro.patch b/Spigot-API-Patches-Unmapped/0050-Add-configuration-option-to-prevent-player-names-fro.patch
new file mode 100644
index 0000000000..929cd00666
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0050-Add-configuration-option-to-prevent-player-names-fro.patch
@@ -0,0 +1,60 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Fri, 9 Jun 2017 07:24:24 -0700
+Subject: [PATCH] Add configuration option to prevent player names from being
+ suggested
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 555ef4c187ce0c83cc29af145694ec9c448d452e..2527e896a4409326ea2612723b829696d44fc199 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1756,6 +1756,16 @@ public final class Bukkit {
+ public static boolean reloadCommandAliases() {
+ return server.reloadCommandAliases();
+ }
++
++ /**
++ * Checks if player names should be suggested when a command returns {@code null} as
++ * their tab completion result.
++ *
++ * @return true if player names should be suggested
++ */
++ public static boolean suggestPlayerNamesWhenNullTabCompletions() {
++ return server.suggestPlayerNamesWhenNullTabCompletions();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 9d81f25e39d345b797f73855a802b186d77f6d12..7d94242b2f8ecb537b3140f9d6f706d3e266c456 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1542,4 +1542,14 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ void reloadPermissions(); // Paper
+
+ boolean reloadCommandAliases(); // Paper
++
++ // Paper start - allow preventing player name suggestions by default
++ /**
++ * Checks if player names should be suggested when a command returns {@code null} as
++ * their tab completion result.
++ *
++ * @return true if player names should be suggested
++ */
++ boolean suggestPlayerNamesWhenNullTabCompletions();
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java
+index 03bdc1622791e1206406c87065978688d602e39e..7c80dc54776d0d66f7816b77136f6dbd9b801704 100644
+--- a/src/main/java/org/bukkit/command/Command.java
++++ b/src/main/java/org/bukkit/command/Command.java
+@@ -99,7 +99,7 @@ public abstract class Command {
+ Validate.notNull(args, "Arguments cannot be null");
+ Validate.notNull(alias, "Alias cannot be null");
+
+- if (args.length == 0) {
++ if (args.length == 0 || !sender.getServer().suggestPlayerNamesWhenNullTabCompletions()) { // Paper - allow preventing player name suggestions by default) {
+ return ImmutableList.of();
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0051-Fix-upstream-javadoc-warnings-and-errors.patch b/Spigot-API-Patches-Unmapped/0051-Fix-upstream-javadoc-warnings-and-errors.patch
new file mode 100644
index 0000000000..083596bdd2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0051-Fix-upstream-javadoc-warnings-and-errors.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Sat, 10 Jun 2017 16:59:40 -0500
+Subject: [PATCH] Fix upstream javadoc warnings and errors
+
+Upstream still refuses to use Java 8 with the API so they are likely unaware these are even issues.
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 3f68baef538098d9ce66b91195b6fa17f26f0d78..e71b048e53ee2db4e768eea2ddf19b00a14d2484 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -634,7 +634,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ *
+ * Use supplied alternative character to the section symbol to represent legacy color codes.
+ *
+- * @param alternateChar Alternate symbol such as '&'
++ * @param alternateChar Alternate symbol such as '&amp;'
+ * @param message The message to send
+ * @deprecated use {@link #sendActionBar(Component)}
+ */
+diff --git a/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java b/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java
+index 1b2267f4e8ebded198773ec80e2bff2c861c7084..1a58734d919fae247eeb85dd785fd59990856505 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerMoveEvent.java
+@@ -78,7 +78,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
+ *
+ * @return Location the player moved to
+ */
+- @Nullable
++ @NotNull // Paper
+ public Location getTo() {
+ return to;
+ }
+diff --git a/src/main/java/org/bukkit/inventory/PlayerInventory.java b/src/main/java/org/bukkit/inventory/PlayerInventory.java
+index 91afd844dafec4ed9ab9e2e16b220ffbd35e7495..1e45c9078ffffe9d3c25538fdd433780ae751270 100644
+--- a/src/main/java/org/bukkit/inventory/PlayerInventory.java
++++ b/src/main/java/org/bukkit/inventory/PlayerInventory.java
+@@ -106,7 +106,7 @@ public interface PlayerInventory extends Inventory {
+ *
+ * @return the ItemStack in the given slot
+ */
+- @NotNull
++ @Nullable
+ public ItemStack getItem(@NotNull EquipmentSlot slot);
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0052-Item-canEntityPickup.patch b/Spigot-API-Patches-Unmapped/0052-Item-canEntityPickup.patch
new file mode 100644
index 0000000000..27894bda93
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0052-Item-canEntityPickup.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 5 May 2017 03:57:08 -0500
+Subject: [PATCH] Item#canEntityPickup
+
+
+diff --git a/src/main/java/org/bukkit/entity/Item.java b/src/main/java/org/bukkit/entity/Item.java
+index 3f2736fbddd8661e764bc2f8d0499bd13ca91df5..c404a5b8efea7c780db5ddae19456753808abb3d 100644
+--- a/src/main/java/org/bukkit/entity/Item.java
++++ b/src/main/java/org/bukkit/entity/Item.java
+@@ -75,4 +75,20 @@ public interface Item extends Entity {
+ */
+ @Nullable
+ public UUID getThrower();
++
++ // Paper Start
++ /**
++ * Gets if non-player entities can pick this Item up
++ *
++ * @return True if non-player entities can pickup
++ */
++ public boolean canMobPickup();
++
++ /**
++ * Sets if non-player entities can pick this Item up
++ *
++ * @param canMobPickup True to allow non-player entity pickup
++ */
++ public void setCanMobPickup(boolean canMobPickup);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0053-PlayerPickupItemEvent-setFlyAtPlayer.patch b/Spigot-API-Patches-Unmapped/0053-PlayerPickupItemEvent-setFlyAtPlayer.patch
new file mode 100644
index 0000000000..353d395911
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0053-PlayerPickupItemEvent-setFlyAtPlayer.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 7 May 2017 06:26:01 -0500
+Subject: [PATCH] PlayerPickupItemEvent#setFlyAtPlayer
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java b/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java
+index 951ea2cc763973655beedcba3c75332d3f297313..18d82c111f30e0279c10a174a51bac018185cd38 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerPickupItemEvent.java
+@@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull;
+ public class PlayerPickupItemEvent extends PlayerEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private final Item item;
++ private boolean flyAtPlayer = true; // Paper
+ private boolean cancel = false;
+ private final int remaining;
+
+@@ -45,6 +46,27 @@ public class PlayerPickupItemEvent extends PlayerEvent implements Cancellable {
+ return remaining;
+ }
+
++ // Paper Start
++ /**
++ * Set if the item will fly at the player
++ * <p>Cancelling the event will set this value to false.</p>
++ *
++ * @param flyAtPlayer True for item to fly at player
++ */
++ public void setFlyAtPlayer(boolean flyAtPlayer) {
++ this.flyAtPlayer = flyAtPlayer;
++ }
++
++ /**
++ * Gets if the item will fly at the player
++ *
++ * @return True if the item will fly at the player
++ */
++ public boolean getFlyAtPlayer() {
++ return flyAtPlayer;
++ }
++ // Paper End
++
+ @Override
+ public boolean isCancelled() {
+ return cancel;
+@@ -53,6 +75,7 @@ public class PlayerPickupItemEvent extends PlayerEvent implements Cancellable {
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancel = cancel;
++ this.flyAtPlayer = !cancel; // Paper
+ }
+
+ @NotNull
diff --git a/Spigot-API-Patches-Unmapped/0054-PlayerAttemptPickupItemEvent.patch b/Spigot-API-Patches-Unmapped/0054-PlayerAttemptPickupItemEvent.patch
new file mode 100644
index 0000000000..6b504ae688
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0054-PlayerAttemptPickupItemEvent.patch
@@ -0,0 +1,102 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 11 Jun 2017 16:30:37 -0500
+Subject: [PATCH] PlayerAttemptPickupItemEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java b/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0788153a9641e75da565d2e6eee37eeee1cbc61e
+--- /dev/null
++++ b/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java
+@@ -0,0 +1,90 @@
++package org.bukkit.event.player;
++
++import org.bukkit.entity.Item;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Thrown when a player attempts to pick an item up from the ground
++ */
++public class PlayerAttemptPickupItemEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final Item item;
++ private final int remaining;
++ private boolean flyAtPlayer = true;
++ private boolean isCancelled = false;
++
++ @Deprecated // Remove in 1.13 // Remove in 1.14?
++ public PlayerAttemptPickupItemEvent(@NotNull final Player player, @NotNull final Item item) {
++ this(player, item, 0);
++ }
++
++ public PlayerAttemptPickupItemEvent(@NotNull final Player player, @NotNull final Item item, final int remaining) {
++ super(player);
++ this.item = item;
++ this.remaining = remaining;
++ }
++
++ /**
++ * Gets the Item attempted by the player.
++ *
++ * @return Item
++ */
++ @NotNull
++ public Item getItem() {
++ return item;
++ }
++
++ /**
++ * Gets the amount that will remain on the ground, if any
++ *
++ * @return amount that will remain on the ground
++ */
++ public int getRemaining() {
++ return remaining;
++ }
++
++ /**
++ * Set if the item will fly at the player
++ * <p>Cancelling the event will set this value to false.</p>
++ *
++ * @param flyAtPlayer True for item to fly at player
++ */
++ public void setFlyAtPlayer(boolean flyAtPlayer) {
++ this.flyAtPlayer = flyAtPlayer;
++ }
++
++ /**
++ * Gets if the item will fly at the player
++ *
++ * @return True if the item will fly at the player
++ */
++ public boolean getFlyAtPlayer() {
++ return this.flyAtPlayer;
++ }
++
++
++ @Override
++ public boolean isCancelled() {
++ return this.isCancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.isCancelled = cancel;
++ this.flyAtPlayer = !cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0055-Add-UnknownCommandEvent.patch b/Spigot-API-Patches-Unmapped/0055-Add-UnknownCommandEvent.patch
new file mode 100644
index 0000000000..9e3d997b0d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0055-Add-UnknownCommandEvent.patch
@@ -0,0 +1,126 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sweepyoface <[email protected]>
+Date: Sat, 17 Jun 2017 18:48:06 -0400
+Subject: [PATCH] Add UnknownCommandEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java b/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..d5632d352590dec6982a372b285a8d4a332fa589
+--- /dev/null
++++ b/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java
+@@ -0,0 +1,114 @@
++package org.bukkit.event.command;
++
++import net.kyori.adventure.text.Component;
++import org.bukkit.Bukkit;
++import org.bukkit.command.CommandSender;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.Event;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Thrown when a player executes a command that is not defined
++ */
++public class UnknownCommandEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private CommandSender sender;
++ @NotNull private String commandLine;
++ @Nullable private Component message;
++
++ @Deprecated
++ public UnknownCommandEvent(@NotNull final CommandSender sender, @NotNull final String commandLine, @Nullable final String message) {
++ this(sender, commandLine, message == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message));
++ }
++
++ public UnknownCommandEvent(@NotNull final CommandSender sender, @NotNull final String commandLine, @Nullable final Component message) {
++ super(false);
++ this.sender = sender;
++ this.commandLine = commandLine;
++ this.message = message;
++ }
++
++ /**
++ * Gets the CommandSender or ConsoleCommandSender
++ * <p>
++ *
++ * @return Sender of the command
++ */
++ @NotNull
++ public CommandSender getSender() {
++ return sender;
++ }
++
++ /**
++ * Gets the command that was send
++ * <p>
++ *
++ * @return Command sent
++ */
++ @NotNull
++ public String getCommandLine() {
++ return commandLine;
++ }
++
++ /**
++ * Gets message that will be returned
++ * <p>
++ *
++ * @return Unknown command message
++ * @deprecated use {@link #message()}
++ */
++ @Nullable
++ @Deprecated
++ public String getMessage() {
++ return this.message == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.message);
++ }
++
++ /**
++ * Sets message that will be returned
++ * <p>
++ * Set to null to avoid any message being sent
++ *
++ * @param message the message to be returned, or null
++ * @deprecated use {@link #message(Component)}
++ */
++ @Deprecated
++ public void setMessage(@Nullable String message) {
++ this.message(message == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().deserialize(message));
++ }
++
++ /**
++ * Gets message that will be returned
++ * <p>
++ *
++ * @return Unknown command message
++ */
++ @Nullable
++ public Component message() {
++ return this.message;
++ }
++
++ /**
++ * Sets message that will be returned
++ * <p>
++ * Set to null to avoid any message being sent
++ *
++ * @param message the message to be returned, or null
++ */
++ public void message(@Nullable Component message) {
++ this.message = message;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
++
diff --git a/Spigot-API-Patches-Unmapped/0056-Basic-PlayerProfile-API.patch b/Spigot-API-Patches-Unmapped/0056-Basic-PlayerProfile-API.patch
new file mode 100644
index 0000000000..96944d3449
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0056-Basic-PlayerProfile-API.patch
@@ -0,0 +1,351 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 15 Jan 2018 21:46:46 -0500
+Subject: [PATCH] Basic PlayerProfile API
+
+Provides basic elements of a PlayerProfile to be used by future API/events
+
+diff --git a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2ef9a7bd55e2c9cf8cb20d5f77282676ae11181f
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java
+@@ -0,0 +1,177 @@
++package com.destroystokyo.paper.profile;
++
++import java.util.Collection;
++import java.util.Set;
++import java.util.UUID;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Represents a players profile for the game, such as UUID, Name, and textures.
++ */
++public interface PlayerProfile {
++
++ /**
++ * @return The players name, if set
++ */
++ @Nullable
++ String getName();
++
++ /**
++ * Sets this profiles Name
++ *
++ * @param name The new Name
++ * @return The previous Name
++ */
++ @NotNull
++ String setName(@Nullable String name);
++
++ /**
++ * @return The players unique identifier, if set
++ */
++ @Nullable UUID getId();
++
++ /**
++ * Sets this profiles UUID
++ *
++ * @param uuid The new UUID
++ * @return The previous UUID
++ */
++ @Nullable
++ UUID setId(@Nullable UUID uuid);
++
++ /**
++ * @return A Mutable set of this players properties, such as textures.
++ * Values specified here are subject to implementation details.
++ */
++ @NotNull Set<ProfileProperty> getProperties();
++
++ /**
++ * Check if the Profile has the specified property
++ * @param property Property name to check
++ * @return If the property is set
++ */
++ boolean hasProperty(@Nullable String property);
++
++ /**
++ * Sets a property. If the property already exists, the previous one will be replaced
++ * @param property Property to set.
++ */
++ void setProperty(@NotNull ProfileProperty property);
++
++ /**
++ * Sets multiple properties. If any of the set properties already exist, it will be replaced
++ * @param properties The properties to set
++ */
++ void setProperties(@NotNull Collection<ProfileProperty> properties);
++
++ /**
++ * Removes a specific property from this profile
++ * @param property The property to remove
++ * @return If a property was removed
++ */
++ boolean removeProperty(@Nullable String property);
++
++ /**
++ * Removes a specific property from this profile
++ * @param property The property to remove
++ * @return If a property was removed
++ */
++ default boolean removeProperty(@NotNull ProfileProperty property) {
++ return removeProperty(property.getName());
++ }
++
++ /**
++ * Removes all properties in the collection
++ * @param properties The properties to remove
++ * @return If any property was removed
++ */
++ default boolean removeProperties(@NotNull Collection<ProfileProperty> properties) {
++ boolean removed = false;
++ for (ProfileProperty property : properties) {
++ if (removeProperty(property)) {
++ removed = true;
++ }
++ }
++ return removed;
++ }
++
++ /**
++ * Clears all properties on this profile
++ */
++ void clearProperties();
++
++ /**
++ * @return If the profile is now complete (has UUID and Name)
++ */
++ boolean isComplete();
++
++ /**
++ * Like {@link #complete(boolean)} but will try only from cache, and not make network calls
++ * Does not account for textures.
++ *
++ * @return If the profile is now complete (has UUID and Name)
++ */
++ boolean completeFromCache();
++
++ /**
++ * Like {@link #complete(boolean)} but will try only from cache, and not make network calls
++ * Does not account for textures.
++ *
++ * @param onlineMode Treat this as online mode or not
++ * @return If the profile is now complete (has UUID and Name)
++ */
++ boolean completeFromCache(boolean onlineMode);
++
++ /**
++ * Like {@link #complete(boolean)} but will try only from cache, and not make network calls
++ * Does not account for textures.
++ *
++ * @param lookupUUID If only name is supplied, should we do a UUID lookup
++ * @param onlineMode Treat this as online mode or not
++ * @return If the profile is now complete (has UUID and Name)
++ */
++ boolean completeFromCache(boolean lookupUUID, boolean onlineMode);
++
++ /**
++ * If this profile is not complete, then make the API call to complete it.
++ * This is a blocking operation and should be done asynchronously.
++ *
++ * This will also complete textures. If you do not want to load textures, use {{@link #complete(boolean)}}
++ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
++ */
++ default boolean complete() {
++ return complete(true);
++ }
++
++ /**
++ * If this profile is not complete, then make the API call to complete it.
++ * This is a blocking operation and should be done asynchronously.
++ *
++ * Optionally will also fill textures.
++ *
++ * Online mode will be automatically determined
++ * @param textures controls if we should fill the profile with texture properties
++ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
++ */
++ boolean complete(boolean textures);
++
++ /**
++ * If this profile is not complete, then make the API call to complete it.
++ * This is a blocking operation and should be done asynchronously.
++ *
++ * Optionally will also fill textures.
++ * @param textures controls if we should fill the profile with texture properties
++ * @param onlineMode Treat this server as online mode or not
++ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
++ */
++ boolean complete(boolean textures, boolean onlineMode);
++
++ /**
++ * Whether or not this Profile has textures associated to it
++ * @return If has a textures property
++ */
++ default boolean hasTextures() {
++ return hasProperty("textures");
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java b/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7b3b6ef533d32169fbeca389bd61cfc6b0e0faee
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java
+@@ -0,0 +1,72 @@
++package com.destroystokyo.paper.profile;
++
++import com.google.common.base.Preconditions;
++
++import java.util.Objects;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Represents a property on a {@link PlayerProfile}
++ */
++public class ProfileProperty {
++ private final String name;
++ private final String value;
++ private final String signature;
++
++ public ProfileProperty(@NotNull String name, @NotNull String value) {
++ this(name, value, null);
++ }
++
++ public ProfileProperty(@NotNull String name, @NotNull String value, @Nullable String signature) {
++ this.name = Preconditions.checkNotNull(name, "ProfileProperty name can not be null");
++ this.value = Preconditions.checkNotNull(value, "ProfileProperty value can not be null");
++ this.signature = signature;
++ }
++
++ /**
++ * @return The property name, ie "textures"
++ */
++ @NotNull
++ public String getName() {
++ return name;
++ }
++
++ /**
++ * @return The property value, likely to be base64 encoded
++ */
++ @NotNull
++ public String getValue() {
++ return value;
++ }
++
++ /**
++ * @return A signature from Mojang for signed properties
++ */
++ @Nullable
++ public String getSignature() {
++ return signature;
++ }
++
++ /**
++ * @return If this property has a signature or not
++ */
++ public boolean isSigned() {
++ return this.signature != null;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (this == o) return true;
++ if (o == null || getClass() != o.getClass()) return false;
++ ProfileProperty that = (ProfileProperty) o;
++ return Objects.equals(name, that.name) &&
++ Objects.equals(value, that.value) &&
++ Objects.equals(signature, that.signature);
++ }
++
++ @Override
++ public int hashCode() {
++ return Objects.hash(name);
++ }
++}
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 2527e896a4409326ea2612723b829696d44fc199..f5d3a7370390871d1b6075f32846d1a942b05b7f 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1766,6 +1766,40 @@ public final class Bukkit {
+ public static boolean suggestPlayerNamesWhenNullTabCompletions() {
+ return server.suggestPlayerNamesWhenNullTabCompletions();
+ }
++
++ /**
++ * Creates a PlayerProfile for the specified uuid, with name as null
++ * @param uuid UUID to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ public static com.destroystokyo.paper.profile.PlayerProfile createProfile(@NotNull UUID uuid) {
++ return server.createProfile(uuid);
++ }
++
++ /**
++ * Creates a PlayerProfile for the specified name, with UUID as null
++ * @param name Name to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ public static com.destroystokyo.paper.profile.PlayerProfile createProfile(@NotNull String name) {
++ return server.createProfile(name);
++ }
++
++ /**
++ * Creates a PlayerProfile for the specified name/uuid
++ *
++ * Both UUID and Name can not be null at same time. One must be supplied.
++ *
++ * @param uuid UUID to create profile for
++ * @param name Name to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ public static com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
++ return server.createProfile(uuid, name);
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 7d94242b2f8ecb537b3140f9d6f706d3e266c456..38d138b217734e598581ed14065ff2015135ee9a 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1551,5 +1551,33 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ * @return true if player names should be suggested
+ */
+ boolean suggestPlayerNamesWhenNullTabCompletions();
++
++ /**
++ * Creates a PlayerProfile for the specified uuid, with name as null
++ * @param uuid UUID to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ com.destroystokyo.paper.profile.PlayerProfile createProfile(@NotNull UUID uuid);
++
++ /**
++ * Creates a PlayerProfile for the specified name, with UUID as null
++ * @param name Name to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ com.destroystokyo.paper.profile.PlayerProfile createProfile(@NotNull String name);
++
++ /**
++ * Creates a PlayerProfile for the specified name/uuid
++ *
++ * Both UUID and Name can not be null at same time. One must be supplied.
++ *
++ * @param uuid UUID to create profile for
++ * @param name Name to create profile for
++ * @return A PlayerProfile object
++ */
++ @NotNull
++ com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0057-Shoulder-Entities-Release-API.patch b/Spigot-API-Patches-Unmapped/0057-Shoulder-Entities-Release-API.patch
new file mode 100644
index 0000000000..249e81f7ad
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0057-Shoulder-Entities-Release-API.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 17 Jun 2017 15:04:51 -0400
+Subject: [PATCH] Shoulder Entities Release API
+
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index 72178c3036f567ceaf15016978a9737eb9c4afc4..d212d5123b6294f7873d72f125505a006c290b05 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -300,6 +300,26 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ */
+ public int getExpToLevel();
+
++ // Paper start
++ /**
++ * If there is an Entity on this entities left shoulder, it will be released to the world and returned.
++ * If no Entity is released, null will be returned.
++ *
++ * @return The released entity, or null
++ */
++ @Nullable
++ public Entity releaseLeftShoulderEntity();
++
++ /**
++ * If there is an Entity on this entities left shoulder, it will be released to the world and returned.
++ * If no Entity is released, null will be returned.
++ *
++ * @return The released entity, or null
++ */
++ @Nullable
++ public Entity releaseRightShoulderEntity();
++ // Paper end
++
+ /**
+ * Gets the current cooldown for a player's attack.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0058-Profile-Lookup-Events.patch b/Spigot-API-Patches-Unmapped/0058-Profile-Lookup-Events.patch
new file mode 100644
index 0000000000..1cdb35476d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0058-Profile-Lookup-Events.patch
@@ -0,0 +1,174 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 17 Jun 2017 16:30:44 -0400
+Subject: [PATCH] Profile Lookup Events
+
+Adds a Pre Lookup Event and a Post Lookup Event so that plugins may prefill in profile data, and cache the responses from
+profiles that had to be looked up.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/profile/LookupProfileEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/LookupProfileEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8df37c07cd55ddf110d1dd68183d7b697f7a6756
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/profile/LookupProfileEvent.java
+@@ -0,0 +1,46 @@
++package com.destroystokyo.paper.event.profile;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Allows a plugin to be notified anytime AFTER a Profile has been looked up from the Mojang API
++ * This is an opportunity to view the response and potentially cache things.
++ *
++ * No guarantees are made about thread execution context for this event. If you need to know, check
++ * event.isAsync()
++ */
++public class LookupProfileEvent extends Event {
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull private final PlayerProfile profile;
++
++ public LookupProfileEvent(@NotNull PlayerProfile profile) {
++ super(!Bukkit.isPrimaryThread());
++ this.profile = profile;
++ }
++
++ /**
++ * @return The profile that was recently looked up. This profile can be mutated
++ */
++ @NotNull
++ public PlayerProfile getPlayerProfile() {
++ return profile;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/profile/PreLookupProfileEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/PreLookupProfileEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4dcf6242c9acc62d030a94f67b78729ed29f8c85
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/profile/PreLookupProfileEvent.java
+@@ -0,0 +1,108 @@
++package com.destroystokyo.paper.event.profile;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import com.destroystokyo.paper.profile.ProfileProperty;
++import com.google.common.collect.ArrayListMultimap;
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.util.HashSet;
++import java.util.Set;
++import java.util.UUID;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Allows a plugin to intercept a Profile Lookup for a Profile by name
++ *
++ * At the point of event fire, the UUID and properties are unset.
++ *
++ * If a plugin sets the UUID, and optionally the properties, the API call to look up the profile may be skipped.
++ *
++ * No guarantees are made about thread execution context for this event. If you need to know, check
++ * event.isAsync()
++ */
++public class PreLookupProfileEvent extends Event {
++
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final String name;
++ private UUID uuid;
++ @NotNull private Set<ProfileProperty> properties = new HashSet<>();
++
++ public PreLookupProfileEvent(@NotNull String name) {
++ super(!Bukkit.isPrimaryThread());
++ this.name = name;
++ }
++
++ /**
++ * @return Name of the profile
++ */
++ @NotNull
++ public String getName() {
++ return name;
++ }
++
++ /**
++ * If this value is left null by the completion of the event call, then the server will
++ * trigger a call to the Mojang API to look up the UUID (Network Request), and subsequently, fire a
++ * {@link LookupProfileEvent}
++ *
++ * @return The UUID of the profile if it has already been provided by a plugin
++ */
++ @Nullable
++ public UUID getUUID() {
++ return uuid;
++ }
++
++ /**
++ * Sets the UUID for this player name. This will skip the initial API call to find the players UUID.
++ *
++ * However, if Profile Properties are needed by the server, you must also set them or else an API call might still be made.
++ *
++ * @param uuid the UUID to set for the profile or null to reset
++ */
++ public void setUUID(@Nullable UUID uuid) {
++ this.uuid = uuid;
++ }
++
++ /**
++ * @return The currently pending prepopulated properties.
++ * Any property in this Set will be automatically prefilled on this Profile
++ */
++ @NotNull
++ public Set<ProfileProperty> getProfileProperties() {
++ return this.properties;
++ }
++
++ /**
++ * Clears any existing prepopulated properties and uses the supplied properties
++ * Any property in this Set will be automatically prefilled on this Profile
++ * @param properties The properties to add
++ */
++ public void setProfileProperties(@NotNull Set<ProfileProperty> properties) {
++ this.properties = new HashSet<>();
++ this.properties.addAll(properties);
++ }
++
++ /**
++ * Adds any properties currently missing to the prepopulated properties set, replacing any that already were set.
++ * Any property in this Set will be automatically prefilled on this Profile
++ * @param properties The properties to add
++ */
++ public void addProfileProperties(@NotNull Set<ProfileProperty> properties) {
++ this.properties.addAll(properties);
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++}
diff --git a/Spigot-API-Patches-Unmapped/0059-Entity-fromMobSpawner.patch b/Spigot-API-Patches-Unmapped/0059-Entity-fromMobSpawner.patch
new file mode 100644
index 0000000000..6912236f76
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0059-Entity-fromMobSpawner.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 18 Jun 2017 18:17:05 -0500
+Subject: [PATCH] Entity#fromMobSpawner()
+
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index e9d0d507b47b0347b975b1a83f5ae70dca5587b8..feb9507a972bf797144a01adeeaac83ec2bd165a 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -619,5 +619,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ */
+ @Nullable
+ Location getOrigin();
++
++ /**
++ * Returns whether this entity was spawned from a mob spawner.
++ *
++ * @return True if entity spawned from a mob spawner
++ */
++ boolean fromMobSpawner();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0060-Improve-the-Saddle-API-for-Horses.patch b/Spigot-API-Patches-Unmapped/0060-Improve-the-Saddle-API-for-Horses.patch
new file mode 100644
index 0000000000..85814456bf
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0060-Improve-the-Saddle-API-for-Horses.patch
@@ -0,0 +1,83 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 10 Dec 2016 16:12:48 -0500
+Subject: [PATCH] Improve the Saddle API for Horses
+
+Not all horses with Saddles have armor. This lets us break up the horses with saddles
+and access their saddle state separately from an interface shared with Armor.
+
+diff --git a/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java b/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..163ffe8ff76ded6265d865901d5110fb6a56950d
+--- /dev/null
++++ b/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java
+@@ -0,0 +1,21 @@
++package org.bukkit.inventory;
++
++import org.jetbrains.annotations.Nullable;
++
++public interface ArmoredHorseInventory extends AbstractHorseInventory {
++
++ /**
++ * Gets the item in the horse's armor slot.
++ *
++ * @return the armor item
++ */
++ @Nullable
++ ItemStack getArmor();
++
++ /**
++ * Sets the item in the horse's armor slot.
++ *
++ * @param stack the new item
++ */
++ void setArmor(@Nullable ItemStack stack);
++}
+diff --git a/src/main/java/org/bukkit/inventory/HorseInventory.java b/src/main/java/org/bukkit/inventory/HorseInventory.java
+index 608e99c4207405bf9dd88d44ad8e82eefa19e45c..53498debe4cfb80592ef3025270bc8e5df4a5fec 100644
+--- a/src/main/java/org/bukkit/inventory/HorseInventory.java
++++ b/src/main/java/org/bukkit/inventory/HorseInventory.java
+@@ -5,20 +5,4 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * An interface to the inventory of a Horse.
+ */
+-public interface HorseInventory extends AbstractHorseInventory {
+-
+- /**
+- * Gets the item in the horse's armor slot.
+- *
+- * @return the armor item
+- */
+- @Nullable
+- ItemStack getArmor();
+-
+- /**
+- * Sets the item in the horse's armor slot.
+- *
+- * @param stack the new item
+- */
+- void setArmor(@Nullable ItemStack stack);
+-}
++public interface HorseInventory extends AbstractHorseInventory, ArmoredHorseInventory {}
+diff --git a/src/main/java/org/bukkit/inventory/LlamaInventory.java b/src/main/java/org/bukkit/inventory/LlamaInventory.java
+index 2fa2c9d07ecbafaf2396d913af90f1f4d432b238..5ac1afb8a213fa0fe344db4730ecbc5de6eed445 100644
+--- a/src/main/java/org/bukkit/inventory/LlamaInventory.java
++++ b/src/main/java/org/bukkit/inventory/LlamaInventory.java
+@@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * An interface to the inventory of a {@link Llama}.
+ */
+-public interface LlamaInventory extends AbstractHorseInventory {
++public interface LlamaInventory extends SaddledHorseInventory {
+
+ /**
+ * Gets the item in the llama's decor slot.
+diff --git a/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java b/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7944f26a3e2a92601c3be0e55c00c39cc16cf177
+--- /dev/null
++++ b/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java
+@@ -0,0 +1,3 @@
++package org.bukkit.inventory;
++
++public interface SaddledHorseInventory extends AbstractHorseInventory {}
diff --git a/Spigot-API-Patches-Unmapped/0061-ensureServerConversions-API.patch b/Spigot-API-Patches-Unmapped/0061-ensureServerConversions-API.patch
new file mode 100644
index 0000000000..56030876f6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0061-ensureServerConversions-API.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 4 May 2016 23:55:48 -0400
+Subject: [PATCH] ensureServerConversions API
+
+This will take a Bukkit ItemStack and run it through any conversions a server process would perform on it,
+to ensure it meets latest minecraft expectations.
+
+diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
+index 3dba4c361993e143e511b4f108ac0b444a84d964..30ab5d38197bccfc06f6f5554b162f8bdbfe2a45 100644
+--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
+@@ -151,5 +151,17 @@ public interface ItemFactory {
+ */
+ @NotNull
+ net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull ItemStack item, final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op);
++
++ /**
++ * Minecart updates are converting simple item stacks into more complex NBT oriented Item Stacks.
++ *
++ * Use this method to to ensure any desired data conversions are processed.
++ * The input itemstack will not be the same as the returned itemstack.
++ *
++ * @param item The item to process conversions on
++ * @return A potentially Data Converted ItemStack
++ */
++ @NotNull
++ ItemStack ensureServerConversions(@NotNull ItemStack item);
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index 4b20b557eaa958cf1ad1baf8d6cc17f38b180ff1..c31f667c466ba80bd88ae560d14dbd18a84118b4 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -536,7 +536,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ }
+ }
+
+- return result;
++ return result.ensureServerConversions(); // Paper
+ }
+
+ /**
+@@ -602,5 +602,18 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ public net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowItem> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowItem> op) {
+ return org.bukkit.Bukkit.getServer().getItemFactory().asHoverEvent(this, op);
+ }
++
++ /**
++ * Minecart updates are converting simple item stacks into more complex NBT oriented Item Stacks.
++ *
++ * Use this method to to ensure any desired data conversions are processed.
++ * The input itemstack will not be the same as the returned itemstack.
++ *
++ * @return A potentially Data Converted ItemStack
++ */
++ @NotNull
++ public ItemStack ensureServerConversions() {
++ return Bukkit.getServer().getItemFactory().ensureServerConversions(this);
++ }
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0062-Add-getI18NDisplayName-API.patch b/Spigot-API-Patches-Unmapped/0062-Add-getI18NDisplayName-API.patch
new file mode 100644
index 0000000000..a655a8a501
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0062-Add-getI18NDisplayName-API.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 4 May 2016 23:55:48 -0400
+Subject: [PATCH] Add getI18NDisplayName API
+
+Gets the Display name as seen in the Client.
+Currently the server only supports the English language. To override this,
+You must replace the language file embedded in the server jar.
+
+diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
+index 30ab5d38197bccfc06f6f5554b162f8bdbfe2a45..3578f491a053154789ad696e93c70fdde74912e6 100644
+--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
+@@ -163,5 +163,16 @@ public interface ItemFactory {
+ */
+ @NotNull
+ ItemStack ensureServerConversions(@NotNull ItemStack item);
++
++ /**
++ * Gets the Display name as seen in the Client.
++ * Currently the server only supports the English language. To override this,
++ * You must replace the language file embedded in the server jar.
++ *
++ * @param item Item to return Display name of
++ * @return Display name of Item
++ */
++ @Nullable
++ String getI18NDisplayName(@Nullable ItemStack item);
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index c31f667c466ba80bd88ae560d14dbd18a84118b4..77d6b2ed68d8ce30b5cadb156941d2d1f7dcf5b1 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -615,5 +615,17 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ public ItemStack ensureServerConversions() {
+ return Bukkit.getServer().getItemFactory().ensureServerConversions(this);
+ }
++
++ /**
++ * Gets the Display name as seen in the Client.
++ * Currently the server only supports the English language. To override this,
++ * You must replace the language file embedded in the server jar.
++ *
++ * @return Display name of Item
++ */
++ @Nullable
++ public String getI18NDisplayName() {
++ return Bukkit.getServer().getItemFactory().getI18NDisplayName(this);
++ }
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0063-ProfileWhitelistVerifyEvent.patch b/Spigot-API-Patches-Unmapped/0063-ProfileWhitelistVerifyEvent.patch
new file mode 100644
index 0000000000..ff996377f4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0063-ProfileWhitelistVerifyEvent.patch
@@ -0,0 +1,157 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 3 Jul 2017 18:11:34 -0500
+Subject: [PATCH] ProfileWhitelistVerifyEvent
+
+Fires when the server is validating if a player is whitelisted.
+
+Allows you to do dynamic whitelisting and change of kick message
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8a259ab49ea79673b6da9e4e2aaecec67469994e
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java
+@@ -0,0 +1,142 @@
++/*
++ * Copyright (c) 2017 - Daniel Ennis (Aikar) - MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.profile;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import net.kyori.adventure.text.Component;
++import org.bukkit.Bukkit;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Fires when the server needs to verify if a player is whitelisted.
++ *
++ * Plugins may override/control the servers whitelist with this event,
++ * and dynamically change the kick message.
++ */
++public class ProfileWhitelistVerifyEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final PlayerProfile profile;
++ private final boolean whitelistEnabled;
++ private boolean whitelisted;
++ private final boolean isOp;
++ @Nullable private Component kickMessage;
++
++ @Deprecated
++ public ProfileWhitelistVerifyEvent(@NotNull final PlayerProfile profile, boolean whitelistEnabled, boolean whitelisted, boolean isOp, @Nullable String kickMessage) {
++ this(profile, whitelistEnabled, whitelisted, isOp, kickMessage == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().deserialize(kickMessage));
++ }
++
++ public ProfileWhitelistVerifyEvent(@NotNull final PlayerProfile profile, boolean whitelistEnabled, boolean whitelisted, boolean isOp, @Nullable Component kickMessage) {
++ this.profile = profile;
++ this.whitelistEnabled = whitelistEnabled;
++ this.whitelisted = whitelisted;
++ this.isOp = isOp;
++ this.kickMessage = kickMessage;
++ }
++
++ /**
++ * @return the currently planned message to send to the user if they are not whitelisted
++ * @deprecated use {@link #kickMessage()}
++ */
++ @Deprecated
++ @Nullable
++ public String getKickMessage() {
++ return this.kickMessage == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().serialize(kickMessage);
++ }
++
++ /**
++ * @param kickMessage The message to send to the player on kick if not whitelisted. May set to null to use the server configured default
++ * @deprecated Use {@link #kickMessage(Component)}
++ */
++ @Deprecated
++ public void setKickMessage(@Nullable String kickMessage) {
++ this.kickMessage(kickMessage == null ? null : Bukkit.getUnsafe().legacyComponentSerializer().deserialize(kickMessage));
++ }
++
++ /**
++ * @return the currently planned message to send to the user if they are not whitelisted
++ */
++ @Nullable
++ public Component kickMessage() {
++ return this.kickMessage;
++ }
++
++ /**
++ * @param kickMessage The message to send to the player on kick if not whitelisted. May set to null to use the server configured default
++ */
++ public void kickMessage(@Nullable Component kickMessage) {
++ this.kickMessage = kickMessage;
++ }
++
++ /**
++ * @return The profile of the player trying to connect
++ */
++ @NotNull
++ public PlayerProfile getPlayerProfile() {
++ return profile;
++ }
++
++ /**
++ * @return Whether the player is whitelisted to play on this server (whitelist may be off is why its true)
++ */
++ public boolean isWhitelisted() {
++ return whitelisted;
++ }
++
++ /**
++ * Changes the players whitelisted state. false will deny the login
++ * @param whitelisted The new whitelisted state
++ */
++ public void setWhitelisted(boolean whitelisted) {
++ this.whitelisted = whitelisted;
++ }
++
++ /**
++ * @return if the player obtained whitelist status by having op
++ */
++ public boolean isOp() {
++ return isOp;
++ }
++
++ /**
++ * @return if the server even has whitelist on
++ */
++ public boolean isWhitelistEnabled() {
++ return whitelistEnabled;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0064-Make-plugins-list-alphabetical.patch b/Spigot-API-Patches-Unmapped/0064-Make-plugins-list-alphabetical.patch
new file mode 100644
index 0000000000..ec1fc9814a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0064-Make-plugins-list-alphabetical.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Mon, 31 Jul 2017 02:08:55 -0500
+Subject: [PATCH] Make /plugins list alphabetical
+
+
+diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
+index bcb576a4271b1ec7b1cfe6f83cf161b7d89ed2e5..4de959bbd1270d7d6ea8e5e69521bcca6abe2138 100644
+--- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
++++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
+@@ -3,6 +3,9 @@ package org.bukkit.command.defaults;
+ import java.util.Arrays;
+ import java.util.Collections;
+ import java.util.List;
++import java.util.Map;
++import java.util.TreeMap;
++
+ import org.bukkit.Bukkit;
+ import org.bukkit.ChatColor;
+ import org.bukkit.command.CommandSender;
+@@ -34,15 +37,22 @@ public class PluginsCommand extends BukkitCommand {
+
+ @NotNull
+ private String getPluginList() {
+- StringBuilder pluginList = new StringBuilder();
+- Plugin[] plugins = Bukkit.getPluginManager().getPlugins();
++ // Paper start
++ TreeMap<String, Plugin> plugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
++
++ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
++ plugins.put(plugin.getDescription().getName(), plugin);
++ }
+
+- for (Plugin plugin : plugins) {
++ StringBuilder pluginList = new StringBuilder();
++ for (Map.Entry<String, Plugin> entry : plugins.entrySet()) {
+ if (pluginList.length() > 0) {
+ pluginList.append(ChatColor.WHITE);
+ pluginList.append(", ");
+ }
+
++ Plugin plugin = entry.getValue();
++
+ pluginList.append(plugin.isEnabled() ? ChatColor.GREEN : ChatColor.RED);
+ pluginList.append(plugin.getDescription().getName());
+
+@@ -51,6 +61,8 @@ public class PluginsCommand extends BukkitCommand {
+ }
+ }
+
+- return "(" + plugins.length + "): " + pluginList.toString();
++ return "(" + plugins.size() + "): " + pluginList.toString();
++ // Paper end
+ }
++
+ }
diff --git a/Spigot-API-Patches-Unmapped/0065-LivingEntity-setKiller.patch b/Spigot-API-Patches-Unmapped/0065-LivingEntity-setKiller.patch
new file mode 100644
index 0000000000..b7b93870eb
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0065-LivingEntity-setKiller.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Mon, 31 Jul 2017 01:49:43 -0500
+Subject: [PATCH] LivingEntity#setKiller
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 45e9f585c3e522ecf94a6bc42cdc190e1a191a5c..33fffda7c8b05cde3c95623937e7eb6c8b628ec6 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -279,6 +279,15 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ @Nullable
+ public Player getKiller();
+
++ // Paper start
++ /**
++ * Sets the player identified as the killer of the living entity.
++ *
++ * @param killer player
++ */
++ public void setKiller(@Nullable Player killer);
++ // Paper end
++
+ /**
+ * Adds the given {@link PotionEffect} to the living entity.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0066-Handle-plugin-prefixes-in-implementation-logging-con.patch b/Spigot-API-Patches-Unmapped/0066-Handle-plugin-prefixes-in-implementation-logging-con.patch
new file mode 100644
index 0000000000..a1273c87c6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0066-Handle-plugin-prefixes-in-implementation-logging-con.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Thu, 21 Sep 2017 16:14:13 +0200
+Subject: [PATCH] Handle plugin prefixes in implementation logging
+ configuration
+
+Currently, plugin prefixes are prepended to the log message in
+the PluginLogger before passing the message to the underlying
+logging framework. This is bad design because they need to be
+stripped manually when using custom appenders to log messages
+in a different format.
+
+Additionally, it makes integration of alternative logging APIs hard
+because all logging must go through the PluginLogger. Avoid using
+PluginLogger and create a regular logger using the plugin name.
+The implementation should handle plugin prefixes by displaying
+logger names when appropriate.
+
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+index 2231900549607a0917dd04e8b433c027b846cef9..bb2e55e97bf887a28cac7d4f9a0a23960d22cf56 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+@@ -42,7 +42,7 @@ public abstract class JavaPlugin extends PluginBase {
+ private boolean naggable = true;
+ private FileConfiguration newConfig = null;
+ private File configFile = null;
+- private PluginLogger logger = null;
++ private Logger logger = null; // Paper - PluginLogger -> Logger
+
+ public JavaPlugin() {
+ final ClassLoader classLoader = this.getClass().getClassLoader();
+@@ -276,7 +276,8 @@ public abstract class JavaPlugin extends PluginBase {
+ this.dataFolder = dataFolder;
+ this.classLoader = classLoader;
+ this.configFile = new File(dataFolder, "config.yml");
+- this.logger = new PluginLogger(this);
++ // Paper - Handle plugin prefix in implementation
++ this.logger = Logger.getLogger(description.getPrefix() != null ? description.getPrefix() : description.getName());
+ }
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0067-Allow-plugins-to-use-SLF4J-for-logging.patch b/Spigot-API-Patches-Unmapped/0067-Allow-plugins-to-use-SLF4J-for-logging.patch
new file mode 100644
index 0000000000..40c87d8a85
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0067-Allow-plugins-to-use-SLF4J-for-logging.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Thu, 21 Sep 2017 16:33:12 +0200
+Subject: [PATCH] Allow plugins to use SLF4J for logging
+
+SLF4J is a commonly used abstraction for various logging frameworks
+such as java.util.logging (JUL) or Log4j. Currently, plugins are
+required to do all their logging using the provided JUL logger.
+This is annoying for plugins that target multiple platforms or when
+using libraries that log messages using SLF4J.
+
+Expose SLF4J as optional logging API for plugins, so they can use
+it without having to shade it in the plugin and going through
+several layers of logging abstraction.
+
+diff --git a/pom.xml b/pom.xml
+index 89918b5af7fbf42357e96dffb00b8a011a8ef13c..e1d1635889d68d1e17dc66f3a65545b44deffa69 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -124,6 +124,13 @@
+ <version>20.1.0</version>
+ <scope>provided</scope>
+ </dependency>
++ <!-- Paper - Add SLF4J -->
++ <dependency>
++ <groupId>org.slf4j</groupId>
++ <artifactId>slf4j-api</artifactId>
++ <version>1.7.25</version>
++ <scope>compile</scope>
++ </dependency>
+ <!-- testing -->
+ <dependency>
+ <groupId>junit</groupId>
+diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java
+index febfec6efafd76bb59b4b43aa223af16f73339b4..79890c68f1ad31f951dfdbd9a16dac500ec58c40 100644
+--- a/src/main/java/org/bukkit/plugin/Plugin.java
++++ b/src/main/java/org/bukkit/plugin/Plugin.java
+@@ -166,6 +166,13 @@ public interface Plugin extends TabExecutor {
+ @NotNull
+ public Logger getLogger();
+
++ // Paper start - Add SLF4J logger
++ @NotNull
++ default org.slf4j.Logger getSLF4JLogger() {
++ return org.slf4j.LoggerFactory.getLogger(getLogger().getName());
++ }
++ // Paper end
++
+ /**
+ * Returns the name of the plugin.
+ * <p>
diff --git a/Spigot-API-Patches-Unmapped/0068-Add-workaround-for-plugins-modifying-the-parent-of-t.patch b/Spigot-API-Patches-Unmapped/0068-Add-workaround-for-plugins-modifying-the-parent-of-t.patch
new file mode 100644
index 0000000000..ff6a815968
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0068-Add-workaround-for-plugins-modifying-the-parent-of-t.patch
@@ -0,0 +1,117 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Thu, 21 Sep 2017 19:41:20 +0200
+Subject: [PATCH] Add workaround for plugins modifying the parent of the plugin
+ logger
+
+Essentials uses a custom logger name ("Essentials") instead of the
+plugin logger. Log messages are redirected to the plugin logger by
+setting the parent of the "Essentials" logger to the plugin logger.
+
+With our changes, the plugin logger is now also called "Essentials",
+resulting in an infinite loop. Make sure plugins can't change the
+parent of the plugin logger to avoid this.
+
+diff --git a/src/main/java/com/destroystokyo/paper/utils/PaperPluginLogger.java b/src/main/java/com/destroystokyo/paper/utils/PaperPluginLogger.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..76f2cb9cd99cad2a9484eab2becd8c36f1dd91b3
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/utils/PaperPluginLogger.java
+@@ -0,0 +1,41 @@
++package com.destroystokyo.paper.utils;
++
++import org.bukkit.plugin.PluginDescriptionFile;
++
++import java.util.logging.Level;
++import java.util.logging.LogManager;
++import java.util.logging.Logger;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Prevents plugins (e.g. Essentials) from changing the parent of the plugin logger.
++ */
++public class PaperPluginLogger extends Logger {
++
++ @NotNull
++ public static Logger getLogger(@NotNull PluginDescriptionFile description) {
++ Logger logger = new PaperPluginLogger(description);
++ if (!LogManager.getLogManager().addLogger(logger)) {
++ // Disable this if it's going to happen across reloads anyways...
++ //logger.log(Level.WARNING, "Could not insert plugin logger - one was already found: {}", LogManager.getLogManager().getLogger(this.getName()));
++ logger = LogManager.getLogManager().getLogger(description.getPrefix() != null ? description.getPrefix() : description.getName());
++ }
++
++ return logger;
++ }
++
++ private PaperPluginLogger(@NotNull PluginDescriptionFile description) {
++ super(description.getPrefix() != null ? description.getPrefix() : description.getName(), null);
++ }
++
++ @Override
++ public void setParent(@NotNull Logger parent) {
++ if (getParent() != null) {
++ warning("Ignoring attempt to change parent of plugin logger");
++ } else {
++ this.log(Level.FINE, "Setting plugin logger parent to {0}", parent);
++ super.setParent(parent);
++ }
++ }
++
++}
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+index bb2e55e97bf887a28cac7d4f9a0a23960d22cf56..04fa3991f6ce4e9dad804f28fc6c947695857089 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+@@ -42,7 +42,7 @@ public abstract class JavaPlugin extends PluginBase {
+ private boolean naggable = true;
+ private FileConfiguration newConfig = null;
+ private File configFile = null;
+- private Logger logger = null; // Paper - PluginLogger -> Logger
++ Logger logger = null; // Paper - PluginLogger -> Logger, package-private
+
+ public JavaPlugin() {
+ final ClassLoader classLoader = this.getClass().getClassLoader();
+@@ -276,8 +276,11 @@ public abstract class JavaPlugin extends PluginBase {
+ this.dataFolder = dataFolder;
+ this.classLoader = classLoader;
+ this.configFile = new File(dataFolder, "config.yml");
+- // Paper - Handle plugin prefix in implementation
+- this.logger = Logger.getLogger(description.getPrefix() != null ? description.getPrefix() : description.getName());
++ // Paper start
++ if (this.logger == null) {
++ this.logger = com.destroystokyo.paper.utils.PaperPluginLogger.getLogger(this.description);
++ }
++ // Paper end
+ }
+
+ /**
+diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+index 36f542a85e0f16e97c65c0ca64ec660ddf75d63e..3a02dbe9d183bc907dcce081d8338d5716ed5242 100644
+--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+@@ -42,6 +42,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+ private JavaPlugin pluginInit;
+ private IllegalStateException pluginState;
+ private final Set<String> seenIllegalAccess = Collections.newSetFromMap(new ConcurrentHashMap<>());
++ private java.util.logging.Logger logger; // Paper - add field
+
+ static {
+ ClassLoader.registerAsParallelCapable();
+@@ -59,6 +60,8 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+ this.manifest = jar.getManifest();
+ this.url = file.toURI().toURL();
+
++ this.logger = com.destroystokyo.paper.utils.PaperPluginLogger.getLogger(description); // Paper - Register logger early
++
+ try {
+ Class<?> jarClass;
+ try {
+@@ -203,6 +206,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+ pluginState = new IllegalStateException("Initial initialization");
+ this.pluginInit = javaPlugin;
+
++ javaPlugin.logger = this.logger; // Paper - set logger
+ javaPlugin.init(loader, loader.server, description, dataFolder, file, this);
+ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0069-Add-PlayerJumpEvent.patch b/Spigot-API-Patches-Unmapped/0069-Add-PlayerJumpEvent.patch
new file mode 100644
index 0000000000..18f2c35b4d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0069-Add-PlayerJumpEvent.patch
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Thu, 28 Sep 2017 17:21:32 -0400
+Subject: [PATCH] Add PlayerJumpEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerJumpEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerJumpEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..289a0d784a3c74caf8a7231b4dd166096b1849a1
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerJumpEvent.java
+@@ -0,0 +1,106 @@
++package com.destroystokyo.paper.event.player;
++
++import com.google.common.base.Preconditions;
++import org.bukkit.Location;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when the server detects the player is jumping.
++ * <p>
++ * Added to avoid the overhead and special case logic that many plugins use
++ * when checking for jumps via PlayerMoveEvent, this event is fired whenever
++ * the server detects that the player is jumping.
++ */
++public class PlayerJumpEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancel = false;
++ @NotNull private Location from;
++ @NotNull private Location to;
++
++ public PlayerJumpEvent(@NotNull final Player player, @NotNull final Location from, @NotNull final Location to) {
++ super(player);
++ this.from = from;
++ this.to = to;
++ }
++
++ /**
++ * Gets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ * <p>
++ * If a jump event is cancelled, the player will be moved or
++ * teleported back to the Location as defined by getFrom(). This will not
++ * fire an event
++ *
++ * @return true if this event is cancelled
++ */
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ /**
++ * Sets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ * <p>
++ * If a jump event is cancelled, the player will be moved or
++ * teleported back to the Location as defined by getFrom(). This will not
++ * fire an event
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ /**
++ * Gets the location this player jumped from
++ *
++ * @return Location the player jumped from
++ */
++ @NotNull
++ public Location getFrom() {
++ return from;
++ }
++
++ /**
++ * Sets the location to mark as where the player jumped from
++ *
++ * @param from New location to mark as the players previous location
++ */
++ public void setFrom(@NotNull Location from) {
++ validateLocation(from);
++ this.from = from;
++ }
++
++ /**
++ * Gets the location this player jumped to
++ *
++ * This information is based on what the client sends, it typically
++ * has little relation to the arc of the jump at any given point.
++ *
++ * @return Location the player jumped to
++ */
++ @NotNull
++ public Location getTo() {
++ return to;
++ }
++
++ private void validateLocation(Location loc) {
++ Preconditions.checkArgument(loc != null, "Cannot use null location!");
++ Preconditions.checkArgument(loc.getWorld() != null, "Cannot use location with null world!");
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0070-Expose-client-protocol-version-and-virtual-host.patch b/Spigot-API-Patches-Unmapped/0070-Expose-client-protocol-version-and-virtual-host.patch
new file mode 100644
index 0000000000..ff8f83ac14
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0070-Expose-client-protocol-version-and-virtual-host.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Tue, 10 Oct 2017 18:44:42 +0200
+Subject: [PATCH] Expose client protocol version and virtual host
+
+Add a NetworkClient interface that provides access to:
+ - The socket address
+ - The protocol version
+ - The virtual host (the hostname/port the client used to connect
+ to the server)
+
+diff --git a/src/main/java/com/destroystokyo/paper/network/NetworkClient.java b/src/main/java/com/destroystokyo/paper/network/NetworkClient.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7b2af1bd72dfbcf4e962a982940fc49b851aa04f
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/network/NetworkClient.java
+@@ -0,0 +1,41 @@
++package com.destroystokyo.paper.network;
++
++import java.net.InetSocketAddress;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Represents a client connected to the server.
++ */
++public interface NetworkClient {
++
++ /**
++ * Returns the socket address of the client.
++ *
++ * @return The client's socket address
++ */
++ @NotNull
++ InetSocketAddress getAddress();
++
++ /**
++ * Returns the protocol version of the client.
++ *
++ * @return The client's protocol version, or {@code -1} if unknown
++ * @see <a href="http://wiki.vg/Protocol_version_numbers">List of protocol
++ * version numbers</a>
++ */
++ int getProtocolVersion();
++
++ /**
++ * Returns the virtual host the client is connected to.
++ *
++ * <p>The virtual host refers to the hostname/port the client used to
++ * connect to the server.</p>
++ *
++ * @return The client's virtual host, or {@code null} if unknown
++ */
++ @Nullable
++ InetSocketAddress getVirtualHost();
++
++}
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 9cf7adf244335ac7dccbdf11f605a8c6910f7414..04f1a6513711dde8576c9b5c2b04619c56b48d8a 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a player, connected or not
+ */
+-public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified { // Paper
++public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, com.destroystokyo.paper.network.NetworkClient { // Paper
+
+ // Paper start
+ @Override
diff --git a/Spigot-API-Patches-Unmapped/0071-Add-PlayerArmorChangeEvent.patch b/Spigot-API-Patches-Unmapped/0071-Add-PlayerArmorChangeEvent.patch
new file mode 100644
index 0000000000..4e9d795db3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0071-Add-PlayerArmorChangeEvent.patch
@@ -0,0 +1,149 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: pkt77 <[email protected]>
+Date: Fri, 10 Nov 2017 23:45:59 -0500
+Subject: [PATCH] Add PlayerArmorChangeEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerArmorChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerArmorChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e406ce639a2e88b78f82f25e71678a669d0a958b
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerArmorChangeEvent.java
+@@ -0,0 +1,137 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.Material;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++
++import java.util.Arrays;
++import java.util.Collections;
++import java.util.HashSet;
++import java.util.Set;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++import static org.bukkit.Material.*;
++
++/**
++ * Called when the player themselves change their armor items
++ * <p>
++ * Not currently called for environmental factors though it <strong>MAY BE IN THE FUTURE</strong>
++ */
++public class PlayerArmorChangeEvent extends PlayerEvent {
++ private static final HandlerList HANDLERS = new HandlerList();
++
++ @NotNull private final SlotType slotType;
++ @Nullable private final ItemStack oldItem;
++ @Nullable private final ItemStack newItem;
++
++ public PlayerArmorChangeEvent(@NotNull Player player, @NotNull SlotType slotType, @Nullable ItemStack oldItem, @Nullable ItemStack newItem) {
++ super(player);
++ this.slotType = slotType;
++ this.oldItem = oldItem;
++ this.newItem = newItem;
++ }
++
++ /**
++ * Gets the type of slot being altered.
++ *
++ * @return type of slot being altered
++ */
++ @NotNull
++ public SlotType getSlotType() {
++ return this.slotType;
++ }
++
++ /**
++ * Gets the existing item that's being replaced
++ *
++ * @return old item
++ */
++ @Nullable
++ public ItemStack getOldItem() {
++ return this.oldItem;
++ }
++
++ /**
++ * Gets the new item that's replacing the old
++ *
++ * @return new item
++ */
++ @Nullable
++ public ItemStack getNewItem() {
++ return this.newItem;
++ }
++
++ @Override
++ public String toString() {
++ return "ArmorChangeEvent{" + "player=" + player + ", slotType=" + slotType + ", oldItem=" + oldItem + ", newItem=" + newItem + '}';
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLERS;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLERS;
++ }
++
++ public enum SlotType {
++ HEAD(NETHERITE_HELMET, DIAMOND_HELMET, GOLDEN_HELMET, IRON_HELMET, CHAINMAIL_HELMET, LEATHER_HELMET, CARVED_PUMPKIN, PLAYER_HEAD, SKELETON_SKULL, ZOMBIE_HEAD, CREEPER_HEAD, WITHER_SKELETON_SKULL, TURTLE_HELMET),
++ CHEST(NETHERITE_CHESTPLATE, DIAMOND_CHESTPLATE, GOLDEN_CHESTPLATE, IRON_CHESTPLATE, CHAINMAIL_CHESTPLATE, LEATHER_CHESTPLATE, ELYTRA),
++ LEGS(NETHERITE_LEGGINGS, DIAMOND_LEGGINGS, GOLDEN_LEGGINGS, IRON_LEGGINGS, CHAINMAIL_LEGGINGS, LEATHER_LEGGINGS),
++ FEET(NETHERITE_BOOTS, DIAMOND_BOOTS, GOLDEN_BOOTS, IRON_BOOTS, CHAINMAIL_BOOTS, LEATHER_BOOTS);
++
++ private final Set<Material> mutableTypes = new HashSet<>();
++ private Set<Material> immutableTypes;
++
++ SlotType(Material... types) {
++ this.mutableTypes.addAll(Arrays.asList(types));
++ }
++
++ /**
++ * Gets an immutable set of all allowed material types that can be placed in an
++ * armor slot.
++ *
++ * @return immutable set of material types
++ */
++ @NotNull
++ public Set<Material> getTypes() {
++ if (immutableTypes == null) {
++ immutableTypes = Collections.unmodifiableSet(mutableTypes);
++ }
++
++ return immutableTypes;
++ }
++
++ /**
++ * Gets the type of slot via the specified material
++ *
++ * @param material material to get slot by
++ * @return slot type the material will go in, or null if it won't
++ */
++ @Nullable
++ public static SlotType getByMaterial(@NotNull Material material) {
++ for (SlotType slotType : values()) {
++ if (slotType.getTypes().contains(material)) {
++ return slotType;
++ }
++ }
++ return null;
++ }
++
++ /**
++ * Gets whether or not this material can be equipped to a slot
++ *
++ * @param material material to check
++ * @return whether or not this material can be equipped
++ */
++ public static boolean isEquipable(@NotNull Material material) {
++ return getByMaterial(material) != null;
++ }
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0072-API-to-get-a-BlockState-without-a-snapshot.patch b/Spigot-API-Patches-Unmapped/0072-API-to-get-a-BlockState-without-a-snapshot.patch
new file mode 100644
index 0000000000..9d4f264793
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0072-API-to-get-a-BlockState-without-a-snapshot.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 6 Nov 2017 21:10:01 -0500
+Subject: [PATCH] API to get a BlockState without a snapshot
+
+This allows you to get a BlockState without creating a snapshot, operating
+on the real tile entity.
+
+This is useful for where performance is needed
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index 0cfad6f84eda6f7bfa1fae041341ccb1021b157d..e89c8079625525667f496c06207da655fe43d749 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -269,6 +269,16 @@ public interface Block extends Metadatable {
+ @NotNull
+ BlockState getState();
+
++ // Paper start
++ /**
++ * @see #getState() optionally disables use of snapshot, to operate on real block data
++ * @param useSnapshot if this block is a TE, should we create a fully copy of the TileEntity
++ * @return BlockState with the current state of this block
++ */
++ @NotNull
++ BlockState getState(boolean useSnapshot);
++ // Paper end
++
+ /**
+ * Returns the biome that this block resides in
+ *
diff --git a/Spigot-API-Patches-Unmapped/0073-AsyncTabCompleteEvent.patch b/Spigot-API-Patches-Unmapped/0073-AsyncTabCompleteEvent.patch
new file mode 100644
index 0000000000..e86dd9b401
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0073-AsyncTabCompleteEvent.patch
@@ -0,0 +1,266 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 26 Nov 2017 13:17:09 -0500
+Subject: [PATCH] AsyncTabCompleteEvent
+
+Let plugins be able to control tab completion of commands and chat async.
+
+This will be useful for frameworks like ACF so we can define async safe completion handlers,
+and avoid going to main for tab completions.
+
+Especially useful if you need to query a database in order to obtain the results for tab
+completion, such as offline players.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..619ed37169c126a8c75d02699a04728bac49d10d
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
+@@ -0,0 +1,177 @@
++/*
++ * Copyright (c) 2017 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.server;
++
++import com.google.common.collect.ImmutableList;
++import org.apache.commons.lang.Validate;
++import org.bukkit.Location;
++import org.bukkit.command.Command;
++import org.bukkit.command.CommandSender;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.util.ArrayList;
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Allows plugins to compute tab completion results asynchronously. If this event provides completions, then the standard synchronous process will not be fired to populate the results. However, the synchronous TabCompleteEvent will fire with the Async results.
++ *
++ * Only 1 process will be allowed to provide completions, the Async Event, or the standard process.
++ */
++public class AsyncTabCompleteEvent extends Event implements Cancellable {
++ @NotNull private final CommandSender sender;
++ @NotNull private final String buffer;
++ private final boolean isCommand;
++ @Nullable
++ private final Location loc;
++ @NotNull private List<String> completions;
++ private boolean cancelled;
++ private boolean handled = false;
++ private boolean fireSyncHandler = true;
++
++ public AsyncTabCompleteEvent(@NotNull CommandSender sender, @NotNull List<String> completions, @NotNull String buffer, boolean isCommand, @Nullable Location loc) {
++ super(true);
++ this.sender = sender;
++ this.completions = completions;
++ this.buffer = buffer;
++ this.isCommand = isCommand;
++ this.loc = loc;
++ }
++
++ /**
++ * Get the sender completing this command.
++ *
++ * @return the {@link CommandSender} instance
++ */
++ @NotNull
++ public CommandSender getSender() {
++ return sender;
++ }
++
++ /**
++ * The list of completions which will be offered to the sender, in order.
++ * This list is mutable and reflects what will be offered.
++ *
++ * If this collection is not empty after the event is fired, then
++ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
++ * or current player names will not be called.
++ *
++ * @return a list of offered completions
++ */
++ @NotNull
++ public List<String> getCompletions() {
++ return completions;
++ }
++
++ /**
++ * Set the completions offered, overriding any already set.
++ * If this collection is not empty after the event is fired, then
++ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
++ * or current player names will not be called.
++ *
++ * The passed collection will be cloned to a new List. You must call {{@link #getCompletions()}} to mutate from here
++ *
++ * @param completions the new completions
++ */
++ public void setCompletions(@NotNull List<String> completions) {
++ Validate.notNull(completions);
++ this.completions = new ArrayList<>(completions);
++ }
++
++ /**
++ * Return the entire buffer which formed the basis of this completion.
++ *
++ * @return command buffer, as entered
++ */
++ @NotNull
++ public String getBuffer() {
++ return buffer;
++ }
++
++ /**
++ * @return True if it is a command being tab completed, false if it is a chat message.
++ */
++ public boolean isCommand() {
++ return isCommand;
++ }
++
++ /**
++ * @return The position looked at by the sender, or null if none
++ */
++ @Nullable
++ public Location getLocation() {
++ return loc;
++ }
++
++ /**
++ * If true, the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
++ * or current player names will not be called.
++ *
++ * @return Is completions considered handled. Always true if completions is not empty.
++ */
++ public boolean isHandled() {
++ return !completions.isEmpty() || handled;
++ }
++
++ /**
++ * Sets whether or not to consider the completion request handled.
++ * If true, the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
++ * or current player names will not be called.
++ *
++ * @param handled if this completion should be marked as being handled
++ */
++ public void setHandled(boolean handled) {
++ this.handled = handled;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Will provide no completions, and will not fire the synchronous process
++ * @param cancelled true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/event/server/TabCompleteEvent.java b/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
+index d1a9956a1573dab54c5ff2e5d67ca86cfe1dc01a..f96c4ba53ab41ea66d4f9a4d54eeabb63f992b58 100644
+--- a/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
++++ b/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.event.server;
+
++import java.util.ArrayList;
+ import java.util.List;
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.command.CommandSender;
+@@ -8,6 +9,7 @@ import org.bukkit.event.Event;
+ import org.bukkit.event.HandlerList;
+ import org.bukkit.event.player.PlayerCommandSendEvent;
+ import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
+
+ /**
+ * Called when a {@link CommandSender} of any description (ie: player or
+@@ -29,6 +31,13 @@ public class TabCompleteEvent extends Event implements Cancellable {
+ private boolean cancelled;
+
+ public TabCompleteEvent(@NotNull CommandSender sender, @NotNull String buffer, @NotNull List<String> completions) {
++ // Paper start
++ this(sender, buffer, completions, sender instanceof org.bukkit.command.ConsoleCommandSender || buffer.startsWith("/"), null);
++ }
++ public TabCompleteEvent(@NotNull CommandSender sender, @NotNull String buffer, @NotNull List<String> completions, boolean isCommand, @Nullable org.bukkit.Location location) {
++ this.isCommand = isCommand;
++ this.loc = location;
++ // Paper end
+ Validate.notNull(sender, "sender");
+ Validate.notNull(buffer, "buffer");
+ Validate.notNull(completions, "completions");
+@@ -69,14 +78,35 @@ public class TabCompleteEvent extends Event implements Cancellable {
+ return completions;
+ }
+
++ // Paper start
++ private final boolean isCommand;
++ private final org.bukkit.Location loc;
++ /**
++ * @return True if it is a command being tab completed, false if it is a chat message.
++ */
++ public boolean isCommand() {
++ return isCommand;
++ }
++
++ /**
++ * @return The position looked at by the sender, or null if none
++ */
++ @Nullable
++ public org.bukkit.Location getLocation() {
++ return loc;
++ }
++ // Paper end
++
+ /**
+ * Set the completions offered, overriding any already set.
+ *
++ * The passed collection will be cloned to a new List. You must call {{@link #getCompletions()}} to mutate from here
++ *
+ * @param completions the new completions
+ */
+ public void setCompletions(@NotNull List<String> completions) {
+ Validate.notNull(completions);
+- this.completions = completions;
++ this.completions = new ArrayList<>(completions); // Paper
+ }
+
+ @Override
diff --git a/Spigot-API-Patches-Unmapped/0074-Display-warning-on-deprecated-recipe-API.patch b/Spigot-API-Patches-Unmapped/0074-Display-warning-on-deprecated-recipe-API.patch
new file mode 100644
index 0000000000..2b6c78a2c4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0074-Display-warning-on-deprecated-recipe-API.patch
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 9 Dec 2017 12:40:25 -0500
+Subject: [PATCH] Display warning on deprecated recipe API
+
+Any plugin still using this API will result in the server saving an inconsistent UUID to player data files,
+which then triggers warnings such as "Tried to load unrecognized recipe: bukkit:9e5b92f5-e549-4f47-b0a8-9f89390ed77b removed now."
+on the players login.
+
+Plugin authors need to define a key to keep it consistent between server restarts.
+
+diff --git a/src/main/java/org/bukkit/inventory/ShapedRecipe.java b/src/main/java/org/bukkit/inventory/ShapedRecipe.java
+index d74b3114f535e1e5e36ae007f1fe0522916a0362..d742c4058ba9aed4fbe1591fd755a06608b06e98 100644
+--- a/src/main/java/org/bukkit/inventory/ShapedRecipe.java
++++ b/src/main/java/org/bukkit/inventory/ShapedRecipe.java
+@@ -25,6 +25,7 @@ public class ShapedRecipe implements Recipe, Keyed {
+ public ShapedRecipe(@NotNull ItemStack result) {
+ Preconditions.checkArgument(result.getType() != Material.AIR, "Recipe must have non-AIR result.");
+ this.key = NamespacedKey.randomKey();
++ new Throwable("Warning: A plugin is creating a recipe using a Deprecated method. This will cause you to receive warnings stating 'Tried to load unrecognized recipe: bukkit:<ID>'. Please ask the author to give their recipe a static key using NamespacedKey.").printStackTrace();
+ this.output = new ItemStack(result);
+ }
+
+diff --git a/src/main/java/org/bukkit/inventory/ShapelessRecipe.java b/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
+index 68447fb8c12356e779b96ec98c54119045046751..84062dd719cb8a6142dc8c806777cb208c6b42b2 100644
+--- a/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
++++ b/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
+@@ -26,6 +26,7 @@ public class ShapelessRecipe implements Recipe, Keyed {
+ public ShapelessRecipe(@NotNull ItemStack result) {
+ Preconditions.checkArgument(result.getType() != Material.AIR, "Recipe must have non-AIR result.");
+ this.key = NamespacedKey.randomKey();
++ new Throwable("Warning: A plugin is creating a recipe using a Deprecated method. This will cause you to receive warnings stating 'Tried to load unrecognized recipe: bukkit:<ID>'. Please ask the author to give their recipe a static key using NamespacedKey.").printStackTrace();
+ this.output = new ItemStack(result);
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0075-PlayerPickupExperienceEvent.patch b/Spigot-API-Patches-Unmapped/0075-PlayerPickupExperienceEvent.patch
new file mode 100644
index 0000000000..906a8ec7c3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0075-PlayerPickupExperienceEvent.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 19 Dec 2017 22:00:41 -0500
+Subject: [PATCH] PlayerPickupExperienceEvent
+
+Allows plugins to cancel a player picking up an experience orb
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerPickupExperienceEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerPickupExperienceEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f7beb22d5105157940b39efe594ace9d4cb153f5
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerPickupExperienceEvent.java
+@@ -0,0 +1,80 @@
++/*
++ * Copyright (c) 2017 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.ExperienceOrb;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a player is attempting to pick up an experience orb
++ */
++public class PlayerPickupExperienceEvent extends PlayerEvent implements Cancellable {
++ @NotNull private final ExperienceOrb experienceOrb;
++
++ public PlayerPickupExperienceEvent(@NotNull Player player, @NotNull ExperienceOrb experienceOrb) {
++ super(player);
++ this.experienceOrb = experienceOrb;
++ }
++
++ /**
++ * @return Returns the Orb that the player is picking up
++ */
++ @NotNull
++ public ExperienceOrb getExperienceOrb() {
++ return experienceOrb;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * If true, Cancels picking up the experience orb, leaving it in the world
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0076-ExperienceOrbMergeEvent.patch b/Spigot-API-Patches-Unmapped/0076-ExperienceOrbMergeEvent.patch
new file mode 100644
index 0000000000..3a4722a776
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0076-ExperienceOrbMergeEvent.patch
@@ -0,0 +1,102 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 19 Dec 2017 22:56:24 -0500
+Subject: [PATCH] ExperienceOrbMergeEvent
+
+Fired when the server is about to merge 2 experience orbs
+Plugins can cancel this if they want to ensure experience orbs do not lose important
+metadata such as spawn reason, or conditionally move data from source to target.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/ExperienceOrbMergeEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/ExperienceOrbMergeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0ce3e397716c28c30ed05e153babd0bfb9dd354a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/ExperienceOrbMergeEvent.java
+@@ -0,0 +1,87 @@
++/*
++ * Copyright (c) 2017 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.ExperienceOrb;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired anytime the server is about to merge 2 experience orbs into one
++ */
++public class ExperienceOrbMergeEvent extends EntityEvent implements Cancellable {
++ @NotNull private final ExperienceOrb mergeTarget;
++ @NotNull private final ExperienceOrb mergeSource;
++
++ public ExperienceOrbMergeEvent(@NotNull ExperienceOrb mergeTarget, @NotNull ExperienceOrb mergeSource) {
++ super(mergeTarget);
++ this.mergeTarget = mergeTarget;
++ this.mergeSource = mergeSource;
++ }
++
++ /**
++ * @return The orb that will absorb the other experience orb
++ */
++ @NotNull
++ public ExperienceOrb getMergeTarget() {
++ return mergeTarget;
++ }
++
++ /**
++ * @return The orb that is subject to being removed and merged into the target orb
++ */
++ @NotNull
++ public ExperienceOrb getMergeSource() {
++ return mergeSource;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * @param cancel true if you wish to cancel this event, and prevent the orbs from merging
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0077-Ability-to-apply-mending-to-XP-API.patch b/Spigot-API-Patches-Unmapped/0077-Ability-to-apply-mending-to-XP-API.patch
new file mode 100644
index 0000000000..a225f3dedc
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0077-Ability-to-apply-mending-to-XP-API.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 20 Dec 2017 17:38:07 -0500
+Subject: [PATCH] Ability to apply mending to XP API
+
+This allows plugins that give players the ability to apply the experience
+points to the Item Mending formula, which will repair an item instead
+of giving the player experience points.
+
+Both an API To standalone mend, and apply mending logic to .giveExp has been added.
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 21bed0d8075335538374fadfdf1cb868e4eebe80..9d1ad67b7d220ab425ac9bf6b1c8d8fb8d5f416c 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -892,12 +892,33 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ public void resetPlayerWeather();
+
++ // Paper start
++ /**
++ * Gives the player the amount of experience specified.
++ *
++ * @param amount Exp amount to give
++ */
++ public default void giveExp(int amount) {
++ giveExp(amount, false);
++ }
+ /**
+ * Gives the player the amount of experience specified.
+ *
+ * @param amount Exp amount to give
++ * @param applyMending Mend players items with mending, with same behavior as picking up orbs. calls {@link #applyMending(int)}
+ */
+- public void giveExp(int amount);
++ public void giveExp(int amount, boolean applyMending);
++
++ /**
++ * Applies the mending effect to any items just as picking up an orb would.
++ *
++ * Can also be called with {@link #giveExp(int, boolean)} by passing true to applyMending
++ *
++ * @param amount Exp to apply
++ * @return the remaining experience
++ */
++ public int applyMending(int amount);
++ // Paper end
+
+ /**
+ * Gives the player the amount of experience levels specified. Levels can
diff --git a/Spigot-API-Patches-Unmapped/0078-PreCreatureSpawnEvent.patch b/Spigot-API-Patches-Unmapped/0078-PreCreatureSpawnEvent.patch
new file mode 100644
index 0000000000..0090330de9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0078-PreCreatureSpawnEvent.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 14 Jan 2018 16:59:43 -0500
+Subject: [PATCH] PreCreatureSpawnEvent
+
+Adds an event to fire before an Entity is created, so that plugins that need to cancel
+CreatureSpawnEvent can do so from this event instead.
+
+Cancelling CreatureSpawnEvent rapidly causes a lot of garbage collection and CPU waste
+as it's done after the Entity object has been fully created.
+
+Mob Limiting plugins and blanket "ban this type of monster" plugins should use this event
+instead and save a lot of server resources.
+
+See: https://github.com/PaperMC/Paper/issues/917
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/PreCreatureSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/PreCreatureSpawnEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..3ad231aa3206c8cfd5ec995249584cebab5d11f3
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/PreCreatureSpawnEvent.java
+@@ -0,0 +1,105 @@
++package com.destroystokyo.paper.event.entity;
++
++import com.google.common.base.Preconditions;
++import org.bukkit.Location;
++import org.bukkit.entity.EntityType;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * WARNING: This event only fires for a limited number of cases, and not for every case that CreatureSpawnEvent does.
++ *
++ * You should still listen to CreatureSpawnEvent as a backup, and only use this event as an "enhancement".
++ * The intent of this event is to improve server performance, so it fires even if the spawning might fail later, for
++ * example when the entity would be unable to spawn due to limited space or lighting.
++ *
++ * Currently: NATURAL and SPAWNER based reasons. Please submit a Pull Request for future additions.
++ * Also, Plugins that replace Entity Registrations with their own custom entities might not fire this event.
++ */
++public class PreCreatureSpawnEvent extends Event implements Cancellable {
++ @NotNull private final Location location;
++ @NotNull private final EntityType type;
++ @NotNull private final CreatureSpawnEvent.SpawnReason reason;
++ private boolean shouldAbortSpawn;
++
++ public PreCreatureSpawnEvent(@NotNull Location location, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason) {
++ this.location = Preconditions.checkNotNull(location, "Location may not be null").clone();
++ this.type = Preconditions.checkNotNull(type, "Type may not be null");
++ this.reason = Preconditions.checkNotNull(reason, "Reason may not be null");
++ }
++
++ /**
++ * @return The location this creature is being spawned at
++ */
++ @NotNull
++ public Location getSpawnLocation() {
++ return location;
++ }
++
++ /**
++ * @return The type of creature being spawned
++ */
++ @NotNull
++ public EntityType getType() {
++ return type;
++ }
++
++ /**
++ * @return Reason this creature is spawning (ie, NATURAL vs SPAWNER)
++ */
++ @NotNull
++ public CreatureSpawnEvent.SpawnReason getReason() {
++ return reason;
++ }
++
++ /**
++ * @return If the spawn process should be aborted vs trying more attempts
++ */
++ public boolean shouldAbortSpawn() {
++ return shouldAbortSpawn;
++ }
++
++ /**
++ * Set this if you are more blanket blocking all types of these spawns, and wish to abort the spawn process from
++ * trying more attempts after this cancellation.
++ *
++ * @param shouldAbortSpawn Set if the spawn process should be aborted vs trying more attempts
++ */
++ public void setShouldAbortSpawn(boolean shouldAbortSpawn) {
++ this.shouldAbortSpawn = shouldAbortSpawn;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ /**
++ * @return If the spawn of this creature is cancelled or not
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Cancelling this event is more effecient than cancelling CreatureSpawnEvent
++ * @param cancel true if you wish to cancel this event, and abort the spawn of this creature
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0079-PlayerNaturallySpawnCreaturesEvent.patch b/Spigot-API-Patches-Unmapped/0079-PlayerNaturallySpawnCreaturesEvent.patch
new file mode 100644
index 0000000000..a6fa8a9b72
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0079-PlayerNaturallySpawnCreaturesEvent.patch
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 14 Jan 2018 17:31:37 -0500
+Subject: [PATCH] PlayerNaturallySpawnCreaturesEvent
+
+This event can be used for when you want to exclude a certain player
+from triggering monster spawns on a server.
+
+Also a highly more effecient way to blanket block spawns in a world
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/PlayerNaturallySpawnCreaturesEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/PlayerNaturallySpawnCreaturesEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..112a0dbf522b8e74ce882678434923814e6b187f
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/PlayerNaturallySpawnCreaturesEvent.java
+@@ -0,0 +1,64 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when the server is calculating what chunks to try to spawn monsters in every Monster Spawn Tick event
++ */
++public class PlayerNaturallySpawnCreaturesEvent extends PlayerEvent implements Cancellable {
++ private byte radius;
++
++ public PlayerNaturallySpawnCreaturesEvent(@NotNull Player player, byte radius) {
++ super(player);
++ this.radius = radius;
++ }
++
++ /**
++ * @return The radius of chunks around this player to be included in natural spawn selection
++ */
++ public byte getSpawnRadius() {
++ return radius;
++ }
++
++ /**
++ * @param radius The radius of chunks around this player to be included in natural spawn selection
++ */
++ public void setSpawnRadius(byte radius) {
++ this.radius = radius;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ /**
++ * @return If this players chunks will be excluded from natural spawns
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * @param cancel true if you wish to cancel this event, and not include this players chunks for natural spawning
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0080-Add-setPlayerProfile-API-for-Skulls.patch b/Spigot-API-Patches-Unmapped/0080-Add-setPlayerProfile-API-for-Skulls.patch
new file mode 100644
index 0000000000..42ffd3f948
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0080-Add-setPlayerProfile-API-for-Skulls.patch
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 19 Jan 2018 00:29:28 -0500
+Subject: [PATCH] Add setPlayerProfile API for Skulls
+
+This allows you to create already filled textures on Skulls to avoid texture lookups
+which commonly cause rate limit issues with Mojang API
+
+diff --git a/src/main/java/org/bukkit/block/Skull.java b/src/main/java/org/bukkit/block/Skull.java
+index 943d751fb3e48212fbe258845beba03c25fa22d9..a6914f01e01e9103702185f92b0209b3c84c152a 100644
+--- a/src/main/java/org/bukkit/block/Skull.java
++++ b/src/main/java/org/bukkit/block/Skull.java
+@@ -7,6 +7,7 @@ import org.bukkit.block.data.BlockData;
+ import org.jetbrains.annotations.Contract;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
++import com.destroystokyo.paper.profile.PlayerProfile; // Paper
+
+ /**
+ * Represents a captured state of a skull block.
+@@ -61,6 +62,20 @@ public interface Skull extends TileState {
+ */
+ public void setOwningPlayer(@NotNull OfflinePlayer player);
+
++ // Paper start
++ /**
++ * Sets this skull to use the supplied Player Profile, which can include textures already prefilled.
++ * @param profile The profile to set this Skull to use, may not be null
++ */
++ void setPlayerProfile(@NotNull PlayerProfile profile);
++
++ /**
++ * If the skull has an owner, per {@link #hasOwner()}, return the owners {@link PlayerProfile}
++ * @return The profile of the owner, if set
++ */
++ @Nullable PlayerProfile getPlayerProfile();
++ // Paper end
++
+ /**
+ * Gets the rotation of the skull in the world (or facing direction if this
+ * is a wall mounted skull).
+diff --git a/src/main/java/org/bukkit/inventory/meta/SkullMeta.java b/src/main/java/org/bukkit/inventory/meta/SkullMeta.java
+index 496254f959345d74167a9b44d160ea1bb428c5a1..88d1c889c09adb91abb09a8e43a30c871b217da2 100644
+--- a/src/main/java/org/bukkit/inventory/meta/SkullMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/SkullMeta.java
+@@ -1,9 +1,11 @@
+ package org.bukkit.inventory.meta;
+
++import com.destroystokyo.paper.profile.PlayerProfile;
+ import org.bukkit.OfflinePlayer;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
++
+ /**
+ * Represents a skull that can have an owner.
+ */
+@@ -36,6 +38,20 @@ public interface SkullMeta extends ItemMeta {
+ @Deprecated
+ boolean setOwner(@Nullable String owner);
+
++ // Paper start
++ /**
++ * Sets this skull to use the supplied Player Profile, which can include textures already prefilled.
++ * @param profile The profile to set this Skull to use, or null to clear owner
++ */
++ void setPlayerProfile(@Nullable PlayerProfile profile);
++
++ /**
++ * If the skull has an owner, per {@link #hasOwner()}, return the owners {@link PlayerProfile}
++ * @return The profile of the owner, if set
++ */
++ @Nullable PlayerProfile getPlayerProfile();
++ // Paper end
++
+ /**
+ * Gets the owner of the skull.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0081-Fill-Profile-Property-Events.patch b/Spigot-API-Patches-Unmapped/0081-Fill-Profile-Property-Events.patch
new file mode 100644
index 0000000000..5511f6449a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0081-Fill-Profile-Property-Events.patch
@@ -0,0 +1,176 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 2 Jan 2018 00:31:08 -0500
+Subject: [PATCH] Fill Profile Property Events
+
+Allows plugins to populate profile properties from local sources to avoid calls out to Mojang API
+to fill in textures for example.
+
+If Mojang API does need to be hit, event fire so you can get the results.
+
+This is useful for implementing a ProfileCache for Player Skulls
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/profile/FillProfileEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/FillProfileEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..71f36e9cae209ec6861835a5e76e018de959040a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/profile/FillProfileEvent.java
+@@ -0,0 +1,75 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.profile;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import com.destroystokyo.paper.profile.ProfileProperty;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.util.Set;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired once a profiles additional properties (such as textures) has been filled
++ */
++public class FillProfileEvent extends Event {
++ @NotNull private final PlayerProfile profile;
++
++ public FillProfileEvent(@NotNull PlayerProfile profile) {
++ super(!org.bukkit.Bukkit.isPrimaryThread());
++ this.profile = profile;
++ }
++
++ /**
++ * @return The Profile that had properties filled
++ */
++ @NotNull
++ public PlayerProfile getPlayerProfile() {
++ return profile;
++ }
++
++ /**
++ * Same as .getPlayerProfile().getProperties()
++ *
++ * @see PlayerProfile#getProperties()
++ * @return The new properties on the profile.
++ */
++ @NotNull
++ public Set<ProfileProperty> getProperties() {
++ return profile.getProperties();
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/profile/PreFillProfileEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/PreFillProfileEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..021bc86310a06f84b39459e0eb8927802726399c
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/profile/PreFillProfileEvent.java
+@@ -0,0 +1,77 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.profile;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import com.destroystokyo.paper.profile.ProfileProperty;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.util.Collection;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when the server is requesting to fill in properties of an incomplete profile, such as textures.
++ *
++ * Allows plugins to pre populate cached properties and avoid a call to the Mojang API
++ */
++public class PreFillProfileEvent extends Event {
++ @NotNull private final PlayerProfile profile;
++
++ public PreFillProfileEvent(@NotNull PlayerProfile profile) {
++ super(!org.bukkit.Bukkit.isPrimaryThread());
++ this.profile = profile;
++ }
++
++ /**
++ * @return The profile that needs its properties filled
++ */
++ @NotNull
++ public PlayerProfile getPlayerProfile() {
++ return profile;
++ }
++
++ /**
++ * Sets the properties on the profile, avoiding the call to the Mojang API
++ * Same as .getPlayerProfile().setProperties(properties);
++ *
++ * @see PlayerProfile#setProperties(Collection)
++ * @param properties The properties to set/append
++ */
++ public void setProperties(@NotNull Collection<ProfileProperty> properties) {
++ profile.setProperties(properties);
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0082-PlayerAdvancementCriterionGrantEvent.patch b/Spigot-API-Patches-Unmapped/0082-PlayerAdvancementCriterionGrantEvent.patch
new file mode 100644
index 0000000000..5e8eaca58a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0082-PlayerAdvancementCriterionGrantEvent.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 19 Jan 2018 08:15:14 -0600
+Subject: [PATCH] PlayerAdvancementCriterionGrantEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerAdvancementCriterionGrantEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerAdvancementCriterionGrantEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..bb8d7c959cdea4b66455a49e74804ea4b126620d
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerAdvancementCriterionGrantEvent.java
+@@ -0,0 +1,63 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.advancement.Advancement;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player is granted a criteria in an advancement.
++ */
++public class PlayerAdvancementCriterionGrantEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final Advancement advancement;
++ @NotNull private final String criterion;
++ private boolean cancel = false;
++
++ public PlayerAdvancementCriterionGrantEvent(@NotNull Player who, @NotNull Advancement advancement, @NotNull String criterion) {
++ super(who);
++ this.advancement = advancement;
++ this.criterion = criterion;
++ }
++
++ /**
++ * Get the advancement which has been affected.
++ *
++ * @return affected advancement
++ */
++ @NotNull
++ public Advancement getAdvancement() {
++ return advancement;
++ }
++
++ /**
++ * Get the criterion which has been granted.
++ *
++ * @return granted criterion
++ */
++ @NotNull
++ public String getCriterion() {
++ return criterion;
++ }
++
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0083-Add-ArmorStand-Item-Meta.patch b/Spigot-API-Patches-Unmapped/0083-Add-ArmorStand-Item-Meta.patch
new file mode 100644
index 0000000000..6287ef49e7
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0083-Add-ArmorStand-Item-Meta.patch
@@ -0,0 +1,96 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Sat, 27 Jan 2018 17:06:24 -0500
+Subject: [PATCH] Add ArmorStand Item Meta
+
+This is adds basic item meta for armor stands. It does not add all
+possible metadata however.
+
+There are armor, hand, and equipment types, as well as position data
+that can also be added here. This initial addition should serve a
+starting point for future additions in this area.
+
+diff --git a/src/main/java/com/destroystokyo/paper/inventory/meta/ArmorStandMeta.java b/src/main/java/com/destroystokyo/paper/inventory/meta/ArmorStandMeta.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7e4acfff16db80a75e1ff2fee1972b16955b0918
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/inventory/meta/ArmorStandMeta.java
+@@ -0,0 +1,78 @@
++package com.destroystokyo.paper.inventory.meta;
++
++import org.bukkit.inventory.meta.ItemMeta;
++
++public interface ArmorStandMeta extends ItemMeta {
++
++ /**
++ * Gets whether the ArmorStand should be invisible when spawned
++ *
++ * @return true if this should be invisible
++ */
++ boolean isInvisible();
++
++ /**
++ * Gets whether this ArmorStand should have no base plate when spawned
++ *
++ * @return true if it will not have a base plate
++ */
++ boolean hasNoBasePlate();
++
++ /**
++ * Gets whether this ArmorStand should show arms when spawned
++ *
++ * @return true if it will show arms
++ */
++ boolean shouldShowArms();
++
++ /**
++ * Gets whether this ArmorStand will be small when spawned
++ *
++ * @return true if it will be small
++ */
++ boolean isSmall();
++
++ /**
++ * Gets whether this ArmorStand will be a marker when spawned
++ * The exact details of this flag are an implementation detail
++ *
++ * @return true if it will be a marker
++ */
++ boolean isMarker();
++
++ /**
++ * Sets that this ArmorStand should be invisible when spawned
++ *
++ * @param invisible true if set invisible
++ */
++ void setInvisible(boolean invisible);
++
++ /**
++ * Sets that this ArmorStand should have no base plate when spawned
++ *
++ * @param noBasePlate true if no base plate
++ */
++ void setNoBasePlate(boolean noBasePlate);
++
++ /**
++ * Sets that this ArmorStand should show arms when spawned
++ *
++ * @param showArms true if show arms
++ */
++ void setShowArms(boolean showArms);
++
++ /**
++ * Sets that this ArmorStand should be small when spawned
++ *
++ * @param small true if small
++ */
++ void setSmall(boolean small);
++
++ /**
++ * Sets that this ArmorStand should be a marker when spawned
++ * The exact details of this flag are an implementation detail
++ *
++ * @param marker true if a marker
++ */
++ void setMarker(boolean marker);
++}
diff --git a/Spigot-API-Patches-Unmapped/0084-Optimize-Hoppers.patch b/Spigot-API-Patches-Unmapped/0084-Optimize-Hoppers.patch
new file mode 100644
index 0000000000..79c141af55
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0084-Optimize-Hoppers.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 18 Jan 2018 01:00:27 -0500
+Subject: [PATCH] Optimize Hoppers
+
+Adds data about what Item related methods were used in InventoryMoveItem event
+so that the server can improve the performance of this event.
+
+diff --git a/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java b/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java
+index a8c48f5a416326e96c431e5fa22edee04825530e..04d4a83bfc4f86341f9d72128458154d08c8ec43 100644
+--- a/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/InventoryMoveItemEvent.java
+@@ -31,6 +31,8 @@ public class InventoryMoveItemEvent extends Event implements Cancellable {
+ private final Inventory destinationInventory;
+ private ItemStack itemStack;
+ private final boolean didSourceInitiate;
++ public boolean calledGetItem; // Paper
++ public boolean calledSetItem; // Paper
+
+ public InventoryMoveItemEvent(@NotNull final Inventory sourceInventory, @NotNull final ItemStack itemStack, @NotNull final Inventory destinationInventory, final boolean didSourceInitiate) {
+ Validate.notNull(itemStack, "ItemStack cannot be null");
+@@ -58,7 +60,8 @@ public class InventoryMoveItemEvent extends Event implements Cancellable {
+ */
+ @NotNull
+ public ItemStack getItem() {
+- return itemStack.clone();
++ calledGetItem = true; // Paper - record this method was used for auto detection of mode
++ return itemStack; // Paper - Removed clone, handled better in Server
+ }
+
+ /**
+@@ -70,6 +73,7 @@ public class InventoryMoveItemEvent extends Event implements Cancellable {
+ */
+ public void setItem(@NotNull ItemStack itemStack) {
+ Validate.notNull(itemStack, "ItemStack cannot be null. Cancel the event if you want nothing to be transferred.");
++ calledSetItem = true; // Paper - record this method was used for auto detection of mode
+ this.itemStack = itemStack.clone();
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0085-Tameable-getOwnerUniqueId-API.patch b/Spigot-API-Patches-Unmapped/0085-Tameable-getOwnerUniqueId-API.patch
new file mode 100644
index 0000000000..b911542a60
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0085-Tameable-getOwnerUniqueId-API.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 24 Feb 2018 00:55:52 -0500
+Subject: [PATCH] Tameable#getOwnerUniqueId API
+
+This is faster if all you need is the UUID, as .getOwner() will cause
+an OfflinePlayer to be loaded from disk.
+
+diff --git a/src/main/java/org/bukkit/entity/Tameable.java b/src/main/java/org/bukkit/entity/Tameable.java
+index 26c996cd41acc88490ac0135a9239cffc03e8efb..65e68da98ab66ed781bce2f0dbe0913be48d2990 100644
+--- a/src/main/java/org/bukkit/entity/Tameable.java
++++ b/src/main/java/org/bukkit/entity/Tameable.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.entity;
+
++import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
+ public interface Tameable extends Animals {
+@@ -25,9 +26,22 @@ public interface Tameable extends Animals {
+ */
+ public void setTamed(boolean tame);
+
++ // Paper start
++ /**
++ * Gets the owners UUID
++ *
++ * @return the owners UUID, or null if not owned
++ */
++ @Nullable
++ public java.util.UUID getOwnerUniqueId();
++ // Paper end
++
+ /**
+ * Gets the current owning AnimalTamer
+ *
++ * @see #getOwnerUniqueId() Recommended to use UUID version instead of this for performance.
++ * This method will cause OfflinePlayer to be loaded from disk if the owner is not online.
++ *
+ * @return the owning AnimalTamer, or null if not owned
+ */
+ @Nullable
diff --git a/Spigot-API-Patches-Unmapped/0086-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch b/Spigot-API-Patches-Unmapped/0086-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch
new file mode 100644
index 0000000000..a2a9f5270f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0086-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch
@@ -0,0 +1,91 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 18 Mar 2018 11:43:30 -0400
+Subject: [PATCH] Ability to change PlayerProfile in AsyncPreLoginEvent
+
+This will allow you to change the players name or skin on login.
+
+diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+index 992d1025ca02020e87a9ab5db83d249427f41d69..a40b57edb1aeff71fc0b9767d410950da5c06283 100644
+--- a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
++++ b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+@@ -2,6 +2,9 @@ package org.bukkit.event.player;
+
+ import java.net.InetAddress;
+ import java.util.UUID;
++
++import com.destroystokyo.paper.profile.PlayerProfile;
++import org.bukkit.Bukkit;
+ import org.bukkit.event.Event;
+ import org.bukkit.event.HandlerList;
+ import org.jetbrains.annotations.NotNull;
+@@ -15,9 +18,9 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ private Result result;
+ private net.kyori.adventure.text.Component message; // Paper
+- private final String name;
++ //private String name; // Paper - Not used anymore
+ private final InetAddress ipAddress;
+- private final UUID uniqueId;
++ //private UUID uniqueId; // Paper - Not used anymore
+
+ @Deprecated
+ public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress) {
+@@ -25,12 +28,37 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ }
+
+ public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId) {
++ // Paper start
++ this(name, ipAddress, uniqueId, Bukkit.createProfile(uniqueId, name));
++ }
++ private PlayerProfile profile;
++
++ /**
++ * Gets the PlayerProfile of the player logging in
++ * @return The Profile
++ */
++ @NotNull
++ public PlayerProfile getPlayerProfile() {
++ return profile;
++ }
++
++ /**
++ * Changes the PlayerProfile the player will login as
++ * @param profile The profile to use
++ */
++ public void setPlayerProfile(@NotNull PlayerProfile profile) {
++ this.profile = profile;
++ }
++
++ public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final UUID uniqueId, @NotNull PlayerProfile profile) {
+ super(true);
++ this.profile = profile;
++ // Paper end
+ this.result = Result.ALLOWED;
+ this.message = net.kyori.adventure.text.Component.empty(); // Paper
+- this.name = name;
++ //this.name = name; // Paper - Not used anymore
+ this.ipAddress = ipAddress;
+- this.uniqueId = uniqueId;
++ //this.uniqueId = uniqueId; // Paper - Not used anymore
+ }
+
+ /**
+@@ -193,7 +221,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ */
+ @NotNull
+ public String getName() {
+- return name;
++ return profile.getName(); // Paper
+ }
+
+ /**
+@@ -213,7 +241,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ */
+ @NotNull
+ public UUID getUniqueId() {
+- return uniqueId;
++ return profile.getId(); // Paper
+ }
+
+ @NotNull
diff --git a/Spigot-API-Patches-Unmapped/0087-Add-extended-PaperServerListPingEvent.patch b/Spigot-API-Patches-Unmapped/0087-Add-extended-PaperServerListPingEvent.patch
new file mode 100644
index 0000000000..f35ae4323f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0087-Add-extended-PaperServerListPingEvent.patch
@@ -0,0 +1,381 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Wed, 11 Oct 2017 15:55:38 +0200
+Subject: [PATCH] Add extended PaperServerListPingEvent
+
+Add a new event that extends the original ServerListPingEvent
+and allows full control of the response sent to the client.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java b/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..baac2e4f090a490372ef4aed92c8a5771955e921
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java
+@@ -0,0 +1,334 @@
++package com.destroystokyo.paper.event.server;
++
++import static java.util.Objects.requireNonNull;
++
++import com.destroystokyo.paper.network.StatusClient;
++import com.destroystokyo.paper.profile.PlayerProfile;
++import org.bukkit.Bukkit;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.server.ServerListPingEvent;
++import org.bukkit.util.CachedServerIcon;
++
++import java.util.ArrayList;
++import java.util.Iterator;
++import java.util.List;
++import java.util.NoSuchElementException;
++import java.util.UUID;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Extended version of {@link ServerListPingEvent} that allows full control
++ * of the response sent to the client.
++ */
++public class PaperServerListPingEvent extends ServerListPingEvent implements Cancellable {
++
++ @NotNull private final StatusClient client;
++
++ private int numPlayers;
++ private boolean hidePlayers;
++ @NotNull private final List<PlayerProfile> playerSample = new ArrayList<>();
++
++ @NotNull private String version;
++ private int protocolVersion;
++
++ @Nullable private CachedServerIcon favicon;
++
++ private boolean cancelled;
++
++ private boolean originalPlayerCount = true;
++ private Object[] players;
++
++ @Deprecated
++ public PaperServerListPingEvent(@NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
++ @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon) {
++ super(client.getAddress().getAddress(), motd, numPlayers, maxPlayers);
++ this.client = client;
++ this.numPlayers = numPlayers;
++ this.version = version;
++ this.protocolVersion = protocolVersion;
++ setServerIcon(favicon);
++ }
++
++ public PaperServerListPingEvent(@NotNull StatusClient client, @NotNull net.kyori.adventure.text.Component motd, int numPlayers, int maxPlayers,
++ @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon) {
++ super(client.getAddress().getAddress(), motd, numPlayers, maxPlayers);
++ this.client = client;
++ this.numPlayers = numPlayers;
++ this.version = version;
++ this.protocolVersion = protocolVersion;
++ setServerIcon(favicon);
++ }
++
++ /**
++ * Returns the {@link StatusClient} pinging the server.
++ *
++ * @return The client
++ */
++ @NotNull
++ public StatusClient getClient() {
++ return this.client;
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * <p>Returns {@code -1} if players are hidden using
++ * {@link #shouldHidePlayers()}.</p>
++ */
++ @Override
++ public int getNumPlayers() {
++ if (this.hidePlayers) {
++ return -1;
++ }
++
++ return this.numPlayers;
++ }
++
++ /**
++ * Sets the number of players displayed in the server list.
++ *
++ * <p>Note that this won't have any effect if {@link #shouldHidePlayers()}
++ * is enabled.</p>
++ *
++ * @param numPlayers The number of online players
++ */
++ public void setNumPlayers(int numPlayers) {
++ if (this.numPlayers != numPlayers) {
++ this.numPlayers = numPlayers;
++ this.originalPlayerCount = false;
++ }
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * <p>Returns {@code -1} if players are hidden using
++ * {@link #shouldHidePlayers()}.</p>
++ */
++ @Override
++ public int getMaxPlayers() {
++ if (this.hidePlayers) {
++ return -1;
++ }
++
++ return super.getMaxPlayers();
++ }
++
++ /**
++ * Returns whether all player related information is hidden in the server
++ * list. This will cause {@link #getNumPlayers()}, {@link #getMaxPlayers()}
++ * and {@link #getPlayerSample()} to be skipped in the response.
++ *
++ * <p>The Vanilla Minecraft client will display the player count as {@code ???}
++ * when this option is enabled.</p>
++ *
++ * @return {@code true} if the player count is hidden
++ */
++ public boolean shouldHidePlayers() {
++ return hidePlayers;
++ }
++
++ /**
++ * Sets whether all player related information is hidden in the server
++ * list. This will cause {@link #getNumPlayers()}, {@link #getMaxPlayers()}
++ * and {@link #getPlayerSample()} to be skipped in the response.
++ *
++ * <p>The Vanilla Minecraft client will display the player count as {@code ???}
++ * when this option is enabled.</p>
++ *
++ * @param hidePlayers {@code true} if the player count should be hidden
++ */
++ public void setHidePlayers(boolean hidePlayers) {
++ this.hidePlayers = hidePlayers;
++ }
++
++ /**
++ * Returns a mutable list of {@link PlayerProfile} that will be displayed
++ * as online players on the client.
++ *
++ * <p>The Vanilla Minecraft client will display them when hovering the
++ * player count with the mouse.</p>
++ *
++ * @return The mutable player sample list
++ */
++ @NotNull
++ public List<PlayerProfile> getPlayerSample() {
++ return this.playerSample;
++ }
++
++ /**
++ * Returns the version that will be sent as server version on the client.
++ *
++ * @return The server version
++ */
++ @NotNull
++ public String getVersion() {
++ return version;
++ }
++
++ /**
++ * Sets the version that will be sent as server version to the client.
++ *
++ * @param version The server version
++ */
++ public void setVersion(@NotNull String version) {
++ this.version = requireNonNull(version, "version");
++ }
++
++ /**
++ * Returns the protocol version that will be sent as the protocol version
++ * of the server to the client.
++ *
++ * @return The protocol version of the server, or {@code -1} if the server
++ * has not finished initialization yet
++ */
++ public int getProtocolVersion() {
++ return protocolVersion;
++ }
++
++ /**
++ * Sets the protocol version that will be sent as the protocol version
++ * of the server to the client.
++ *
++ * @param protocolVersion The protocol version of the server
++ */
++ public void setProtocolVersion(int protocolVersion) {
++ this.protocolVersion = protocolVersion;
++ }
++
++ /**
++ * Gets the server icon sent to the client.
++ *
++ * @return The icon to send to the client, or {@code null} for none
++ */
++ @Nullable
++ public CachedServerIcon getServerIcon() {
++ return this.favicon;
++ }
++
++ /**
++ * Sets the server icon sent to the client.
++ *
++ * @param icon The icon to send to the client, or {@code null} for none
++ */
++ @Override
++ public void setServerIcon(@Nullable CachedServerIcon icon) {
++ if (icon != null && icon.isEmpty()) {
++ // Represent empty icons as null
++ icon = null;
++ }
++
++ this.favicon = icon;
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * <p>Cancelling this event will cause the connection to be closed immediately,
++ * without sending a response to the client.</p>
++ */
++ @Override
++ public boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * <p>Cancelling this event will cause the connection to be closed immediately,
++ * without sending a response to the client.</p>
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * <p><b>Note:</b> For compatibility reasons, this method will return all
++ * online players, not just the ones referenced in {@link #getPlayerSample()}.
++ * Removing a player will:</p>
++ *
++ * <ul>
++ * <li>Decrement the online player count (if and only if) the player
++ * count wasn't changed by another plugin before.</li>
++ * <li>Remove all entries from {@link #getPlayerSample()} that refer to
++ * the removed player (based on their {@link UUID}).</li>
++ * </ul>
++ */
++ @NotNull
++ @Override
++ public Iterator<Player> iterator() {
++ if (this.players == null) {
++ this.players = getOnlinePlayers();
++ }
++
++ return new PlayerIterator();
++ }
++
++ @NotNull
++ protected Object[] getOnlinePlayers() {
++ return Bukkit.getOnlinePlayers().toArray();
++ }
++
++ @NotNull
++ protected Player getBukkitPlayer(@NotNull Object player) {
++ return (Player) player;
++ }
++
++ private final class PlayerIterator implements Iterator<Player> {
++
++ private int next;
++ private int current;
++ @Nullable private Player player;
++
++ @Override
++ public boolean hasNext() {
++ for (; this.next < players.length; this.next++) {
++ if (players[this.next] != null) {
++ return true;
++ }
++ }
++
++ return false;
++ }
++
++ @NotNull
++ @Override
++ public Player next() {
++ if (!hasNext()) {
++ this.player = null;
++ throw new NoSuchElementException();
++ }
++
++ this.current = this.next++;
++ return this.player = getBukkitPlayer(players[this.current]);
++ }
++
++ @Override
++ public void remove() {
++ if (this.player == null) {
++ throw new IllegalStateException();
++ }
++
++ UUID uniqueId = this.player.getUniqueId();
++ this.player = null;
++
++ // Remove player from iterator
++ players[this.current] = null;
++
++ // Remove player from sample
++ getPlayerSample().removeIf(p -> uniqueId.equals(p.getId()));
++
++ // Decrement player count
++ if (originalPlayerCount) {
++ numPlayers--;
++ }
++ }
++ }
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/network/StatusClient.java b/src/main/java/com/destroystokyo/paper/network/StatusClient.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..517d15238ed117f38bbd39f570874014cecf7bb5
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/network/StatusClient.java
+@@ -0,0 +1,13 @@
++package com.destroystokyo.paper.network;
++
++import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
++
++/**
++ * Represents a client requesting the current status from the server (e.g. from
++ * the server list).
++ *
++ * @see PaperServerListPingEvent
++ */
++public interface StatusClient extends NetworkClient {
++
++}
+diff --git a/src/main/java/org/bukkit/util/CachedServerIcon.java b/src/main/java/org/bukkit/util/CachedServerIcon.java
+index 612958a331575d1da2715531ebdf6b1168f2e860..bb4f7702ced0baf0670a7a21d48ad528b7249361 100644
+--- a/src/main/java/org/bukkit/util/CachedServerIcon.java
++++ b/src/main/java/org/bukkit/util/CachedServerIcon.java
+@@ -18,4 +18,9 @@ public interface CachedServerIcon {
+ @Nullable
+ public String getData(); // Paper
+
++ // Paper start
++ default boolean isEmpty() {
++ return getData() == null;
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0088-Player.setPlayerProfile-API.patch b/Spigot-API-Patches-Unmapped/0088-Player.setPlayerProfile-API.patch
new file mode 100644
index 0000000000..0740589758
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0088-Player.setPlayerProfile-API.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 18 Mar 2018 12:28:55 -0400
+Subject: [PATCH] Player.setPlayerProfile API
+
+This can be useful for changing name or skins after a player has logged in.
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 3063f917f34a28e347daac54bc73e000d6771fed..30b97d4e20f5d89533f0aebd2245fa7578c97420 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -4,6 +4,7 @@ import java.net.InetSocketAddress;
+ import java.util.UUID;
+ import com.destroystokyo.paper.Title; // Paper
+ import net.kyori.adventure.text.Component;
++import com.destroystokyo.paper.profile.PlayerProfile; // Paper
+ import org.bukkit.DyeColor;
+ import org.bukkit.Effect;
+ import org.bukkit.GameMode;
+@@ -1699,6 +1700,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED}
+ */
+ boolean hasResourcePack();
++
++ /**
++ * Gets a copy of this players profile
++ * @return The players profile object
++ */
++ @NotNull
++ PlayerProfile getPlayerProfile();
++
++ /**
++ * Changes the PlayerProfile for this player. This will cause this player
++ * to be reregistered to all clients that can currently see this player
++ * @param profile The new profile to use
++ */
++ void setPlayerProfile(@NotNull PlayerProfile profile);
+ // Paper end
+
+ // Spigot start
diff --git a/Spigot-API-Patches-Unmapped/0089-getPlayerUniqueId-API.patch b/Spigot-API-Patches-Unmapped/0089-getPlayerUniqueId-API.patch
new file mode 100644
index 0000000000..6586351ea9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0089-getPlayerUniqueId-API.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 22 Mar 2018 01:39:28 -0400
+Subject: [PATCH] getPlayerUniqueId API
+
+Gets the unique ID of the player currently known as the specified player name
+In Offline Mode, will return an Offline UUID
+
+This is a more performant way to obtain a UUID for a name than loading an OfflinePlayer
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index f5d3a7370390871d1b6075f32846d1a942b05b7f..2d7f8e128e23934a8fe26baf19198b7ffc8447bb 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -504,6 +504,20 @@ public final class Bukkit {
+ return server.getPlayer(id);
+ }
+
++ // Paper start
++ /**
++ * Gets the unique ID of the player currently known as the specified player name
++ * In Offline Mode, will return an Offline UUID
++ *
++ * @param playerName the player name to look up the unique ID for
++ * @return A UUID, or null if that player name is not registered with Minecraft and the server is in online mode
++ */
++ @Nullable
++ public static UUID getPlayerUniqueId(@NotNull String playerName) {
++ return server.getPlayerUniqueId(playerName);
++ }
++ // Paper end
++
+ /**
+ * Gets the plugin manager for interfacing with plugins.
+ *
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 38d138b217734e598581ed14065ff2015135ee9a..01657abaff86cf7bb3ffb857024c5032781b8660 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -429,6 +429,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @Nullable
+ public Player getPlayer(@NotNull UUID id);
+
++ // Paper start
++ /**
++ * Gets the unique ID of the player currently known as the specified player name
++ * In Offline Mode, will return an Offline UUID
++ *
++ * @param playerName the player name to look up the unique ID for
++ * @return A UUID, or null if that player name is not registered with Minecraft and the server is in online mode
++ */
++ @Nullable
++ public UUID getPlayerUniqueId(@NotNull String playerName);
++ // Paper end
++
+ /**
+ * Gets the plugin manager for interfacing with plugins.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0090-Add-legacy-ping-support-to-PaperServerListPingEvent.patch b/Spigot-API-Patches-Unmapped/0090-Add-legacy-ping-support-to-PaperServerListPingEvent.patch
new file mode 100644
index 0000000000..f21c766148
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0090-Add-legacy-ping-support-to-PaperServerListPingEvent.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Wed, 11 Oct 2017 19:30:20 +0200
+Subject: [PATCH] Add legacy ping support to PaperServerListPingEvent
+
+Add a new method to StatusClient check if the client is a legacy
+client that does not support all of the features provided in the
+event.
+
+diff --git a/src/main/java/com/destroystokyo/paper/network/StatusClient.java b/src/main/java/com/destroystokyo/paper/network/StatusClient.java
+index 517d15238ed117f38bbd39f570874014cecf7bb5..ffda9f6a8b094942009aa78b331d22d9dcca2802 100644
+--- a/src/main/java/com/destroystokyo/paper/network/StatusClient.java
++++ b/src/main/java/com/destroystokyo/paper/network/StatusClient.java
+@@ -10,4 +10,16 @@ import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
+ */
+ public interface StatusClient extends NetworkClient {
+
++ /**
++ * Returns whether the client is using an older version that doesn't
++ * support all of the features in {@link PaperServerListPingEvent}.
++ *
++ * <p>For Vanilla, this returns {@code true} for all clients older than 1.7.</p>
++ *
++ * @return {@code true} if the client is using legacy ping
++ */
++ default boolean isLegacy() {
++ return false;
++ }
++
+ }
diff --git a/Spigot-API-Patches-Unmapped/0091-Add-method-to-open-already-placed-sign.patch b/Spigot-API-Patches-Unmapped/0091-Add-method-to-open-already-placed-sign.patch
new file mode 100644
index 0000000000..c75d281c23
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0091-Add-method-to-open-already-placed-sign.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Sun, 1 Apr 2018 02:28:43 +0300
+Subject: [PATCH] Add method to open already placed sign
+
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index d212d5123b6294f7873d72f125505a006c290b05..7430bc85301d0fcb34c6035fbe08ae245c76e043 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -461,6 +461,14 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ */
+ @Deprecated
+ public void setShoulderEntityRight(@Nullable Entity entity);
++ // Paper start - Add method to open already placed sign
++ /**
++ * Opens an editor window for the specified sign
++ *
++ * @param sign The sign to open
++ */
++ void openSign(@NotNull org.bukkit.block.Sign sign);
++ // Paper end
+
+ /**
+ * Make the entity drop the item in their hand.
diff --git a/Spigot-API-Patches-Unmapped/0092-Add-Ban-Methods-to-Player-Objects.patch b/Spigot-API-Patches-Unmapped/0092-Add-Ban-Methods-to-Player-Objects.patch
new file mode 100644
index 0000000000..540eeb4d36
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0092-Add-Ban-Methods-to-Player-Objects.patch
@@ -0,0 +1,253 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 28 Apr 2018 10:28:50 -0400
+Subject: [PATCH] Add Ban Methods to Player Objects
+
+Allows a more logical API for banning players.
+
+player.banPlayer("Breaking the rules");
+
+diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java
+index 58313929f81509030216a0e5e3869da63e11108e..6cf05fed701c67a2c797a4e0839c795802a238a1 100644
+--- a/src/main/java/org/bukkit/OfflinePlayer.java
++++ b/src/main/java/org/bukkit/OfflinePlayer.java
+@@ -45,6 +45,61 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
+ * @return true if banned, otherwise false
+ */
+ public boolean isBanned();
++ // Paper start
++
++ /**
++ * Permanently Bans this player from the server
++ *
++ * @param reason Reason for Ban
++ * @return Ban Entry
++ */
++ @NotNull
++ public default BanEntry banPlayer(@Nullable String reason) {
++ return banPlayer(reason, null, null);
++ }
++
++ /**
++ * Permanently Bans this player from the server
++ * @param reason Reason for Ban
++ * @param source Source of the ban, or null for default
++ * @return Ban Entry
++ */
++ @NotNull
++ public default BanEntry banPlayer(@Nullable String reason, @Nullable String source) {
++ return banPlayer(reason, null, source);
++ }
++
++ /**
++ * Bans this player from the server
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @return Ban Entry
++ */
++ @NotNull
++ public default BanEntry banPlayer(@Nullable String reason, @Nullable java.util.Date expires) {
++ return banPlayer(reason, expires, null);
++ }
++
++ /**
++ * Bans this player from the server
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @param source Source of the ban or null for default
++ * @return Ban Entry
++ */
++ @NotNull
++ public default BanEntry banPlayer(@Nullable String reason, @Nullable java.util.Date expires, @Nullable String source) {
++ return banPlayer(reason, expires, source, true);
++ }
++ @NotNull
++ public default BanEntry banPlayer(@Nullable String reason, @Nullable java.util.Date expires, @Nullable String source, boolean kickIfOnline) {
++ BanEntry banEntry = Bukkit.getServer().getBanList(BanList.Type.NAME).addBan(getName(), reason, expires, source);
++ if (kickIfOnline && isOnline()) {
++ getPlayer().kickPlayer(reason);
++ }
++ return banEntry;
++ }
++ // Paper end
+
+ /**
+ * Checks if this player is whitelisted or not
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 6769330f336afcd5f26c0f7286defa00bead543c..88fd4cdeaad4d27f414c6b268022cdedf73492e7 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -5,6 +5,10 @@ import java.util.UUID;
+ import com.destroystokyo.paper.Title; // Paper
+ import net.kyori.adventure.text.Component;
+ import com.destroystokyo.paper.profile.PlayerProfile; // Paper
++import java.util.Date; // Paper
++import org.bukkit.BanEntry; // Paper
++import org.bukkit.BanList; // Paper
++import org.bukkit.Bukkit; // Paper
+ import org.bukkit.DyeColor;
+ import org.bukkit.Effect;
+ import org.bukkit.GameMode;
+@@ -618,6 +622,162 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ public void sendMap(@NotNull MapView map);
+
+ // Paper start
++ /**
++ * Permanently Bans the Profile and IP address currently used by the player.
++ *
++ * @param reason Reason for ban
++ * @return Ban Entry
++ */
++ // For reference, Bukkit defines this as nullable, while they impl isn't, we'll follow API.
++ @Nullable
++ public default BanEntry banPlayerFull(@Nullable String reason) {
++ return banPlayerFull(reason, null, null);
++ }
++
++ /**
++ * Permanently Bans the Profile and IP address currently used by the player.
++ *
++ * @param reason Reason for ban
++ * @param source Source of ban, or null for default
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerFull(@Nullable String reason, @Nullable String source) {
++ return banPlayerFull(reason, null, source);
++ }
++
++ /**
++ * Bans the Profile and IP address currently used by the player.
++ *
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerFull(@Nullable String reason, @Nullable Date expires) {
++ return banPlayerFull(reason, expires, null);
++ }
++
++ /**
++ * Bans the Profile and IP address currently used by the player.
++ *
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @param source Source of the ban, or null for default
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerFull(@Nullable String reason, @Nullable Date expires, @Nullable String source) {
++ banPlayer(reason, expires, source);
++ return banPlayerIP(reason, expires, source, true);
++ }
++
++ /**
++ * Permanently Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ *
++ * @param reason Reason for ban
++ * @param kickPlayer Whether or not to kick the player afterwards
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, boolean kickPlayer) {
++ return banPlayerIP(reason, null, null, kickPlayer);
++ }
++
++ /**
++ * Permanently Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for ban
++ * @param source Source of ban, or null for default
++ * @param kickPlayer Whether or not to kick the player afterwards
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable String source, boolean kickPlayer) {
++ return banPlayerIP(reason, null, source, kickPlayer);
++ }
++
++ /**
++ * Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @param kickPlayer Whether or not to kick the player afterwards
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable Date expires, boolean kickPlayer) {
++ return banPlayerIP(reason, expires, null, kickPlayer);
++ }
++
++ /**
++ * Permanently Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ *
++ * @param reason Reason for ban
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason) {
++ return banPlayerIP(reason, null, null);
++ }
++
++ /**
++ * Permanently Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for ban
++ * @param source Source of ban, or null for default
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable String source) {
++ return banPlayerIP(reason, null, source);
++ }
++
++ /**
++ * Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable Date expires) {
++ return banPlayerIP(reason, expires, null);
++ }
++
++ /**
++ * Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @param source Source of the banm or null for default
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable Date expires, @Nullable String source) {
++ return banPlayerIP(reason, expires, source, true);
++ }
++
++ /**
++ * Bans the IP address currently used by the player.
++ * Does not ban the Profile, use {@link #banPlayerFull(String, Date, String)}
++ * @param reason Reason for Ban
++ * @param expires When to expire the ban
++ * @param source Source of the banm or null for default
++ * @param kickPlayer if the targeted player should be kicked
++ * @return Ban Entry
++ */
++ @Nullable
++ public default BanEntry banPlayerIP(@Nullable String reason, @Nullable Date expires, @Nullable String source, boolean kickPlayer) {
++ BanEntry banEntry = Bukkit.getServer().getBanList(BanList.Type.IP).addBan(getAddress().getAddress().getHostAddress(), reason, expires, source);
++ if (kickPlayer && isOnline()) {
++ getPlayer().kickPlayer(reason);
++ }
++
++ return banEntry;
++ }
+
+ /**
+ * Sends an Action Bar message to the client.
diff --git a/Spigot-API-Patches-Unmapped/0093-EndermanEscapeEvent.patch b/Spigot-API-Patches-Unmapped/0093-EndermanEscapeEvent.patch
new file mode 100644
index 0000000000..82d22fd0ea
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0093-EndermanEscapeEvent.patch
@@ -0,0 +1,102 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 30 Apr 2018 13:14:30 -0400
+Subject: [PATCH] EndermanEscapeEvent
+
+Fires an event anytime an enderman intends to teleport away from the player
+
+You may cancel this, enabling ranged attacks to damage the enderman for example.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EndermanEscapeEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EndermanEscapeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..806112a8b5a7ce31166675f5b074ceaf42e364b6
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EndermanEscapeEvent.java
+@@ -0,0 +1,87 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Enderman;
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++public class EndermanEscapeEvent extends EntityEvent implements Cancellable {
++ @NotNull private final Reason reason;
++
++ public EndermanEscapeEvent(@NotNull Enderman entity, @NotNull Reason reason) {
++ super(entity);
++ this.reason = reason;
++ }
++
++ @NotNull
++ @Override
++ public Enderman getEntity() {
++ return (Enderman) super.getEntity();
++ }
++
++ /**
++ * @return The reason the enderman is trying to escape
++ */
++ @NotNull
++ public Reason getReason() {
++ return reason;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Cancels the escape.
++ *
++ * If this escape normally would of resulted in damage avoidance such as indirect,
++ * the enderman will now take damage.
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ public enum Reason {
++ /**
++ * The enderman has stopped attacking and ran away
++ */
++ RUNAWAY,
++ /**
++ * The enderman has teleported away due to indirect damage (ranged)
++ */
++ INDIRECT,
++ /**
++ * The enderman has teleported away due to a critical hit
++ */
++ CRITICAL_HIT,
++ /**
++ * The enderman has teleported away due to the player staring at it during combat
++ */
++ STARE,
++ /**
++ * Specific case for CRITICAL_HIT where the enderman is taking rain damage
++ */
++ DROWN
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0094-Enderman.teleportRandomly.patch b/Spigot-API-Patches-Unmapped/0094-Enderman.teleportRandomly.patch
new file mode 100644
index 0000000000..9c226d009c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0094-Enderman.teleportRandomly.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 30 Apr 2018 13:29:15 -0400
+Subject: [PATCH] Enderman.teleportRandomly()
+
+Ability to trigger the vanilla "teleport randomly" mechanic of an enderman.
+
+diff --git a/src/main/java/org/bukkit/entity/Enderman.java b/src/main/java/org/bukkit/entity/Enderman.java
+index bb325d9c802e33431530bbccdcf5de5839e5fe68..821c690f8a32918bdb284ffec4af98f411f76ccc 100644
+--- a/src/main/java/org/bukkit/entity/Enderman.java
++++ b/src/main/java/org/bukkit/entity/Enderman.java
+@@ -10,6 +10,17 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public interface Enderman extends Monster {
+
++ // Paper start
++ /**
++ * Try to teleport the enderman to a random nearby location.
++ *
++ * May conditionally fail if the random location was not valid
++ * @return If the enderman teleported successfully or not
++ */
++
++ public boolean teleportRandomly();
++ // Paper end
++
+ /**
+ * Gets the id and data of the block that the Enderman is carrying.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0095-Additional-world.getNearbyEntities-API-s.patch b/Spigot-API-Patches-Unmapped/0095-Additional-world.getNearbyEntities-API-s.patch
new file mode 100644
index 0000000000..cde5bb2148
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0095-Additional-world.getNearbyEntities-API-s.patch
@@ -0,0 +1,292 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 30 Apr 2018 17:55:28 -0400
+Subject: [PATCH] Additional world.getNearbyEntities API's
+
+Provides more methods to get nearby entities, and filter by types and predicates
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 0f86a9c67797fd662cbbfdb808789bcab95caba4..cae848ce698337d0b254bd48938abfc1e68ad561 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -1,6 +1,9 @@
+ package org.bukkit;
+
+ import java.io.File;
++import org.bukkit.generator.ChunkGenerator;
++
++import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.List;
+@@ -659,6 +662,256 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public Collection<Entity> getEntitiesByClasses(@NotNull Class<?>... classes);
+
++ // Paper start
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param radius Radius
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double radius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, radius, radius, radius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, xzRadius, yRadius, xzRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z radius
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, xRadius, yRadius, zRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param radius X Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double radius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double xzRadius, double yRadius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<LivingEntity> getNearbyLivingEntities(@NotNull Location loc, double xRadius, double yRadius, double zRadius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, loc, xRadius, yRadius, zRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param radius X/Y/Z Radius
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double radius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, radius, radius, radius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, xzRadius, yRadius, xzRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, xRadius, yRadius, zRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param radius X/Y/Z Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double radius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double xzRadius, double yRadius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default Collection<Player> getNearbyPlayers(@NotNull Location loc, double xRadius, double yRadius, double zRadius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, loc, xRadius, yRadius, zRadius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param radius X/Y/Z radius to search within
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, @NotNull Location loc, double radius) {
++ return getNearbyEntitiesByType(clazz, loc, radius, radius, radius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius, with x and x radius matching (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param xzRadius X/Z radius to search within
++ * @param yRadius Y radius to search within
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, @NotNull Location loc, double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(clazz, loc, xzRadius, yRadius, xzRadius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, @NotNull Location loc, double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(clazz, loc, xRadius, yRadius, zRadius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param radius X/Y/Z radius to search within
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, @NotNull Location loc, double radius, @Nullable Predicate<T> predicate) {
++ return getNearbyEntitiesByType(clazz, loc, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius, with x and x radius matching (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param xzRadius X/Z radius to search within
++ * @param yRadius Y radius to search within
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, @NotNull Location loc, double xzRadius, double yRadius, @Nullable Predicate<T> predicate) {
++ return getNearbyEntitiesByType(clazz, loc, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param loc Center location
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public default <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends Entity> clazz, @NotNull Location loc, double xRadius, double yRadius, double zRadius, @Nullable Predicate<T> predicate) {
++ if (clazz == null) {
++ clazz = Entity.class;
++ }
++ List<T> nearby = new ArrayList<>();
++ for (Entity bukkitEntity : getNearbyEntities(loc, xRadius, yRadius, zRadius)) {
++ //noinspection unchecked
++ if (clazz.isAssignableFrom(bukkitEntity.getClass()) && (predicate == null || predicate.test((T) bukkitEntity))) {
++ //noinspection unchecked
++ nearby.add((T) bukkitEntity);
++ }
++ }
++ return nearby;
++ }
++ // Paper end
++
+ /**
+ * Get a list of all players in this World
+ *
+diff --git a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+index a40b57edb1aeff71fc0b9767d410950da5c06283..184d9462ebbc500d8b81aaf14fe138d247bf2470 100644
+--- a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
++++ b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+@@ -42,8 +42,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
+ return profile;
+ }
+
+- /**
+- * Changes the PlayerProfile the player will login as
++ /* * Changes the PlayerProfile the player will login as
+ * @param profile The profile to use
+ */
+ public void setPlayerProfile(@NotNull PlayerProfile profile) {
diff --git a/Spigot-API-Patches-Unmapped/0096-Location.isChunkLoaded-API.patch b/Spigot-API-Patches-Unmapped/0096-Location.isChunkLoaded-API.patch
new file mode 100644
index 0000000000..1670d20348
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0096-Location.isChunkLoaded-API.patch
@@ -0,0 +1,18 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 30 Apr 2018 19:27:31 -0400
+Subject: [PATCH] Location.isChunkLoaded() API
+
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 0939a8070f9cc4f66f1679fef74862debb7d32ae..6c8b8eddcdb81f7151202eb12541308040790d45 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -533,6 +533,7 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ return this;
+ }
+
++ public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
diff --git a/Spigot-API-Patches-Unmapped/0097-Expand-World.spawnParticle-API-and-add-Builder.patch b/Spigot-API-Patches-Unmapped/0097-Expand-World.spawnParticle-API-and-add-Builder.patch
new file mode 100644
index 0000000000..ee5d06483d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0097-Expand-World.spawnParticle-API-and-add-Builder.patch
@@ -0,0 +1,579 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 29 Aug 2017 23:58:48 -0400
+Subject: [PATCH] Expand World.spawnParticle API and add Builder
+
+Adds ability to control who receives it and who is the source/sender (vanish API)
+the standard API is to send the packet to everyone in the world, which is ineffecient.
+
+This adds a new Builder API which is much friendlier to use.
+
+diff --git a/src/main/java/com/destroystokyo/paper/ParticleBuilder.java b/src/main/java/com/destroystokyo/paper/ParticleBuilder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..06f1602f5b327705f726d0a99dd6b95e1554d382
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/ParticleBuilder.java
+@@ -0,0 +1,478 @@
++package com.destroystokyo.paper;
++
++import com.google.common.collect.Lists;
++import org.bukkit.Color;
++import org.bukkit.Location;
++import org.bukkit.Particle;
++import org.bukkit.World;
++import org.bukkit.entity.Player;
++import org.bukkit.util.NumberConversions;
++
++import java.util.Collection;
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Helps prepare a particle to be sent to players.
++ *
++ * Usage of the builder is preferred over the super long {@link World#spawnParticle(Particle, Location, int, double, double, double, double, Object)} API
++ */
++public class ParticleBuilder {
++
++ private Particle particle;
++ private List<Player> receivers;
++ private Player source;
++ private Location location;
++ private int count = 1;
++ private double offsetX = 0, offsetY = 0, offsetZ = 0;
++ private double extra = 1;
++ private Object data;
++ private boolean force = true;
++
++ public ParticleBuilder(@NotNull Particle particle) {
++ this.particle = particle;
++ }
++
++ /**
++ * Sends the particle to all receiving players (or all). This method is safe to use
++ * Asynchronously
++ *
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder spawn() {
++ if (this.location == null) {
++ throw new IllegalStateException("Please specify location for this particle");
++ }
++ location.getWorld().spawnParticle(particle, receivers, source,
++ location.getX(), location.getY(), location.getZ(),
++ count, offsetX, offsetY, offsetZ, extra, data, force
++ );
++ return this;
++ }
++
++ /**
++ * @return The particle going to be sent
++ */
++ @NotNull
++ public Particle particle() {
++ return particle;
++ }
++
++ /**
++ * Changes what particle will be sent
++ *
++ * @param particle The particle
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder particle(@NotNull Particle particle) {
++ this.particle = particle;
++ return this;
++ }
++
++ /**
++ * @return List of players who will receive the particle, or null for all in world
++ */
++ @Nullable
++ public List<Player> receivers() {
++ return receivers;
++ }
++
++ /**
++ * Example use:
++ *
++ * builder.receivers(16); if (builder.hasReceivers()) { sendParticleAsync(builder); }
++ *
++ * @return If this particle is going to be sent to someone
++ */
++ public boolean hasReceivers() {
++ return (receivers == null && !location.getWorld().getPlayers().isEmpty()) || (
++ receivers != null && !receivers.isEmpty());
++ }
++
++ /**
++ * Sends this particle to all players in the world. This is rather silly and you should likely not
++ * be doing this.
++ *
++ * Just be a logical person and use receivers by radius or collection.
++ *
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder allPlayers() {
++ this.receivers = null;
++ return this;
++ }
++
++ /**
++ * @param receivers List of players to receive this particle, or null for all players in the
++ * world
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(@Nullable List<Player> receivers) {
++ // Had to keep this as we first made API List<> and not Collection, but removing this may break plugins compiled on older jars
++ // TODO: deprecate?
++ this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
++ return this;
++ }
++
++ /**
++ * @param receivers List of players to receive this particle, or null for all players in the
++ * world
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(@Nullable Collection<Player> receivers) {
++ this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
++ return this;
++ }
++
++ /**
++ * @param receivers List of players to be receive this particle, or null for all players in the
++ * world
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(@Nullable Player... receivers) {
++ this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
++ return this;
++ }
++
++ /**
++ * Selects all players within a cuboid selection around the particle location, within the
++ * specified bounding box. If you want a more spherical check, see {@link #receivers(int,
++ * boolean)}
++ *
++ * @param radius amount to add on all axis
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(int radius) {
++ return receivers(radius, radius);
++ }
++
++ /**
++ * Selects all players within the specified radius around the particle location. If byDistance is
++ * false, behavior uses cuboid selection the same as {@link #receivers(int, int)} If byDistance is
++ * true, radius is tested by distance in a spherical shape
++ *
++ * @param radius amount to add on each axis
++ * @param byDistance true to use a spherical radius, false to use a cuboid
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(int radius, boolean byDistance) {
++ if (!byDistance) {
++ return receivers(radius, radius, radius);
++ } else {
++ this.receivers = Lists.newArrayList();
++ for (Player nearbyPlayer : location.getWorld()
++ .getNearbyPlayers(location, radius, radius, radius)) {
++ Location loc = nearbyPlayer.getLocation();
++ double x = NumberConversions.square(location.getX() - loc.getX());
++ double y = NumberConversions.square(location.getY() - loc.getY());
++ double z = NumberConversions.square(location.getZ() - loc.getZ());
++ if (Math.sqrt(x + y + z) > radius) {
++ continue;
++ }
++ this.receivers.add(nearbyPlayer);
++ }
++ return this;
++ }
++ }
++
++ /**
++ * Selects all players within a cuboid selection around the particle location, within the
++ * specified bounding box. Allows specifying a different Y size than X and Z If you want a more
++ * cylinder check, see {@link #receivers(int, int, boolean)} If you want a more spherical check,
++ * see {@link #receivers(int, boolean)}
++ *
++ * @param xzRadius amount to add on the x/z axis
++ * @param yRadius amount to add on the y axis
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(int xzRadius, int yRadius) {
++ return receivers(xzRadius, yRadius, xzRadius);
++ }
++
++ /**
++ * Selects all players within the specified radius around the particle location. If byDistance is
++ * false, behavior uses cuboid selection the same as {@link #receivers(int, int)} If byDistance is
++ * true, radius is tested by distance on the y plane and on the x/z plane, in a cylinder shape.
++ *
++ * @param xzRadius amount to add on the x/z axis
++ * @param yRadius amount to add on the y axis
++ * @param byDistance true to use a cylinder shape, false to use cuboid
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(int xzRadius, int yRadius, boolean byDistance) {
++ if (!byDistance) {
++ return receivers(xzRadius, yRadius, xzRadius);
++ } else {
++ this.receivers = Lists.newArrayList();
++ for (Player nearbyPlayer : location.getWorld()
++ .getNearbyPlayers(location, xzRadius, yRadius, xzRadius)) {
++ Location loc = nearbyPlayer.getLocation();
++ if (Math.abs(loc.getY() - this.location.getY()) > yRadius) {
++ continue;
++ }
++ double x = NumberConversions.square(location.getX() - loc.getX());
++ double z = NumberConversions.square(location.getZ() - loc.getZ());
++ if (x + z > NumberConversions.square(xzRadius)) {
++ continue;
++ }
++ this.receivers.add(nearbyPlayer);
++ }
++ return this;
++ }
++ }
++
++ /**
++ * Selects all players within a cuboid selection around the particle location, within the
++ * specified bounding box. If you want a more cylinder check, see {@link #receivers(int, int,
++ * boolean)} If you want a more spherical check, see {@link #receivers(int, boolean)}
++ *
++ * @param xRadius amount to add on the x axis
++ * @param yRadius amount to add on the y axis
++ * @param zRadius amount to add on the z axis
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder receivers(int xRadius, int yRadius, int zRadius) {
++ if (location == null) {
++ throw new IllegalStateException("Please set location first");
++ }
++ return receivers(location.getWorld().getNearbyPlayers(location, xRadius, yRadius, zRadius));
++ }
++
++ /**
++ * @return The player considered the source of this particle (for Visibility concerns), or null
++ */
++ @Nullable
++ public Player source() {
++ return source;
++ }
++
++ /**
++ * Sets the source of this particle for visibility concerns (Vanish API)
++ *
++ * @param source The player who is considered the source
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder source(@Nullable Player source) {
++ this.source = source;
++ return this;
++ }
++
++ /**
++ * @return Location of where the particle will spawn
++ */
++ @Nullable
++ public Location location() {
++ return location;
++ }
++
++ /**
++ * Sets the location of where to spawn the particle
++ *
++ * @param location The location of the particle
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder location(@NotNull Location location) {
++ this.location = location.clone();
++ return this;
++ }
++
++ /**
++ * Sets the location of where to spawn the particle
++ *
++ * @param world World to spawn particle in
++ * @param x X location
++ * @param y Y location
++ * @param z Z location
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder location(@NotNull World world, double x, double y, double z) {
++ this.location = new Location(world, x, y, z);
++ return this;
++ }
++
++ /**
++ * @return Number of particles to spawn
++ */
++ public int count() {
++ return count;
++ }
++
++ /**
++ * Sets the number of particles to spawn
++ *
++ * @param count Number of particles
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder count(int count) {
++ this.count = count;
++ return this;
++ }
++
++ /**
++ * Particle offset X. Varies by particle on how this is used
++ *
++ * @return Particle offset X.
++ */
++ public double offsetX() {
++ return offsetX;
++ }
++
++ /**
++ * Particle offset Y. Varies by particle on how this is used
++ *
++ * @return Particle offset Y.
++ */
++ public double offsetY() {
++ return offsetY;
++ }
++
++ /**
++ * Particle offset Z. Varies by particle on how this is used
++ *
++ * @return Particle offset Z.
++ */
++ public double offsetZ() {
++ return offsetZ;
++ }
++
++ /**
++ * Sets the particle offset. Varies by particle on how this is used
++ *
++ * @param offsetX Particle offset X
++ * @param offsetY Particle offset Y
++ * @param offsetZ Particle offset Z
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder offset(double offsetX, double offsetY, double offsetZ) {
++ this.offsetX = offsetX;
++ this.offsetY = offsetY;
++ this.offsetZ = offsetZ;
++ return this;
++ }
++
++ /**
++ * Gets the Particle extra data. Varies by particle on how this is used
++ *
++ * @return the extra particle data
++ */
++ public double extra() {
++ return extra;
++ }
++
++ /**
++ * Sets the particle extra data. Varies by particle on how this is used
++ *
++ * @param extra the extra particle data
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder extra(double extra) {
++ this.extra = extra;
++ return this;
++ }
++
++ /**
++ * Gets the particle custom data. Varies by particle on how this is used
++ *
++ * @param <T> The Particle data type
++ * @return the ParticleData for this particle
++ */
++ @Nullable
++ public <T> T data() {
++ //noinspection unchecked
++ return (T) data;
++ }
++
++ /**
++ * Sets the particle custom data. Varies by particle on how this is used
++ *
++ * @param data The new particle data
++ * @param <T> The Particle data type
++ * @return a reference to this object.
++ */
++ @NotNull
++ public <T> ParticleBuilder data(@Nullable T data) {
++ this.data = data;
++ return this;
++ }
++
++ /**
++ * Sets whether the particle is forcefully shown to the player. If forced, the particle will show
++ * faraway, as far as the player's view distance allows. If false, the particle will show
++ * according to the client's particle settings.
++ *
++ * @param force true to force, false for normal
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder force(boolean force) {
++ this.force = force;
++ return this;
++ }
++
++ /**
++ * Sets the particle Color. Only valid for REDSTONE.
++ *
++ * @param color the new particle color
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder color(@Nullable Color color) {
++ return color(color, 1);
++ }
++
++ /**
++ * Sets the particle Color and size. Only valid for REDSTONE.
++ *
++ * @param color the new particle color
++ * @param size the size of the particle
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder color(@Nullable Color color, float size) {
++ if (particle != Particle.REDSTONE && color != null) {
++ throw new IllegalStateException("Color may only be set on REDSTONE");
++ }
++
++ // We don't officially support reusing these objects, but here we go
++ if (color == null) {
++ if (data instanceof Particle.DustOptions) {
++ return data(null);
++ } else {
++ return this;
++ }
++ }
++
++ return data(new Particle.DustOptions(color, size));
++ }
++
++ /**
++ * Sets the particle Color.
++ * Only valid for REDSTONE.
++ * @param r red color component
++ * @param g green color component
++ * @param b blue color component
++ * @return a reference to this object.
++ */
++ @NotNull
++ public ParticleBuilder color(int r, int g, int b) {
++ return color(Color.fromRGB(r, g, b));
++ }
++}
+diff --git a/src/main/java/org/bukkit/Particle.java b/src/main/java/org/bukkit/Particle.java
+index b32de827cf8d1780861c271b4215276fdaab7165..1020002ff7127877db2d7e096f2c521751bf13a7 100644
+--- a/src/main/java/org/bukkit/Particle.java
++++ b/src/main/java/org/bukkit/Particle.java
+@@ -106,6 +106,17 @@ public enum Particle {
+ return dataType;
+ }
+
++ // Paper start - Particle API expansion
++ /**
++ * Creates a {@link com.destroystokyo.paper.ParticleBuilder}
++ *
++ * @return a {@link com.destroystokyo.paper.ParticleBuilder} for the particle
++ */
++ @NotNull
++ public com.destroystokyo.paper.ParticleBuilder builder() {
++ return new com.destroystokyo.paper.ParticleBuilder(this);
++ }
++ // Paper end
+ /**
+ * Options which can be applied to redstone dust particles - a particle
+ * color and size.
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 27bbddac946879149ec43d428417c4707f6a9a8c..fe125ae7bbd7c9dde9db7838169e4819416e148d 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -2592,7 +2592,57 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ * @param data the data to use for the particle or null,
+ * the type of this depends on {@link Particle#getDataType()}
+ */
+- public <T> void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data);
++ public default <T> void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { spawnParticle(particle, null, null, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, true); }// Paper start - Expand Particle API
++ /**
++ * Spawns the particle (the number of times specified by count)
++ * at the target location. The position of each particle will be
++ * randomized positively and negatively by the offset parameters
++ * on each axis.
++ *
++ * @param particle the particle to spawn
++ * @param receivers List of players to receive the particles, or null for all in world
++ * @param source Source of the particles to be used in visibility checks, or null if no player source
++ * @param x the position on the x axis to spawn at
++ * @param y the position on the y axis to spawn at
++ * @param z the position on the z axis to spawn at
++ * @param count the number of particles
++ * @param offsetX the maximum random offset on the X axis
++ * @param offsetY the maximum random offset on the Y axis
++ * @param offsetZ the maximum random offset on the Z axis
++ * @param extra the extra data for this particle, depends on the
++ * particle used (normally speed)
++ * @param data the data to use for the particle or null,
++ * the type of this depends on {@link Particle#getDataType()}
++ * @param <T> Type
++ */
++ public default <T> void spawnParticle(@NotNull Particle particle, @Nullable List<Player> receivers, @NotNull Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { spawnParticle(particle, receivers, source, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, true); }
++ /**
++ * Spawns the particle (the number of times specified by count)
++ * at the target location. The position of each particle will be
++ * randomized positively and negatively by the offset parameters
++ * on each axis.
++ *
++ * @param particle the particle to spawn
++ * @param receivers List of players to receive the particles, or null for all in world
++ * @param source Source of the particles to be used in visibility checks, or null if no player source
++ * @param x the position on the x axis to spawn at
++ * @param y the position on the y axis to spawn at
++ * @param z the position on the z axis to spawn at
++ * @param count the number of particles
++ * @param offsetX the maximum random offset on the X axis
++ * @param offsetY the maximum random offset on the Y axis
++ * @param offsetZ the maximum random offset on the Z axis
++ * @param extra the extra data for this particle, depends on the
++ * particle used (normally speed)
++ * @param data the data to use for the particle or null,
++ * the type of this depends on {@link Particle#getDataType()}
++ * @param <T> Type
++ * @param force allows the particle to be seen further away from the player
++ * and shows to players using any vanilla client particle settings
++ */
++ public <T> void spawnParticle(@NotNull Particle particle, @Nullable List<Player> receivers, @Nullable Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force);
++ // Paper end
++
+
+ /**
+ * Spawns the particle (the number of times specified by count)
diff --git a/Spigot-API-Patches-Unmapped/0098-EndermanAttackPlayerEvent.patch b/Spigot-API-Patches-Unmapped/0098-EndermanAttackPlayerEvent.patch
new file mode 100644
index 0000000000..9f3e105c9d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0098-EndermanAttackPlayerEvent.patch
@@ -0,0 +1,116 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 1 May 2018 20:17:44 -0400
+Subject: [PATCH] EndermanAttackPlayerEvent
+
+Allow control over whether or not an enderman aggros a player.
+
+This allows you to override/extend the pumpkin/stare logic.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EndermanAttackPlayerEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EndermanAttackPlayerEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f530a3d9314e17d1da896cac633f6a422258d9a9
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EndermanAttackPlayerEvent.java
+@@ -0,0 +1,101 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Enderman;
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when an Enderman determines if it should attack a player or not.
++ * Starts off cancelled if the player is wearing a pumpkin head or is not looking
++ * at the Enderman, according to Vanilla rules.
++ *
++ */
++public class EndermanAttackPlayerEvent extends EntityEvent implements Cancellable {
++ @NotNull private final Player player;
++
++ public EndermanAttackPlayerEvent(@NotNull Enderman entity, @NotNull Player player) {
++ super(entity);
++ this.player = player;
++ }
++
++ /**
++ * The enderman considering attacking
++ *
++ * @return The enderman considering attacking
++ */
++ @NotNull
++ @Override
++ public Enderman getEntity() {
++ return (Enderman) super.getEntity();
++ }
++
++ /**
++ * The player the Enderman is considering attacking
++ *
++ * @return The player the Enderman is considering attacking
++ */
++ @NotNull
++ public Player getPlayer() {
++ return player;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ /**
++ *
++ * @return If cancelled, the enderman will not attack
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Cancels if the Enderman will attack this player
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0099-Close-Plugin-Class-Loaders-on-Disable.patch b/Spigot-API-Patches-Unmapped/0099-Close-Plugin-Class-Loaders-on-Disable.patch
new file mode 100644
index 0000000000..4272393c94
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0099-Close-Plugin-Class-Loaders-on-Disable.patch
@@ -0,0 +1,141 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 1 May 2018 21:33:35 -0400
+Subject: [PATCH] Close Plugin Class Loaders on Disable
+
+This should close more memory leaks from /reload and disabling plugins,
+by closing the class loader and the jar file.
+
+diff --git a/src/main/java/org/bukkit/plugin/PluginLoader.java b/src/main/java/org/bukkit/plugin/PluginLoader.java
+index a88733f1cd1ddb5d85ab1b0e6af4fd5b80bbc1c6..6ab9cd8213cbe35943748dcf42948d5fc048c84c 100644
+--- a/src/main/java/org/bukkit/plugin/PluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/PluginLoader.java
+@@ -77,4 +77,18 @@ public interface PluginLoader {
+ * @param plugin Plugin to disable
+ */
+ public void disablePlugin(@NotNull Plugin plugin);
++ // Paper start - close Classloader on disable
++ /**
++ * Disables the specified plugin
++ * <p>
++ * Attempting to disable a plugin that is not enabled will have no effect
++ *
++ * @param plugin Plugin to disable
++ * @param closeClassloader if the classloader for the Plugin should be closed
++ */
++ // provide default to allow other PluginLoader implementations to work
++ default public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader) {
++ disablePlugin(plugin);
++ }
++ // Paper end - close Classloader on disable
+ }
+diff --git a/src/main/java/org/bukkit/plugin/PluginManager.java b/src/main/java/org/bukkit/plugin/PluginManager.java
+index 41e26451fe12d8e6e0ef73c85731b24b4e3f200c..86cc5025ad98f7a752c51713b7cd6a39d5136ecc 100644
+--- a/src/main/java/org/bukkit/plugin/PluginManager.java
++++ b/src/main/java/org/bukkit/plugin/PluginManager.java
+@@ -161,6 +161,18 @@ public interface PluginManager {
+ */
+ public void disablePlugin(@NotNull Plugin plugin);
+
++ // Paper start - close Classloader on disable
++ /**
++ * Disables the specified plugin
++ * <p>
++ * Attempting to disable a plugin that is not enabled will have no effect
++ *
++ * @param plugin Plugin to disable
++ * @param closeClassloader if the classloader for the Plugin should be closed
++ */
++ public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader);
++ // Paper end - close Classloader on disable
++
+ /**
+ * Gets a {@link Permission} from its fully qualified name
+ *
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index 8b33d914d29897c0276f9e2e7ce83bd2c316d5e2..a7393d2830b95d7167121b02066a3f357cee6085 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -492,17 +492,28 @@ public final class SimplePluginManager implements PluginManager {
+
+ @Override
+ public void disablePlugins() {
++ disablePlugins(false);
++ }
++
++ public void disablePlugins(boolean closeClassloaders) {
++ // Paper end - close Classloader on disable
+ Plugin[] plugins = getPlugins();
+ for (int i = plugins.length - 1; i >= 0; i--) {
+- disablePlugin(plugins[i]);
++ disablePlugin(plugins[i], closeClassloaders); // Paper - close Classloader on disable
+ }
+ }
+
+ @Override
+ public void disablePlugin(@NotNull final Plugin plugin) {
++ disablePlugin(plugin, false);
++ }
++
++ @Override
++ public void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) {
++ // Paper end - close Classloader on disable
+ if (plugin.isEnabled()) {
+ try {
+- plugin.getPluginLoader().disablePlugin(plugin);
++ plugin.getPluginLoader().disablePlugin(plugin, closeClassloader); // Paper - close Classloader on disable
+ } catch (Throwable ex) {
+ handlePluginException("Error occurred (in the plugin loader) while disabling "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
+@@ -557,7 +568,7 @@ public final class SimplePluginManager implements PluginManager {
+ @Override
+ public void clearPlugins() {
+ synchronized (this) {
+- disablePlugins();
++ disablePlugins(true); // Paper - close Classloader on disable
+ plugins.clear();
+ lookupNames.clear();
+ dependencyGraph = GraphBuilder.directed().build();
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index 5be6460e8eb81381c7e305cb7ab6b77c0c7a8fe5..bef88a6e2e6f7071401a3af0aec31e62aa265566 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -331,7 +331,7 @@ public final class JavaPluginLoader implements PluginLoader {
+ } catch (Throwable ex) {
+ server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Paper start - Disable plugins that fail to load
+- disablePlugin(jPlugin);
++ server.getPluginManager().disablePlugin(jPlugin, true); // Paper - close Classloader on disable - She's dead jim
+ return;
+ // Paper end
+ }
+@@ -344,6 +344,12 @@ public final class JavaPluginLoader implements PluginLoader {
+
+ @Override
+ public void disablePlugin(@NotNull Plugin plugin) {
++ // Paper start - close Classloader on disable
++ disablePlugin(plugin, false); // Retain old behavior unless requested
++ }
++
++ public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader) {
++ // Paper end - close Class Loader on disable
+ Validate.isTrue(plugin instanceof JavaPlugin, "Plugin is not associated with this PluginLoader");
+
+ if (plugin.isEnabled()) {
+@@ -370,6 +376,16 @@ public final class JavaPluginLoader implements PluginLoader {
+ for (String name : names) {
+ removeClass(name);
+ }
++ // Paper start - close Class Loader on disable
++ try {
++ if (closeClassloader) {
++ loader.close();
++ }
++ } catch (IOException e) {
++ server.getLogger().log(Level.WARNING, "Error closing the Plugin Class Loader for " + plugin.getDescription().getFullName());
++ e.printStackTrace();
++ }
++ // Paper end
+ }
+ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0100-WitchConsumePotionEvent.patch b/Spigot-API-Patches-Unmapped/0100-WitchConsumePotionEvent.patch
new file mode 100644
index 0000000000..fab6106411
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0100-WitchConsumePotionEvent.patch
@@ -0,0 +1,122 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 16 May 2018 20:26:16 -0400
+Subject: [PATCH] WitchConsumePotionEvent
+
+Fires when a witch consumes the potion in their hand
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/WitchConsumePotionEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/WitchConsumePotionEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fbbace36d69373046a7f3618ed5c1c1318b489b9
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/WitchConsumePotionEvent.java
+@@ -0,0 +1,70 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Witch;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Fired when a witch consumes the potion in their hand to buff themselves.
++ */
++public class WitchConsumePotionEvent extends EntityEvent implements Cancellable {
++ @Nullable private ItemStack potion;
++
++ public WitchConsumePotionEvent(@NotNull Witch witch, @Nullable ItemStack potion) {
++ super(witch);
++ this.potion = potion;
++ }
++
++ @NotNull
++ @Override
++ public Witch getEntity() {
++ return (Witch) super.getEntity();
++ }
++
++ /**
++ * @return the potion the witch will consume and have the effects applied.
++ */
++ @Nullable
++ public ItemStack getPotion() {
++ return potion;
++ }
++
++ /**
++ * Sets the potion to be consumed and applied to the witch.
++ * @param potion The potion
++ */
++ public void setPotion(@Nullable ItemStack potion) {
++ this.potion = potion != null ? potion.clone() : null;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ /**
++ * @return Event was cancelled or potion was null
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled || potion == null;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..6ef6367b67261c2b653a97322b9703a9409b3499
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java
+@@ -0,0 +1,33 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++public class WitchThrowPotionEvent extends Event implements Cancellable {
++ public WitchThrowPotionEvent() {
++ }
++
++
++ private static final HandlerList handlers = new HandlerList();
++
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0101-WitchThrowPotionEvent.patch b/Spigot-API-Patches-Unmapped/0101-WitchThrowPotionEvent.patch
new file mode 100644
index 0000000000..a5dfe42f35
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0101-WitchThrowPotionEvent.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 16 May 2018 20:39:09 -0400
+Subject: [PATCH] WitchThrowPotionEvent
+
+Fired when a witch throws a potion at a player
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java
+index 6ef6367b67261c2b653a97322b9703a9409b3499..688a596aa2b925651a92bf092e1ef4d77a47258c 100644
+--- a/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java
++++ b/src/main/java/com/destroystokyo/paper/event/entity/WitchThrowPotionEvent.java
+@@ -1,29 +1,77 @@
+ package com.destroystokyo.paper.event.entity;
+
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Witch;
+ import org.bukkit.event.Cancellable;
+-import org.bukkit.event.Event;
+ import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
+
+-public class WitchThrowPotionEvent extends Event implements Cancellable {
+- public WitchThrowPotionEvent() {
++/**
++ * Fired when a witch throws a potion at a player
++ */
++public class WitchThrowPotionEvent extends EntityEvent implements Cancellable {
++ @NotNull private final LivingEntity target;
++ @Nullable private ItemStack potion;
++
++ public WitchThrowPotionEvent(@NotNull Witch witch, @NotNull LivingEntity target, @Nullable ItemStack potion) {
++ super(witch);
++ this.target = target;
++ this.potion = potion;
+ }
+
++ @NotNull
++ @Override
++ public Witch getEntity() {
++ return (Witch) super.getEntity();
++ }
++
++ /**
++ * @return The target of the potion
++ */
++ @NotNull
++ public LivingEntity getTarget() {
++ return target;
++ }
++
++ /**
++ * @return The potion the witch will throw at a player
++ */
++ @Nullable
++ public ItemStack getPotion() {
++ return potion;
++ }
++
++ /**
++ * Sets the potion to be thrown at a player
++ * @param potion The potion
++ */
++ public void setPotion(@Nullable ItemStack potion) {
++ this.potion = potion != null ? potion.clone() : null;
++ }
+
+ private static final HandlerList handlers = new HandlerList();
+
++ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
++ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ private boolean cancelled = false;
+
++ /**
++ * @return Event was cancelled or potion was null
++ */
+ @Override
+ public boolean isCancelled() {
+- return cancelled;
++ return cancelled || potion == null;
+ }
+
+ @Override
diff --git a/Spigot-API-Patches-Unmapped/0102-Location.toBlockLocation-toCenterLocation.patch b/Spigot-API-Patches-Unmapped/0102-Location.toBlockLocation-toCenterLocation.patch
new file mode 100644
index 0000000000..1f3ad622a7
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0102-Location.toBlockLocation-toCenterLocation.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 24 May 2018 21:01:13 -0400
+Subject: [PATCH] Location.toBlockLocation/toCenterLocation()
+
+Convert location objects to their block coordinates, or the center of the block
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 6c8b8eddcdb81f7151202eb12541308040790d45..f61bf28afe99f83cdac6490bcc114509698d0aad 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -534,6 +534,31 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ }
+
+ public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
++
++ // Paper start
++ /**
++ * @return A new location where X/Y/Z are on the Block location (integer value of X/Y/Z)
++ */
++ @NotNull
++ public Location toBlockLocation() {
++ Location blockLoc = clone();
++ blockLoc.setX(getBlockX());
++ blockLoc.setY(getBlockY());
++ blockLoc.setZ(getBlockZ());
++ return blockLoc;
++ }
++ /**
++ * @return A new location where X/Y/Z are the center of the block
++ */
++ @NotNull
++ public Location toCenterLocation() {
++ Location centerLoc = clone();
++ centerLoc.setX(getBlockX() + 0.5);
++ centerLoc.setY(getBlockY() + 0.5);
++ centerLoc.setZ(getBlockZ() + 0.5);
++ return centerLoc;
++ }
++ // Paper end
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
diff --git a/Spigot-API-Patches-Unmapped/0103-PotionEffect-clone-methods.patch b/Spigot-API-Patches-Unmapped/0103-PotionEffect-clone-methods.patch
new file mode 100644
index 0000000000..7f4e064a78
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0103-PotionEffect-clone-methods.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 3 Jun 2018 04:10:13 -0400
+Subject: [PATCH] PotionEffect clone methods
+
+
+diff --git a/src/main/java/org/bukkit/potion/PotionEffect.java b/src/main/java/org/bukkit/potion/PotionEffect.java
+index 60716c627d315b08c3fe03b1a945af60c350711f..74767751199bce03d63f2a9524712656193f850c 100644
+--- a/src/main/java/org/bukkit/potion/PotionEffect.java
++++ b/src/main/java/org/bukkit/potion/PotionEffect.java
+@@ -101,6 +101,33 @@ public class PotionEffect implements ConfigurationSerializable {
+ this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT, false), getBool(map, PARTICLES, true), getBool(map, ICON, getBool(map, PARTICLES, true)));
+ }
+
++ // Paper start
++ @NotNull
++ public PotionEffect withType(@NotNull PotionEffectType type) {
++ return new PotionEffect(type, duration, amplifier, ambient, particles, icon);
++ }
++ @NotNull
++ public PotionEffect withDuration(int duration) {
++ return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
++ }
++ @NotNull
++ public PotionEffect withAmplifier(int amplifier) {
++ return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
++ }
++ @NotNull
++ public PotionEffect withAmbient(boolean ambient) {
++ return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
++ }
++ @NotNull
++ public PotionEffect withParticles(boolean particles) {
++ return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
++ }
++ @NotNull
++ public PotionEffect withIcon(boolean icon) {
++ return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon);
++ }
++ // Paper end
++
+ @NotNull
+ private static PotionEffectType getEffectType(@NotNull Map<?, ?> map) {
+ int type = getInt(map, TYPE);
diff --git a/Spigot-API-Patches-Unmapped/0104-WitchReadyPotionEvent.patch b/Spigot-API-Patches-Unmapped/0104-WitchReadyPotionEvent.patch
new file mode 100644
index 0000000000..5722426049
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0104-WitchReadyPotionEvent.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 5 Jun 2018 22:47:08 -0400
+Subject: [PATCH] WitchReadyPotionEvent
+
+Control what potion the witch readies to use
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/WitchReadyPotionEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/WitchReadyPotionEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5351b523defa054ba56ae3fb591029283ca7510d
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/WitchReadyPotionEvent.java
+@@ -0,0 +1,80 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Material;
++import org.bukkit.entity.Witch;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class WitchReadyPotionEvent extends EntityEvent implements Cancellable {
++ private ItemStack potion;
++
++ public WitchReadyPotionEvent(@NotNull Witch witch, @Nullable ItemStack potion) {
++ super(witch);
++ this.potion = potion;
++ }
++
++ /**
++ * Fires thee event, returning the desired potion, or air of cancelled
++ * @param witch the witch whom is readying to use a potion
++ * @param potion the potion to be used
++ * @return The ItemStack to be used
++ */
++ @Nullable
++ public static ItemStack process(@NotNull Witch witch, @Nullable ItemStack potion) {
++ WitchReadyPotionEvent event = new WitchReadyPotionEvent(witch, potion);
++ if (!event.callEvent() || event.getPotion() == null) {
++ return new ItemStack(Material.AIR);
++ }
++ return event.getPotion();
++ }
++
++ @NotNull
++ @Override
++ public Witch getEntity() {
++ return (Witch) super.getEntity();
++ }
++
++ /**
++ * @return the potion the witch is readying to use
++ */
++ @Nullable
++ public ItemStack getPotion() {
++ return potion;
++ }
++
++ /**
++ * Sets the potion the which is going to hold and use
++ * @param potion The potion
++ */
++ public void setPotion(@Nullable ItemStack potion) {
++ this.potion = potion != null ? potion.clone() : null;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0105-ItemStack-getMaxItemUseDuration.patch b/Spigot-API-Patches-Unmapped/0105-ItemStack-getMaxItemUseDuration.patch
new file mode 100644
index 0000000000..a9bb661918
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0105-ItemStack-getMaxItemUseDuration.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 5 Jun 2018 22:59:50 -0400
+Subject: [PATCH] ItemStack#getMaxItemUseDuration
+
+Allows you to determine how long it takes to use a usable/consumable item
+
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index 77d6b2ed68d8ce30b5cadb156941d2d1f7dcf5b1..cc065602db56c51b87d273a52d9ef82439fcaa7a 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -627,5 +627,13 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ public String getI18NDisplayName() {
+ return Bukkit.getServer().getItemFactory().getI18NDisplayName(this);
+ }
++
++ public int getMaxItemUseDuration() {
++ if (type == null || type == Material.AIR || !type.isItem()) {
++ return 0;
++ }
++ // Requires access to NMS
++ return ensureServerConversions().getMaxItemUseDuration();
++ }
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0106-Add-EntityTeleportEndGatewayEvent.patch b/Spigot-API-Patches-Unmapped/0106-Add-EntityTeleportEndGatewayEvent.patch
new file mode 100644
index 0000000000..0422ec9858
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0106-Add-EntityTeleportEndGatewayEvent.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sat, 9 Jun 2018 13:08:21 +0100
+Subject: [PATCH] Add EntityTeleportEndGatewayEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityTeleportEndGatewayEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityTeleportEndGatewayEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..bfc69a43c291fbed91b9d0387e4ef18b0ed1b9de
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityTeleportEndGatewayEvent.java
+@@ -0,0 +1,31 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Location;
++import org.bukkit.block.EndGateway;
++import org.bukkit.entity.Entity;
++import org.bukkit.event.entity.EntityTeleportEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired any time an entity attempts to teleport in an end gateway
++ */
++public class EntityTeleportEndGatewayEvent extends EntityTeleportEvent {
++
++ @NotNull private final EndGateway gateway;
++
++ public EntityTeleportEndGatewayEvent(@NotNull Entity what, @NotNull Location from, @NotNull Location to, @NotNull EndGateway gateway) {
++ super(what, from, to);
++ this.gateway = gateway;
++ }
++
++ /**
++ * The gateway triggering the teleport
++ *
++ * @return EndGateway used
++ */
++ @NotNull
++ public EndGateway getGateway() {
++ return gateway;
++ }
++
++}
diff --git a/Spigot-API-Patches-Unmapped/0107-Make-shield-blocking-delay-configurable.patch b/Spigot-API-Patches-Unmapped/0107-Make-shield-blocking-delay-configurable.patch
new file mode 100644
index 0000000000..cec33b7a78
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0107-Make-shield-blocking-delay-configurable.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 16 Jun 2018 01:17:39 -0500
+Subject: [PATCH] Make shield blocking delay configurable
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 33fffda7c8b05cde3c95623937e7eb6c8b628ec6..879dec59f202ee95043bd7317a672cd59ab3bbbe 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -628,5 +628,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @param arrows Number of arrows to stick in this entity
+ */
+ void setArrowsStuck(int arrows);
++
++ /**
++ * Get the delay (in ticks) before blocking is effective for this entity
++ *
++ * @return Delay in ticks
++ */
++ int getShieldBlockingDelay();
++
++ /**
++ * Set the delay (in ticks) before blocking is effective for this entity
++ *
++ * @param delay Delay in ticks
++ */
++ void setShieldBlockingDelay(int delay);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0108-EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch b/Spigot-API-Patches-Unmapped/0108-EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
new file mode 100644
index 0000000000..71a82a8eae
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0108-EntityShootBowEvent-consumeArrow-and-getArrowItem-AP.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 15 Jun 2013 19:52:04 -0400
+Subject: [PATCH] EntityShootBowEvent consumeArrow and getArrowItem API
+
+Adds ability to get what arrow was shot, and control if it should be consumed.
+
+diff --git a/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java b/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java
+index d4d7ad9c3c953680342c121f39ddaef476549047..719d0d878320c1903b44076053989ba99fa0e92a 100644
+--- a/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java
++++ b/src/main/java/org/bukkit/event/entity/EntityShootBowEvent.java
+@@ -22,7 +22,32 @@ public class EntityShootBowEvent extends EntityEvent implements Cancellable {
+ private final float force;
+ private boolean consumeItem;
+ private boolean cancelled;
++ // Paper start
++ @Deprecated
++ public void setConsumeArrow(boolean consumeArrow) {
++ this.setConsumeItem(consumeArrow);
++ }
++
++ @Deprecated
++ public boolean getConsumeArrow() {
++ return this.shouldConsumeItem();
++ }
++
++ @NotNull @Deprecated
++ public ItemStack getArrowItem() {
++ return this.getConsumable();
++ }
++
++ @Deprecated
++ public EntityShootBowEvent(@NotNull final LivingEntity shooter, @Nullable final ItemStack bow, @NotNull final Entity projectile, final float force) {
++ this(shooter, bow, new ItemStack(org.bukkit.Material.AIR), projectile, force);
++ }
+
++ @Deprecated
++ public EntityShootBowEvent(@NotNull final LivingEntity shooter, @Nullable final ItemStack bow, @NotNull ItemStack arrowItem, @NotNull final Entity projectile, final float force) {
++ this(shooter, bow, arrowItem, projectile, EquipmentSlot.HAND, force, true);
++ }
++ // Paper end
+ public EntityShootBowEvent(@NotNull final LivingEntity shooter, @Nullable final ItemStack bow, @Nullable final ItemStack consumable, @NotNull final Entity projectile, @NotNull final EquipmentSlot hand, final float force, final boolean consumeItem) {
+ super(shooter);
+ this.bow = bow;
diff --git a/Spigot-API-Patches-Unmapped/0109-Add-getNearbyXXX-methods-to-Location.patch b/Spigot-API-Patches-Unmapped/0109-Add-getNearbyXXX-methods-to-Location.patch
new file mode 100644
index 0000000000..a362af6260
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0109-Add-getNearbyXXX-methods-to-Location.patch
@@ -0,0 +1,275 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Mon, 18 Jun 2018 00:41:46 -0500
+Subject: [PATCH] Add "getNearbyXXX" methods to Location
+
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index f61bf28afe99f83cdac6490bcc114509698d0aad..4cf22afc3c1f1cc19b6e5350043431215908a612 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -12,6 +12,15 @@ import org.bukkit.util.Vector;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
++// Paper start
++import java.util.Collection;
++import java.util.Collections;
++import java.util.function.Predicate;
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Player;
++// Paper end
++
+ /**
+ * Represents a 3-dimensional position in a world.
+ * <br>
+@@ -558,6 +567,248 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ centerLoc.setZ(getBlockZ() + 0.5);
+ return centerLoc;
+ }
++
++ /**
++ * Returns a list of entities within a bounding box centered around a Location.
++ *
++ * Some implementations may impose artificial restrictions on the size of the search bounding box.
++ *
++ * @param x 1/2 the size of the box along x axis
++ * @param y 1/2 the size of the box along y axis
++ * @param z 1/2 the size of the box along z axis
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Entity> getNearbyEntities(double x, double y, double z) {
++ World world = this.getWorld();
++ if (world == null) {
++ throw new IllegalArgumentException("Location has no world");
++ }
++ return world.getNearbyEntities(this, x, y, z);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param radius X Radius
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double radius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, radius, radius, radius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, xzRadius, yRadius, xzRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z radius
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, xRadius, yRadius, zRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param radius Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double radius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double xzRadius, double yRadius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of living entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<LivingEntity> getNearbyLivingEntities(double xRadius, double yRadius, double zRadius, @Nullable Predicate<LivingEntity> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.LivingEntity.class, xRadius, yRadius, zRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param radius X/Y/Z Radius
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double radius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, radius, radius, radius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, xzRadius, yRadius, xzRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, xRadius, yRadius, zRadius);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param radius X/Y/Z Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double radius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xzRadius X/Z Radius
++ * @param yRadius Y Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double xzRadius, double yRadius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets nearby players within the specified radius (bounding box)
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param predicate a predicate used to filter results
++ * @return the collection of players near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public Collection<Player> getNearbyPlayers(double xRadius, double yRadius, double zRadius, @Nullable Predicate<Player> predicate) {
++ return getNearbyEntitiesByType(org.bukkit.entity.Player.class, xRadius, yRadius, zRadius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param radius X/Y/Z radius to search within
++ * @param <T> the entity type
++ * @return the collection of entities of type clazz near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, double radius) {
++ return getNearbyEntitiesByType(clazz, radius, radius, radius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius, with x and x radius matching (bounding box)
++ * @param clazz Type to filter by
++ * @param xzRadius X/Z radius to search within
++ * @param yRadius Y radius to search within
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, double xzRadius, double yRadius) {
++ return getNearbyEntitiesByType(clazz, xzRadius, yRadius, xzRadius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, double xRadius, double yRadius, double zRadius) {
++ return getNearbyEntitiesByType(clazz, xRadius, yRadius, zRadius, null);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param radius X/Y/Z radius to search within
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, double radius, @Nullable Predicate<T> predicate) {
++ return getNearbyEntitiesByType(clazz, radius, radius, radius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius, with x and x radius matching (bounding box)
++ * @param clazz Type to filter by
++ * @param xzRadius X/Z radius to search within
++ * @param yRadius Y radius to search within
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends T> clazz, double xzRadius, double yRadius, @Nullable Predicate<T> predicate) {
++ return getNearbyEntitiesByType(clazz, xzRadius, yRadius, xzRadius, predicate);
++ }
++
++ /**
++ * Gets all nearby entities of the specified type, within the specified radius (bounding box)
++ * @param clazz Type to filter by
++ * @param xRadius X Radius
++ * @param yRadius Y Radius
++ * @param zRadius Z Radius
++ * @param predicate a predicate used to filter results
++ * @param <T> the entity type
++ * @return the collection of entities near location. This will always be a non-null collection.
++ */
++ @NotNull
++ public <T extends Entity> Collection<T> getNearbyEntitiesByType(@Nullable Class<? extends Entity> clazz, double xRadius, double yRadius, double zRadius, @Nullable Predicate<T> predicate) {
++ World world = this.getWorld();
++ if (world == null) {
++ throw new IllegalArgumentException("Location has no world");
++ }
++ return world.getNearbyEntitiesByType(clazz, this, xRadius, yRadius, zRadius, predicate);
++ }
+ // Paper end
+ @Override
+ public boolean equals(Object obj) {
diff --git a/Spigot-API-Patches-Unmapped/0110-PlayerReadyArrowEvent.patch b/Spigot-API-Patches-Unmapped/0110-PlayerReadyArrowEvent.patch
new file mode 100644
index 0000000000..7ea1740581
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0110-PlayerReadyArrowEvent.patch
@@ -0,0 +1,107 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 18 Jun 2018 01:09:27 -0400
+Subject: [PATCH] PlayerReadyArrowEvent
+
+Called when a player is firing a bow and the server is choosing an arrow to use.
+Plugins can skip selection of certain arrows and control which is used.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerReadyArrowEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerReadyArrowEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5d04a22fd6964d8d44a2aa069c9629722893b1f4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerReadyArrowEvent.java
+@@ -0,0 +1,93 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player is firing a bow and the server is choosing an arrow to use.
++ */
++public class PlayerReadyArrowEvent extends PlayerEvent implements Cancellable {
++ @NotNull private final ItemStack bow;
++ @NotNull private final ItemStack arrow;
++
++ public PlayerReadyArrowEvent(@NotNull Player player, @NotNull ItemStack bow, @NotNull ItemStack arrow) {
++ super(player);
++ this.bow = bow;
++ this.arrow = arrow;
++ }
++
++ /**
++ * @return the player is using to fire the arrow
++ */
++ @NotNull
++ public ItemStack getBow() {
++ return bow;
++ }
++
++ /**
++ * @return the arrow that is attempting to be used
++ */
++ @NotNull
++ public ItemStack getArrow() {
++ return arrow;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ /**
++ * Whether or not use of this arrow is cancelled. On cancel, the server will try the next arrow available and fire another event.
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Cancel use of this arrow. On cancel, the server will try the next arrow available and fire another event.
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0111-Add-EntityKnockbackByEntityEvent.patch b/Spigot-API-Patches-Unmapped/0111-Add-EntityKnockbackByEntityEvent.patch
new file mode 100644
index 0000000000..1620ab68a6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0111-Add-EntityKnockbackByEntityEvent.patch
@@ -0,0 +1,94 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Brokkonaut <[email protected]>
+Date: Mon, 18 Jun 2018 15:40:39 +0200
+Subject: [PATCH] Add EntityKnockbackByEntityEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityKnockbackByEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityKnockbackByEntityEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9efecabab813f575bb447a356e5e7e952d110f30
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityKnockbackByEntityEvent.java
+@@ -0,0 +1,82 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.util.Vector;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when an Entity is knocked back by the hit of another Entity. The acceleration
++ * vector can be modified. If this event is cancelled, the entity is not knocked back.
++ *
++ */
++public class EntityKnockbackByEntityEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull private final Entity hitBy;
++ private final float knockbackStrength;
++ @NotNull private final Vector acceleration;
++ private boolean cancelled = false;
++
++ public EntityKnockbackByEntityEvent(@NotNull LivingEntity entity, @NotNull Entity hitBy, float knockbackStrength, @NotNull Vector acceleration) {
++ super(entity);
++ this.hitBy = hitBy;
++ this.knockbackStrength = knockbackStrength;
++ this.acceleration = acceleration;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ /**
++ * @return the entity which was knocked back
++ */
++ @NotNull
++ @Override
++ public LivingEntity getEntity() {
++ return (LivingEntity) super.getEntity();
++ }
++
++ /**
++ * @return the original knockback strength.
++ */
++ public float getKnockbackStrength() {
++ return knockbackStrength;
++ }
++
++ /**
++ * @return the Entity which hit
++ */
++ @NotNull
++ public Entity getHitBy() {
++ return hitBy;
++ }
++
++ /**
++ * @return the acceleration that will be applied
++ */
++ @NotNull
++ public Vector getAcceleration() {
++ return acceleration;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0112-Expand-Explosions-API.patch b/Spigot-API-Patches-Unmapped/0112-Expand-Explosions-API.patch
new file mode 100644
index 0000000000..389fec9570
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0112-Expand-Explosions-API.patch
@@ -0,0 +1,200 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 19 Dec 2017 16:24:42 -0500
+Subject: [PATCH] Expand Explosions API
+
+Add Entity as a Source capability, and add more API choices, and on Location.
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 4cf22afc3c1f1cc19b6e5350043431215908a612..af2ee43f2c5133668c18710f526a107d94a5d898 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -7,6 +7,7 @@ import java.util.HashMap;
+ import java.util.Map;
+ import org.bukkit.block.Block;
+ import org.bukkit.configuration.serialization.ConfigurationSerializable;
++import org.bukkit.entity.Entity; // Paper
+ import org.bukkit.util.NumberConversions;
+ import org.bukkit.util.Vector;
+ import org.jetbrains.annotations.NotNull;
+@@ -568,6 +569,87 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ return centerLoc;
+ }
+
++ /**
++ * Creates explosion at this location with given power
++ *
++ * Will break blocks and ignite blocks on fire.
++ *
++ * @param power The power of explosion, where 4F is TNT
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(float power) {
++ return this.getWorld().createExplosion(this, power);
++ }
++
++ /**
++ * Creates explosion at this location with given power and optionally
++ * setting blocks on fire.
++ *
++ * Will break blocks.
++ *
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(float power, boolean setFire) {
++ return this.getWorld().createExplosion(this, power, setFire);
++ }
++
++ /**
++ * Creates explosion at this location with given power and optionally
++ * setting blocks on fire.
++ *
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @param breakBlocks Whether or not to have blocks be destroyed
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(float power, boolean setFire, boolean breakBlocks) {
++ return this.getWorld().createExplosion(this, power, setFire, breakBlocks);
++ }
++
++ /**
++ * Creates explosion at this location with given power, with the specified entity as the source.
++ *
++ * Will break blocks and ignite blocks on fire.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(@Nullable Entity source, float power) {
++ return this.getWorld().createExplosion(source, this, power, true, true);
++ }
++
++ /**
++ * Creates explosion at this location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * Will break blocks.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(@Nullable Entity source, float power, boolean setFire) {
++ return this.getWorld().createExplosion(source, this, power, setFire, true);
++ }
++
++ /**
++ * Creates explosion at this location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @param breakBlocks Whether or not to have blocks be destroyed
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(@NotNull Entity source, float power, boolean setFire, boolean breakBlocks) {
++ return this.getWorld().createExplosion(source, this, power, setFire, breakBlocks);
++ }
++
+ /**
+ * Returns a list of entities within a bounding box centered around a Location.
+ *
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 05643d0f2bf2cb2dedb0a2ad693b2121565d3f1f..c7cdbc36f96a8ee9eaac2a2145afbfc76bc38cc9 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -1442,6 +1442,88 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ */
+ public boolean createExplosion(@NotNull Location loc, float power, boolean setFire);
+
++ // Paper start
++ /**
++ * Creates explosion at given location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * @param source The source entity of the explosion
++ * @param loc Location to blow up
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @param breakBlocks Whether or not to have blocks be destroyed
++ * @return false if explosion was canceled, otherwise true
++ */
++ public boolean createExplosion(@Nullable Entity source, @NotNull Location loc, float power, boolean setFire, boolean breakBlocks);
++
++ /**
++ * Creates explosion at given location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * Will destroy other blocks
++ *
++ * @param source The source entity of the explosion
++ * @param loc Location to blow up
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @return false if explosion was canceled, otherwise true
++ */
++ public default boolean createExplosion(@Nullable Entity source, @NotNull Location loc, float power, boolean setFire) {
++ return createExplosion(source, loc, power, setFire, true);
++ }
++ /**
++ * Creates explosion at given location with given power, with the specified entity as the source.
++ * Will set blocks on fire and destroy blocks.
++ *
++ * @param source The source entity of the explosion
++ * @param loc Location to blow up
++ * @param power The power of explosion, where 4F is TNT
++ * @return false if explosion was canceled, otherwise true
++ */
++ public default boolean createExplosion(@Nullable Entity source, @NotNull Location loc, float power) {
++ return createExplosion(source, loc, power, true, true);
++ }
++ /**
++ * Creates explosion at given entities location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @param breakBlocks Whether or not to have blocks be destroyed
++ * @return false if explosion was canceled, otherwise true
++ */
++ public default boolean createExplosion(@NotNull Entity source, float power, boolean setFire, boolean breakBlocks) {
++ return createExplosion(source, source.getLocation(), power, setFire, breakBlocks);
++ }
++ /**
++ * Creates explosion at given entities location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * Will destroy blocks.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @param setFire Whether or not to set blocks on fire
++ * @return false if explosion was canceled, otherwise true
++ */
++ public default boolean createExplosion(@NotNull Entity source, float power, boolean setFire) {
++ return createExplosion(source, source.getLocation(), power, setFire, true);
++ }
++
++ /**
++ * Creates explosion at given entities location with given power and optionally
++ * setting blocks on fire, with the specified entity as the source.
++ *
++ * @param source The source entity of the explosion
++ * @param power The power of explosion, where 4F is TNT
++ * @return false if explosion was canceled, otherwise true
++ */
++ public default boolean createExplosion(@NotNull Entity source, float power) {
++ return createExplosion(source, source.getLocation(), power, true, true);
++ }
++ // Paper end
++
+ /**
+ * Creates explosion at given coordinates with given power and optionally
+ * setting blocks on fire or breaking blocks.
diff --git a/Spigot-API-Patches-Unmapped/0113-ItemStack-API-additions-for-quantity-flags-lore.patch b/Spigot-API-Patches-Unmapped/0113-ItemStack-API-additions-for-quantity-flags-lore.patch
new file mode 100644
index 0000000000..0118df10ac
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0113-ItemStack-API-additions-for-quantity-flags-lore.patch
@@ -0,0 +1,206 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 22 Jun 2018 22:59:18 -0400
+Subject: [PATCH] ItemStack API additions for quantity/flags/lore
+
+
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index cc065602db56c51b87d273a52d9ef82439fcaa7a..c6eca5a69cb8f5b161ff99ecad9475be64dcfed5 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -2,7 +2,9 @@ package org.bukkit.inventory;
+
+ import com.google.common.collect.ImmutableMap;
+ import java.util.LinkedHashMap;
++import java.util.List; // Paper
+ import java.util.Map;
++import java.util.Set; // Paper
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.Bukkit;
+ import org.bukkit.Material;
+@@ -635,5 +637,185 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ // Requires access to NMS
+ return ensureServerConversions().getMaxItemUseDuration();
+ }
++
++ /**
++ * Clones the itemstack and returns it a single quantity.
++ * @return The new itemstack with 1 quantity
++ */
++ @NotNull
++ public ItemStack asOne() {
++ return asQuantity(1);
++ }
++
++ /**
++ * Clones the itemstack and returns it as the specified quantity
++ * @param qty The quantity of the cloned item
++ * @return The new itemstack with specified quantity
++ */
++ @NotNull
++ public ItemStack asQuantity(int qty) {
++ ItemStack clone = clone();
++ clone.setAmount(qty);
++ return clone;
++ }
++
++ /**
++ * Adds 1 to this itemstack. Will not go over the items max stack size.
++ * @return The same item (not a clone)
++ */
++ @NotNull
++ public ItemStack add() {
++ return add(1);
++ }
++
++ /**
++ * Adds quantity to this itemstack. Will not go over the items max stack size.
++ *
++ * @param qty The amount to add
++ * @return The same item (not a clone)
++ */
++ @NotNull
++ public ItemStack add(int qty) {
++ setAmount(Math.min(getMaxStackSize(), getAmount() + qty));
++ return this;
++ }
++
++ /**
++ * Subtracts 1 to this itemstack. Going to 0 or less will invalidate the item.
++ * @return The same item (not a clone)
++ */
++ @NotNull
++ public ItemStack subtract() {
++ return subtract(1);
++ }
++
++ /**
++ * Subtracts quantity to this itemstack. Going to 0 or less will invalidate the item.
++ *
++ * @param qty The amount to add
++ * @return The same item (not a clone)
++ */
++ @NotNull
++ public ItemStack subtract(int qty) {
++ setAmount(Math.max(0, getAmount() - qty));
++ return this;
++ }
++
++ /**
++ * If the item has lore, returns it, else it will return null
++ * @return The lore, or null
++ * @deprecated in favor of {@link #lore()}
++ */
++ @Deprecated
++ public @Nullable List<String> getLore() {
++ if (!hasItemMeta()) {
++ return null;
++ }
++ ItemMeta itemMeta = getItemMeta();
++ if (!itemMeta.hasLore()) {
++ return null;
++ }
++ return itemMeta.getLore();
++ }
++
++ /**
++ * If the item has lore, returns it, else it will return null
++ * @return The lore, or null
++ */
++ public @Nullable List<net.kyori.adventure.text.Component> lore() {
++ if (!this.hasItemMeta()) {
++ return null;
++ }
++ final ItemMeta itemMeta = getItemMeta();
++ if (!itemMeta.hasLore()) {
++ return null;
++ }
++ return itemMeta.lore();
++ }
++
++ /**
++ * Sets the lore for this item.
++ * Removes lore when given null.
++ *
++ * @param lore the lore that will be set
++ * @deprecated in favour of {@link #lore(List)}
++ */
++ @Deprecated
++ public void setLore(@Nullable List<String> lore) {
++ ItemMeta itemMeta = getItemMeta();
++ if (itemMeta == null) {
++ throw new IllegalStateException("Cannot set lore on " + getType());
++ }
++ itemMeta.setLore(lore);
++ setItemMeta(itemMeta);
++ }
++
++ /**
++ * Sets the lore for this item.
++ * Removes lore when given null.
++ *
++ * @param lore the lore that will be set
++ */
++ public void lore(@Nullable List<net.kyori.adventure.text.Component> lore) {
++ ItemMeta itemMeta = getItemMeta();
++ if (itemMeta == null) {
++ throw new IllegalStateException("Cannot set lore on " + getType());
++ }
++ itemMeta.lore(lore);
++ this.setItemMeta(itemMeta);
++ }
++
++ /**
++ * Set itemflags which should be ignored when rendering a ItemStack in the Client. This Method does silently ignore double set itemFlags.
++ *
++ * @param itemFlags The hideflags which shouldn't be rendered
++ */
++ public void addItemFlags(@NotNull ItemFlag... itemFlags) {
++ ItemMeta itemMeta = getItemMeta();
++ if (itemMeta == null) {
++ throw new IllegalStateException("Cannot add flags on " + getType());
++ }
++ itemMeta.addItemFlags(itemFlags);
++ setItemMeta(itemMeta);
++ }
++
++ /**
++ * Remove specific set of itemFlags. This tells the Client it should render it again. This Method does silently ignore double removed itemFlags.
++ *
++ * @param itemFlags Hideflags which should be removed
++ */
++ public void removeItemFlags(@NotNull ItemFlag... itemFlags) {
++ ItemMeta itemMeta = getItemMeta();
++ if (itemMeta == null) {
++ throw new IllegalStateException("Cannot remove flags on " + getType());
++ }
++ itemMeta.removeItemFlags(itemFlags);
++ setItemMeta(itemMeta);
++ }
++
++ /**
++ * Get current set itemFlags. The collection returned is unmodifiable.
++ *
++ * @return A set of all itemFlags set
++ */
++ @NotNull
++ public Set<ItemFlag> getItemFlags() {
++ ItemMeta itemMeta = getItemMeta();
++ if (itemMeta == null) {
++ return java.util.Collections.emptySet();
++ }
++ return itemMeta.getItemFlags();
++ }
++
++ /**
++ * Check if the specified flag is present on this item.
++ *
++ * @param flag the flag to check
++ * @return if it is present
++ */
++ public boolean hasItemFlag(@NotNull ItemFlag flag) {
++ ItemMeta itemMeta = getItemMeta();
++ return itemMeta != null && itemMeta.hasItemFlag(flag);
++ }
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0114-LivingEntity-Hand-Raised-Item-Use-API.patch b/Spigot-API-Patches-Unmapped/0114-LivingEntity-Hand-Raised-Item-Use-API.patch
new file mode 100644
index 0000000000..342ef584d5
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0114-LivingEntity-Hand-Raised-Item-Use-API.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 29 Jun 2018 00:19:19 -0400
+Subject: [PATCH] LivingEntity Hand Raised/Item Use API
+
+How long an entity has raised hands to charge an attack or use an item
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 879dec59f202ee95043bd7317a672cd59ab3bbbe..8b89c0701dd557bcab0c05c1593354ee704b9fe4 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -12,6 +12,7 @@ import org.bukkit.attribute.Attributable;
+ import org.bukkit.block.Block;
+ import org.bukkit.entity.memory.MemoryKey;
+ import org.bukkit.inventory.EntityEquipment;
++import org.bukkit.inventory.ItemStack;
+ import org.bukkit.potion.PotionEffect;
+ import org.bukkit.potion.PotionEffectType;
+ import org.bukkit.projectiles.ProjectileSource;
+@@ -642,5 +643,32 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @param delay Delay in ticks
+ */
+ void setShieldBlockingDelay(int delay);
++
++ /**
++ * Get's the item being actively "used" or consumed.
++ * @return The item. Will be null if no active item.
++ */
++ @Nullable
++ ItemStack getActiveItem();
++
++ /**
++ * Get's remaining time a player needs to keep hands raised with an item to finish using it.
++ * @return Remaining ticks to use the item
++ */
++ int getItemUseRemainingTime();
++
++ /**
++ * Get how long the players hands have been raised (Charging Bow attack, using a potion, etc)
++ *
++ * @return Get how long the players hands have been raised (Charging Bow attack, using a potion, etc)
++ */
++ int getHandRaisedTime();
++
++ /**
++ * Whether or not this entity is using or charging an attack (Bow pulled back, drinking potion, eating food)
++ *
++ * @return Whether or not this entity is using or charging an attack (Bow pulled back, drinking potion, eating food)
++ */
++ boolean isHandRaised();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0115-RangedEntity-API.patch b/Spigot-API-Patches-Unmapped/0115-RangedEntity-API.patch
new file mode 100644
index 0000000000..9d154dd500
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0115-RangedEntity-API.patch
@@ -0,0 +1,192 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 26 Jun 2018 21:34:40 -0400
+Subject: [PATCH] RangedEntity API
+
+Allows you to determine if an entity is capable of ranged attacks,
+and to perform an attack.
+
+diff --git a/src/main/java/com/destroystokyo/paper/entity/RangedEntity.java b/src/main/java/com/destroystokyo/paper/entity/RangedEntity.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f2e3233a3d1744e32fb76d3731b9858ef0067e30
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/RangedEntity.java
+@@ -0,0 +1,31 @@
++package com.destroystokyo.paper.entity;
++
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Mob;
++import org.jetbrains.annotations.NotNull;
++
++public interface RangedEntity extends Mob {
++ /**
++ * Attack the specified entity using a ranged attack.
++ *
++ * @param target the entity to target
++ * @param charge How "charged" the attack is (how far back the bow was pulled for Bow attacks).
++ * This should be a value between 0 and 1, represented as targetDistance/maxDistance.
++ */
++ void rangedAttack(@NotNull LivingEntity target, float charge);
++
++ /**
++ * Sets that the Entity is "charging" up an attack, by raising its hands
++ *
++ * @param raiseHands Whether the entities hands are raised to charge attack
++ */
++ void setChargingAttack(boolean raiseHands);
++
++ /**
++ * Alias to {@link LivingEntity#isHandRaised()}, if the entity is charging an attack
++ * @return If entities hands are raised
++ */
++ default boolean isChargingAttack() {
++ return isHandRaised();
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Drowned.java b/src/main/java/org/bukkit/entity/Drowned.java
+index 1dee177ae6e21da000607dc8dd8fd76857f323b9..8d1ad9ef757cb1e8d72b145262df73612a76c746 100644
+--- a/src/main/java/org/bukkit/entity/Drowned.java
++++ b/src/main/java/org/bukkit/entity/Drowned.java
+@@ -1,6 +1,8 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
++
+ /**
+ * Drowned zombie.
+ */
+-public interface Drowned extends Zombie { }
++public interface Drowned extends Zombie, RangedEntity { } // Paper
+diff --git a/src/main/java/org/bukkit/entity/Illusioner.java b/src/main/java/org/bukkit/entity/Illusioner.java
+index 7c92c431b32754dca12b4d584bd6fa93ff73badf..14e6c5ee06ece3d1bbc1239afa67c847a479948f 100644
+--- a/src/main/java/org/bukkit/entity/Illusioner.java
++++ b/src/main/java/org/bukkit/entity/Illusioner.java
+@@ -1,6 +1,10 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
++
+ /**
+ * Represents an Illusioner "Illager".
+ */
+-public interface Illusioner extends Spellcaster { }
++public interface Illusioner extends Spellcaster, RangedEntity { // Paper
++
++}
+diff --git a/src/main/java/org/bukkit/entity/Llama.java b/src/main/java/org/bukkit/entity/Llama.java
+index c43854298548391679c1d280bd42edbeed7759b9..d23226ccb0f6c25028f000ce31346cd0a8898e6a 100644
+--- a/src/main/java/org/bukkit/entity/Llama.java
++++ b/src/main/java/org/bukkit/entity/Llama.java
+@@ -1,12 +1,13 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
+ import org.bukkit.inventory.LlamaInventory;
+ import org.jetbrains.annotations.NotNull;
+
+ /**
+ * Represents a Llama.
+ */
+-public interface Llama extends ChestedHorse {
++public interface Llama extends ChestedHorse, RangedEntity { // Paper
+
+ /**
+ * Represents the base color that the llama has.
+diff --git a/src/main/java/org/bukkit/entity/Piglin.java b/src/main/java/org/bukkit/entity/Piglin.java
+index eefe96c997af7d4547de3e047c2d36f0e09e25e2..6e106e1291370416f53a597b48822d3e839ee73d 100644
+--- a/src/main/java/org/bukkit/entity/Piglin.java
++++ b/src/main/java/org/bukkit/entity/Piglin.java
+@@ -1,9 +1,11 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity; // Paper
++
+ /**
+ * Represents a Piglin.
+ */
+-public interface Piglin extends PiglinAbstract {
++public interface Piglin extends PiglinAbstract, RangedEntity { // Paper
+
+ /**
+ * Get whether the piglin is able to hunt hoglins.
+diff --git a/src/main/java/org/bukkit/entity/Pillager.java b/src/main/java/org/bukkit/entity/Pillager.java
+index 9a2252fef56be1ed3ae2169aea46cb567e965c6c..11f38187fca830d974be01fea2966a31936184cb 100644
+--- a/src/main/java/org/bukkit/entity/Pillager.java
++++ b/src/main/java/org/bukkit/entity/Pillager.java
+@@ -1,8 +1,10 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity; // Paper
++
+ import org.bukkit.inventory.InventoryHolder;
+
+ /**
+ * Illager entity.
+ */
+-public interface Pillager extends Illager, InventoryHolder { }
++public interface Pillager extends Illager, InventoryHolder, RangedEntity { } // Paper
+diff --git a/src/main/java/org/bukkit/entity/Skeleton.java b/src/main/java/org/bukkit/entity/Skeleton.java
+index 16b1293887cee2bc5267f3da771fb5a6ece1b4e9..1c367f78eadf24850061a84ce63b950b79d3c435 100644
+--- a/src/main/java/org/bukkit/entity/Skeleton.java
++++ b/src/main/java/org/bukkit/entity/Skeleton.java
+@@ -2,11 +2,12 @@ package org.bukkit.entity;
+
+ import org.jetbrains.annotations.Contract;
+ import org.jetbrains.annotations.NotNull;
++import com.destroystokyo.paper.entity.RangedEntity;
+
+ /**
+ * Represents a Skeleton.
+ */
+-public interface Skeleton extends Monster {
++public interface Skeleton extends Monster, RangedEntity { // Paper
+
+ /**
+ * Gets the current type of this skeleton.
+diff --git a/src/main/java/org/bukkit/entity/Snowman.java b/src/main/java/org/bukkit/entity/Snowman.java
+index 818efe2a4d1ac0c4d8dca6c757850d99cdc2cb4b..10f8f6d45ae9280651c3ebddd1f90acbd7d6ff29 100644
+--- a/src/main/java/org/bukkit/entity/Snowman.java
++++ b/src/main/java/org/bukkit/entity/Snowman.java
+@@ -1,9 +1,11 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
++
+ /**
+ * Represents a snowman entity
+ */
+-public interface Snowman extends Golem {
++public interface Snowman extends Golem, RangedEntity { // Paper
+
+ /**
+ * Gets whether this snowman is in "derp mode", meaning it is not wearing a
+diff --git a/src/main/java/org/bukkit/entity/Witch.java b/src/main/java/org/bukkit/entity/Witch.java
+index b4343903b66a7fb5250c1da2e09c9e5863c20daf..aa88aede6c4e66a608a63d07bc66d60357b0bee9 100644
+--- a/src/main/java/org/bukkit/entity/Witch.java
++++ b/src/main/java/org/bukkit/entity/Witch.java
+@@ -1,7 +1,9 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
++
+ /**
+ * Represents a Witch
+ */
+-public interface Witch extends Raider {
++public interface Witch extends Raider, RangedEntity { // Paper
+ }
+diff --git a/src/main/java/org/bukkit/entity/Wither.java b/src/main/java/org/bukkit/entity/Wither.java
+index 3bc332ee7f7d428bef6e2566ddded8b941858e2e..426d3693317cd303d35d8203026b528d87e401d5 100644
+--- a/src/main/java/org/bukkit/entity/Wither.java
++++ b/src/main/java/org/bukkit/entity/Wither.java
+@@ -1,7 +1,9 @@
+ package org.bukkit.entity;
+
++import com.destroystokyo.paper.entity.RangedEntity;
++
+ /**
+ * Represents a Wither boss
+ */
+-public interface Wither extends Monster, Boss {
++public interface Wither extends Monster, Boss, RangedEntity { // Paper
+ }
diff --git a/Spigot-API-Patches-Unmapped/0116-Add-World.getEntity-UUID-API.patch b/Spigot-API-Patches-Unmapped/0116-Add-World.getEntity-UUID-API.patch
new file mode 100644
index 0000000000..bb7e7eafdf
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0116-Add-World.getEntity-UUID-API.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Brokkonaut <[email protected]>
+Date: Tue, 3 Jul 2018 16:07:16 +0200
+Subject: [PATCH] Add World.getEntity(UUID) API
+
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index c7cdbc36f96a8ee9eaac2a2145afbfc76bc38cc9..edf136ef50c9cdd8ccea2e35508e4464c3de99bd 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -946,6 +946,17 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public Collection<Entity> getNearbyEntities(@NotNull Location location, double x, double y, double z);
+
++ // Paper start - getEntity by UUID API
++ /**
++ * Gets an entity in this world by its UUID
++ *
++ * @param uuid the UUID of the entity
++ * @return the entity with the given UUID, or null if it isn't found
++ */
++ @Nullable
++ public Entity getEntity(@NotNull UUID uuid);
++ // Paper end
++
+ /**
+ * Returns a list of entities within a bounding box centered around a
+ * Location.
diff --git a/Spigot-API-Patches-Unmapped/0117-InventoryCloseEvent-Reason-API.patch b/Spigot-API-Patches-Unmapped/0117-InventoryCloseEvent-Reason-API.patch
new file mode 100644
index 0000000000..c03581bf2f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0117-InventoryCloseEvent-Reason-API.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 3 Jul 2018 21:52:52 -0400
+Subject: [PATCH] InventoryCloseEvent Reason API
+
+Allows you to determine why an inventory was closed, enabling plugin developers
+to "confirm" things based on if it was player triggered close or not.
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index 7430bc85301d0fcb34c6035fbe08ae245c76e043..3418133d07250a7fd50caad8d97924b86fb30bad 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -153,6 +153,15 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ */
+ public void closeInventory();
+
++ // Paper start
++ /**
++ * Force-closes the currently open inventory view for this player, if any.
++ *
++ * @param reason why the inventory is closing
++ */
++ public void closeInventory(@NotNull org.bukkit.event.inventory.InventoryCloseEvent.Reason reason);
++ // Paper end
++
+ /**
+ * Returns the ItemStack currently in your hand, can be empty.
+ *
+diff --git a/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java b/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java
+index 5861247c1b8ee4fe2736fd5098e05a2ca9ab78ea..21ad8888c0e403bfc63518502577d651c02dda05 100644
+--- a/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java
+@@ -11,9 +11,60 @@ import org.jetbrains.annotations.NotNull;
+ */
+ public class InventoryCloseEvent extends InventoryEvent {
+ private static final HandlerList handlers = new HandlerList();
++ // Paper start
++ private final Reason reason;
++ @NotNull
++ public Reason getReason() {
++ return reason;
++ }
++
++ public enum Reason {
++ /**
++ * Unknown reason
++ */
++ UNKNOWN,
++ /**
++ * Player is teleporting
++ */
++ TELEPORT,
++ /**
++ * Player is no longer permitted to use this inventory
++ */
++ CANT_USE,
++ /**
++ * The chunk the inventory was in was unloaded
++ */
++ UNLOADED,
++ /**
++ * Opening new inventory instead
++ */
++ OPEN_NEW,
++ /**
++ * Closed
++ */
++ PLAYER,
++ /**
++ * Closed due to disconnect
++ */
++ DISCONNECT,
++ /**
++ * The player died
++ */
++ DEATH,
++ /**
++ * Closed by Bukkit API
++ */
++ PLUGIN,
++ }
+
+ public InventoryCloseEvent(@NotNull InventoryView transaction) {
++ this(transaction, Reason.UNKNOWN);
++ }
++
++ public InventoryCloseEvent(@NotNull InventoryView transaction, @NotNull Reason reason) {
+ super(transaction);
++ this.reason = reason;
++ // Paper end
+ }
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0118-Entity-getChunk-API.patch b/Spigot-API-Patches-Unmapped/0118-Entity-getChunk-API.patch
new file mode 100644
index 0000000000..8eaf47d2a6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0118-Entity-getChunk-API.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 4 Jul 2018 02:25:48 -0400
+Subject: [PATCH] Entity#getChunk API
+
+Get the chunk the entity is currently registered to
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index feb9507a972bf797144a01adeeaac83ec2bd165a..9b8823279524d1c1566176c589aa5794eb8aafbc 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -3,6 +3,7 @@ package org.bukkit.entity;
+ import java.util.List;
+ import java.util.Set;
+ import java.util.UUID;
++import org.bukkit.Chunk; // Paper
+ import org.bukkit.EntityEffect;
+ import org.bukkit.Location;
+ import org.bukkit.Nameable;
+@@ -626,5 +627,13 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ * @return True if entity spawned from a mob spawner
+ */
+ boolean fromMobSpawner();
++
++ /**
++ * Gets the latest chunk an entity is currently or was in.
++ *
++ * @return The current, or most recent chunk if the entity is invalid (which may load the chunk)
++ */
++ @NotNull
++ Chunk getChunk();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0119-Add-an-asterisk-to-legacy-API-plugins.patch b/Spigot-API-Patches-Unmapped/0119-Add-an-asterisk-to-legacy-API-plugins.patch
new file mode 100644
index 0000000000..474486c1e0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0119-Add-an-asterisk-to-legacy-API-plugins.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Phoenix616 <[email protected]>
+Date: Tue, 1 Dec 2020 14:57:02 +0100
+Subject: [PATCH] Add an asterisk to legacy API plugins
+
+Not here to name and shame, only so server admins can be aware of which
+plugins have and haven't been updated.
+
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 195b6bb328de92c4d17d1cd14e13578226b1ac3c..d6897f43a0692e031bed8a212d9a637ef548cc60 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -91,5 +91,11 @@ public interface UnsafeValues {
+ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
+ return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher();
+ }
++
++ boolean isSupportedApiVersion(String apiVersion);
++
++ static boolean isLegacyPlugin(org.bukkit.plugin.Plugin plugin) {
++ return !Bukkit.getUnsafe().isSupportedApiVersion(plugin.getDescription().getAPIVersion());
++ }
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
+index 4de959bbd1270d7d6ea8e5e69521bcca6abe2138..1aa58c59e1e8738bbdc77752885ff3b18b29de42 100644
+--- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
++++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java
+@@ -52,9 +52,15 @@ public class PluginsCommand extends BukkitCommand {
+ }
+
+ Plugin plugin = entry.getValue();
+-
++
+ pluginList.append(plugin.isEnabled() ? ChatColor.GREEN : ChatColor.RED);
+- pluginList.append(plugin.getDescription().getName());
++ // Paper start - Add an asterisk to legacy plugins (so admins are aware)
++ String pluginName = plugin.getDescription().getName();
++ if (org.bukkit.UnsafeValues.isLegacyPlugin(plugin)) {
++ pluginName += "*";
++ }
++ pluginList.append(pluginName);
++ // Paper end
+
+ if (plugin.getDescription().getProvides().size() > 0) {
+ pluginList.append(" (").append(String.join(", ", plugin.getDescription().getProvides())).append(")");
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index bef88a6e2e6f7071401a3af0aec31e62aa265566..de44d850d7b3ab3e528eb6f2de375a6c3e0e5cf9 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -315,7 +315,14 @@ public final class JavaPluginLoader implements PluginLoader {
+ Validate.isTrue(plugin instanceof JavaPlugin, "Plugin is not associated with this PluginLoader");
+
+ if (!plugin.isEnabled()) {
+- plugin.getLogger().info("Enabling " + plugin.getDescription().getFullName());
++ // Paper start - Add an asterisk to legacy plugins (so admins are aware)
++ String enableMsg = "Enabling " + plugin.getDescription().getFullName();
++ if (org.bukkit.UnsafeValues.isLegacyPlugin(plugin)) {
++ enableMsg += "*";
++ }
++
++ plugin.getLogger().info(enableMsg);
++ // Paper end
+
+ JavaPlugin jPlugin = (JavaPlugin) plugin;
+
diff --git a/Spigot-API-Patches-Unmapped/0120-EnderDragon-Events.patch b/Spigot-API-Patches-Unmapped/0120-EnderDragon-Events.patch
new file mode 100644
index 0000000000..68ed791382
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0120-EnderDragon-Events.patch
@@ -0,0 +1,225 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 21 Jul 2018 01:51:05 -0500
+Subject: [PATCH] EnderDragon Events
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFireballHitEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFireballHitEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..118c7b6772a52c250649af2a9286f483f43da385
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFireballHitEvent.java
+@@ -0,0 +1,79 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.AreaEffectCloud;
++import org.bukkit.entity.DragonFireball;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++
++import java.util.Collection;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Fired when a DragonFireball collides with a block/entity and spawns an AreaEffectCloud
++ */
++public class EnderDragonFireballHitEvent extends EntityEvent implements Cancellable {
++ @Nullable private final Collection<LivingEntity> targets;
++ @NotNull private final AreaEffectCloud areaEffectCloud;
++
++ public EnderDragonFireballHitEvent(@NotNull DragonFireball fireball, @Nullable Collection<LivingEntity> targets, @NotNull AreaEffectCloud areaEffectCloud) {
++ super(fireball);
++ this.targets = targets;
++ this.areaEffectCloud = areaEffectCloud;
++ }
++
++ /**
++ * The fireball involved in this event
++ */
++ @NotNull
++ @Override
++ public DragonFireball getEntity() {
++ return (DragonFireball) super.getEntity();
++ }
++
++ /**
++ * The living entities hit by fireball
++ *
++ * May be null if no entities were hit
++ *
++ * @return the targets
++ */
++ @Nullable
++ public Collection<LivingEntity> getTargets() {
++ return targets;
++ }
++
++ /**
++ * @return The area effect cloud spawned in this collision
++ */
++ @NotNull
++ public AreaEffectCloud getAreaEffectCloud() {
++ return areaEffectCloud;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFlameEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFlameEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..1915177f4b8f8013656fbdb41240f6c5c88f95d7
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonFlameEvent.java
+@@ -0,0 +1,61 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.AreaEffectCloud;
++import org.bukkit.entity.EnderDragon;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when an EnderDragon spawns an AreaEffectCloud by shooting flames
++ */
++public class EnderDragonFlameEvent extends EntityEvent implements Cancellable {
++ @NotNull private final AreaEffectCloud areaEffectCloud;
++
++ public EnderDragonFlameEvent(@NotNull EnderDragon enderDragon, @NotNull AreaEffectCloud areaEffectCloud) {
++ super(enderDragon);
++ this.areaEffectCloud = areaEffectCloud;
++ }
++
++ /**
++ * The enderdragon involved in this event
++ */
++ @NotNull
++ @Override
++ public EnderDragon getEntity() {
++ return (EnderDragon) super.getEntity();
++ }
++
++ /**
++ * @return The area effect cloud spawned in this collision
++ */
++ @NotNull
++ public AreaEffectCloud getAreaEffectCloud() {
++ return areaEffectCloud;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonShootFireballEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonShootFireballEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8414bd805ec68d7b305fbf645c59f8d5b762c9ce
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EnderDragonShootFireballEvent.java
+@@ -0,0 +1,61 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.DragonFireball;
++import org.bukkit.entity.EnderDragon;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when an EnderDragon shoots a fireball
++ */
++public class EnderDragonShootFireballEvent extends EntityEvent implements Cancellable {
++ @NotNull private final DragonFireball fireball;
++
++ public EnderDragonShootFireballEvent(@NotNull EnderDragon entity, @NotNull DragonFireball fireball) {
++ super(entity);
++ this.fireball = fireball;
++ }
++
++ /**
++ * The enderdragon shooting the fireball
++ */
++ @NotNull
++ @Override
++ public EnderDragon getEntity() {
++ return (EnderDragon) super.getEntity();
++ }
++
++ /**
++ * @return The fireball being shot
++ */
++ @NotNull
++ public DragonFireball getFireball() {
++ return fireball;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0121-PlayerLaunchProjectileEvent.patch b/Spigot-API-Patches-Unmapped/0121-PlayerLaunchProjectileEvent.patch
new file mode 100644
index 0000000000..7524daea92
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0121-PlayerLaunchProjectileEvent.patch
@@ -0,0 +1,95 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 21 Jul 2018 03:10:50 -0500
+Subject: [PATCH] PlayerLaunchProjectileEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerLaunchProjectileEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerLaunchProjectileEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9074b2ede01f76c0560e5318246382163cc91591
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerLaunchProjectileEvent.java
+@@ -0,0 +1,83 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.entity.Projectile;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player shoots a projectile
++ */
++public class PlayerLaunchProjectileEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final Projectile projectile;
++ @NotNull private final ItemStack itemStack;
++ private boolean consumeItem = true;
++ private boolean cancelled;
++
++ public PlayerLaunchProjectileEvent(@NotNull Player shooter, @NotNull ItemStack itemStack, @NotNull Projectile projectile) {
++ super(shooter);
++ this.itemStack = itemStack;
++ this.projectile = projectile;
++ }
++
++ /**
++ * Gets the projectile which will be launched by this event
++ *
++ * @return the launched projectile
++ */
++ @NotNull
++ public Projectile getProjectile() {
++ return projectile;
++ }
++
++ /**
++ * Get the ItemStack used to fire the projectile
++ *
++ * @return The ItemStack used
++ */
++ @NotNull
++ public ItemStack getItemStack() {
++ return itemStack;
++ }
++
++ /**
++ * Get whether to consume the ItemStack or not
++ *
++ * @return True to consume
++ */
++ public boolean shouldConsume() {
++ return consumeItem;
++ }
++
++ /**
++ * Set whether to consume the ItemStack or not
++ *
++ * @param consumeItem True to consume
++ */
++ public void setShouldConsume(boolean consumeItem) {
++ this.consumeItem = consumeItem;
++ }
++
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0122-PlayerElytraBoostEvent.patch b/Spigot-API-Patches-Unmapped/0122-PlayerElytraBoostEvent.patch
new file mode 100644
index 0000000000..c5d8e5ddf3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0122-PlayerElytraBoostEvent.patch
@@ -0,0 +1,97 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 21 Jul 2018 01:59:53 -0500
+Subject: [PATCH] PlayerElytraBoostEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerElytraBoostEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerElytraBoostEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e9a76a25fa5445905a09dbc2fd5b35bff56d80b3
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerElytraBoostEvent.java
+@@ -0,0 +1,85 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Firework;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a player boosts elytra flight with a firework
++ */
++public class PlayerElytraBoostEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++ @NotNull private final ItemStack itemStack;
++ @NotNull private Firework firework;
++ private boolean consume = true;
++
++ public PlayerElytraBoostEvent(@NotNull Player player, @NotNull ItemStack itemStack, @NotNull Firework firework) {
++ super(player);
++ this.itemStack = itemStack;
++ this.firework = firework;
++ }
++
++ /**
++ * Get the firework itemstack used
++ *
++ * @return ItemStack of firework
++ */
++ @NotNull
++ public ItemStack getItemStack() {
++ return itemStack;
++ }
++
++ /**
++ * Get the firework entity that was spawned
++ *
++ * @return Firework entity
++ */
++ @NotNull
++ public Firework getFirework() {
++ return firework;
++ }
++
++ /**
++ * Get whether to consume the firework or not
++ *
++ * @return True to consume
++ */
++ public boolean shouldConsume() {
++ return consume;
++ }
++
++ /**
++ * Set whether to consume the firework or not
++ *
++ * @param consume True to consume
++ */
++ public void setShouldConsume(boolean consume) {
++ this.consume = consume;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0123-EntityTransformedEvent.patch b/Spigot-API-Patches-Unmapped/0123-EntityTransformedEvent.patch
new file mode 100644
index 0000000000..c77c4a8ba6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0123-EntityTransformedEvent.patch
@@ -0,0 +1,104 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Anthony MacAllister <[email protected]>
+Date: Thu, 26 Jul 2018 15:28:53 -0400
+Subject: [PATCH] EntityTransformedEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityTransformedEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityTransformedEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..12194f1fc7f03ca6785904b6187b3dfd03b16461
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityTransformedEvent.java
+@@ -0,0 +1,92 @@
++package com.destroystokyo.paper.event.entity;
++
++
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.event.entity.EntityTransformEvent;
++
++/**
++ * Fired when an entity transforms into another entity
++ * <p>
++ * If the event is cancelled, the entity will not transform
++ * @deprecated Bukkit has added {@link EntityTransformEvent}, you should start using that
++ */
++@Deprecated
++public class EntityTransformedEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ private final Entity transformed;
++ private final TransformedReason reason;
++
++ public EntityTransformedEvent(Entity entity, Entity transformed, TransformedReason reason) {
++ super(entity);
++ this.transformed = transformed;
++ this.reason = reason;
++ }
++
++ /**
++ * The entity after it has transformed
++ *
++ * @return Transformed entity
++ * @deprecated see {@link EntityTransformEvent#getTransformedEntity()}
++ */
++ @Deprecated
++ public Entity getTransformed() {
++ return transformed;
++ }
++
++ /**
++ * @return The reason for the transformation
++ * @deprecated see {@link EntityTransformEvent#getTransformReason()}
++ */
++ @Deprecated
++ public TransformedReason getReason() {
++ return reason;
++ }
++
++
++ @Override
++ public HandlerList getHandlers(){
++ return handlers;
++ }
++
++ public static HandlerList getHandlerList(){
++ return handlers;
++ }
++
++ @Override
++ public boolean isCancelled(){
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel){
++ cancelled = cancel;
++ }
++
++ public enum TransformedReason {
++ /**
++ * When a zombie drowns
++ */
++ DROWNED,
++ /**
++ * When a zombie villager is cured
++ */
++ CURED,
++ /**
++ * When a villager turns to a zombie villager
++ */
++ INFECTED,
++ /**
++ * When a mooshroom turns to a cow
++ */
++ SHEARED,
++ /**
++ * When a pig turns to a zombiepigman
++ */
++ LIGHTNING
++
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0124-Allow-disabling-armour-stand-ticking.patch b/Spigot-API-Patches-Unmapped/0124-Allow-disabling-armour-stand-ticking.patch
new file mode 100644
index 0000000000..30f0bf5227
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0124-Allow-disabling-armour-stand-ticking.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: kashike <[email protected]>
+Date: Wed, 15 Aug 2018 01:26:03 -0700
+Subject: [PATCH] Allow disabling armour stand ticking
+
+
+diff --git a/src/main/java/org/bukkit/entity/ArmorStand.java b/src/main/java/org/bukkit/entity/ArmorStand.java
+index fddc063798edc8084ca695578a47485204a7f3cd..365d3a3c5fc4a47efe56225ef1eb87b5046034f4 100644
+--- a/src/main/java/org/bukkit/entity/ArmorStand.java
++++ b/src/main/java/org/bukkit/entity/ArmorStand.java
+@@ -360,5 +360,21 @@ public interface ArmorStand extends LivingEntity {
+ * @param move {@code true} if this armour stand can move, {@code false} otherwise
+ */
+ void setCanMove(boolean move);
++
++ /**
++ * Tests if this armor stand can tick.
++ *
++ * <p>The default value is defined in {@code paper.yml}.</p>
++ *
++ * @return {@code true} if this armour stand can tick, {@code false} otherwise
++ */
++ boolean canTick();
++
++ /**
++ * Sets if this armor stand can tick.
++ *
++ * @param tick {@code true} if this armour stand can tick, {@code false} otherwise
++ */
++ void setCanTick(final boolean tick);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0125-SkeletonHorse-Additions.patch b/Spigot-API-Patches-Unmapped/0125-SkeletonHorse-Additions.patch
new file mode 100644
index 0000000000..8d4dd478ab
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0125-SkeletonHorse-Additions.patch
@@ -0,0 +1,92 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 27 Jul 2018 22:36:17 -0500
+Subject: [PATCH] SkeletonHorse Additions
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SkeletonHorseTrapEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SkeletonHorseTrapEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9ce2948dfaa56d0adf53fe9b6117a90d7773b771
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SkeletonHorseTrapEvent.java
+@@ -0,0 +1,62 @@
++package com.destroystokyo.paper.event.entity;
++
++import com.google.common.collect.ImmutableList;
++import org.bukkit.entity.HumanEntity;
++import org.bukkit.entity.SkeletonHorse;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++import java.util.List;
++
++/**
++ * Event called when a player gets close to a skeleton horse and triggers the lightning trap
++ */
++public class SkeletonHorseTrapEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ private final List<HumanEntity> eligibleHumans;
++
++ public SkeletonHorseTrapEvent(@NotNull SkeletonHorse horse) {
++ this(horse, ImmutableList.of());
++ }
++
++ public SkeletonHorseTrapEvent(@NotNull SkeletonHorse horse, @NotNull List<HumanEntity> eligibleHumans) {
++ super(horse);
++ this.eligibleHumans = eligibleHumans;
++ }
++
++ @NotNull
++ @Override
++ public SkeletonHorse getEntity() {
++ return (SkeletonHorse) super.getEntity();
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ public List<HumanEntity> getEligibleHumans() {
++ return eligibleHumans;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
++
+diff --git a/src/main/java/org/bukkit/entity/SkeletonHorse.java b/src/main/java/org/bukkit/entity/SkeletonHorse.java
+index b2c6b6a8689f6bd6ce784bbe6f571f756dd700c1..ba9983463263f77db3d3487bc12f01db4508a32b 100644
+--- a/src/main/java/org/bukkit/entity/SkeletonHorse.java
++++ b/src/main/java/org/bukkit/entity/SkeletonHorse.java
+@@ -3,4 +3,12 @@ package org.bukkit.entity;
+ /**
+ * Represents a SkeletonHorse - variant of {@link AbstractHorse}.
+ */
+-public interface SkeletonHorse extends AbstractHorse { }
++public interface SkeletonHorse extends AbstractHorse {
++ // Paper start
++ int getTrapTime();
++
++ boolean isTrap();
++
++ void setTrap(boolean trap);
++ // Paper end
++}
diff --git a/Spigot-API-Patches-Unmapped/0126-Expand-Location-Manipulation-API.patch b/Spigot-API-Patches-Unmapped/0126-Expand-Location-Manipulation-API.patch
new file mode 100644
index 0000000000..271041f256
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0126-Expand-Location-Manipulation-API.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 25 Jul 2018 01:36:07 -0400
+Subject: [PATCH] Expand Location Manipulation API
+
+Adds set(x, y, z), add(base, x, y, z), subtract(base, x, y, z);
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index af2ee43f2c5133668c18710f526a107d94a5d898..369ce9ff6c8bb97a64a8e229115564412e6e7654 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -546,6 +546,54 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
+
+ // Paper start
++
++ /**
++ * Sets the position of this Location and returns itself
++ *
++ * This mutates this object, clone first.
++ * @param x X coordinate
++ * @param y Y coordinate
++ * @param z Z coordinate
++ * @return self (not cloned)
++ */
++ @NotNull
++ public Location set(double x, double y, double z) {
++ this.x = x;
++ this.y = y;
++ this.z = z;
++ return this;
++ }
++
++ /**
++ * Takes the x/y/z from base and adds the specified x/y/z to it and returns self
++ *
++ * This mutates this object, clone first.
++ * @param base The base coordinate to modify
++ * @param x X coordinate to add to base
++ * @param y Y coordinate to add to base
++ * @param z Z coordinate to add to base
++ * @return self (not cloned)
++ */
++ @NotNull
++ public Location add(@NotNull Location base, double x, double y, double z) {
++ return this.set(base.x + x, base.y + y, base.z + z);
++ }
++
++ /**
++ * Takes the x/y/z from base and subtracts the specified x/y/z to it and returns self
++ *
++ * This mutates this object, clone first.
++ * @param base The base coordinate to modify
++ * @param x X coordinate to subtract from base
++ * @param y Y coordinate to subtract from base
++ * @param z Z coordinate to subtract from base
++ * @return self (not cloned)
++ */
++ @NotNull
++ public Location subtract(@NotNull Location base, double x, double y, double z) {
++ return this.set(base.x - x, base.y - y, base.z - z);
++ }
++
+ /**
+ * @return A new location where X/Y/Z are on the Block location (integer value of X/Y/Z)
+ */
diff --git a/Spigot-API-Patches-Unmapped/0127-Expand-ArmorStand-API.patch b/Spigot-API-Patches-Unmapped/0127-Expand-ArmorStand-API.patch
new file mode 100644
index 0000000000..036951f69d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0127-Expand-ArmorStand-API.patch
@@ -0,0 +1,103 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: willies952002 <[email protected]>
+Date: Thu, 26 Jul 2018 02:22:44 -0400
+Subject: [PATCH] Expand ArmorStand API
+
+Add the following:
+- Add proper methods for getting and setting items in both hands. Deprecates old methods
+- Enable/Disable slot interactions
+
+diff --git a/src/main/java/org/bukkit/entity/ArmorStand.java b/src/main/java/org/bukkit/entity/ArmorStand.java
+index 365d3a3c5fc4a47efe56225ef1eb87b5046034f4..8ca6c9eba926f436203af211c6e274a59ddb15e8 100644
+--- a/src/main/java/org/bukkit/entity/ArmorStand.java
++++ b/src/main/java/org/bukkit/entity/ArmorStand.java
+@@ -13,7 +13,7 @@ public interface ArmorStand extends LivingEntity {
+ * Returns the item the armor stand is currently holding.
+ *
+ * @return the held item
+- * @deprecated prefer {@link EntityEquipment#getItemInHand()}
++ * @deprecated prefer {@link ArmorStand#getItem(EquipmentSlot)} // Paper
+ * @see #getEquipment()
+ */
+ @NotNull
+@@ -25,7 +25,7 @@ public interface ArmorStand extends LivingEntity {
+ *
+ * @param item the item to hold
+ * @deprecated prefer
+- * {@link EntityEquipment#setItemInHand(org.bukkit.inventory.ItemStack)}
++ * {@link ArmorStand#setItem(EquipmentSlot, ItemStack)} // Paper
+ * @see #getEquipment()
+ */
+ @Deprecated
+@@ -376,5 +376,71 @@ public interface ArmorStand extends LivingEntity {
+ * @param tick {@code true} if this armour stand can tick, {@code false} otherwise
+ */
+ void setCanTick(final boolean tick);
++
++ /**
++ * Returns the item the armor stand has
++ * equip in the given equipment slot
++ *
++ * @param slot the equipment slot to get
++ * @return the ItemStack in the equipment slot
++ */
++ @NotNull
++ ItemStack getItem(@NotNull final org.bukkit.inventory.EquipmentSlot slot);
++
++ /**
++ * Sets the item the armor stand has
++ * equip in the given equipment slot
++ *
++ * @param slot the equipment slot to set
++ * @param item the item to hold
++ */
++ void setItem(@NotNull final org.bukkit.inventory.EquipmentSlot slot, @Nullable final ItemStack item);
++
++ /**
++ * Get the list of disabled slots
++ *
++ * @return list of disabled slots
++ */
++ @NotNull
++ java.util.Set<org.bukkit.inventory.EquipmentSlot> getDisabledSlots();
++
++ /**
++ * Set the disabled slots
++ *
++ * This makes it so a player is unable to interact with the Armor Stand to place, remove, or replace an item in the given slot(s)
++ * Note: Once a slot is disabled, the only way to get an item back it to break the armor stand.
++ *
++ * @param slots var-arg array of slots to lock
++ */
++ void setDisabledSlots(@NotNull org.bukkit.inventory.EquipmentSlot... slots);
++
++ /**
++ * Disable specific slots, adding them
++ * to the currently disabled slots
++ *
++ * This makes it so a player is unable to interact with the Armor Stand to place, remove, or replace an item in the given slot(s)
++ * Note: Once a slot is disabled, the only way to get an item back it to break the armor stand.
++ *
++ * @param slots var-arg array of slots to lock
++ */
++ void addDisabledSlots(@NotNull final org.bukkit.inventory.EquipmentSlot... slots);
++
++ /**
++ * Remove the given slots from the disabled
++ * slots list, enabling them.
++ *
++ * This makes it so a player is able to interact with the Armor Stand to place, remove, or replace an item in the given slot(s)
++ *
++ * @param slots var-arg array of slots to unlock
++ */
++ void removeDisabledSlots(@NotNull final org.bukkit.inventory.EquipmentSlot... slots);
++
++ /**
++ * Check if a specific slot is disabled
++ *
++ * @param slot The slot to check
++ * @return {@code true} if the slot is disabled, else {@code false}.
++ */
++ boolean isSlotDisabled(@NotNull org.bukkit.inventory.EquipmentSlot slot);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0128-AnvilDamageEvent.patch b/Spigot-API-Patches-Unmapped/0128-AnvilDamageEvent.patch
new file mode 100644
index 0000000000..7561b6a9db
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0128-AnvilDamageEvent.patch
@@ -0,0 +1,160 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 20 Jul 2018 23:36:55 -0500
+Subject: [PATCH] AnvilDamageEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/block/AnvilDamagedEvent.java b/src/main/java/com/destroystokyo/paper/event/block/AnvilDamagedEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a83c286c1c11af25fc4d16af7a42b95ce90b9dee
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/block/AnvilDamagedEvent.java
+@@ -0,0 +1,148 @@
++package com.destroystokyo.paper.event.block;
++
++import org.bukkit.Material;
++import org.bukkit.block.data.BlockData;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.inventory.InventoryEvent;
++import org.bukkit.inventory.AnvilInventory;
++import org.bukkit.inventory.InventoryView;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when an anvil is damaged from being used
++ */
++public class AnvilDamagedEvent extends InventoryEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancel;
++ private DamageState damageState;
++
++ public AnvilDamagedEvent(@NotNull InventoryView inventory, @NotNull BlockData blockData) {
++ super(inventory);
++ this.damageState = DamageState.getState(blockData);
++ }
++
++ @NotNull
++ @Override
++ public AnvilInventory getInventory() {
++ return (AnvilInventory) super.getInventory();
++ }
++
++ /**
++ * Gets the new state of damage on the anvil
++ *
++ * @return Damage state
++ */
++ @NotNull
++ public DamageState getDamageState() {
++ return damageState;
++ }
++
++ /**
++ * Sets the new state of damage on the anvil
++ *
++ * @param damageState Damage state
++ */
++ public void setDamageState(@NotNull DamageState damageState) {
++ this.damageState = damageState;
++ }
++
++ /**
++ * Gets if anvil is breaking on this use
++ *
++ * @return True if breaking
++ */
++ public boolean isBreaking() {
++ return damageState == DamageState.BROKEN;
++ }
++
++ /**
++ * Sets if anvil is breaking on this use
++ *
++ * @param breaking True if breaking
++ */
++ public void setBreaking(boolean breaking) {
++ if (breaking) {
++ damageState = DamageState.BROKEN;
++ } else if (damageState == DamageState.BROKEN) {
++ damageState = DamageState.DAMAGED;
++ }
++ }
++
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ /**
++ * Represents the amount of damage on an anvil block
++ */
++ public enum DamageState {
++ FULL(Material.ANVIL),
++ CHIPPED(Material.CHIPPED_ANVIL),
++ DAMAGED(Material.DAMAGED_ANVIL),
++ BROKEN(Material.AIR);
++
++ private Material material;
++
++ DamageState(@NotNull Material material) {
++ this.material = material;
++ }
++
++ /**
++ * Get block material of this state
++ *
++ * @return Material
++ */
++ @NotNull
++ public Material getMaterial() {
++ return material;
++ }
++
++ /**
++ * Get damaged state by block data
++ *
++ * @param blockData Block data
++ * @return DamageState
++ * @throws IllegalArgumentException If non anvil block data is given
++ */
++ @NotNull
++ public static DamageState getState(@Nullable BlockData blockData) {
++ return blockData == null ? BROKEN : getState(blockData.getMaterial());
++ }
++
++ /**
++ * Get damaged state by block material
++ *
++ * @param material Block material
++ * @return DamageState
++ * @throws IllegalArgumentException If non anvil material is given
++ */
++ @NotNull
++ public static DamageState getState(@Nullable Material material) {
++ if (material == null) {
++ return BROKEN;
++ }
++ for (DamageState state : values()) {
++ if (state.material == material) {
++ return state;
++ }
++ }
++ throw new IllegalArgumentException("Material not an anvil");
++ }
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0129-Remove-deadlock-risk-in-firing-async-events.patch b/Spigot-API-Patches-Unmapped/0129-Remove-deadlock-risk-in-firing-async-events.patch
new file mode 100644
index 0000000000..2725c79f2e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0129-Remove-deadlock-risk-in-firing-async-events.patch
@@ -0,0 +1,137 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 9 Sep 2018 00:32:05 -0400
+Subject: [PATCH] Remove deadlock risk in firing async events
+
+The PluginManager incorrectly used synchronization on firing any event
+that was marked as synchronous.
+
+This synchronized did not even protect any concurrency risk as
+handlers were already thread safe in terms of mutations during event
+dispatch.
+
+The way it was used, has commonly led to deadlocks on the server,
+which results in a hard crash.
+
+This change removes the synchronize and adds some protection around enable/disable
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 9b8823279524d1c1566176c589aa5794eb8aafbc..707638c327077a74c777a603b9f2392f46b51c0c 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowEntity> { // Paper
+
+- /**
++ /*
+ * Gets the entity's current position
+ *
+ * @return a new copy of Location containing the position of this entity
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index a7393d2830b95d7167121b02066a3f357cee6085..a1a805004941d67abb0b9aa1721e0370c45b5289 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -462,7 +462,7 @@ public final class SimplePluginManager implements PluginManager {
+ * @return true if the plugin is enabled, otherwise false
+ */
+ @Override
+- public boolean isPluginEnabled(@Nullable Plugin plugin) {
++ public synchronized boolean isPluginEnabled(@Nullable Plugin plugin) { // Paper - synchronize
+ if ((plugin != null) && (plugins.contains(plugin))) {
+ return plugin.isEnabled();
+ } else {
+@@ -471,7 +471,7 @@ public final class SimplePluginManager implements PluginManager {
+ }
+
+ @Override
+- public void enablePlugin(@NotNull final Plugin plugin) {
++ public synchronized void enablePlugin(@NotNull final Plugin plugin) { // Paper - synchronize
+ if (!plugin.isEnabled()) {
+ List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
+
+@@ -509,7 +509,7 @@ public final class SimplePluginManager implements PluginManager {
+ }
+
+ @Override
+- public void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) {
++ public synchronized void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) { // Paper - synchronize
+ // Paper end - close Classloader on disable
+ if (plugin.isEnabled()) {
+ try {
+@@ -579,6 +579,7 @@ public final class SimplePluginManager implements PluginManager {
+ defaultPerms.get(false).clear();
+ }
+ }
++ private void fireEvent(Event event) { callEvent(event); } // Paper - support old method incase plugin uses reflection
+
+ /**
+ * Calls an event with the given details.
+@@ -587,23 +588,13 @@ public final class SimplePluginManager implements PluginManager {
+ */
+ @Override
+ public void callEvent(@NotNull Event event) {
+- if (event.isAsynchronous()) {
+- if (Thread.holdsLock(this)) {
+- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
+- }
+- if (server.isPrimaryThread()) {
+- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
+- }
+- } else {
+- if (!server.isPrimaryThread()) {
+- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from another thread.");
+- }
++ // Paper - replace callEvent by merging to below method
++ if (event.isAsynchronous() && server.isPrimaryThread()) {
++ throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
++ } else if (!event.isAsynchronous() && !server.isPrimaryThread()) {
++ throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
+ }
+
+- fireEvent(event);
+- }
+-
+- private void fireEvent(@NotNull Event event) {
+ HandlerList handlers = event.getHandlers();
+ RegisteredListener[] listeners = handlers.getRegisteredListeners();
+
+diff --git a/src/test/java/org/bukkit/plugin/PluginManagerTest.java b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
+index f188cd4f3b07027c30d41f1162db77a506b7b6bb..1941c9f49e9514c1236c5f4ea9f7af47f7be85c5 100644
+--- a/src/test/java/org/bukkit/plugin/PluginManagerTest.java
++++ b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
+@@ -17,7 +17,7 @@ public class PluginManagerTest {
+ private static final PluginManager pm = TestServer.getInstance().getPluginManager();
+
+ private final MutableObject store = new MutableObject();
+-
++/* // Paper start - remove unneeded test
+ @Test
+ public void testAsyncSameThread() {
+ final Event event = new TestEvent(true);
+@@ -28,14 +28,14 @@ public class PluginManagerTest {
+ return;
+ }
+ throw new IllegalStateException("No exception thrown");
+- }
++ }*/ // Paper end
+
+ @Test
+ public void testSyncSameThread() {
+ final Event event = new TestEvent(false);
+ pm.callEvent(event);
+ }
+-
++/* // Paper start - remove unneeded test
+ @Test
+ public void testAsyncLocked() throws InterruptedException {
+ final Event event = new TestEvent(true);
+@@ -129,7 +129,7 @@ public class PluginManagerTest {
+ if (store.value == null) {
+ throw new IllegalStateException("No exception thrown");
+ }
+- }
++ } */ // Paper
+
+ @Test
+ public void testRemovePermissionByNameLower() {
diff --git a/Spigot-API-Patches-Unmapped/0130-Add-hand-to-bucket-events.patch b/Spigot-API-Patches-Unmapped/0130-Add-hand-to-bucket-events.patch
new file mode 100644
index 0000000000..85c5c370f3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0130-Add-hand-to-bucket-events.patch
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Thu, 2 Aug 2018 08:44:20 -0500
+Subject: [PATCH] Add hand to bucket events
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java b/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java
+index 7f225baa9fd3ff6f4f950ae70f9500141c674f66..25bd8153ef2ab7ab1052cf756bb599f1095732e7 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerBucketEmptyEvent.java
+@@ -5,6 +5,7 @@ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockFace;
+ import org.bukkit.entity.Player;
+ import org.bukkit.event.HandlerList;
++import org.bukkit.inventory.EquipmentSlot;
+ import org.bukkit.inventory.ItemStack;
+ import org.jetbrains.annotations.NotNull;
+
+@@ -22,6 +23,16 @@ public class PlayerBucketEmptyEvent extends PlayerBucketEvent {
+ public PlayerBucketEmptyEvent(@NotNull final Player who, @NotNull final Block block, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand) {
+ super(who, block, blockClicked, blockFace, bucket, itemInHand);
+ }
++ // Paper start - add EquipmentSlot
++ @Deprecated
++ public PlayerBucketEmptyEvent(@NotNull final Player who, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @org.jetbrains.annotations.Nullable final EquipmentSlot hand) {
++ super(who, blockClicked, blockFace, bucket, itemInHand, hand);
++ }
++
++ public PlayerBucketEmptyEvent(@NotNull final Player who, @NotNull final Block block, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @org.jetbrains.annotations.Nullable final EquipmentSlot hand) {
++ super(who, block, blockClicked, blockFace, bucket, itemInHand, hand);
++ }
++ // Paper end
+
+ @NotNull
+ @Override
+diff --git a/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java b/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java
+index 0e4fa04ea73baaf2f9ad86725d379b569d7d6381..1e0f7ee7d198c08ce421ce105be42c4d01dc924f 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerBucketEvent.java
+@@ -5,6 +5,7 @@ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockFace;
+ import org.bukkit.entity.Player;
+ import org.bukkit.event.Cancellable;
++import org.bukkit.inventory.EquipmentSlot;
+ import org.bukkit.inventory.ItemStack;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+@@ -19,6 +20,7 @@ public abstract class PlayerBucketEvent extends PlayerEvent implements Cancellab
+ private final Block blockClicked;
+ private final BlockFace blockFace;
+ private final Material bucket;
++ private final EquipmentSlot hand; // Paper - add EquipmentSlot
+
+ @Deprecated
+ public PlayerBucketEvent(@NotNull final Player who, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand) {
+@@ -26,12 +28,24 @@ public abstract class PlayerBucketEvent extends PlayerEvent implements Cancellab
+ }
+
+ public PlayerBucketEvent(@NotNull final Player who, @NotNull final Block block, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand) {
++ // Paper start - add EquipmentSlot
++ this(who, block, blockClicked, blockFace, bucket, itemInHand, null);
++ }
++
++ @Deprecated
++ public PlayerBucketEvent(@NotNull final Player who,@NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @Nullable final EquipmentSlot hand) {
++ this(who, null, blockClicked, blockFace, bucket, itemInHand, hand);
++ }
++
++ public PlayerBucketEvent(@NotNull final Player who, @NotNull final Block block, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @Nullable final EquipmentSlot hand) {
++ // Paper end
+ super(who);
+ this.block = block;
+ this.blockClicked = blockClicked;
+ this.blockFace = blockFace;
+ this.itemStack = itemInHand;
+ this.bucket = bucket;
++ this.hand = hand == null ? player.getInventory().getItemInMainHand().equals(itemInHand) ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND : hand; // Paper - add EquipmentSlot
+ }
+
+ /**
+@@ -93,6 +107,18 @@ public abstract class PlayerBucketEvent extends PlayerEvent implements Cancellab
+ return blockFace;
+ }
+
++ // Paper start
++ /**
++ * The hand used to perform this action.
++ *
++ * @return the hand used
++ */
++ @NotNull
++ public EquipmentSlot getHand() {
++ return hand;
++ }
++ // Paper end
++
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+diff --git a/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java b/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java
+index 77c3a6e5c89ffde564d63b98b2d9e36c356d79fd..56f1cc2d773d2c58207ee291bac596692980a731 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerBucketFillEvent.java
+@@ -5,6 +5,7 @@ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockFace;
+ import org.bukkit.entity.Player;
+ import org.bukkit.event.HandlerList;
++import org.bukkit.inventory.EquipmentSlot;
+ import org.bukkit.inventory.ItemStack;
+ import org.jetbrains.annotations.NotNull;
+
+@@ -23,6 +24,18 @@ public class PlayerBucketFillEvent extends PlayerBucketEvent {
+ super(who, block, blockClicked, blockFace, bucket, itemInHand);
+ }
+
++ // Paper start - add EquipmentSlot
++ @Deprecated
++ public PlayerBucketFillEvent(@NotNull final Player who, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @org.jetbrains.annotations.Nullable final EquipmentSlot hand) {
++ super(who, blockClicked, blockFace, bucket, itemInHand, hand);
++ }
++
++ // Paper start - add EquipmentSlot
++ public PlayerBucketFillEvent(@NotNull final Player who, @NotNull Block block, @NotNull final Block blockClicked, @NotNull final BlockFace blockFace, @NotNull final Material bucket, @NotNull final ItemStack itemInHand, @org.jetbrains.annotations.Nullable final EquipmentSlot hand) {
++ super(who, block, blockClicked, blockFace, bucket, itemInHand, hand);
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
diff --git a/Spigot-API-Patches-Unmapped/0131-Add-TNTPrimeEvent.patch b/Spigot-API-Patches-Unmapped/0131-Add-TNTPrimeEvent.patch
new file mode 100644
index 0000000000..2bddc56f13
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0131-Add-TNTPrimeEvent.patch
@@ -0,0 +1,126 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Sun, 15 Jul 2018 22:17:55 +0300
+Subject: [PATCH] Add TNTPrimeEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/block/TNTPrimeEvent.java b/src/main/java/com/destroystokyo/paper/event/block/TNTPrimeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..73dabb82c7fbea3f0cccade0a2944b11a80ede06
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/block/TNTPrimeEvent.java
+@@ -0,0 +1,114 @@
++package com.destroystokyo.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when TNT block is about to turn into {@link org.bukkit.entity.TNTPrimed}
++ * <p>
++ * Cancelling it won't turn TNT into {@link org.bukkit.entity.TNTPrimed} and leaves
++ * the TNT block as-is
++ *
++ * @author Mark Vainomaa
++ */
++public class TNTPrimeEvent extends BlockEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ @NotNull private PrimeReason reason;
++ @Nullable private Entity primerEntity;
++
++ public TNTPrimeEvent(@NotNull Block theBlock, @NotNull PrimeReason reason, @Nullable Entity primerEntity) {
++ super(theBlock);
++ this.reason = reason;
++ this.primerEntity = primerEntity;
++ }
++
++ /**
++ * Gets the TNT prime reason
++ *
++ * @return Prime reason
++ */
++ @NotNull
++ public PrimeReason getReason() {
++ return this.reason;
++ }
++
++ /**
++ * Gets the TNT primer {@link Entity}.
++ *
++ * It's null if {@link #getReason()} is {@link PrimeReason#REDSTONE} or {@link PrimeReason#FIRE}.
++ * It's not null if {@link #getReason()} is {@link PrimeReason#ITEM} or {@link PrimeReason#PROJECTILE}
++ * It might be null if {@link #getReason()} is {@link PrimeReason#EXPLOSION}
++ *
++ * @return The {@link Entity} who primed the TNT
++ */
++ @Nullable
++ public Entity getPrimerEntity() {
++ return this.primerEntity;
++ }
++
++ /**
++ * Gets whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
++ *
++ * @return Whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
++ */
++ @Override
++ public boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ /**
++ * Sets whether to cancel spawning {@link org.bukkit.entity.TNTPrimed} or not
++ *
++ * @param cancel whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @Nullable
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @Nullable
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ public enum PrimeReason {
++ /**
++ * When TNT prime was caused by other explosion (chain reaction)
++ */
++ EXPLOSION,
++
++ /**
++ * When TNT prime was caused by fire
++ */
++ FIRE,
++
++ /**
++ * When {@link org.bukkit.entity.Player} used {@link org.bukkit.Material#FLINT_AND_STEEL} or
++ * {@link org.bukkit.Material#FIRE_CHARGE} on given TNT block
++ */
++ ITEM,
++
++ /**
++ * When TNT prime was caused by an {@link Entity} shooting TNT
++ * using a bow with {@link org.bukkit.enchantments.Enchantment#ARROW_FIRE} enchantment
++ */
++ PROJECTILE,
++
++ /**
++ * When redstone power triggered the TNT prime
++ */
++ REDSTONE
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0132-Provide-Chunk-Coordinates-as-a-Long-API.patch b/Spigot-API-Patches-Unmapped/0132-Provide-Chunk-Coordinates-as-a-Long-API.patch
new file mode 100644
index 0000000000..3616f82f03
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0132-Provide-Chunk-Coordinates-as-a-Long-API.patch
@@ -0,0 +1,72 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 4 Aug 2018 19:37:35 -0400
+Subject: [PATCH] Provide Chunk Coordinates as a Long API
+
+Allows you to easily access the chunks X/z as a long, and a method
+to look up by the long key too.
+
+diff --git a/src/main/java/org/bukkit/Chunk.java b/src/main/java/org/bukkit/Chunk.java
+index beac1439c71fb28f1a3baecf56157237e12ccfd5..fa576096e908f8fbdbef53e1bd91215ac9e73ed6 100644
+--- a/src/main/java/org/bukkit/Chunk.java
++++ b/src/main/java/org/bukkit/Chunk.java
+@@ -28,6 +28,32 @@ public interface Chunk extends PersistentDataHolder {
+ */
+ int getZ();
+
++ // Paper start
++ /**
++ * @return The Chunks X and Z coordinates packed into a long
++ */
++ default long getChunkKey() {
++ return getChunkKey(getX(), getZ());
++ }
++
++ /**
++ * @param loc Location to get chunk key
++ * @return Location's chunk coordinates packed into a long
++ */
++ static long getChunkKey(@NotNull Location loc) {
++ return getChunkKey((int) Math.floor(loc.getX()) >> 4, (int) Math.floor(loc.getZ()) >> 4);
++ }
++
++ /**
++ * @param x X Coordinate
++ * @param z Z Coordinate
++ * @return Chunk coordinates packed into a long
++ */
++ static long getChunkKey(int x, int z) {
++ return (long) x & 0xffffffffL | ((long) z & 0xffffffffL) << 32;
++ }
++ // Paper end
++
+ /**
+ * Gets the world containing this chunk
+ *
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 9c4f2d9e1a4011e3a9860d913e1a718030696bed..e372b3d43960ac7df58985609ef729c68fca0533 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -207,6 +207,22 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public Chunk getChunkAt(@NotNull Block block);
+
++ // Paper start
++ /**
++ * Gets the chunk at the specified chunk key, which is the X and Z packed into a long.
++ *
++ * See {@link Chunk#getChunkKey()} for easy access to the key, or you may calculate it as:
++ * long chunkKey = (long) chunkX &amp; 0xffffffffL | ((long) chunkZ &amp; 0xffffffffL) &gt;&gt; 32;
++ *
++ * @param chunkKey The Chunk Key to look up the chunk by
++ * @return The chunk at the specified key
++ */
++ @NotNull
++ public default Chunk getChunkAt(long chunkKey) {
++ return getChunkAt((int) chunkKey, (int) (chunkKey >> 32));
++ }
++ // Paper end
++
+ /**
+ * Checks if the specified {@link Chunk} is loaded
+ *
diff --git a/Spigot-API-Patches-Unmapped/0133-Async-Chunks-API.patch b/Spigot-API-Patches-Unmapped/0133-Async-Chunks-API.patch
new file mode 100644
index 0000000000..992af6f95b
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0133-Async-Chunks-API.patch
@@ -0,0 +1,534 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 29 Feb 2016 17:43:33 -0600
+Subject: [PATCH] Async Chunks API
+
+Adds API's to load or generate chunks asynchronously.
+
+Also adds utility methods to Entity to teleport asynchronously.
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index e372b3d43960ac7df58985609ef729c68fca0533..3f231c28842f02f80fd3136c36fe99b41726137f 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -221,6 +221,482 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ public default Chunk getChunkAt(long chunkKey) {
+ return getChunkAt((int) chunkKey, (int) (chunkKey >> 32));
+ }
++
++ /**
++ * This is the Legacy API before Java 8 was supported. Java 8 Consumer is provided,
++ * as well as future support
++ *
++ * Used by {@link World#getChunkAtAsync(Location,ChunkLoadCallback)} methods
++ * to request a {@link Chunk} to be loaded, with this callback receiving
++ * the chunk when it is finished.
++ *
++ * This callback will be executed on synchronously on the main thread.
++ *
++ * Timing and order this callback is fired is intentionally not defined and
++ * and subject to change.
++ *
++ * @deprecated Use either the Future or the Consumer based methods
++ */
++ @Deprecated
++ public static interface ChunkLoadCallback extends java.util.function.Consumer<Chunk> {
++ public void onLoad(@NotNull Chunk chunk);
++
++ // backwards compat to old api
++ @Override
++ default void accept(@NotNull Chunk chunk) {
++ onLoad(chunk);
++ }
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link ChunkLoadCallback} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @deprecated Use either the Future or the Consumer based methods
++ * @param x Chunk X-coordinate of the chunk - (world coordinate / 16)
++ * @param z Chunk Z-coordinate of the chunk - (world coordinate / 16)
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ @Deprecated
++ public default void getChunkAtAsync(int x, int z, @NotNull ChunkLoadCallback cb) {
++ getChunkAtAsync(x, z, true).thenAccept(cb::onLoad).exceptionally((ex) -> {
++ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
++ return null;
++ });
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given {@link Location}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link ChunkLoadCallback} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @deprecated Use either the Future or the Consumer based methods
++ * @param loc Location of the chunk
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ @Deprecated
++ public default void getChunkAtAsync(@NotNull Location loc, @NotNull ChunkLoadCallback cb) {
++ getChunkAtAsync(loc, true).thenAccept(cb::onLoad).exceptionally((ex) -> {
++ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
++ return null;
++ });
++ }
++
++ /**
++ * Requests {@link Chunk} to be loaded that contains the given {@link Block}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link ChunkLoadCallback} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @deprecated Use either the Future or the Consumer based methods
++ * @param block Block to get the containing chunk from
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ @Deprecated
++ public default void getChunkAtAsync(@NotNull Block block, @NotNull ChunkLoadCallback cb) {
++ getChunkAtAsync(block, true).thenAccept(cb::onLoad).exceptionally((ex) -> {
++ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
++ return null;
++ });
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param x Chunk X-coordinate of the chunk - (world coordinate / 16)
++ * @param z Chunk Z-coordinate of the chunk - (world coordinate / 16)
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(int x, int z, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync(x, z, true).thenAccept(cb).exceptionally((ex) -> {
++ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
++ return null;
++ });
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param x Chunk X-coordinate of the chunk - (world coordinate / 16)
++ * @param z Chunk Z-coordinate of the chunk - (world coordinate / 16)
++ * @param gen Should we generate a chunk if it doesn't exists or not
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(int x, int z, boolean gen, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync(x, z, gen).thenAccept(cb).exceptionally((ex) -> {
++ Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex);
++ return null;
++ });
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given {@link Location}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param loc Location of the chunk
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(@NotNull Location loc, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, true, cb);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given {@link Location}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param loc Location of the chunk
++ * @param gen Should the chunk generate
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(@NotNull Location loc, boolean gen, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, gen, cb);
++ }
++
++ /**
++ * Requests {@link Chunk} to be loaded that contains the given {@link Block}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param block Block to get the containing chunk from
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(@NotNull Block block, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, true, cb);
++ }
++
++ /**
++ * Requests {@link Chunk} to be loaded that contains the given {@link Block}
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The {@link java.util.function.Consumer} will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param block Block to get the containing chunk from
++ * @param gen Should the chunk generate
++ * @param cb Callback to receive the chunk when it is loaded.
++ * will be executed synchronously
++ */
++ public default void getChunkAtAsync(@NotNull Block block, boolean gen, @NotNull java.util.function.Consumer<Chunk> cb) {
++ getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen, cb);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param loc Location to load the corresponding chunk from
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(@NotNull Location loc) {
++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param loc Location to load the corresponding chunk from
++ * @param gen Should the chunk generate
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(@NotNull Location loc, boolean gen) {
++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, gen);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param block Block to load the corresponding chunk from
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(@NotNull Block block) {
++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param block Block to load the corresponding chunk from
++ * @param gen Should the chunk generate
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(@NotNull Block block, boolean gen) {
++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param x X Coord
++ * @param z Z Coord
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z) {
++ return getChunkAtAsync(x, z, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param x Chunk X-coordinate of the chunk - (world coordinate / 16)
++ * @param z Chunk Z-coordinate of the chunk - (world coordinate / 16)
++ * @param gen Should we generate a chunk if it doesn't exists or not
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen) {
++ return getChunkAtAsync(x, z, gen, false);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param loc Location to load the corresponding chunk from
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsyncUrgently(@NotNull Location loc) {
++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, true, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param loc Location to load the corresponding chunk from
++ * @param gen Should the chunk generate
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsyncUrgently(@NotNull Location loc, boolean gen) {
++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, gen, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param block Block to load the corresponding chunk from
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsyncUrgently(@NotNull Block block) {
++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, true, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ * @param block Block to load the corresponding chunk from
++ * @param gen Should the chunk generate
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsyncUrgently(@NotNull Block block, boolean gen) {
++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen, true);
++ }
++
++ /**
++ * Requests a {@link Chunk} to be loaded at the given coordinates
++ *
++ * This method makes no guarantee on how fast the chunk will load,
++ * and will return the chunk to the callback at a later time.
++ *
++ * You should use this method if you need a chunk but do not need it
++ * immediately, and you wish to let the server control the speed
++ * of chunk loads, keeping performance in mind.
++ *
++ * The future will always be executed synchronously
++ * on the main Server Thread.
++ *
++ * @param x X Coord
++ * @param z Z Coord
++ * @return Future that will resolve when the chunk is loaded
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsyncUrgently(int x, int z) {
++ return getChunkAtAsync(x, z, true, true);
++ }
++
++ @NotNull
++ java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent);
+ // Paper end
+
+ /**
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 707638c327077a74c777a603b9f2392f46b51c0c..c137199ed0537874010f1abf311a9cbee56831ac 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -163,6 +163,33 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ */
+ public boolean teleport(@NotNull Entity destination, @NotNull TeleportCause cause);
+
++ // Paper start
++ /**
++ * Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready.
++ * @param loc Location to teleport to
++ * @return A future that will be completed with the result of the teleport
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Boolean> teleportAsync(@NotNull Location loc) {
++ return teleportAsync(loc, TeleportCause.PLUGIN);
++ }
++ /**
++ * Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready.
++ * @param loc Location to teleport to
++ * @param cause Reason for teleport
++ * @return A future that will be completed with the result of the teleport
++ */
++ @NotNull
++ public default java.util.concurrent.CompletableFuture<Boolean> teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause) {
++ java.util.concurrent.CompletableFuture<Boolean> future = new java.util.concurrent.CompletableFuture<>();
++ loc.getWorld().getChunkAtAsyncUrgently(loc).thenAccept((chunk) -> future.complete(teleport(loc, cause))).exceptionally(ex -> {
++ future.completeExceptionally(ex);
++ return null;
++ });
++ return future;
++ }
++ // Paper end
++
+ /**
+ * Returns a list of entities within a bounding box centered around this
+ * entity
diff --git a/Spigot-API-Patches-Unmapped/0134-Make-EnderDragon-extend-Mob.patch b/Spigot-API-Patches-Unmapped/0134-Make-EnderDragon-extend-Mob.patch
new file mode 100644
index 0000000000..6779d66171
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0134-Make-EnderDragon-extend-Mob.patch
@@ -0,0 +1,19 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 10 Aug 2018 22:08:34 -0400
+Subject: [PATCH] Make EnderDragon extend Mob
+
+
+diff --git a/src/main/java/org/bukkit/entity/EnderDragon.java b/src/main/java/org/bukkit/entity/EnderDragon.java
+index c14278d2c99deedddfd9c8afdc900760b0331546..92da609fb2bdf7c6396ba868410a40725fda05f0 100644
+--- a/src/main/java/org/bukkit/entity/EnderDragon.java
++++ b/src/main/java/org/bukkit/entity/EnderDragon.java
+@@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents an Ender Dragon
+ */
+-public interface EnderDragon extends ComplexLivingEntity, Boss {
++public interface EnderDragon extends ComplexLivingEntity, Boss, org.bukkit.entity.Mob { // Paper - add Mob
+
+ /**
+ * Represents a phase or action that an Ender Dragon can perform.
diff --git a/Spigot-API-Patches-Unmapped/0135-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch b/Spigot-API-Patches-Unmapped/0135-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
new file mode 100644
index 0000000000..cbe8cd6f69
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0135-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 15 Aug 2018 01:04:58 -0400
+Subject: [PATCH] Ability to get Tile Entities from a chunk without snapshots
+
+
+diff --git a/src/main/java/org/bukkit/Chunk.java b/src/main/java/org/bukkit/Chunk.java
+index fa576096e908f8fbdbef53e1bd91215ac9e73ed6..98263d896f316983609432c45b85401a2692432d 100644
+--- a/src/main/java/org/bukkit/Chunk.java
++++ b/src/main/java/org/bukkit/Chunk.java
+@@ -1,6 +1,8 @@
+ package org.bukkit;
+
+ import java.util.Collection;
++import java.util.function.Predicate;
++
+ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockState;
+ import org.bukkit.block.data.BlockData;
+@@ -103,13 +105,36 @@ public interface Chunk extends PersistentDataHolder {
+ @NotNull
+ Entity[] getEntities();
+
++ // Paper start
+ /**
+ * Get a list of all tile entities in the chunk.
+ *
+ * @return The tile entities.
+ */
+ @NotNull
+- BlockState[] getTileEntities();
++ default BlockState[] getTileEntities() {
++ return getTileEntities(true);
++ }
++
++ /**
++ * Get a list of all tile entities in the chunk.
++ *
++ * @param useSnapshot Take snapshots or direct references
++ * @return The tile entities.
++ */
++ @NotNull
++ BlockState[] getTileEntities(boolean useSnapshot);
++
++ /**
++ * Get a list of all tile entities that match a given predicate in the chunk.
++ *
++ * @param blockPredicate The predicate of blocks to return tile entities for
++ * @param useSnapshot Take snapshots or direct references
++ * @return The tile entities.
++ */
++ @NotNull
++ Collection<BlockState> getTileEntities(@NotNull Predicate<Block> blockPredicate, boolean useSnapshot);
++ // Paper end
+
+ /**
+ * Checks if the chunk is loaded.
diff --git a/Spigot-API-Patches-Unmapped/0136-Don-t-use-snapshots-for-Timings-Tile-Entity-reports.patch b/Spigot-API-Patches-Unmapped/0136-Don-t-use-snapshots-for-Timings-Tile-Entity-reports.patch
new file mode 100644
index 0000000000..536fdc2d06
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0136-Don-t-use-snapshots-for-Timings-Tile-Entity-reports.patch
@@ -0,0 +1,19 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 15 Aug 2018 01:19:37 -0400
+Subject: [PATCH] Don't use snapshots for Timings Tile Entity reports
+
+
+diff --git a/src/main/java/co/aikar/timings/TimingHistory.java b/src/main/java/co/aikar/timings/TimingHistory.java
+index ddaed81275fcc12d1671b668697acf318e96888b..203cda0f9a4dea4f28a21ea9ee8db7a7369842e3 100644
+--- a/src/main/java/co/aikar/timings/TimingHistory.java
++++ b/src/main/java/co/aikar/timings/TimingHistory.java
+@@ -119,7 +119,7 @@ public class TimingHistory {
+ data.entityCounts.get(entity.getType()).increment();
+ }
+
+- for (BlockState tileEntity : chunk.getTileEntities()) {
++ for (BlockState tileEntity : chunk.getTileEntities(false)) {
+ if (tileEntity == null) {
+ Bukkit.getLogger().warning("Null tileentity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ());
+ continue;
diff --git a/Spigot-API-Patches-Unmapped/0137-Allow-Blocks-to-be-accessed-via-a-long-key.patch b/Spigot-API-Patches-Unmapped/0137-Allow-Blocks-to-be-accessed-via-a-long-key.patch
new file mode 100644
index 0000000000..568d6bcd6e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0137-Allow-Blocks-to-be-accessed-via-a-long-key.patch
@@ -0,0 +1,169 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Tue, 14 Aug 2018 21:42:10 -0700
+Subject: [PATCH] Allow Blocks to be accessed via a long key
+
+The key can be retrieved via methods Location#toBlockKey() and
+Block#getBlockKey()
+
+World provides lookup for blocks by long key via method World#getBlockAtKey(long)
+
+The formatting for the key is as follows:
+
+10 bit y|27 bit z|27 bit x
+
+The y value is considered unsigned while z and x are considered two's complement
+
+Y range: [0, 1023]
+X, Z range: [-67 108 864, 67 108 863]
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 369ce9ff6c8bb97a64a8e229115564412e6e7654..e700875beb76dadd55b585aca748338def286908 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -15,7 +15,6 @@ import org.jetbrains.annotations.Nullable;
+
+ // Paper start
+ import java.util.Collection;
+-import java.util.Collections;
+ import java.util.function.Predicate;
+ import org.bukkit.entity.Entity;
+ import org.bukkit.entity.LivingEntity;
+@@ -605,6 +604,17 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ blockLoc.setZ(getBlockZ());
+ return blockLoc;
+ }
++
++ // Paper Start
++ /**
++ * @return The block key for this location's block location.
++ * @see Block#getBlockKey(int, int, int)
++ */
++ public long toBlockKey() {
++ return Block.getBlockKey(getBlockX(), getBlockY(), getBlockZ());
++ }
++ // Paper End
++
+ /**
+ * @return A new location where X/Y/Z are the center of the block
+ */
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 3f231c28842f02f80fd3136c36fe99b41726137f..d8674c773d61517f79d0fe77276392e47fd3e1e1 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -90,6 +90,38 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public Block getBlockAt(@NotNull Location location);
+
++ // Paper start
++ /**
++ * Gets the {@link Block} at the given block key
++ *
++ * @param key The block key. See {@link Block#getBlockKey()}
++ * @return Block at the key
++ * @see Block#getBlockKey(int, int, int)
++ */
++ @NotNull
++ public default Block getBlockAtKey(long key) {
++ int x = Block.getBlockKeyX(key);
++ int y = Block.getBlockKeyY(key);
++ int z = Block.getBlockKeyZ(key);
++ return getBlockAt(x, y, z);
++ }
++
++ /**
++ * Gets the {@link Location} at the given block key
++ *
++ * @param key The block key. See {@link Location#toBlockKey()}
++ * @return Location at the key
++ * @see Block#getBlockKey(int, int, int)
++ */
++ @NotNull
++ public default Location getLocationAtKey(long key) {
++ int x = Block.getBlockKeyX(key);
++ int y = Block.getBlockKeyY(key);
++ int z = Block.getBlockKeyZ(key);
++ return new Location(this, x, y, z);
++ }
++ // Paper end
++
+ /**
+ * Gets the highest non-empty (impassable) coordinate at the given
+ * coordinates.
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index e89c8079625525667f496c06207da655fe43d749..d6f74dbcfeb153d4b17be2827e2989f2d8160d21 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -153,6 +153,72 @@ public interface Block extends Metadatable {
+ */
+ int getZ();
+
++ // Paper Start
++ /**
++ * Returns this block's coordinates packed into a long value.
++ * Computed via: {@code Block.getBlockKey(this.getX(), this.getY(), this.getZ())}
++ * @see Block#getBlockKey(int, int, int)
++ * @return This block's x, y, and z coordinates packed into a long value
++ */
++ public default long getBlockKey() {
++ return Block.getBlockKey(this.getX(), this.getY(), this.getZ());
++ }
++
++ /**
++ * Returns the specified block coordinates packed into a long value
++ * <p>
++ * The return value can be computed as follows:
++ * <br>
++ * {@code long value = ((long)x & 0x7FFFFFF) | (((long)z & 0x7FFFFFF) << 27) | ((long)y << 54);}
++ * </p>
++ *
++ * <p>
++ * And may be unpacked as follows:
++ * <br>
++ * {@code int x = (int) ((packed << 37) >> 37);}
++ * <br>
++ * {@code int y = (int) (packed >>> 54);}
++ * <br>
++ * {@code int z = (int) ((packed << 10) >> 37);}
++ * </p>
++ *
++ * @return This block's x, y, and z coordinates packed into a long value
++ */
++ public static long getBlockKey(int x, int y, int z) {
++ return ((long)x & 0x7FFFFFF) | (((long)z & 0x7FFFFFF) << 27) | ((long)y << 54);
++ }
++
++ /**
++ * Returns the x component from the packed value.
++ * @param packed The packed value, as computed by {@link Block#getBlockKey(int, int, int)}
++ * @see Block#getBlockKey(int, int, int)
++ * @return The x component from the packed value.
++ */
++ public static int getBlockKeyX(long packed) {
++ return (int) ((packed << 37) >> 37);
++ }
++
++ /**
++ * Returns the y component from the packed value.
++ * @param packed The packed value, as computed by {@link Block#getBlockKey(int, int, int)}
++ * @see Block#getBlockKey(int, int, int)
++ * @return The y component from the packed value.
++ */
++ public static int getBlockKeyY(long packed) {
++ return (int) (packed >>> 54);
++ }
++
++ /**
++ * Returns the z component from the packed value.
++ * @param packed The packed value, as computed by {@link Block#getBlockKey(int, int, int)}
++ * @see Block#getBlockKey(int, int, int)
++ * @return The z component from the packed value.
++ */
++ public static int getBlockKeyZ(long packed) {
++ return (int) ((packed << 10) >> 37);
++ }
++ // Paper End
++
+ /**
+ * Gets the Location of the block
+ *
diff --git a/Spigot-API-Patches-Unmapped/0138-Slime-Pathfinder-Events.patch b/Spigot-API-Patches-Unmapped/0138-Slime-Pathfinder-Events.patch
new file mode 100644
index 0000000000..8d27bd1b14
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0138-Slime-Pathfinder-Events.patch
@@ -0,0 +1,217 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 24 Aug 2018 08:18:27 -0500
+Subject: [PATCH] Slime Pathfinder Events
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SlimeChangeDirectionEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SlimeChangeDirectionEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2638bbd3e1392b3d8640be58163f6eb2789dee4a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SlimeChangeDirectionEvent.java
+@@ -0,0 +1,38 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Slime;
++import org.bukkit.event.Cancellable;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Slime decides to change it's facing direction.
++ * <p>
++ * This event does not fire for the entity's actual movement. Only when it
++ * is choosing to change direction.
++ */
++public class SlimeChangeDirectionEvent extends SlimePathfindEvent implements Cancellable {
++ private float yaw;
++
++ public SlimeChangeDirectionEvent(@NotNull Slime slime, float yaw) {
++ super(slime);
++ this.yaw = yaw;
++ }
++
++ /**
++ * Get the new chosen yaw
++ *
++ * @return Chosen yaw
++ */
++ public float getNewYaw() {
++ return yaw;
++ }
++
++ /**
++ * Set the new chosen yaw
++ *
++ * @param yaw Chosen yaw
++ */
++ public void setNewYaw(float yaw) {
++ this.yaw = yaw;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SlimePathfindEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SlimePathfindEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..14b67da109321ae6521eab2ac6f6945f05d02db5
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SlimePathfindEvent.java
+@@ -0,0 +1,53 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Slime;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Slime decides to start pathfinding.
++ * <p>
++ * This event does not fire for the entity's actual movement. Only when it
++ * is choosing to start moving.
++ */
++public class SlimePathfindEvent extends EntityEvent implements Cancellable {
++ public SlimePathfindEvent(@NotNull Slime slime) {
++ super(slime);
++ }
++
++ /**
++ * The Slime that is pathfinding.
++ *
++ * @return The Slime that is pathfinding.
++ */
++ @NotNull
++ public Slime getEntity() {
++ return (Slime) entity;
++ }
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ private boolean cancelled = false;
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SlimeSwimEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SlimeSwimEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c8dd49d11da5a90a1bac965a75f2b65fd825f3f7
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SlimeSwimEvent.java
+@@ -0,0 +1,17 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Slime;
++import org.bukkit.event.Cancellable;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Slime decides to start jumping while swimming in water/lava.
++ * <p>
++ * This event does not fire for the entity's actual movement. Only when it
++ * is choosing to start jumping.
++ */
++public class SlimeSwimEvent extends SlimeWanderEvent implements Cancellable {
++ public SlimeSwimEvent(@NotNull Slime slime) {
++ super(slime);
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SlimeTargetLivingEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SlimeTargetLivingEntityEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e9ba32799ed838779e49cd4c5011b7515b3363cb
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SlimeTargetLivingEntityEvent.java
+@@ -0,0 +1,31 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Slime;
++import org.bukkit.event.Cancellable;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Slime decides to change direction to target a LivingEntity.
++ * <p>
++ * This event does not fire for the entity's actual movement. Only when it
++ * is choosing to start moving.
++ */
++public class SlimeTargetLivingEntityEvent extends SlimePathfindEvent implements Cancellable {
++ @NotNull private final LivingEntity target;
++
++ public SlimeTargetLivingEntityEvent(@NotNull Slime slime, @NotNull LivingEntity target) {
++ super(slime);
++ this.target = target;
++ }
++
++ /**
++ * Get the targeted entity
++ *
++ * @return Targeted entity
++ */
++ @NotNull
++ public LivingEntity getTarget() {
++ return target;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/SlimeWanderEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/SlimeWanderEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4683a7237d2ed527fc85b9b4e5b2eaaf5ae3d797
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/SlimeWanderEvent.java
+@@ -0,0 +1,17 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Slime;
++import org.bukkit.event.Cancellable;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Slime decides to start wandering.
++ * <p>
++ * This event does not fire for the entity's actual movement. Only when it
++ * is choosing to start moving.
++ */
++public class SlimeWanderEvent extends SlimePathfindEvent implements Cancellable {
++ public SlimeWanderEvent(@NotNull Slime slime) {
++ super(slime);
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Slime.java b/src/main/java/org/bukkit/entity/Slime.java
+index 1119e26e270bb45f517955b19d95a9ec3d113634..c4791f95d788d3a9e013dc89d8e64103ad8480a1 100644
+--- a/src/main/java/org/bukkit/entity/Slime.java
++++ b/src/main/java/org/bukkit/entity/Slime.java
+@@ -14,4 +14,20 @@ public interface Slime extends Mob {
+ * @param sz The new size of the slime.
+ */
+ public void setSize(int sz);
++
++ // Paper start
++ /**
++ * Get whether this slime can randomly wander/jump around on its own
++ *
++ * @return true if can wander
++ */
++ public boolean canWander();
++
++ /**
++ * Set whether this slime can randomly wander/jump around on its own
++ *
++ * @param canWander true if can wander
++ */
++ public void setWander(boolean canWander);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0139-isChunkGenerated-API.patch b/Spigot-API-Patches-Unmapped/0139-isChunkGenerated-API.patch
new file mode 100644
index 0000000000..a26301e9c3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0139-isChunkGenerated-API.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: cswhite2000 <[email protected]>
+Date: Tue, 21 Aug 2018 19:39:46 -0700
+Subject: [PATCH] isChunkGenerated API
+
+
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index e700875beb76dadd55b585aca748338def286908..9c91c49ed7302c12fcb1d8e9bc58712efc199954 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -3,6 +3,7 @@ package org.bukkit;
+ import com.google.common.base.Preconditions;
+ import java.lang.ref.Reference;
+ import java.lang.ref.WeakReference;
++import com.google.common.base.Preconditions; // Paper
+ import java.util.HashMap;
+ import java.util.Map;
+ import org.bukkit.block.Block;
+@@ -545,6 +546,16 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
+
+ // Paper start
++ /**
++ * Checks if a {@link Chunk} has been generated at this location.
++ *
++ * @return true if a chunk has been generated at this location
++ */
++ public boolean isGenerated() {
++ World world = this.getWorld();
++ Preconditions.checkNotNull(world, "Location has no world!");
++ return world.isChunkGenerated(locToBlock(x) >> 4, locToBlock(z) >> 4);
++ }
+
+ /**
+ * Sets the position of this Location and returns itself
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index d8674c773d61517f79d0fe77276392e47fd3e1e1..cf831f96efa302f789cf26a384a091df51215a76 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -254,6 +254,17 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ return getChunkAt((int) chunkKey, (int) (chunkKey >> 32));
+ }
+
++ /**
++ * Checks if a {@link Chunk} has been generated at the specified chunk key,
++ * which is the X and Z packed into a long.
++ *
++ * @param chunkKey The Chunk Key to look up the chunk by
++ * @return true if the chunk has been generated, otherwise false
++ */
++ public default boolean isChunkGenerated(long chunkKey) {
++ return isChunkGenerated((int) chunkKey, (int) (chunkKey >> 32));
++ }
++
+ /**
+ * This is the Legacy API before Java 8 was supported. Java 8 Consumer is provided,
+ * as well as future support
diff --git a/Spigot-API-Patches-Unmapped/0140-Add-More-Creeper-API.patch b/Spigot-API-Patches-Unmapped/0140-Add-More-Creeper-API.patch
new file mode 100644
index 0000000000..b45e2fbeba
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0140-Add-More-Creeper-API.patch
@@ -0,0 +1,91 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 24 Aug 2018 11:50:16 -0500
+Subject: [PATCH] Add More Creeper API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/CreeperIgniteEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/CreeperIgniteEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ff10251b6ded533b08048ec533525176eff03707
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/CreeperIgniteEvent.java
+@@ -0,0 +1,54 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Creeper;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a Creeper is ignite flag is changed (armed/disarmed to explode).
++ */
++public class CreeperIgniteEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean canceled;
++ private boolean ignited;
++
++ public CreeperIgniteEvent(@NotNull Creeper creeper, boolean ignited) {
++ super(creeper);
++ this.ignited = ignited;
++ }
++
++ @NotNull
++ @Override
++ public Creeper getEntity() {
++ return (Creeper) entity;
++ }
++
++ public boolean isIgnited() {
++ return ignited;
++ }
++
++ public void setIgnited(boolean ignited) {
++ this.ignited = ignited;
++ }
++
++ public boolean isCancelled() {
++ return canceled;
++ }
++
++ public void setCancelled(boolean cancel) {
++ canceled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Creeper.java b/src/main/java/org/bukkit/entity/Creeper.java
+index 5793193d93d76a062fd0431475c269c4978ec993..e144f618702122bf28ebedc5cb8ce0f6ef27c107 100644
+--- a/src/main/java/org/bukkit/entity/Creeper.java
++++ b/src/main/java/org/bukkit/entity/Creeper.java
+@@ -87,4 +87,20 @@ public interface Creeper extends Monster {
+ * griefing gamerule.
+ */
+ public void ignite();
++ // Paper start
++
++ /**
++ * Set whether creeper is ignited or not (armed to explode)
++ *
++ * @param ignited New ignited state
++ */
++ public void setIgnited(boolean ignited);
++
++ /**
++ * Check if creeper is ignited or not (armed to explode)
++ *
++ * @return Ignited state
++ */
++ public boolean isIgnited();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0141-Add-PhantomPreSpawnEvent.patch b/Spigot-API-Patches-Unmapped/0141-Add-PhantomPreSpawnEvent.patch
new file mode 100644
index 0000000000..5ffdc818cb
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0141-Add-PhantomPreSpawnEvent.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 25 Aug 2018 19:56:42 -0500
+Subject: [PATCH] Add PhantomPreSpawnEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/PhantomPreSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/PhantomPreSpawnEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9022f697ab244df43074e48c9150f39d44217531
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/PhantomPreSpawnEvent.java
+@@ -0,0 +1,31 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.EntityType;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++
++/**
++ * Called when a phantom is spawned for an exhausted player
++ */
++public class PhantomPreSpawnEvent extends PreCreatureSpawnEvent {
++ @NotNull private final Entity entity;
++
++ public PhantomPreSpawnEvent(@NotNull Location location, @NotNull Entity entity, @NotNull CreatureSpawnEvent.SpawnReason reason) {
++ super(location, EntityType.PHANTOM, reason);
++ this.entity = entity;
++ }
++
++ /**
++ * Get the entity this phantom is spawning for
++ *
++ * @return Entity
++ */
++ @Nullable
++ public Entity getSpawningEntity() {
++ return entity;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Phantom.java b/src/main/java/org/bukkit/entity/Phantom.java
+index 1a1044edc57078f96c4a95c994d0865da382c152..ed4d417c2deefb78807cb61b01df5afcd334d754 100644
+--- a/src/main/java/org/bukkit/entity/Phantom.java
++++ b/src/main/java/org/bukkit/entity/Phantom.java
+@@ -1,5 +1,8 @@
+ package org.bukkit.entity;
+
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
+ /**
+ * Represents a phantom.
+ */
+@@ -14,4 +17,14 @@ public interface Phantom extends Flying {
+ * @param sz The new size of the phantom.
+ */
+ public void setSize(int sz);
++
++ // Paper start
++ /**
++ * Get the UUID of the entity that caused this phantom to spawn
++ *
++ * @return UUID
++ */
++ @Nullable
++ public java.util.UUID getSpawningEntity();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0142-Add-source-block-to-BlockPhysicsEvent.patch b/Spigot-API-Patches-Unmapped/0142-Add-source-block-to-BlockPhysicsEvent.patch
new file mode 100644
index 0000000000..965de7c24a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0142-Add-source-block-to-BlockPhysicsEvent.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Sotr <[email protected]>
+Date: Thu, 23 Aug 2018 16:14:25 +0800
+Subject: [PATCH] Add source block to BlockPhysicsEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java b/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java
+index e3a5f5824ed882058f5bac5003f66ce79733a868..c382f9fc2b8c5b959df7071007110dab737e400e 100644
+--- a/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java
++++ b/src/main/java/org/bukkit/event/block/BlockPhysicsEvent.java
+@@ -32,6 +32,13 @@ public class BlockPhysicsEvent extends BlockEvent implements Cancellable {
+ private final Block sourceBlock;
+ private boolean cancel = false;
+
++ // Paper start - Legacy constructor, use #BlockPhysicsEvent(Block, BlockData, Block)
++ @Deprecated
++ public BlockPhysicsEvent(final Block block, final BlockData changed, final int sourceX, final int sourceY, final int sourceZ) {
++ this(block, changed, block.getWorld().getBlockAt(sourceX, sourceY, sourceZ));
++ }
++ // Paper end
++
+ public BlockPhysicsEvent(@NotNull final Block block, @NotNull final BlockData changed) {
+ this(block, changed, block);
+ }
diff --git a/Spigot-API-Patches-Unmapped/0143-Inventory-removeItemAnySlot.patch b/Spigot-API-Patches-Unmapped/0143-Inventory-removeItemAnySlot.patch
new file mode 100644
index 0000000000..4d6f2e9e57
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0143-Inventory-removeItemAnySlot.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Tue, 28 Aug 2018 23:04:06 -0400
+Subject: [PATCH] Inventory#removeItemAnySlot
+
+
+diff --git a/src/main/java/org/bukkit/inventory/Inventory.java b/src/main/java/org/bukkit/inventory/Inventory.java
+index 5576a6a8df8c95164bf2dde45d756ce8b7ec957a..9c6a5bdac8c3ab682bbfae04ff24b76a62bc2883 100644
+--- a/src/main/java/org/bukkit/inventory/Inventory.java
++++ b/src/main/java/org/bukkit/inventory/Inventory.java
+@@ -125,6 +125,34 @@ public interface Inventory extends Iterable<ItemStack> {
+ @NotNull
+ public HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException;
+
++ // Paper start
++ /**
++ * Searches all possible inventory slots in order to remove the given ItemStacks.
++ * <p>
++ * Similar to {@link Inventory#removeItem(ItemStack...)} in behavior, except this
++ * method will check all possible slots in the inventory, rather than just the main
++ * storage contents.
++ * <p>
++ * It will try to remove 'as much as possible' from the types and amounts
++ * you give as arguments.
++ * <p>
++ * The returned HashMap contains what it couldn't remove, where the key is
++ * the index of the parameter, and the value is the ItemStack at that
++ * index of the varargs parameter. If all the given ItemStacks are
++ * removed, it will return an empty HashMap.
++ * <p>
++ * It is known that in some implementations this method will also set the
++ * inputted argument amount to the number of that item not removed from
++ * slots.
++ *
++ * @param items The ItemStacks to remove
++ * @return A HashMap containing items that couldn't be removed.
++ * @throws IllegalArgumentException if items is null
++ */
++ @NotNull
++ public HashMap<Integer, ItemStack> removeItemAnySlot(@NotNull ItemStack... items) throws IllegalArgumentException;
++ // Paper end
++
+ /**
+ * Returns all ItemStacks from the inventory
+ *
diff --git a/Spigot-API-Patches-Unmapped/0144-Add-ray-tracing-methods-to-LivingEntity.patch b/Spigot-API-Patches-Unmapped/0144-Add-ray-tracing-methods-to-LivingEntity.patch
new file mode 100644
index 0000000000..96c37c9d46
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0144-Add-ray-tracing-methods-to-LivingEntity.patch
@@ -0,0 +1,148 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Mon, 3 Sep 2018 18:13:53 -0500
+Subject: [PATCH] Add ray tracing methods to LivingEntity
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/block/TargetBlockInfo.java b/src/main/java/com/destroystokyo/paper/block/TargetBlockInfo.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..18a96dbb01d3b34476652264b2d6be3782a154ec
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/block/TargetBlockInfo.java
+@@ -0,0 +1,54 @@
++package com.destroystokyo.paper.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.block.BlockFace;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents information about a targeted block
++ */
++public class TargetBlockInfo {
++ private final Block block;
++ private final BlockFace blockFace;
++
++ public TargetBlockInfo(@NotNull Block block, @NotNull BlockFace blockFace) {
++ this.block = block;
++ this.blockFace = blockFace;
++ }
++
++ /**
++ * Get the block that is targeted
++ *
++ * @return Targeted block
++ */
++ @NotNull
++ public Block getBlock() {
++ return block;
++ }
++
++ /**
++ * Get the targeted BlockFace
++ *
++ * @return Targeted blockface
++ */
++ @NotNull
++ public BlockFace getBlockFace() {
++ return blockFace;
++ }
++
++ /**
++ * Get the relative Block to the targeted block on the side it is targeted at
++ *
++ * @return Block relative to targeted block
++ */
++ @NotNull
++ public Block getRelativeBlock() {
++ return block.getRelative(blockFace);
++ }
++
++ public enum FluidMode {
++ NEVER,
++ SOURCE_ONLY,
++ ALWAYS
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 8b89c0701dd557bcab0c05c1593354ee704b9fe4..8fe7ccf12339355554835542cc1068d9f6c3a435 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -82,6 +82,77 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ @NotNull
+ public Block getTargetBlock(@Nullable Set<Material> transparent, int maxDistance);
+
++ // Paper start
++ /**
++ * Gets the block that the living entity has targeted, ignoring fluids
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @return block that the living entity has targeted,
++ * or null if no block is within maxDistance
++ */
++ @Nullable
++ public default Block getTargetBlock(int maxDistance) {
++ return getTargetBlock(maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode.NEVER);
++ }
++
++ /**
++ * Gets the block that the living entity has targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @param fluidMode whether to check fluids or not
++ * @return block that the living entity has targeted,
++ * or null if no block is within maxDistance
++ */
++ @Nullable
++ public Block getTargetBlock(int maxDistance, @NotNull com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode);
++
++ /**
++ * Gets the blockface of that block that the living entity has targeted, ignoring fluids
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @return blockface of the block that the living entity has targeted,
++ * or null if no block is targeted
++ */
++ @Nullable
++ public default org.bukkit.block.BlockFace getTargetBlockFace(int maxDistance) {
++ return getTargetBlockFace(maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode.NEVER);
++ }
++
++ /**
++ * Gets the blockface of that block that the living entity has targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @param fluidMode whether to check fluids or not
++ * @return blockface of the block that the living entity has targeted,
++ * or null if no block is targeted
++ */
++ @Nullable
++ public org.bukkit.block.BlockFace getTargetBlockFace(int maxDistance, @NotNull com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode);
++
++ /**
++ * Gets information about the block the living entity has targeted, ignoring fluids
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @return TargetBlockInfo about the block the living entity has targeted,
++ * or null if no block is targeted
++ */
++ @Nullable
++ public default com.destroystokyo.paper.block.TargetBlockInfo getTargetBlockInfo(int maxDistance) {
++ return getTargetBlockInfo(maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode.NEVER);
++ }
++
++ /**
++ * Gets information about the block the living entity has targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @param fluidMode whether to check fluids or not
++ * @return TargetBlockInfo about the block the living entity has targeted,
++ * or null if no block is targeted
++ */
++ @Nullable
++ public com.destroystokyo.paper.block.TargetBlockInfo getTargetBlockInfo(int maxDistance, @NotNull com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode);
++ // Paper end
++
+ /**
+ * Gets the last two blocks along the living entity's line of sight.
+ * <p>
diff --git a/Spigot-API-Patches-Unmapped/0145-Improve-death-events.patch b/Spigot-API-Patches-Unmapped/0145-Improve-death-events.patch
new file mode 100644
index 0000000000..f42921460c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0145-Improve-death-events.patch
@@ -0,0 +1,181 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Phoenix616 <[email protected]>
+Date: Tue, 21 Aug 2018 01:32:28 +0100
+Subject: [PATCH] Improve death events
+
+This adds the ability to cancel the death events and to modify the sound
+an entity makes when dying. (In cases were no sound should it will be
+called with shouldPlaySound set to false allowing unsilencing of silent
+entities)
+
+It makes handling of entity deaths a lot nicer as you no longer need
+to listen on the damage event and calculate if the entity dies yourself
+to cancel the death which has the benefit of also receiving the dropped
+items and experience which is otherwise only properly possible by using
+internal code.
+
+diff --git a/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java b/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java
+index a5984ab06cce95d30e70511e125f69339b574c04..e19a3df9aa2204b44c0b029bda141ae6306f60a1 100644
+--- a/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java
++++ b/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java
+@@ -5,14 +5,24 @@ import org.bukkit.entity.LivingEntity;
+ import org.bukkit.event.HandlerList;
+ import org.bukkit.inventory.ItemStack;
+ import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
+
+ /**
+ * Thrown whenever a LivingEntity dies
+ */
+-public class EntityDeathEvent extends EntityEvent {
++public class EntityDeathEvent extends EntityEvent implements org.bukkit.event.Cancellable { // Paper - make cancellable
+ private static final HandlerList handlers = new HandlerList();
+ private final List<ItemStack> drops;
+ private int dropExp = 0;
++ // Paper start - make cancellable
++ private boolean cancelled;
++ private double reviveHealth = 0;
++ private boolean shouldPlayDeathSound;
++ @Nullable private org.bukkit.Sound deathSound;
++ @Nullable private org.bukkit.SoundCategory deathSoundCategory;
++ private float deathSoundVolume;
++ private float deathSoundPitch;
++ // Paper end
+
+ public EntityDeathEvent(@NotNull final LivingEntity entity, @NotNull final List<ItemStack> drops) {
+ this(entity, drops, 0);
+@@ -74,4 +84,134 @@ public class EntityDeathEvent extends EntityEvent {
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
++
++ // Paper start - make cancellable
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ /**
++ * Get the amount of health that the entity should revive with after cancelling the event.
++ * Set to the entity's max health by default.
++ *
++ * @return The amount of health
++ */
++ public double getReviveHealth() {
++ return reviveHealth;
++ }
++
++ /**
++ * Set the amount of health that the entity should revive with after cancelling the event.
++ * Revive health value must be between 0 (exclusive) and the entity's max health (inclusive).
++ *
++ * @param reviveHealth The amount of health
++ * @throws IllegalArgumentException Thrown if the health is {@literal <= 0 or >} max health
++ */
++ public void setReviveHealth(double reviveHealth) throws IllegalArgumentException {
++ double maxHealth = ((LivingEntity) entity).getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue();
++ if ((maxHealth != 0 && reviveHealth <= 0) || (reviveHealth > maxHealth)) {
++ throw new IllegalArgumentException("Health must be between 0 (exclusive) and " + maxHealth + " (inclusive), but was " + reviveHealth);
++ }
++ this.reviveHealth = reviveHealth;
++ }
++
++
++ /**
++ * Whether or not the death sound should play when the entity dies. If the event is cancelled it does not play!
++ *
++ * @return Whether or not the death sound should play. Event is called with this set to false if the entity is silent.
++ */
++ public boolean shouldPlayDeathSound() {
++ return shouldPlayDeathSound;
++ }
++
++ /**
++ * Set whether or not the death sound should play when the entity dies. If the event is cancelled it does not play!
++ *
++ * @param playDeathSound Enable or disable the death sound
++ */
++ public void setShouldPlayDeathSound(boolean playDeathSound) {
++ this.shouldPlayDeathSound = playDeathSound;
++ }
++
++ /**
++ * Get the sound that the entity makes when dying
++ *
++ * @return The sound that the entity makes
++ */
++ @Nullable
++ public org.bukkit.Sound getDeathSound() {
++ return deathSound;
++ }
++
++ /**
++ * Set the sound that the entity makes when dying
++ *
++ * @param sound The sound that the entity should make when dying
++ */
++ public void setDeathSound(@Nullable org.bukkit.Sound sound) {
++ deathSound = sound;
++ }
++
++ /**
++ * Get the sound category that the death sound should play in
++ *
++ * @return The sound category
++ */
++ @Nullable
++ public org.bukkit.SoundCategory getDeathSoundCategory() {
++ return deathSoundCategory;
++ }
++
++ /**
++ * Set the sound category that the death sound should play in.
++ *
++ * @param soundCategory The sound category
++ */
++ public void setDeathSoundCategory(@Nullable org.bukkit.SoundCategory soundCategory) {
++ this.deathSoundCategory = soundCategory;
++ }
++
++ /**
++ * Get the volume that the death sound will play at.
++ *
++ * @return The volume the death sound will play at
++ */
++ public float getDeathSoundVolume() {
++ return deathSoundVolume;
++ }
++
++ /**
++ * Set the volume the death sound should play at. If the event is cancelled this will not play the sound!
++ *
++ * @param volume The volume the death sound should play at
++ */
++ public void setDeathSoundVolume(float volume) {
++ this.deathSoundVolume = volume;
++ }
++
++ /**
++ * Get the pitch that the death sound will play with.
++ *
++ * @return The pitch the death sound will play with
++ */
++ public float getDeathSoundPitch() {
++ return deathSoundPitch;
++ }
++
++ /**
++ * GSetet the pitch that the death sound should play with.
++ *
++ * @param pitch The pitch the death sound should play with
++ */
++ public void setDeathSoundPitch(float pitch) {
++ this.deathSoundPitch = pitch;
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0146-Mob-Pathfinding-API.patch b/Spigot-API-Patches-Unmapped/0146-Mob-Pathfinding-API.patch
new file mode 100644
index 0000000000..8408100069
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0146-Mob-Pathfinding-API.patch
@@ -0,0 +1,259 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 9 Sep 2018 12:39:06 -0400
+Subject: [PATCH] Mob Pathfinding API
+
+Adds an API to allow plugins to instruct a Mob to Pathfind to a Location or Entity
+
+This does not do anything to stop other AI rules from changing the location, so
+it is still up to the plugin to control that or override after another goal changed
+the location.
+
+You can use EntityPathfindEvent to cancel new pathfinds from overriding your current.
+
+diff --git a/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java b/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e2a6f9c3881ff9d7373ac30e60009200432555aa
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/Pathfinder.java
+@@ -0,0 +1,212 @@
++package com.destroystokyo.paper.entity;
++
++import org.bukkit.Location;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Mob;
++
++import java.util.List;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Handles pathfinding operations for an Entity
++ */
++public interface Pathfinder {
++
++ /**
++ *
++ * @return The entity that is controlled by this pathfinder
++ */
++ @NotNull
++ Mob getEntity();
++
++ /**
++ * Instructs the Entity to stop trying to navigate to its current desired location
++ */
++ void stopPathfinding();
++
++ /**
++ * If the entity is currently trying to navigate to a destination, this will return true
++ * @return true if the entity is navigating to a destination
++ */
++ boolean hasPath();
++
++ /**
++ * @return The location the entity is trying to navigate to, or null if there is no destination
++ */
++ @Nullable
++ PathResult getCurrentPath();
++
++ /**
++ * Calculates a destination for the Entity to navigate to, but does not set it
++ * as the current target. Useful for calculating what would happen before setting it.
++ * @param loc Location to navigate to
++ * @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated
++ */
++ @Nullable PathResult findPath(@NotNull Location loc);
++
++ /**
++ * Calculates a destination for the Entity to navigate to to reach the target entity,
++ * but does not set it as the current target.
++ * Useful for calculating what would happen before setting it.
++ *
++ * The behavior of this PathResult is subject to the games pathfinding rules, and may
++ * result in the pathfinding automatically updating to follow the target Entity.
++ *
++ * However, this behavior is not guaranteed, and is subject to the games behavior.
++ *
++ * @param target the Entity to navigate to
++ * @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated
++ */
++ @Nullable PathResult findPath(@NotNull LivingEntity target);
++
++ /**
++ * Calculates a destination for the Entity to navigate to, and sets it with default speed
++ * as the current target.
++ * @param loc Location to navigate to
++ * @return If the pathfinding was successfully started
++ */
++ default boolean moveTo(@NotNull Location loc) {
++ return moveTo(loc, 1);
++ }
++
++ /**
++ * Calculates a destination for the Entity to navigate to, with desired speed
++ * as the current target.
++ * @param loc Location to navigate to
++ * @param speed Speed multiplier to navigate at, where 1 is 'normal'
++ * @return If the pathfinding was successfully started
++ */
++ default boolean moveTo(@NotNull Location loc, double speed) {
++ PathResult path = findPath(loc);
++ return path != null && moveTo(path, speed);
++ }
++
++ /**
++ * Calculates a destination for the Entity to navigate to to reach the target entity,
++ * and sets it with default speed.
++ *
++ * The behavior of this PathResult is subject to the games pathfinding rules, and may
++ * result in the pathfinding automatically updating to follow the target Entity.
++ *
++ * However, this behavior is not guaranteed, and is subject to the games behavior.
++ *
++ * @param target the Entity to navigate to
++ * @return If the pathfinding was successfully started
++ */
++ default boolean moveTo(@NotNull LivingEntity target) {
++ return moveTo(target, 1);
++ }
++
++ /**
++ * Calculates a destination for the Entity to navigate to to reach the target entity,
++ * and sets it with specified speed.
++ *
++ * The behavior of this PathResult is subject to the games pathfinding rules, and may
++ * result in the pathfinding automatically updating to follow the target Entity.
++ *
++ * However, this behavior is not guaranteed, and is subject to the games behavior.
++ *
++ * @param target the Entity to navigate to
++ * @param speed Speed multiplier to navigate at, where 1 is 'normal'
++ * @return If the pathfinding was successfully started
++ */
++ default boolean moveTo(@NotNull LivingEntity target, double speed) {
++ PathResult path = findPath(target);
++ return path != null && moveTo(path, speed);
++ }
++
++ /**
++ * Takes the result of a previous pathfinding calculation and sets it
++ * as the active pathfinding with default speed.
++ *
++ * @param path The Path to start following
++ * @return If the pathfinding was successfully started
++ */
++ default boolean moveTo(@NotNull PathResult path) {
++ return moveTo(path, 1);
++ }
++
++ /**
++ * Takes the result of a previous pathfinding calculation and sets it
++ * as the active pathfinding,
++ *
++ * @param path The Path to start following
++ * @param speed Speed multiplier to navigate at, where 1 is 'normal'
++ * @return If the pathfinding was successfully started
++ */
++ boolean moveTo(@NotNull PathResult path, double speed);
++
++ /**
++ * Checks if this pathfinder allows passing through closed doors.
++ *
++ * @return if this pathfinder allows passing through closed doors
++ */
++ boolean canOpenDoors();
++
++ /**
++ * Allows this pathfinder to pass through closed doors, or not
++ *
++ * @param canOpenDoors if the mob can pass through closed doors, or not
++ */
++ void setCanOpenDoors(boolean canOpenDoors);
++
++ /**
++ * Checks if this pathfinder allows passing through open doors.
++ *
++ * @return if this pathfinder allows passing through open doors
++ */
++ boolean canPassDoors();
++
++ /**
++ * Allows this pathfinder to pass through open doors, or not
++ *
++ * @param canPassDoors if the mob can pass through open doors, or not
++ */
++ void setCanPassDoors(boolean canPassDoors);
++
++ /**
++ * Checks if this pathfinder assumes that the mob can float
++ *
++ * @return if this pathfinder assumes that the mob can float
++ */
++ boolean canFloat();
++
++ /**
++ * Makes this pathfinder assume that the mob can float, or not
++ *
++ * @param canFloat if the mob can float, or not
++ */
++ void setCanFloat(boolean canFloat);
++
++ /**
++ * Represents the result of a pathfinding calculation
++ */
++ interface PathResult {
++
++ /**
++ * All currently calculated points to follow along the path to reach the destination location
++ *
++ * Will return points the entity has already moved past, see {@link #getNextPointIndex()}
++ * @return List of points
++ */
++ @NotNull
++ List<Location> getPoints();
++
++ /**
++ * @return Returns the index of the current point along the points returned in {@link #getPoints()} the entity
++ * is trying to reach, or null if we are done with this pathfinding.
++ */
++ int getNextPointIndex();
++
++ /**
++ * @return The next location in the path points the entity is trying to reach, or null if there is no next point
++ */
++ @Nullable Location getNextPoint();
++
++ /**
++ * @return The closest point the path can get to the target location
++ */
++ @Nullable Location getFinalPoint();
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Mob.java b/src/main/java/org/bukkit/entity/Mob.java
+index be9334a8b5fba9181ad63c211697e798be63da25..b132287817d35579ca5128a1ed5c242bf229771a 100644
+--- a/src/main/java/org/bukkit/entity/Mob.java
++++ b/src/main/java/org/bukkit/entity/Mob.java
+@@ -1,6 +1,7 @@
+ package org.bukkit.entity;
+
+ import org.bukkit.loot.Lootable;
++import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
+ /**
+@@ -8,6 +9,15 @@ import org.jetbrains.annotations.Nullable;
+ */
+ public interface Mob extends LivingEntity, Lootable {
+
++ // Paper start
++ /**
++ * Enables access to control the pathing of an Entity
++ * @return Pathfinding Manager for this entity
++ */
++ @NotNull
++ com.destroystokyo.paper.entity.Pathfinder getPathfinder();
++ // Paper end
++
+ /**
+ * Instructs this Mob to set the specified LivingEntity as its target.
+ * <p>
diff --git a/Spigot-API-Patches-Unmapped/0147-Expose-attack-cooldown-methods-for-Player.patch b/Spigot-API-Patches-Unmapped/0147-Expose-attack-cooldown-methods-for-Player.patch
new file mode 100644
index 0000000000..2a8eb8ac7f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0147-Expose-attack-cooldown-methods-for-Player.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Tue, 4 Sep 2018 15:01:54 -0500
+Subject: [PATCH] Expose attack cooldown methods for Player
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index fe4b301e96f58cc41fb9789f030e00645c9a7e60..6253b43490cac138edbb59ce0647d3f37314e435 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1874,6 +1874,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @param profile The new profile to use
+ */
+ void setPlayerProfile(@NotNull PlayerProfile profile);
++
++ /**
++ * Returns the amount of ticks the current cooldown lasts
++ *
++ * @return Amount of ticks cooldown will last
++ */
++ float getCooldownPeriod();
++
++ /**
++ * Returns the percentage of attack power available based on the cooldown (zero to one).
++ *
++ * @param adjustTicks Amount of ticks to add to cooldown counter for this calculation
++ * @return Percentage of attack power available
++ */
++ float getCooledAttackStrength(float adjustTicks);
++
++ /**
++ * Reset the cooldown counter to 0, effectively starting the cooldown period.
++ */
++ void resetCooldown();
+ // Paper end
+
+ // Spigot start
diff --git a/Spigot-API-Patches-Unmapped/0148-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch b/Spigot-API-Patches-Unmapped/0148-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
new file mode 100644
index 0000000000..c5b4ce2e41
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0148-Add-an-API-for-CanPlaceOn-and-CanDestroy-NBT-values.patch
@@ -0,0 +1,319 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Wed, 12 Sep 2018 18:53:35 +0300
+Subject: [PATCH] Add an API for CanPlaceOn and CanDestroy NBT values
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/Namespaced.java b/src/main/java/com/destroystokyo/paper/Namespaced.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..cd1a34b82870684e09e18c47169bd472ecbbb91f
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/Namespaced.java
+@@ -0,0 +1,40 @@
++package com.destroystokyo.paper;
++
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents a namespaced resource, see {@link org.bukkit.NamespacedKey} for single elements
++ * or {@link com.destroystokyo.paper.NamespacedTag} for a collection of elements
++ *
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ * <p>
++ * You should not be implementing this interface yourself, use {@link org.bukkit.NamespacedKey}
++ * or {@link com.destroystokyo.paper.NamespacedTag} as needed instead.
++ */
++public interface Namespaced {
++ /**
++ * Gets the namespace this resource is a part of
++ * <p>
++ * This is contractually obligated to only contain lowercase alphanumeric characters,
++ * periods, underscores, and hyphens.
++ *
++ * @return resource namespace
++ */
++ @NotNull
++ String getNamespace();
++
++ /**
++ * Gets the key corresponding to this resource
++ * <p>
++ * This is contractually obligated to only contain lowercase alphanumeric characters,
++ * periods, underscores, hyphens, and forward slashes.
++ *
++ * @return resource key
++ */
++ @NotNull
++ String getKey();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/NamespacedTag.java b/src/main/java/com/destroystokyo/paper/NamespacedTag.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..28f3fda950999a9c964a3608042ca60567ae1d6a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/NamespacedTag.java
+@@ -0,0 +1,142 @@
++package com.destroystokyo.paper;
++
++import com.google.common.base.Preconditions;
++import java.util.Locale;
++import java.util.UUID;
++import java.util.regex.Pattern;
++import org.bukkit.plugin.Plugin;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents a String based key pertaining to a tagged entry. Consists of two components - a namespace
++ * and a key.
++ * <p>
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ *
++ */
++// Paper - entire class, based on org.bukkit.NamespacedKey
++public final class NamespacedTag implements com.destroystokyo.paper.Namespaced {
++
++ /**
++ * The namespace representing all inbuilt keys.
++ */
++ public static final String MINECRAFT = "minecraft";
++ /**
++ * The namespace representing all keys generated by Bukkit for backwards
++ * compatibility measures.
++ */
++ public static final String BUKKIT = "bukkit";
++ //
++ private static final Pattern VALID_NAMESPACE = Pattern.compile("[a-z0-9._-]+");
++ private static final Pattern VALID_KEY = Pattern.compile("[a-z0-9/._-]+");
++ //
++ private final String namespace;
++ private final String key;
++
++ /**
++ * Create a key in a specific namespace.
++ *
++ * @param namespace String representing a grouping of keys
++ * @param key Name for this specific key
++ * @deprecated should never be used by plugins, for internal use only!!
++ */
++ @Deprecated
++ public NamespacedTag(@NotNull String namespace, @NotNull String key) {
++ Preconditions.checkArgument(namespace != null && VALID_NAMESPACE.matcher(namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", namespace);
++ Preconditions.checkArgument(key != null && VALID_KEY.matcher(key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", key);
++
++ this.namespace = namespace;
++ this.key = key;
++
++ String string = toString();
++ Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters", string);
++ }
++
++ /**
++ * Create a key in the plugin's namespace.
++ * <p>
++ * Namespaces may only contain lowercase alphanumeric characters, periods,
++ * underscores, and hyphens.
++ * <p>
++ * Keys may only contain lowercase alphanumeric characters, periods,
++ * underscores, hyphens, and forward slashes.
++ *
++ * @param plugin the plugin to use for the namespace
++ * @param key the key to create
++ */
++ public NamespacedTag(@NotNull Plugin plugin, @NotNull String key) {
++ Preconditions.checkArgument(plugin != null, "Plugin cannot be null");
++ Preconditions.checkArgument(key != null, "Key cannot be null");
++
++ this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
++ this.key = key.toLowerCase().toLowerCase(Locale.ROOT);
++
++ // Check validity after normalization
++ Preconditions.checkArgument(VALID_NAMESPACE.matcher(this.namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace);
++ Preconditions.checkArgument(VALID_KEY.matcher(this.key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", this.key);
++
++ String string = toString();
++ Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters (%s)", string);
++ }
++
++ @NotNull
++ public String getNamespace() {
++ return namespace;
++ }
++
++ @NotNull
++ public String getKey() {
++ return key;
++ }
++
++ @Override
++ public int hashCode() {
++ int hash = 7;
++ hash = 47 * hash + this.namespace.hashCode();
++ hash = 47 * hash + this.key.hashCode();
++ return hash;
++ }
++
++ @Override
++ public boolean equals(Object obj) {
++ if (obj == null) {
++ return false;
++ }
++ if (getClass() != obj.getClass()) {
++ return false;
++ }
++ final NamespacedTag other = (NamespacedTag) obj;
++ return this.namespace.equals(other.namespace) && this.key.equals(other.key);
++ }
++
++ @Override
++ public String toString() {
++ return "#" + this.namespace + ":" + this.key;
++ }
++
++ /**
++ * Return a new random key in the {@link #BUKKIT} namespace.
++ *
++ * @return new key
++ * @deprecated should never be used by plugins, for internal use only!!
++ */
++ @Deprecated
++ public static NamespacedTag randomKey() {
++ return new NamespacedTag(BUKKIT, UUID.randomUUID().toString());
++ }
++
++ /**
++ * Get a key in the Minecraft namespace.
++ *
++ * @param key the key to use
++ * @return new key in the Minecraft namespace
++ */
++ @NotNull
++ public static NamespacedTag minecraft(@NotNull String key) {
++ return new NamespacedTag(MINECRAFT, key);
++ }
++}
+diff --git a/src/main/java/org/bukkit/NamespacedKey.java b/src/main/java/org/bukkit/NamespacedKey.java
+index c65f0d6569c130b4920a9e71ad24af6427f1f030..01bcb3a1bdb5accdf844d0178cec3d25746b3eaa 100644
+--- a/src/main/java/org/bukkit/NamespacedKey.java
++++ b/src/main/java/org/bukkit/NamespacedKey.java
+@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
+ * underscores, hyphens, and forward slashes.
+ *
+ */
+-public final class NamespacedKey implements net.kyori.adventure.key.Key { // Paper - implement Key
++public final class NamespacedKey implements net.kyori.adventure.key.Key, com.destroystokyo.paper.Namespaced { // Paper - implement Key and Namespaced
+
+ /**
+ * The namespace representing all inbuilt keys.
+@@ -84,11 +84,13 @@ public final class NamespacedKey implements net.kyori.adventure.key.Key { // Pap
+ }
+
+ @NotNull
++ @Override // Paper
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @NotNull
++ @Override // Paper
+ public String getKey() {
+ return key;
+ }
+diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+index 1c362636c56db0e6c118171ba367c43c4f7cff33..01b462fccce71cef3398dd43944046f322b8e57e 100644
+--- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+@@ -432,4 +432,87 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @SuppressWarnings("javadoc")
+ @NotNull
+ ItemMeta clone();
++
++ // Paper start - Add an API for CanPlaceOn and CanDestroy NBT values
++ /**
++ * Gets set of materials what given item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of materials
++ * @deprecated Minecraft does not limit this to the material enum, Use {@link #getDestroyableKeys()} as a replacement
++ */
++ @Deprecated
++ Set<org.bukkit.Material> getCanDestroy();
++
++ /**
++ * Sets set of materials what given item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canDestroy Set of materials
++ * @deprecated Minecraft does not limit this to the material enum, Use {@link #setDestroyableKeys(Collection)} as a replacement
++ */
++ @Deprecated
++ void setCanDestroy(Set<org.bukkit.Material> canDestroy);
++
++ /**
++ * Gets set of materials where given item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of materials
++ * @deprecated Minecraft does not limit this to the material enum, Use {@link #getPlaceableKeys()} as a replacement
++ */
++ @Deprecated
++ Set<org.bukkit.Material> getCanPlaceOn();
++
++ /**
++ * Sets set of materials where given item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canPlaceOn Set of materials
++ * @deprecated Minecraft does not limit this to the material enum, Use {@link #setPlaceableKeys(Collection)} as a replacement
++ */
++ @Deprecated
++ void setCanPlaceOn(Set<org.bukkit.Material> canPlaceOn);
++
++ /**
++ * Gets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of {@link com.destroystokyo.paper.Namespaced}
++ */
++ @NotNull
++ Set<com.destroystokyo.paper.Namespaced> getDestroyableKeys();
++
++ /**
++ * Sets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canDestroy Collection of {@link com.destroystokyo.paper.Namespaced}
++ */
++ void setDestroyableKeys(@NotNull Collection<com.destroystokyo.paper.Namespaced> canDestroy);
++
++ /**
++ * Gets the collection of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @return Set of {@link com.destroystokyo.paper.Namespaced}
++ */
++ @NotNull
++ Set<com.destroystokyo.paper.Namespaced> getPlaceableKeys();
++
++ /**
++ * Sets the set of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE}
++ *
++ * @param canPlaceOn Collection of {@link com.destroystokyo.paper.Namespaced}
++ */
++ @NotNull
++ void setPlaceableKeys(@NotNull Collection<com.destroystokyo.paper.Namespaced> canPlaceOn);
++
++ /**
++ * Checks for the existence of any keys that the item can be placed on
++ *
++ * @return true if this item has placeable keys
++ */
++ boolean hasPlaceableKeys();
++
++ /**
++ * Checks for the existence of any keys that the item can destroy
++ *
++ * @return true if this item has destroyable keys
++ */
++ boolean hasDestroyableKeys();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0149-Performance-Concurrency-Improvements-to-Permissions.patch b/Spigot-API-Patches-Unmapped/0149-Performance-Concurrency-Improvements-to-Permissions.patch
new file mode 100644
index 0000000000..4d64579e4a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0149-Performance-Concurrency-Improvements-to-Permissions.patch
@@ -0,0 +1,113 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 13 Sep 2018 20:51:50 -0400
+Subject: [PATCH] Performance & Concurrency Improvements to Permissions
+
+Modifying of permissions was only half protected, enabling concurrency
+issues to occur if permissions were modified async.
+
+While no plugin really should be doing that, modifying operations
+are not heavily called, so they are safe to add synchronization to.
+
+Now, all modification API's will be synchronized ensuring safety.
+
+Additionally, hasPermission was victim to a common java newbie mistake
+of calling if (containsKey(k)) return get(k), resulting in 2 map lookups.
+
+Optimized it to simply be a single get call cutting permission map
+lookups in half.
+
+diff --git a/src/main/java/org/bukkit/permissions/PermissibleBase.java b/src/main/java/org/bukkit/permissions/PermissibleBase.java
+index 497775f7f8fa2eae34555ca0f0c6ba72d6cfab3f..c94e4cdb5785d5dfcb704c4adabda0b19a20ec7d 100644
+--- a/src/main/java/org/bukkit/permissions/PermissibleBase.java
++++ b/src/main/java/org/bukkit/permissions/PermissibleBase.java
+@@ -75,8 +75,11 @@ public class PermissibleBase implements Permissible {
+
+ String name = inName.toLowerCase(java.util.Locale.ENGLISH);
+
+- if (isPermissionSet(name)) {
+- return permissions.get(name).getValue();
++ // Paper start
++ PermissionAttachmentInfo info = permissions.get(name);
++ if (info != null) {
++ return info.getValue();
++ // Paper end
+ } else {
+ Permission perm = Bukkit.getServer().getPluginManager().getPermission(name);
+
+@@ -96,15 +99,18 @@ public class PermissibleBase implements Permissible {
+
+ String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
+
+- if (isPermissionSet(name)) {
+- return permissions.get(name).getValue();
++ // Paper start
++ PermissionAttachmentInfo info = permissions.get(name);
++ if (info != null) {
++ return info.getValue();
+ }
++ // Paper end
+ return perm.getDefault().getValue(isOp());
+ }
+
+ @Override
+ @NotNull
+- public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) {
++ public synchronized PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) { // Paper - synchronized
+ if (name == null) {
+ throw new IllegalArgumentException("Permission name cannot be null");
+ } else if (plugin == null) {
+@@ -123,7 +129,7 @@ public class PermissibleBase implements Permissible {
+
+ @Override
+ @NotNull
+- public PermissionAttachment addAttachment(@NotNull Plugin plugin) {
++ public synchronized PermissionAttachment addAttachment(@NotNull Plugin plugin) { // Paper - synchronized
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null");
+ } else if (!plugin.isEnabled()) {
+@@ -139,7 +145,7 @@ public class PermissibleBase implements Permissible {
+ }
+
+ @Override
+- public void removeAttachment(@NotNull PermissionAttachment attachment) {
++ public synchronized void removeAttachment(@NotNull PermissionAttachment attachment) { // Paper - synchronized
+ if (attachment == null) {
+ throw new IllegalArgumentException("Attachment cannot be null");
+ }
+@@ -159,7 +165,7 @@ public class PermissibleBase implements Permissible {
+ }
+
+ @Override
+- public void recalculatePermissions() {
++ public synchronized void recalculatePermissions() { // Paper - synchronized
+ clearPermissions();
+ Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(isOp());
+ Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(isOp(), parent);
+@@ -208,7 +214,7 @@ public class PermissibleBase implements Permissible {
+
+ @Override
+ @Nullable
+- public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) {
++ public synchronized PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) { // Paper
+ if (name == null) {
+ throw new IllegalArgumentException("Permission name cannot be null");
+ } else if (plugin == null) {
+@@ -228,7 +234,7 @@ public class PermissibleBase implements Permissible {
+
+ @Override
+ @Nullable
+- public PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) {
++ public synchronized PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) { // Paper - synchronized
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null");
+ } else if (!plugin.isEnabled()) {
+@@ -248,7 +254,7 @@ public class PermissibleBase implements Permissible {
+
+ @Override
+ @NotNull
+- public Set<PermissionAttachmentInfo> getEffectivePermissions() {
++ public synchronized Set<PermissionAttachmentInfo> getEffectivePermissions() { // Paper - synchronized
+ return new HashSet<PermissionAttachmentInfo>(permissions.values());
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0150-Add-ItemStackRecipeChoice-Draft-API.patch b/Spigot-API-Patches-Unmapped/0150-Add-ItemStackRecipeChoice-Draft-API.patch
new file mode 100644
index 0000000000..1a4eca8cc0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0150-Add-ItemStackRecipeChoice-Draft-API.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 13 Sep 2018 21:39:26 -0400
+Subject: [PATCH] Add ItemStackRecipeChoice Draft API
+
+This is based on Spigots Draft API. This is subject to change
+
+Allows creating recipes that must match isSimilar to full item stack.
+
+diff --git a/src/main/java/com/destroystokyo/paper/inventory/ItemStackRecipeChoice.java b/src/main/java/com/destroystokyo/paper/inventory/ItemStackRecipeChoice.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..43e6576b1d1bb811f9feb22de0024d9c823cb21a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/inventory/ItemStackRecipeChoice.java
+@@ -0,0 +1,51 @@
++package com.destroystokyo.paper.inventory;
++
++import org.bukkit.inventory.ItemStack;
++import org.bukkit.inventory.RecipeChoice;
++
++import java.util.ArrayList;
++import java.util.List;
++
++/**
++ * Allows crafting Items that require full matching itemstacks to complete the recipe for custom items
++ * @deprecated Draft API
++ */
++@Deprecated
++public class ItemStackRecipeChoice implements RecipeChoice {
++
++ protected final List<ItemStack> choices = new ArrayList<>();
++
++ public ItemStackRecipeChoice(ItemStack choices) {
++ this.choices.add(choices);
++ }
++
++ public ItemStackRecipeChoice(List<ItemStack> choices) {
++ this.choices.addAll(choices);
++ }
++
++ @Override
++ public ItemStack getItemStack() {
++ return choices.isEmpty() ? null : choices.get(0);
++ }
++
++ @Override
++ public RecipeChoice clone() {
++ try {
++ ItemStackRecipeChoice clone = (ItemStackRecipeChoice) super.clone();
++ clone.choices.addAll(this.choices);
++ return clone;
++ } catch (CloneNotSupportedException ex) {
++ throw new AssertionError(ex);
++ }
++ }
++
++ @Override
++ public boolean test(ItemStack itemStack) {
++ for (ItemStack stack : choices) {
++ if (stack.isSimilar(itemStack)) {
++ return true;
++ }
++ }
++ return false;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0151-Implement-furnace-cook-speed-multiplier-API.patch b/Spigot-API-Patches-Unmapped/0151-Implement-furnace-cook-speed-multiplier-API.patch
new file mode 100644
index 0000000000..cb4250b87c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0151-Implement-furnace-cook-speed-multiplier-API.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tassu <[email protected]>
+Date: Thu, 13 Sep 2018 08:45:01 +0300
+Subject: [PATCH] Implement furnace cook speed multiplier API
+
+Signed-off-by: Tassu <[email protected]>
+
+diff --git a/src/main/java/org/bukkit/block/Furnace.java b/src/main/java/org/bukkit/block/Furnace.java
+index c5a8c96fa2204d6b4d2409b1bfc97697d39d964e..9063cf370a0fe66c2a27086e125f9111b77366ae 100644
+--- a/src/main/java/org/bukkit/block/Furnace.java
++++ b/src/main/java/org/bukkit/block/Furnace.java
+@@ -61,6 +61,26 @@ public interface Furnace extends Container {
+ */
+ public void setCookTimeTotal(int cookTimeTotal);
+
++ // Paper start
++ /**
++ * Gets the cook speed multiplier that this {@link Furnace} will cook
++ * compared to vanilla.
++ *
++ * @return the multiplier, a value between 0 and 200
++ */
++ public double getCookSpeedMultiplier();
++
++ /**
++ * Sets the speed multiplier that this {@link Furnace} will cook
++ * compared to vanilla.
++ *
++ * @param multiplier the multiplier to set, a value between 0 and 200
++ * @throws IllegalArgumentException if value is less than 0
++ * @throws IllegalArgumentException if value is more than 200
++ */
++ public void setCookSpeedMultiplier(double multiplier);
++ // Paper end
++
+ @NotNull
+ @Override
+ public FurnaceInventory getInventory();
diff --git a/Spigot-API-Patches-Unmapped/0152-PreSpawnerSpawnEvent.patch b/Spigot-API-Patches-Unmapped/0152-PreSpawnerSpawnEvent.patch
new file mode 100644
index 0000000000..70ec0d47bd
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0152-PreSpawnerSpawnEvent.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Phoenix616 <[email protected]>
+Date: Tue, 18 Sep 2018 23:50:10 +0100
+Subject: [PATCH] PreSpawnerSpawnEvent
+
+This adds a separate event before an entity is spawned by a spawner
+which contains the location of the spawner too similarly to how the
+SpawnerSpawnEvent gets called instead of the CreatureSpawnEvent for
+spawners.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/PreSpawnerSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/PreSpawnerSpawnEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..48cff063594840a07aeaf35513780e28ea019a76
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/PreSpawnerSpawnEvent.java
+@@ -0,0 +1,29 @@
++package com.destroystokyo.paper.event.entity;
++
++
++import com.google.common.base.Preconditions;
++import org.bukkit.Location;
++import org.bukkit.entity.EntityType;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called before an entity is spawned into a world by a spawner.
++ *
++ * This only includes the spawner's location and not the full BlockState snapshot for performance reasons.
++ * If you really need it you have to get the spawner yourself.
++ */
++
++public class PreSpawnerSpawnEvent extends PreCreatureSpawnEvent {
++ @NotNull private final Location spawnerLocation;
++
++ public PreSpawnerSpawnEvent(@NotNull Location location, @NotNull EntityType type, @NotNull Location spawnerLocation) {
++ super(location, type, CreatureSpawnEvent.SpawnReason.SPAWNER);
++ this.spawnerLocation = Preconditions.checkNotNull(spawnerLocation, "Spawner location may not be null");
++ }
++
++ @NotNull
++ public Location getSpawnerLocation() {
++ return spawnerLocation;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0153-Material-API-additions.patch b/Spigot-API-Patches-Unmapped/0153-Material-API-additions.patch
new file mode 100644
index 0000000000..d19f497e71
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0153-Material-API-additions.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 6 Oct 2018 21:14:29 -0400
+Subject: [PATCH] Material API additions
+
+
+diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
+index e4d0dc26fb2f2c4435e11b3460d8c93d6a6fc47f..2b53e68e96ea346a6f2b5cadcf9f81b2c231c408 100644
+--- a/src/main/java/org/bukkit/Material.java
++++ b/src/main/java/org/bukkit/Material.java
+@@ -87,6 +87,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * An enum of all material IDs accepted by the official server and client
+ */
++@SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"}) // Paper
+ public enum Material implements Keyed {
+ //<editor-fold desc="Materials" defaultstate="collapsed">
+ AIR(9648, 0),
+@@ -3563,6 +3564,22 @@ public enum Material implements Keyed {
+ }
+ }
+
++ // Paper start
++
++ /**
++ * @return If the type is either AIR, CAVE_AIR or VOID_AIR
++ */
++ public boolean isEmpty() {
++ switch (this) {
++ case AIR:
++ case CAVE_AIR:
++ case VOID_AIR:
++ return true;
++ }
++ return false;
++ }
++ // Paper end
++
+ /**
+ * Do not use for any reason.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0154-Add-Material-Tags.patch b/Spigot-API-Patches-Unmapped/0154-Add-Material-Tags.patch
new file mode 100644
index 0000000000..7fa999bf70
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0154-Add-Material-Tags.patch
@@ -0,0 +1,1044 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 17 Jul 2018 01:27:15 -0400
+Subject: [PATCH] Add Material Tags
+
+This adds a bunch of useful and missing Tags to be able to identify items that
+are related to each other by a trait.
+
+Co-authored-by: Jake Potrebic <[email protected]>
+
+diff --git a/src/main/java/com/destroystokyo/paper/MaterialSetTag.java b/src/main/java/com/destroystokyo/paper/MaterialSetTag.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a02a02aa0c87e0f0ed9e509e4dcab01565b3d92a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/MaterialSetTag.java
+@@ -0,0 +1,97 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ */
++
++package com.destroystokyo.paper;
++
++import com.google.common.collect.Lists;
++import io.papermc.paper.tag.BaseTag;
++import org.bukkit.Material;
++import org.bukkit.NamespacedKey;
++import org.bukkit.block.Block;
++import org.bukkit.block.BlockState;
++import org.bukkit.block.data.BlockData;
++import org.bukkit.inventory.ItemStack;
++
++import java.util.Collection;
++import java.util.Set;
++import java.util.function.Predicate;
++import java.util.stream.Collectors;
++import java.util.stream.Stream;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class MaterialSetTag extends BaseTag<Material, MaterialSetTag> {
++
++ /**
++ * @deprecated Use NamespacedKey version of constructor
++ */
++ @Deprecated
++ public MaterialSetTag(@NotNull Predicate<Material> filter) {
++ this(null, Stream.of(Material.values()).filter(filter).collect(Collectors.toList()));
++ }
++
++ /**
++ * @deprecated Use NamespacedKey version of constructor
++ */
++ @Deprecated
++ public MaterialSetTag(@NotNull Collection<Material> materials) {
++ this(null, materials);
++ }
++
++ /**
++ * @deprecated Use NamespacedKey version of constructor
++ */
++ @Deprecated
++ public MaterialSetTag(@NotNull Material... materials) {
++ this(null, materials);
++ }
++
++ public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Predicate<Material> filter) {
++ this(key, Stream.of(Material.values()).filter(filter).collect(Collectors.toList()));
++ }
++
++ public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Material... materials) {
++ this(key, Lists.newArrayList(materials));
++ }
++
++ public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection<Material> materials) {
++ this(key != null ? key : NamespacedKey.randomKey(), materials, ((Predicate<Material>) Material::isLegacy).negate());
++ }
++
++ public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection<Material> materials, @NotNull Predicate<Material>...globalPredicates) {
++ super(Material.class, key != null ? key : NamespacedKey.randomKey(), materials, globalPredicates);
++ }
++
++ @NotNull
++ @Override
++ protected Set<Material> getAllPossibleValues() {
++ return Stream.of(Material.values()).collect(Collectors.toSet());
++ }
++
++ @Override
++ @NotNull
++ protected String getName(@NotNull Material value) {
++ return value.name();
++ }
++
++ public boolean isTagged(@NotNull BlockData block) {
++ return isTagged(block.getMaterial());
++ }
++
++ public boolean isTagged(@NotNull BlockState block) {
++ return isTagged(block.getType());
++ }
++
++ public boolean isTagged(@NotNull Block block) {
++ return isTagged(block.getType());
++ }
++
++ public boolean isTagged(@NotNull ItemStack item) {
++ return isTagged(item.getType());
++ }
++
++ public boolean isTagged(@NotNull Material material) {
++ return this.tagged.contains(material);
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/MaterialTags.java b/src/main/java/com/destroystokyo/paper/MaterialTags.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2b5a61a8afa8f73006676cdcb8a34640b43de1c3
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/MaterialTags.java
+@@ -0,0 +1,558 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++package com.destroystokyo.paper;
++
++import org.bukkit.Material;
++import org.bukkit.NamespacedKey;
++import org.bukkit.Tag;
++
++/**
++ * Represents a collection tags to identify materials that share common properties.
++ * Will map to minecraft for missing tags, as well as custom ones that may be useful.
++ */
++@SuppressWarnings({"NonFinalUtilityClass", "unused", "WeakerAccess"})
++public class MaterialTags {
++
++ private static NamespacedKey keyFor(String key) {
++ //noinspection deprecation
++ return new NamespacedKey("paper", key + "_settag");
++ }
++ public static final MaterialSetTag ARROWS = new MaterialSetTag(keyFor("arrows"))
++ .endsWith("ARROW")
++ .ensureSize("ARROWS", 3);
++
++ /**
++ * Covers all colors of beds.
++ */
++ public static final MaterialSetTag BEDS = new MaterialSetTag(keyFor("beds"))
++ .endsWith("_BED")
++ .ensureSize("BEDS", 16);
++
++ /**
++ * Covers all bucket items.
++ */
++ public static final MaterialSetTag BUCKETS = new MaterialSetTag(keyFor("buckets"))
++ .endsWith("BUCKET")
++ .ensureSize("BUCKETS", 8);
++
++ /**
++ * Covers coal and charcoal.
++ */
++ public static final MaterialSetTag COALS = new MaterialSetTag(keyFor("coals"))
++ .add(Material.COAL, Material.CHARCOAL);
++
++ /**
++ * Covers both cobblestone wall variants.
++ */
++ public static final MaterialSetTag COBBLESTONE_WALLS = new MaterialSetTag(keyFor("cobblestone_walls"))
++ .endsWith("COBBLESTONE_WALL")
++ .ensureSize("COBBLESTONE_WALLS", 2);
++
++ /**
++ * Covers both cobblestone and mossy Cobblestone.
++ */
++ public static final MaterialSetTag COBBLESTONES = new MaterialSetTag(keyFor("cobblestones"))
++ .add(Material.COBBLESTONE, Material.MOSSY_COBBLESTONE);
++
++ /**
++ * Covers all colors of concrete.
++ */
++ public static final MaterialSetTag CONCRETES = new MaterialSetTag(keyFor("concretes"))
++ .endsWith("_CONCRETE")
++ .ensureSize("CONCRETES", 16);
++
++ /**
++ * Covers all colors of concrete powder.
++ */
++ public static final MaterialSetTag CONCRETE_POWDER = new MaterialSetTag(keyFor("concrete_powder"))
++ .endsWith("_CONCRETE_POWDER")
++ .ensureSize("CONCRETE_POWDER", 16);
++
++ /**
++ * Covers the two types of cooked fish.
++ */
++ public static final MaterialSetTag COOKED_FISH = new MaterialSetTag(keyFor("cooked_fish"))
++ .add(Material.COOKED_COD, Material.COOKED_SALMON);
++
++ /**
++ * Covers all variants of doors.
++ */
++ public static final MaterialSetTag DOORS = new MaterialSetTag(keyFor("doors"))
++ .endsWith("_DOOR")
++ .ensureSize("DOORS", 9);
++
++ /**
++ * Covers all dyes.
++ */
++ public static final MaterialSetTag DYES = new MaterialSetTag(keyFor("dyes"))
++ .endsWith("_DYE")
++ .ensureSize("DYES", 16);
++
++ /**
++ * Covers all variants of gates.
++ */
++ public static final MaterialSetTag FENCE_GATES = new MaterialSetTag(keyFor("fence_gates"))
++ .endsWith("_GATE")
++ .ensureSize("FENCE_GATES", 8);
++
++ /**
++ * Covers all variants of fences.
++ */
++ public static final MaterialSetTag FENCES = new MaterialSetTag(keyFor("fences"))
++ .endsWith("_FENCE")
++ .ensureSize("FENCES", 9);
++
++ /**
++ * Covers all variants of fish buckets.
++ */
++ public static final MaterialSetTag FISH_BUCKETS = new MaterialSetTag(keyFor("fish_buckets"))
++ .add(Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET);
++
++ /**
++ * Covers the non-colored glass and 16 stained glass (not panes).
++ */
++ public static final MaterialSetTag GLASS = new MaterialSetTag(keyFor("glass"))
++ .endsWith("_GLASS")
++ .add(Material.GLASS)
++ .ensureSize("GLASS", 17);
++
++ /**
++ * Covers the non-colored glass panes and stained glass panes (panes only).
++ */
++ public static final MaterialSetTag GLASS_PANES = new MaterialSetTag(keyFor("glass_panes"))
++ .endsWith("GLASS_PANE")
++ .ensureSize("GLASS_PANES", 17);
++
++ /**
++ * Covers all glazed terracotta blocks.
++ */
++ public static final MaterialSetTag GLAZED_TERRACOTTA = new MaterialSetTag(keyFor("glazed_terracotta"))
++ .endsWith("GLAZED_TERRACOTTA")
++ .ensureSize("GLAZED_TERRACOTTA", 16);
++
++ /**
++ * Covers the colors of stained terracotta.
++ */
++ public static final MaterialSetTag STAINED_TERRACOTTA = new MaterialSetTag(keyFor("stained_terracotta"))
++ .endsWith("TERRACOTTA")
++ .not(Material.TERRACOTTA)
++ .notEndsWith("GLAZED_TERRACOTTA")
++ .ensureSize("STAINED_TERRACOTTA", 16);
++
++ /**
++ * Covers terracotta along with the stained variants.
++ */
++ public static final MaterialSetTag TERRACOTTA = new MaterialSetTag(keyFor("terracotta"))
++ .endsWith("TERRACOTTA")
++ .ensureSize("TERRACOTTA", 33);
++
++ /**
++ * Covers both golden apples.
++ */
++ public static final MaterialSetTag GOLDEN_APPLES = new MaterialSetTag(keyFor("golden_apples"))
++ .endsWith("GOLDEN_APPLE")
++ .ensureSize("GOLDEN_APPLES", 2);
++
++ /**
++ * Covers the variants of horse armor.
++ */
++ public static final MaterialSetTag HORSE_ARMORS = new MaterialSetTag(keyFor("horse_armors"))
++ .endsWith("_HORSE_ARMOR")
++ .ensureSize("HORSE_ARMORS", 4);
++
++ /**
++ * Covers the variants of infested blocks.
++ */
++ public static final MaterialSetTag INFESTED_BLOCKS = new MaterialSetTag(keyFor("infested_blocks"))
++ .startsWith("INFESTED_")
++ .ensureSize("INFESTED_BLOCKS", 6);
++
++ /**
++ * Covers the variants of mushroom blocks.
++ */
++ public static final MaterialSetTag MUSHROOM_BLOCKS = new MaterialSetTag(keyFor("mushroom_blocks"))
++ .endsWith("MUSHROOM_BLOCK")
++ .add(Material.MUSHROOM_STEM)
++ .ensureSize("MUSHROOM_BLOCKS", 3);
++
++ /**
++ * Covers all mushrooms.
++ */
++ public static final MaterialSetTag MUSHROOMS = new MaterialSetTag(keyFor("mushrooms"))
++ .add(Material.BROWN_MUSHROOM, Material.RED_MUSHROOM);
++
++ /**
++ * Covers all music disc items.
++ */
++ public static final MaterialSetTag MUSIC_DISCS = new MaterialSetTag(keyFor("music_discs"))
++ .startsWith("MUSIC_DISC_");
++
++ /**
++ * Covers all ores.
++ */
++ public static final MaterialSetTag ORES = new MaterialSetTag(keyFor("ores"))
++ .add(Material.ANCIENT_DEBRIS)
++ .endsWith("_ORE")
++ .ensureSize("ORES", 10);
++
++ /**
++ * Covers all piston typed items and blocks including the piston head and moving piston.
++ */
++ public static final MaterialSetTag PISTONS = new MaterialSetTag(keyFor("pistons"))
++ .contains("PISTON")
++ .ensureSize("PISTONS", 4);
++
++ /**
++ * Covers all potato items.
++ */
++ public static final MaterialSetTag POTATOES = new MaterialSetTag(keyFor("potatoes"))
++ .endsWith("POTATO")
++ .ensureSize("POTATOES", 3);
++
++ /**
++ * Covers all wooden pressure plates and the weighted pressure plates and the stone pressure plate.
++ */
++ public static final MaterialSetTag PRESSURE_PLATES = new MaterialSetTag(keyFor("pressure_plates"))
++ .endsWith("_PRESSURE_PLATE")
++ .ensureSize("PRESSURE_PLATES", 12);
++
++ /**
++ * Covers the variants of prismarine blocks.
++ */
++ public static final MaterialSetTag PRISMARINE = new MaterialSetTag(keyFor("prismarine"))
++ .add(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE);
++
++ /**
++ * Covers the variants of prismarine slabs.
++ */
++ public static final MaterialSetTag PRISMARINE_SLABS = new MaterialSetTag(keyFor("prismarine_slabs"))
++ .add(Material.PRISMARINE_SLAB, Material.PRISMARINE_BRICK_SLAB, Material.DARK_PRISMARINE_SLAB);
++
++ /**
++ * Covers the variants of prismarine stairs.
++ */
++ public static final MaterialSetTag PRISMARINE_STAIRS = new MaterialSetTag(keyFor("prismarine_stairs"))
++ .add(Material.PRISMARINE_STAIRS, Material.PRISMARINE_BRICK_STAIRS, Material.DARK_PRISMARINE_STAIRS);
++
++ /**
++ * Covers the variants of pumpkins.
++ */
++ public static final MaterialSetTag PUMPKINS = new MaterialSetTag(keyFor("pumpkins"))
++ .add(Material.CARVED_PUMPKIN, Material.JACK_O_LANTERN, Material.PUMPKIN);
++
++ /**
++ * Covers the variants of quartz blocks.
++ */
++ public static final MaterialSetTag QUARTZ_BLOCKS = new MaterialSetTag(keyFor("quartz_blocks"))
++ .add(Material.QUARTZ_BLOCK, Material.QUARTZ_PILLAR, Material.CHISELED_QUARTZ_BLOCK, Material.SMOOTH_QUARTZ);
++
++ /**
++ * Covers all uncooked fish items.
++ */
++ public static final MaterialSetTag RAW_FISH = new MaterialSetTag(keyFor("raw_fish"))
++ .add(Material.COD, Material.PUFFERFISH, Material.SALMON, Material.TROPICAL_FISH);
++
++ /**
++ * Covers the variants of red sandstone blocks.
++ */
++ public static final MaterialSetTag RED_SANDSTONES = new MaterialSetTag(keyFor("red_sandstones"))
++ .endsWith("RED_SANDSTONE")
++ .ensureSize("RED_SANDSTONES", 4);
++
++ /**
++ * Covers the variants of sandstone blocks.
++ */
++ public static final MaterialSetTag SANDSTONES = new MaterialSetTag(keyFor("sandstones"))
++ .add(Material.SANDSTONE, Material.CHISELED_SANDSTONE, Material.CUT_SANDSTONE, Material.SMOOTH_SANDSTONE);
++
++ /**
++ * Covers sponge and wet sponge.
++ */
++ public static final MaterialSetTag SPONGES = new MaterialSetTag(keyFor("sponges"))
++ .endsWith("SPONGE")
++ .ensureSize("SPONGES", 2);
++
++ /**
++ * Covers the non-colored and colored shulker boxes.
++ */
++ public static final MaterialSetTag SHULKER_BOXES = new MaterialSetTag(keyFor("shulker_boxes"))
++ .endsWith("SHULKER_BOX")
++ .ensureSize("SHULKER_BOXES", 17);
++
++ /**
++ * Covers zombie, creeper, skeleton, dragon, and player heads.
++ */
++ public static final MaterialSetTag SKULLS = new MaterialSetTag(keyFor("skulls"))
++ .endsWith("_HEAD")
++ .endsWith("_SKULL")
++ .not(Material.PISTON_HEAD)
++ .ensureSize("SKULLS", 12);
++
++ /**
++ * Covers all spawn egg items.
++ */
++ public static final MaterialSetTag SPAWN_EGGS = new MaterialSetTag(keyFor("spawn_eggs"))
++ .endsWith("_SPAWN_EGG")
++ .ensureSize("SPAWN_EGGS", 64);
++
++ /**
++ * Covers all colors of stained glass.
++ */
++ public static final MaterialSetTag STAINED_GLASS = new MaterialSetTag(keyFor("stained_glass"))
++ .endsWith("_STAINED_GLASS")
++ .ensureSize("STAINED_GLASS", 16);
++
++ /**
++ * Covers all colors of stained glass panes.
++ */
++ public static final MaterialSetTag STAINED_GLASS_PANES = new MaterialSetTag(keyFor("stained_glass_panes"))
++ .endsWith("STAINED_GLASS_PANE")
++ .ensureSize("STAINED_GLASS_PANES", 16);
++
++ /**
++ * Covers all variants of trapdoors.
++ */
++ public static final MaterialSetTag TRAPDOORS = new MaterialSetTag(keyFor("trapdoors"))
++ .endsWith("_TRAPDOOR")
++ .ensureSize("TRAPDOORS", 9);
++
++ /**
++ * Covers all wood variants of doors.
++ */
++ public static final MaterialSetTag WOODEN_DOORS = new MaterialSetTag(keyFor("wooden_doors"))
++ .endsWith("_DOOR")
++ .not(Material.IRON_DOOR)
++ .ensureSize("WOODEN_DOORS", 8);
++
++ /**
++ * Covers all wood variants of fences.
++ */
++ public static final MaterialSetTag WOODEN_FENCES = new MaterialSetTag(keyFor("wooden_fences"))
++ .endsWith("_FENCE")
++ .not(Material.NETHER_BRICK_FENCE)
++ .ensureSize("WOODEN_FENCES", 8);
++
++ /**
++ * Covers all wood variants of trapdoors.
++ */
++ public static final MaterialSetTag WOODEN_TRAPDOORS = new MaterialSetTag(keyFor("wooden_trapdoors"))
++ .endsWith("_TRAPDOOR")
++ .not(Material.IRON_TRAPDOOR)
++ .ensureSize("WOODEN_TRAPDOORS", 8);
++
++ /**
++ * Covers the wood variants of gates.
++ */
++ public static final MaterialSetTag WOODEN_GATES = new MaterialSetTag(keyFor("wooden_gates"))
++ .endsWith("_GATE")
++ .ensureSize("WOODEN_GATES", 8);
++
++ /**
++ * Covers the variants of purpur.
++ */
++ public static final MaterialSetTag PURPUR = new MaterialSetTag(keyFor("purpur"))
++ .startsWith("PURPUR_")
++ .ensureSize("PURPUR", 4);
++
++ /**
++ * Covers the variants of signs.
++ */
++ public static final MaterialSetTag SIGNS = new MaterialSetTag(keyFor("signs"))
++ .endsWith("_SIGN")
++ .ensureSize("SIGNS", 16);
++
++ /**
++ * Covers the variants of a regular torch.
++ */
++ public static final MaterialSetTag TORCH = new MaterialSetTag(keyFor("torch"))
++ .add(Material.TORCH, Material.WALL_TORCH)
++ .ensureSize("TORCH", 2);
++
++ /**
++ * Covers the variants of a redstone torch.
++ */
++ public static final MaterialSetTag REDSTONE_TORCH = new MaterialSetTag(keyFor("restone_torch"))
++ .add(Material.REDSTONE_TORCH, Material.REDSTONE_WALL_TORCH)
++ .ensureSize("REDSTONE_TORCH", 2);
++
++ /**
++ * Covers the variants of a soul torch.
++ */
++ public static final MaterialSetTag SOUL_TORCH = new MaterialSetTag(keyFor("soul_torch"))
++ .add(Material.SOUL_TORCH, Material.SOUL_WALL_TORCH)
++ .ensureSize("SOUL_TORCH", 2);
++
++ /**
++ * Covers the variants of torches.
++ */
++ public static final MaterialSetTag TORCHES = new MaterialSetTag(keyFor("torches"))
++ .add(TORCH, REDSTONE_TORCH, SOUL_TORCH)
++ .ensureSize("TORCHES", 6);
++
++ /**
++ * Covers the variants of lanterns.
++ */
++ public static final MaterialSetTag LANTERNS = new MaterialSetTag(keyFor("lanterns"))
++ .add(Material.LANTERN, Material.SOUL_LANTERN)
++ .ensureSize("LANTERNS", 2);
++
++ /**
++ * Covers the variants of rails.
++ */
++ public static final MaterialSetTag RAILS = new MaterialSetTag(keyFor("rails"))
++ .endsWith("RAIL")
++ .ensureSize("RAILS", 4);
++
++ /**
++ * Covers the variants of swords.
++ */
++ public static final MaterialSetTag SWORDS = new MaterialSetTag(keyFor("swords"))
++ .endsWith("_SWORD")
++ .ensureSize("SWORDS", 6);
++
++ /**
++ * Covers the variants of shovels.
++ */
++ public static final MaterialSetTag SHOVELS = new MaterialSetTag(keyFor("shovels"))
++ .endsWith("_SHOVEL")
++ .ensureSize("SHOVELS", 6);
++
++ /**
++ * Covers the variants of pickaxes.
++ */
++ public static final MaterialSetTag PICKAXES = new MaterialSetTag(keyFor("pickaxes"))
++ .endsWith("_PICKAXE")
++ .ensureSize("PICKAXES", 6);
++
++ /**
++ * Covers the variants of axes.
++ */
++ public static final MaterialSetTag AXES = new MaterialSetTag(keyFor("axes"))
++ .endsWith("_AXE")
++ .ensureSize("AXES", 6);
++
++ /**
++ * Covers the variants of hoes.
++ */
++ public static final MaterialSetTag HOES = new MaterialSetTag(keyFor("hoes"))
++ .endsWith("_HOE")
++ .ensureSize("HOES", 6);
++
++ /**
++ * Covers the variants of helmets.
++ */
++ public static final MaterialSetTag HELMETS = new MaterialSetTag(keyFor("helmets"))
++ .endsWith("_HELMET")
++ .ensureSize("HELMETS", 7);
++
++ /**
++ * Covers the variants of items that can be equipped in the helmet slot.
++ */
++ public static final MaterialSetTag HEAD_EQUIPPABLE = new MaterialSetTag(keyFor("head_equippable"))
++ .endsWith("_HELMET")
++ .add(SKULLS)
++ .add(Material.CARVED_PUMPKIN)
++ .ensureSize("HEAD_EQUIPPABLE", 20);
++
++ /**
++ * Covers the variants of chestplate.
++ */
++ public static final MaterialSetTag CHESTPLATES = new MaterialSetTag(keyFor("chestplates"))
++ .endsWith("_CHESTPLATE")
++ .ensureSize("CHESTPLATES", 6);
++
++ /**
++ * Covers the variants of items that can be equipped in the chest slot.
++ */
++ public static final MaterialSetTag CHEST_EQUIPPABLE = new MaterialSetTag(keyFor("chest_equippable"))
++ .endsWith("_CHESTPLATE")
++ .add(Material.ELYTRA)
++ .ensureSize("CHEST_EQUIPPABLE", 7);
++
++ /**
++ * Covers the variants of leggings.
++ */
++ public static final MaterialSetTag LEGGINGS = new MaterialSetTag(keyFor("leggings"))
++ .endsWith("_LEGGINGS")
++ .ensureSize("LEGGINGS", 6);
++
++ /**
++ * Covers the variants of boots.
++ */
++ public static final MaterialSetTag BOOTS = new MaterialSetTag(keyFor("boots"))
++ .endsWith("_BOOTS")
++ .ensureSize("BOOTS", 6);
++
++ /**
++ * Covers the variants of bows.
++ */
++ public static final MaterialSetTag BOWS = new MaterialSetTag(keyFor("bows"))
++ .add(Material.BOW)
++ .add(Material.CROSSBOW)
++ .ensureSize("BOWS", 2);
++
++ /**
++ * Covers the variants of player-throwable projectiles (not requiring a bow or any other "assistance").
++ */
++ public static final MaterialSetTag THROWABLE_PROJECTILES = new MaterialSetTag(keyFor("throwable_projectiles"))
++ .add(Material.EGG, Material.SNOWBALL, Material.SPLASH_POTION, Material.TRIDENT, Material.ENDER_PEARL, Material.EXPERIENCE_BOTTLE, Material.FIREWORK_ROCKET);
++
++ /**
++ * Covers materials that can be colored, such as wool, shulker boxes, stained glass etc.
++ */
++ @SuppressWarnings("unchecked")
++ public static final MaterialSetTag COLORABLE = new MaterialSetTag(keyFor("colorable"))
++ .add(Tag.WOOL, Tag.CARPETS).add(SHULKER_BOXES, STAINED_GLASS, STAINED_GLASS_PANES, CONCRETES, BEDS);
++ //.ensureSize("COLORABLE", 81); unit test don't have the vanilla item tags, so counts don't line up for real
++
++ /**
++ * Covers the variants of coral.
++ */
++ public static final MaterialSetTag CORAL = new MaterialSetTag(keyFor("coral"))
++ .endsWith("_CORAL")
++ .ensureSize("CORAL", 10);
++
++ /**
++ * Covers the variants of coral fans.
++ */
++ public static final MaterialSetTag CORAL_FANS = new MaterialSetTag(keyFor("coral_fans"))
++ .endsWith("_CORAL_FAN")
++ .endsWith("_CORAL_WALL_FAN")
++ .ensureSize("CORAL_FANS", 20);
++
++ /**
++ * Covers the variants of coral blocks.
++ */
++ public static final MaterialSetTag CORAL_BLOCKS = new MaterialSetTag(keyFor("coral_blocks"))
++ .endsWith("_CORAL_BLOCK")
++ .ensureSize("CORAL_BLOCKS", 10);
++
++ /**
++ * Covers all items that can be enchanted from the enchantment table or anvil.
++ */
++ public static final MaterialSetTag ENCHANTABLE = new MaterialSetTag(keyFor("enchantable"))
++ .add(PICKAXES, SWORDS, SHOVELS, AXES, HOES, HELMETS, CHEST_EQUIPPABLE, LEGGINGS, BOOTS, BOWS)
++ .add(Material.TRIDENT, Material.SHIELD, Material.FISHING_ROD, Material.SHEARS, Material.FLINT_AND_STEEL, Material.CARROT_ON_A_STICK, Material.WARPED_FUNGUS_ON_A_STICK)
++ .ensureSize("ENCHANTABLE", 65);
++}
+diff --git a/src/main/java/io/papermc/paper/tag/BaseTag.java b/src/main/java/io/papermc/paper/tag/BaseTag.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4b8552e4e4c07b197fa9431fa911535b0222561e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/tag/BaseTag.java
+@@ -0,0 +1,160 @@
++package io.papermc.paper.tag;
++
++import com.google.common.collect.Lists;
++import org.bukkit.Keyed;
++import org.bukkit.NamespacedKey;
++import org.bukkit.Tag;
++import org.jetbrains.annotations.NotNull;
++
++import java.util.Collection;
++import java.util.EnumSet;
++import java.util.HashSet;
++import java.util.List;
++import java.util.Set;
++import java.util.function.Predicate;
++import java.util.stream.Collectors;
++
++public abstract class BaseTag<T extends Keyed, C extends BaseTag<T, C>> implements Tag<T> {
++
++ protected final NamespacedKey key;
++ protected final Set<T> tagged;
++ private final List<Predicate<T>> globalPredicates;
++
++ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Predicate<T> filter) {
++ this(clazz, key);
++ add(filter);
++ }
++
++ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull T...values) {
++ this(clazz, key, Lists.newArrayList(values));
++ }
++
++ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Collection<T> values) {
++ this(clazz, key, values, o -> true);
++ }
++
++ public BaseTag(@NotNull Class<T> clazz, @NotNull NamespacedKey key, @NotNull Collection<T> values, @NotNull Predicate<T>... globalPredicates) {
++ this.key = key != null ? key : NamespacedKey.randomKey();
++ this.tagged = clazz.isEnum() ? createEnumSet(clazz) : new HashSet<>();
++ this.tagged.addAll(values);
++ this.globalPredicates = Lists.newArrayList(globalPredicates);
++ }
++
++ private <E> Set<E> createEnumSet(Class<E> enumClass) {
++ assert enumClass.isEnum();
++ return (Set<E>) EnumSet.noneOf((Class<Enum>) enumClass);
++ }
++
++ @NotNull
++ @Override
++ public NamespacedKey getKey() {
++ return key;
++ }
++
++ @NotNull
++ @Override
++ public Set<T> getValues() {
++ return tagged;
++ }
++
++ @Override
++ public boolean isTagged(@NotNull T item) {
++ return tagged.contains(item);
++ }
++
++ @NotNull
++ public C add(@NotNull Tag<T>...tags) {
++ for (Tag<T> tag : tags) {
++ add(tag.getValues());
++ }
++ return (C) this;
++ }
++
++ @NotNull
++ public C add(@NotNull T...values) {
++ this.tagged.addAll(Lists.newArrayList(values));
++ return (C) this;
++ }
++
++ @NotNull
++ public C add(@NotNull Collection<T> collection) {
++ this.tagged.addAll(collection);
++ return (C) this;
++ }
++
++ @NotNull
++ public C add(@NotNull Predicate<T> filter) {
++ return add(getAllPossibleValues().stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).filter(filter).collect(Collectors.toSet()));
++ }
++
++ @NotNull
++ public C contains(@NotNull String with) {
++ return add(value -> getName(value).contains(with));
++ }
++
++ @NotNull
++ public C endsWith(@NotNull String with) {
++ return add(value -> getName(value).endsWith(with));
++ }
++
++ @NotNull
++ public C startsWith(@NotNull String with) {
++ return add(value -> getName(value).startsWith(with));
++ }
++
++ @NotNull
++ public C not(@NotNull Tag<T>...tags) {
++ for (Tag<T> tag : tags) {
++ not(tag.getValues());
++ }
++ return (C) this;
++ }
++
++ @NotNull
++ public C not(@NotNull T...values) {
++ this.tagged.removeAll(Lists.newArrayList(values));
++ return (C) this;
++ }
++
++ @NotNull
++ public C not(@NotNull Collection<T> values) {
++ this.tagged.removeAll(values);
++ return (C) this;
++ }
++
++ @NotNull
++ public C not(@NotNull Predicate<T> filter) {
++ not(getAllPossibleValues().stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).filter(filter).collect(Collectors.toSet()));
++ return (C) this;
++ }
++
++ @NotNull
++ public C notContains(@NotNull String with) {
++ return not(value -> getName(value).contains(with));
++ }
++
++ @NotNull
++ public C notEndsWith(@NotNull String with) {
++ return not(value -> getName(value).endsWith(with));
++ }
++
++ @NotNull
++ public C notStartsWith(@NotNull String with) {
++ return not(value -> getName(value).startsWith(with));
++ }
++
++ @NotNull
++ public C ensureSize(@NotNull String label, int size) {
++ long actual = this.tagged.stream().filter(globalPredicates.stream().reduce(Predicate::or).orElse(t -> true)).count();
++ if (size != actual) {
++ throw new IllegalStateException(key.toString() + ": " + label + " - Expected " + size + " values, got " + actual);
++ }
++ return (C) this;
++ }
++
++ @NotNull
++ protected abstract Set<T> getAllPossibleValues();
++
++ @NotNull
++ protected abstract String getName(@NotNull T value);
++}
+diff --git a/src/main/java/io/papermc/paper/tag/EntitySetTag.java b/src/main/java/io/papermc/paper/tag/EntitySetTag.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c89c4619aaf388197834d98eb95af2f1e93db871
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/tag/EntitySetTag.java
+@@ -0,0 +1,42 @@
++package io.papermc.paper.tag;
++
++import org.bukkit.NamespacedKey;
++import org.bukkit.entity.EntityType;
++import org.jetbrains.annotations.NotNull;
++
++import java.util.Collection;
++import java.util.Set;
++import java.util.function.Predicate;
++import java.util.stream.Collectors;
++import java.util.stream.Stream;
++
++public class EntitySetTag extends BaseTag<EntityType, EntitySetTag> {
++
++ public EntitySetTag(@NotNull NamespacedKey key, @NotNull Predicate<EntityType> filter) {
++ super(EntityType.class, key, filter);
++ }
++
++ public EntitySetTag(@NotNull NamespacedKey key, @NotNull EntityType... values) {
++ super(EntityType.class, key, values);
++ }
++
++ public EntitySetTag(@NotNull NamespacedKey key, @NotNull Collection<EntityType> values) {
++ super(EntityType.class, key, values);
++ }
++
++ public EntitySetTag(@NotNull NamespacedKey key, @NotNull Collection<EntityType> values, @NotNull Predicate<EntityType>... globalPredicates) {
++ super(EntityType.class, key, values, globalPredicates);
++ }
++
++ @NotNull
++ @Override
++ protected Set<EntityType> getAllPossibleValues() {
++ return Stream.of(EntityType.values()).collect(Collectors.toSet());
++ }
++
++ @NotNull
++ @Override
++ protected String getName(@NotNull EntityType value) {
++ return value.name();
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/tag/EntityTags.java b/src/main/java/io/papermc/paper/tag/EntityTags.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9266c9d77e2eef7cd717dc729834a190f1fc7c1d
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/tag/EntityTags.java
+@@ -0,0 +1,50 @@
++package io.papermc.paper.tag;
++
++import org.bukkit.NamespacedKey;
++
++import static org.bukkit.entity.EntityType.*;
++
++public class EntityTags {
++
++ private static NamespacedKey keyFor(String key) {
++ //noinspection deprecation
++ return new NamespacedKey("paper", key + "_settag");
++ }
++
++ /**
++ * Covers undead mobs
++ * @see <a href="https://minecraft.gamepedia.com/Mob#Undead_mobs">https://minecraft.gamepedia.com/Mob#Undead_mobs</a>
++ */
++ public static final EntitySetTag UNDEADS = new EntitySetTag(keyFor("undeads"))
++ .add(DROWNED, HUSK, PHANTOM, SKELETON, SKELETON_HORSE, STRAY, WITHER, WITHER_SKELETON, ZOGLIN, ZOMBIE, ZOMBIE_HORSE, ZOMBIE_VILLAGER, ZOMBIFIED_PIGLIN)
++ .ensureSize("UNDEADS", 13);
++
++ /**
++ * Covers all horses
++ */
++ public static final EntitySetTag HORSES = new EntitySetTag(keyFor("horses"))
++ .contains("HORSE")
++ .ensureSize("HORSES", 3);
++
++ /**
++ * Covers all minecarts
++ */
++ public static final EntitySetTag MINECARTS = new EntitySetTag(keyFor("minecarts"))
++ .contains("MINECART")
++ .ensureSize("MINECARTS", 7);
++
++ /**
++ * Covers mobs that split into smaller mobs
++ */
++ public static final EntitySetTag SPLITTING_MOBS = new EntitySetTag(keyFor("splitting_mobs"))
++ .add(SLIME, MAGMA_CUBE)
++ .ensureSize("SLIMES", 2);
++
++ /**
++ * Covers all water based mobs
++ * @see <a href="https://minecraft.gamepedia.com/Mob#Water-based_mobs">https://minecraft.gamepedia.com/Mob#Water-based_mobs</a>
++ */
++ public static final EntitySetTag WATER_BASED = new EntitySetTag(keyFor("water_based"))
++ .add(DOLPHIN, SQUID, GUARDIAN, ELDER_GUARDIAN, TURTLE, COD, SALMON, PUFFERFISH, TROPICAL_FISH)
++ .ensureSize("WATER_BASED", 9);
++}
+diff --git a/src/main/java/org/bukkit/Tag.java b/src/main/java/org/bukkit/Tag.java
+index aacbfadc91f580cc667603c8165eacbadee38cca..3c2a6a2167eab43097f5d6ccf1550e12795fc0b6 100644
+--- a/src/main/java/org/bukkit/Tag.java
++++ b/src/main/java/org/bukkit/Tag.java
+@@ -10,6 +10,10 @@ import org.jetbrains.annotations.NotNull;
+ * Note that whilst all tags defined within this interface must be present in
+ * implementations, their existence is not guaranteed across future versions.
+ *
++ * <p>Custom tags defined by Paper are not present (as constants) in this class.
++ * To access them please refer to {@link com.destroystokyo.paper.MaterialTags}
++ * and {@link io.papermc.paper.tag.EntityTags}.</p>
++ *
+ * @param <T> the type of things grouped by this tag
+ */
+ public interface Tag<T extends Keyed> extends Keyed {
+diff --git a/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java b/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..328c51471dc12e81c1a1b643455337b3fef4d14a
+--- /dev/null
++++ b/src/test/java/com/destroystokyo/paper/MaterialTagsTest.java
+@@ -0,0 +1,25 @@
++/*
++ * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
++ */
++
++package com.destroystokyo.paper;
++
++import org.bukkit.Bukkit;
++import org.bukkit.TestServer;
++import org.junit.Test;
++
++import java.util.logging.Level;
++
++public class MaterialTagsTest {
++ @Test
++ public void testInitialize() {
++ try {
++ TestServer.getInstance();
++ MaterialTags.SHULKER_BOXES.getValues();
++ assert true;
++ } catch (Throwable e) {
++ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
++ assert false;
++ }
++ }
++}
+diff --git a/src/test/java/io/papermc/paper/EntityTagsTest.java b/src/test/java/io/papermc/paper/EntityTagsTest.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..06bb9d1180361d3d00c699796bbacbce5bef2177
+--- /dev/null
++++ b/src/test/java/io/papermc/paper/EntityTagsTest.java
+@@ -0,0 +1,24 @@
++package io.papermc.paper;
++
++import com.destroystokyo.paper.MaterialTags;
++import io.papermc.paper.tag.EntityTags;
++import org.bukkit.Bukkit;
++import org.bukkit.TestServer;
++import org.junit.Test;
++
++import java.util.logging.Level;
++
++public class EntityTagsTest {
++
++ @Test
++ public void testInitialize() {
++ try {
++ TestServer.getInstance();
++ EntityTags.HORSES.getValues();
++ assert true;
++ } catch (Throwable e) {
++ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
++ assert false;
++ }
++ }
++}
+diff --git a/src/test/java/org/bukkit/TestServer.java b/src/test/java/org/bukkit/TestServer.java
+index 61993528e6975c38d82213e9b5caf996fe777328..5f9d348241210689eaf41a39ace5948e7a237b12 100644
+--- a/src/test/java/org/bukkit/TestServer.java
++++ b/src/test/java/org/bukkit/TestServer.java
+@@ -29,6 +29,16 @@ public final class TestServer implements InvocationHandler {
+ }
+ }
+ );
++ // Paper start
++ methodMap.put(
++ Server.class.getMethod("getTag", String.class, NamespacedKey.class, Class.class),
++ new MethodHandler() {
++ public Object handle(TestServer server, Object[] args) {
++ return new com.destroystokyo.paper.MaterialSetTag();
++ }
++ }
++ );
++ // Paper end
+ methodMap.put(
+ Server.class.getMethod("getPluginManager"),
+ new MethodHandler() {
diff --git a/Spigot-API-Patches-Unmapped/0155-Allow-setting-the-vex-s-summoner.patch b/Spigot-API-Patches-Unmapped/0155-Allow-setting-the-vex-s-summoner.patch
new file mode 100644
index 0000000000..68b33503be
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0155-Allow-setting-the-vex-s-summoner.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 6 Oct 2018 21:47:09 -0500
+Subject: [PATCH] Allow setting the vex's summoner
+
+
+diff --git a/src/main/java/org/bukkit/entity/Vex.java b/src/main/java/org/bukkit/entity/Vex.java
+index 6b61c4ab773c731fe5ae9577fd13e44707be9787..c34a3ea7b4d16817b4bee25d5c69787e22ec44d8 100644
+--- a/src/main/java/org/bukkit/entity/Vex.java
++++ b/src/main/java/org/bukkit/entity/Vex.java
+@@ -1,5 +1,7 @@
+ package org.bukkit.entity;
+
++import org.jetbrains.annotations.Nullable;
++
+ /**
+ * Represents a Vex.
+ */
+@@ -22,4 +24,21 @@ public interface Vex extends Monster {
+ * @param charging new state
+ */
+ void setCharging(boolean charging);
++
++ // Paper start
++ /**
++ * Get the Mob that summoned this vex
++ *
++ * @return Mob that summoned this vex
++ */
++ @Nullable
++ Mob getSummoner();
++
++ /**
++ * Set the summoner of this vex
++ *
++ * @param summoner New summoner
++ */
++ void setSummoner(@Nullable Mob summoner);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0156-Add-LivingEntity-getTargetEntity.patch b/Spigot-API-Patches-Unmapped/0156-Add-LivingEntity-getTargetEntity.patch
new file mode 100644
index 0000000000..cc02ae973f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0156-Add-LivingEntity-getTargetEntity.patch
@@ -0,0 +1,105 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 22 Sep 2018 00:32:53 -0500
+Subject: [PATCH] Add LivingEntity#getTargetEntity
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/entity/TargetEntityInfo.java b/src/main/java/com/destroystokyo/paper/entity/TargetEntityInfo.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f52644fab1522bdf83ff4f489e9805b274421094
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/TargetEntityInfo.java
+@@ -0,0 +1,38 @@
++package com.destroystokyo.paper.entity;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.util.Vector;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents information about a targeted entity
++ */
++public class TargetEntityInfo {
++ private final Entity entity;
++ private final Vector hitVec;
++
++ public TargetEntityInfo(@NotNull Entity entity, @NotNull Vector hitVec) {
++ this.entity = entity;
++ this.hitVec = hitVec;
++ }
++
++ /**
++ * Get the entity that is targeted
++ *
++ * @return Targeted entity
++ */
++ @NotNull
++ public Entity getEntity() {
++ return entity;
++ }
++
++ /**
++ * Get the position the entity is targeted at
++ *
++ * @return Targeted position
++ */
++ @NotNull
++ public Vector getHitVector() {
++ return hitVec;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 8fe7ccf12339355554835542cc1068d9f6c3a435..561db9d594633e3909fd6d69dad1dc2976928d58 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -151,6 +151,50 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ */
+ @Nullable
+ public com.destroystokyo.paper.block.TargetBlockInfo getTargetBlockInfo(int maxDistance, @NotNull com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode);
++
++ /**
++ * Gets information about the entity being targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @return entity being targeted, or null if no entity is targeted
++ */
++ @Nullable
++ public default Entity getTargetEntity(int maxDistance) {
++ return getTargetEntity(maxDistance, false);
++ }
++
++ /**
++ * Gets information about the entity being targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @param ignoreBlocks true to scan through blocks
++ * @return entity being targeted, or null if no entity is targeted
++ */
++ @Nullable
++ public Entity getTargetEntity(int maxDistance, boolean ignoreBlocks);
++
++ /**
++ * Gets information about the entity being targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @return TargetEntityInfo about the entity being targeted,
++ * or null if no entity is targeted
++ */
++ @Nullable
++ public default com.destroystokyo.paper.entity.TargetEntityInfo getTargetEntityInfo(int maxDistance) {
++ return getTargetEntityInfo(maxDistance, false);
++ }
++
++ /**
++ * Gets information about the entity being targeted
++ *
++ * @param maxDistance this is the maximum distance to scan
++ * @param ignoreBlocks true to scan through blocks
++ * @return TargetEntityInfo about the entity being targeted,
++ * or null if no entity is targeted
++ */
++ @Nullable
++ public com.destroystokyo.paper.entity.TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks);
+ // Paper end
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0157-Add-sun-related-API.patch b/Spigot-API-Patches-Unmapped/0157-Add-sun-related-API.patch
new file mode 100644
index 0000000000..85efb56e43
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0157-Add-sun-related-API.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 7 Oct 2018 00:54:15 -0500
+Subject: [PATCH] Add sun related API
+
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index ec8e3a949a869545a8e0cb8c2f59f1a6dd8f5485..ce1a3de1d03e10b18c0098ee2778361cc9a4cb01 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -1812,6 +1812,16 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ */
+ public void setFullTime(long time);
+
++ // Paper start
++
++ /**
++ * Check if it is currently daytime in this world
++ *
++ * @return True if it is daytime
++ */
++ public boolean isDayTime();
++ // Paper end
++
+ /**
+ * Gets the full in-game time on this world since the world generation
+ *
+diff --git a/src/main/java/org/bukkit/entity/Mob.java b/src/main/java/org/bukkit/entity/Mob.java
+index b132287817d35579ca5128a1ed5c242bf229771a..d726453c041a980576312b6bee96a07837f37974 100644
+--- a/src/main/java/org/bukkit/entity/Mob.java
++++ b/src/main/java/org/bukkit/entity/Mob.java
+@@ -16,6 +16,13 @@ public interface Mob extends LivingEntity, Lootable {
+ */
+ @NotNull
+ com.destroystokyo.paper.entity.Pathfinder getPathfinder();
++
++ /**
++ * Check if this mob is exposed to daylight
++ *
++ * @return True if mob is exposed to daylight
++ */
++ boolean isInDaylight();
+ // Paper end
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0158-Here-s-Johnny.patch b/Spigot-API-Patches-Unmapped/0158-Here-s-Johnny.patch
new file mode 100644
index 0000000000..faaadfb5db
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0158-Here-s-Johnny.patch
@@ -0,0 +1,42 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 12 Oct 2018 01:37:16 -0500
+Subject: [PATCH] Here's Johnny!
+
+
+diff --git a/src/main/java/org/bukkit/entity/Vindicator.java b/src/main/java/org/bukkit/entity/Vindicator.java
+index b8ea68a8f420c1ba99c0621a15e654d3ee48c8d6..c5d9e76a6a4125eb0409967a57e3836b8f2d24a0 100644
+--- a/src/main/java/org/bukkit/entity/Vindicator.java
++++ b/src/main/java/org/bukkit/entity/Vindicator.java
+@@ -3,4 +3,30 @@ package org.bukkit.entity;
+ /**
+ * Represents a Vindicator.
+ */
+-public interface Vindicator extends Illager { }
++public interface Vindicator extends Illager {
++ // Paper start
++ /**
++ * Check if this Vindicator is set to Johnny mode.
++ * <p>
++ * When in Johnny mode the Vindicator will be hostile to any kind of mob, except
++ * for evokers, ghasts, illusioners and other vindicators. It will even be hostile
++ * to vexes. All mobs, except for endermites, phantoms, guardians, slimes and
++ * magma cubes, will try to attack the vindicator in return.
++ *
++ * @return True if in Johnny mode
++ */
++ boolean isJohnny();
++
++ /**
++ * Set this Vindicator's Johnny mode.
++ * <p>
++ * When in Johnny mode the Vindicator will be hostile to any kind of mob, except
++ * for evokers, ghasts, illusioners and other vindicators. It will even be hostile
++ * to vexes. All mobs, except for endermites, phantoms, guardians, slimes and
++ * magma cubes, will try to attack the vindicator in return.
++ *
++ * @param johnny True to enable Johnny mode
++ */
++ void setJohnny(boolean johnny);
++ // Paper end
++}
diff --git a/Spigot-API-Patches-Unmapped/0159-Turtle-API.patch b/Spigot-API-Patches-Unmapped/0159-Turtle-API.patch
new file mode 100644
index 0000000000..06abb703f4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0159-Turtle-API.patch
@@ -0,0 +1,283 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 28 Sep 2018 17:08:09 -0500
+Subject: [PATCH] Turtle API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/TurtleGoHomeEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/TurtleGoHomeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..021356d151ed638068e3e89b8cc77b3795883233
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/TurtleGoHomeEvent.java
+@@ -0,0 +1,49 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Turtle;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Turtle decides to go home
++ */
++public class TurtleGoHomeEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++
++ public TurtleGoHomeEvent(@NotNull Turtle turtle) {
++ super(turtle);
++ }
++
++ /**
++ * The turtle going home
++ *
++ * @return The turtle
++ */
++ @NotNull
++ public Turtle getEntity() {
++ return (Turtle) entity;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/TurtleLayEggEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/TurtleLayEggEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a315c5185cd465dcf63c0ababef195da76dfc786
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/TurtleLayEggEvent.java
+@@ -0,0 +1,87 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Turtle;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Turtle lays eggs
++ */
++public class TurtleLayEggEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++ @NotNull
++ private final Location location;
++ private int eggCount;
++
++ public TurtleLayEggEvent(@NotNull Turtle turtle, @NotNull Location location, int eggCount) {
++ super(turtle);
++ this.location = location;
++ this.eggCount = eggCount;
++ }
++
++ /**
++ * The turtle laying the eggs
++ *
++ * @return The turtle
++ */
++ @NotNull
++ public Turtle getEntity() {
++ return (Turtle) entity;
++ }
++
++ /**
++ * Get the location where the eggs are being laid
++ *
++ * @return Location of eggs
++ */
++ @NotNull
++ public Location getLocation() {
++ return location;
++ }
++
++ /**
++ * Get the number of eggs being laid
++ *
++ * @return Number of eggs
++ */
++ public int getEggCount() {
++ return eggCount;
++ }
++
++ /**
++ * Set the number of eggs being laid
++ *
++ * @param eggCount Number of eggs
++ */
++ public void setEggCount(int eggCount) {
++ if (eggCount < 1) {
++ cancelled = true;
++ return;
++ }
++ eggCount = Math.min(eggCount, 4);
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/TurtleStartDiggingEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/TurtleStartDiggingEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..abeb24fccda2acfdb0dfdadacb8fe688bd97cf78
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/TurtleStartDiggingEvent.java
+@@ -0,0 +1,62 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Turtle;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a Turtle starts digging to lay eggs
++ */
++public class TurtleStartDiggingEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++ @NotNull private final Location location;
++
++ public TurtleStartDiggingEvent(@NotNull Turtle turtle, @NotNull Location location) {
++ super(turtle);
++ this.location = location;
++ }
++
++ /**
++ * The turtle digging
++ *
++ * @return The turtle
++ */
++ @NotNull
++ public Turtle getEntity() {
++ return (Turtle) entity;
++ }
++
++ /**
++ * Get the location where the turtle is digging
++ *
++ * @return Location where digging
++ */
++ @NotNull
++ public Location getLocation() {
++ return location;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Turtle.java b/src/main/java/org/bukkit/entity/Turtle.java
+index 0a4cd29930c2f1c28f5a3e6884c7dec45b5cac11..5375ea14097f4f10b2294488b92924a35a72d4d7 100644
+--- a/src/main/java/org/bukkit/entity/Turtle.java
++++ b/src/main/java/org/bukkit/entity/Turtle.java
+@@ -1,6 +1,55 @@
+ package org.bukkit.entity;
+
++import org.bukkit.Location;
++import org.jetbrains.annotations.NotNull;
++
+ /**
+ * Represents a turtle.
+ */
+-public interface Turtle extends Animals { }
++public interface Turtle extends Animals {
++ // Paper start
++
++ /**
++ * Get the turtle's home location
++ *
++ * @return Home location
++ */
++ @NotNull
++ Location getHome();
++
++ /**
++ * Set the turtle's home location
++ *
++ * @param location Home location
++ */
++ void setHome(@NotNull Location location);
++
++ /**
++ * Check if turtle is currently pathfinding to it's home
++ *
++ * @return True if going home
++ */
++ boolean isGoingHome();
++
++ /**
++ * Get if turtle is digging to lay eggs
++ *
++ * @return True if digging
++ */
++ boolean isDigging();
++
++ /**
++ * Get if turtle is carrying egg
++ *
++ * @return True if carrying egg
++ */
++ boolean hasEgg();
++
++ /**
++ * Set if turtle is carrying egg
++ *
++ * @param hasEgg True if carrying egg
++ */
++ void setHasEgg(boolean hasEgg);
++ // Paper end
++}
diff --git a/Spigot-API-Patches-Unmapped/0160-Add-spectator-target-events.patch b/Spigot-API-Patches-Unmapped/0160-Add-spectator-target-events.patch
new file mode 100644
index 0000000000..c5d4b7c4db
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0160-Add-spectator-target-events.patch
@@ -0,0 +1,141 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Caleb Bassham <[email protected]>
+Date: Fri, 28 Sep 2018 02:30:56 -0500
+Subject: [PATCH] Add spectator target events
+
+- PlayerStartSpectatingEntityEvent
+- PlayerStopSpectatingEntityEvent
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerStartSpectatingEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerStartSpectatingEntityEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b8ec7ef2d4ef0683cc0d6ca86885dd9a01f47e16
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerStartSpectatingEntityEvent.java
+@@ -0,0 +1,67 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Triggered when a player starts spectating an entity in spectator mode.
++ */
++public class PlayerStartSpectatingEntityEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ @NotNull private final Entity currentSpectatorTarget;
++ @NotNull private final Entity newSpectatorTarget;
++
++ public PlayerStartSpectatingEntityEvent(@NotNull Player player, @NotNull Entity currentSpectatorTarget, @NotNull Entity newSpectatorTarget) {
++ super(player);
++ this.currentSpectatorTarget = currentSpectatorTarget;
++ this.newSpectatorTarget = newSpectatorTarget;
++ }
++
++ /**
++ * Gets the entity that the player is currently spectating or themselves if they weren't spectating anything
++ *
++ * @return The entity the player is currently spectating (before they start spectating the new target).
++ */
++ @NotNull
++ public Entity getCurrentSpectatorTarget() {
++ return currentSpectatorTarget;
++ }
++
++ /**
++ * Gets the new entity that the player will now be spectating
++ *
++ * @return The entity the player is now going to be spectating.
++ */
++ @NotNull
++ public Entity getNewSpectatorTarget() {
++ return newSpectatorTarget;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
++
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerStopSpectatingEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerStopSpectatingEntityEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..693d119ab920a1bd0d1b5a0feb092631715ec0ad
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerStopSpectatingEntityEvent.java
+@@ -0,0 +1,54 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Triggered when a player stops spectating an entity in spectator mode.
++ */
++public class PlayerStopSpectatingEntityEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ @NotNull private final Entity spectatorTarget;
++
++ public PlayerStopSpectatingEntityEvent(@NotNull Player player, @NotNull Entity spectatorTarget) {
++ super(player);
++ this.spectatorTarget = spectatorTarget;
++ }
++
++ /**
++ * Gets the entity that the player is spectating
++ *
++ * @return The entity the player is currently spectating (before they will stop).
++ */
++ @NotNull
++ public Entity getSpectatorTarget() {
++ return spectatorTarget;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0161-Add-more-Witch-API.patch b/Spigot-API-Patches-Unmapped/0161-Add-more-Witch-API.patch
new file mode 100644
index 0000000000..10ae141b6d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0161-Add-more-Witch-API.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Fri, 12 Oct 2018 03:47:26 -0500
+Subject: [PATCH] Add more Witch API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Witch.java b/src/main/java/org/bukkit/entity/Witch.java
+index aa88aede6c4e66a608a63d07bc66d60357b0bee9..cd8d0a20e52ad35f659175e0d24a1dc0e92023b9 100644
+--- a/src/main/java/org/bukkit/entity/Witch.java
++++ b/src/main/java/org/bukkit/entity/Witch.java
+@@ -2,8 +2,43 @@ package org.bukkit.entity;
+
+ import com.destroystokyo.paper.entity.RangedEntity;
+
++// Paper start
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.Nullable;
++// Paper end
++
+ /**
+ * Represents a Witch
+ */
+ public interface Witch extends Raider, RangedEntity { // Paper
++ // Paper start
++ /**
++ * Check if Witch is drinking a potion
++ *
++ * @return True if drinking a potion
++ */
++ boolean isDrinkingPotion();
++
++ /**
++ * Get time remaining (in ticks) the Witch is drinking a potion
++ *
++ * @return Time remaining (in ticks)
++ */
++ int getPotionUseTimeLeft();
++
++ /**
++ * Get the potion the Witch is drinking
++ *
++ * @return The potion the witch is drinking
++ */
++ @Nullable
++ ItemStack getDrinkingPotion();
++
++ /**
++ * Set the potion the Witch should drink
++ *
++ * @param potion Potion to drink
++ */
++ void setDrinkingPotion(@Nullable ItemStack potion);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0162-Make-the-default-permission-message-configurable.patch b/Spigot-API-Patches-Unmapped/0162-Make-the-default-permission-message-configurable.patch
new file mode 100644
index 0000000000..d6094643d2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0162-Make-the-default-permission-message-configurable.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sun, 18 Nov 2018 19:44:54 +0000
+Subject: [PATCH] Make the default permission message configurable
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 2d7f8e128e23934a8fe26baf19198b7ffc8447bb..908b75f0b9897ce830ed7c0a2c1dd0a458a872f1 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1781,6 +1781,15 @@ public final class Bukkit {
+ return server.suggestPlayerNamesWhenNullTabCompletions();
+ }
+
++ /**
++ *
++ * @return the default no permission message used on the server
++ */
++ @NotNull
++ public static String getPermissionMessage() {
++ return server.getPermissionMessage();
++ }
++
+ /**
+ * Creates a PlayerProfile for the specified uuid, with name as null
+ * @param uuid UUID to create profile for
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 01657abaff86cf7bb3ffb857024c5032781b8660..a68b973e7574cae3ee7be7cfc51786589280408a 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1564,6 +1564,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ */
+ boolean suggestPlayerNamesWhenNullTabCompletions();
+
++ /**
++ *
++ * @return the default no permission message used on the server
++ */
++ @NotNull
++ String getPermissionMessage();
++
+ /**
+ * Creates a PlayerProfile for the specified uuid, with name as null
+ * @param uuid UUID to create profile for
+diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java
+index 7c80dc54776d0d66f7816b77136f6dbd9b801704..c10fc8d2386301bc2caddcdb1cd18566bcaa8689 100644
+--- a/src/main/java/org/bukkit/command/Command.java
++++ b/src/main/java/org/bukkit/command/Command.java
+@@ -185,7 +185,7 @@ public abstract class Command {
+ }
+
+ if (permissionMessage == null) {
+- target.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is a mistake.");
++ target.sendMessage(Bukkit.getPermissionMessage()); // Paper
+ } else if (permissionMessage.length() != 0) {
+ for (String line : permissionMessage.replace("<permission>", permission).split("\n")) {
+ target.sendMessage(line);
diff --git a/Spigot-API-Patches-Unmapped/0163-Support-cancellation-supression-of-EntityDismount-Ve.patch b/Spigot-API-Patches-Unmapped/0163-Support-cancellation-supression-of-EntityDismount-Ve.patch
new file mode 100644
index 0000000000..a36772974a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0163-Support-cancellation-supression-of-EntityDismount-Ve.patch
@@ -0,0 +1,109 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sun, 18 Nov 2018 15:53:43 +0000
+Subject: [PATCH] Support cancellation supression of EntityDismount/VehicleExit
+ events"
+
+Entities must be dismounted before teleportation in order to avoid
+multiple issues in the server with regards to teleportation, shamefully,
+too many plugins rely on the events firing, which means that not firing
+these events caues more issues than it solves;
+
+In order to counteract this, Entity dismount/exit vehicle events have
+been modified to supress cancellation (and has a method to allow plugins
+to check if this has been set), noting that cancellation will be silently
+surpressed given that plugins are not expecting this event to not be cancellable.
+
+This is a far from ideal scenario, however: given the current state of this
+event and other alternatives causing issues elsewhere, I believe that
+this is going to be the best soultion all around.
+
+Improvements/suggestions welcome!
+
+diff --git a/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java b/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java
+index 963b9ead4ca0426b2e95c5641b0e89317c48853d..a976c32de6ad5e90b0a96a0f387136ab0f5eb52e 100644
+--- a/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java
++++ b/src/main/java/org/bukkit/event/vehicle/VehicleExitEvent.java
+@@ -13,10 +13,18 @@ public class VehicleExitEvent extends VehicleEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancelled;
+ private final LivingEntity exited;
++ private final boolean isCancellable; // Paper
+
+- public VehicleExitEvent(@NotNull final Vehicle vehicle, @NotNull final LivingEntity exited) {
++ public VehicleExitEvent(@NotNull final Vehicle vehicle, @NotNull final LivingEntity exited, boolean isCancellable) { // Paper
+ super(vehicle);
+ this.exited = exited;
++ // Paper start
++ this.isCancellable = isCancellable;
++ }
++
++ public VehicleExitEvent(@NotNull final Vehicle vehicle, @NotNull final LivingEntity exited) {
++ this(vehicle, exited, true);
++ // Paper end
+ }
+
+ /**
+@@ -36,9 +44,18 @@ public class VehicleExitEvent extends VehicleEvent implements Cancellable {
+
+ @Override
+ public void setCancelled(boolean cancel) {
++ // Paper start
++ if (cancel && !isCancellable) {
++ return;
++ }
+ this.cancelled = cancel;
+ }
+
++ public boolean isCancellable() {
++ return isCancellable;
++ // paper end
++ }
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+diff --git a/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java b/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java
+index 00d8ec81b4ae6ca5e438161ec9135e3c1edea6f4..a7632c8f5cb1bce4be0e456ec34f4a69c5ce80f3 100644
+--- a/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java
++++ b/src/main/java/org/spigotmc/event/entity/EntityDismountEvent.java
+@@ -14,10 +14,19 @@ public class EntityDismountEvent extends EntityEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancelled;
+ private final Entity dismounted;
++ private final boolean isCancellable; // Paper
+
+ public EntityDismountEvent(@NotNull Entity what, @NotNull Entity dismounted) {
+- super(what);
++ // Paper start
++ this(what, dismounted, true);
++ }
++
++
++ public EntityDismountEvent(@NotNull Entity what, @NotNull Entity dismounted, boolean isCancellable) {
++ // Paper end
++ super( what );
+ this.dismounted = dismounted;
++ this.isCancellable = isCancellable; // Paper
+ }
+
+ @NotNull
+@@ -32,9 +41,18 @@ public class EntityDismountEvent extends EntityEvent implements Cancellable {
+
+ @Override
+ public void setCancelled(boolean cancel) {
++ // Paper start
++ if (cancel && !isCancellable) {
++ return;
++ }
+ this.cancelled = cancel;
+ }
+
++ public boolean isCancellable() {
++ return isCancellable;
++ // Paper end
++ }
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
diff --git a/Spigot-API-Patches-Unmapped/0164-Add-more-Zombie-API.patch b/Spigot-API-Patches-Unmapped/0164-Add-more-Zombie-API.patch
new file mode 100644
index 0000000000..ec2c93dc46
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0164-Add-more-Zombie-API.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 7 Oct 2018 04:29:51 -0500
+Subject: [PATCH] Add more Zombie API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Zombie.java b/src/main/java/org/bukkit/entity/Zombie.java
+index a5b20b0454af5ea78e2bb7f16a56c38670c84efb..1217576e6f08abf0175ab800cfca058d5deda116 100644
+--- a/src/main/java/org/bukkit/entity/Zombie.java
++++ b/src/main/java/org/bukkit/entity/Zombie.java
+@@ -90,4 +90,55 @@ public interface Zombie extends Monster, Ageable {
+ * @param time new conversion time
+ */
+ void setConversionTime(int time);
++ // Paper start
++ /**
++ * Check if zombie is drowning
++ *
++ * @return True if zombie conversion process has begun
++ */
++ boolean isDrowning();
++
++ /**
++ * Make zombie start drowning
++ *
++ * @param drownedConversionTime Amount of time until zombie converts from drowning
++ *
++ * @deprecated See {@link #setConversionTime(int)}
++ */
++ @Deprecated
++ void startDrowning(int drownedConversionTime);
++
++ /**
++ * Stop a zombie from starting the drowning conversion process
++ */
++ void stopDrowning();
++
++ /**
++ * Set if zombie has its arms raised
++ *
++ * @param raised True to raise arms
++ */
++ void setArmsRaised(boolean raised);
++
++ /**
++ * Check if zombie has arms raised
++ *
++ * @return True if arms are raised
++ */
++ boolean isArmsRaised();
++
++ /**
++ * Check if this zombie will burn in the sunlight
++ *
++ * @return True if zombie will burn in sunlight
++ */
++ boolean shouldBurnInDay();
++
++ /**
++ * Set if this zombie should burn in the sunlight
++ *
++ * @param shouldBurnInDay True to burn in sunlight
++ */
++ void setShouldBurnInDay(boolean shouldBurnInDay);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0165-Change-the-reserved-channel-check-to-be-sensible.patch b/Spigot-API-Patches-Unmapped/0165-Change-the-reserved-channel-check-to-be-sensible.patch
new file mode 100644
index 0000000000..fa3de62864
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0165-Change-the-reserved-channel-check-to-be-sensible.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: DoNotSpamPls <[email protected]>
+Date: Tue, 23 Oct 2018 19:32:55 +0300
+Subject: [PATCH] Change the reserved channel check to be sensible
+
+
+diff --git a/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java
+index 1d061412cdafa28c6940c7433747ab1dabe23de1..6fda7f3aa68e76af64362e9afed70fc6a5e92986 100644
+--- a/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java
++++ b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java
+@@ -172,7 +172,7 @@ public class StandardMessenger implements Messenger {
+ public boolean isReservedChannel(@NotNull String channel) {
+ channel = validateAndCorrectChannel(channel);
+
+- return channel.contains("minecraft") && !channel.equals("minecraft:brand");
++ return channel.equals("minecraft:register") || channel.equals("minecraft:unregister"); // Paper
+ }
+
+ @Override
+diff --git a/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
+index dce3d619a6f1791197e44277c2dee9eaf19ff56f..7e2335ed8acc692af1e70eddcf97ee7a56e30f68 100644
+--- a/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
++++ b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java
+@@ -25,8 +25,8 @@ public class StandardMessengerTest {
+ assertTrue(messenger.isReservedChannel("minecraft:register"));
+ assertFalse(messenger.isReservedChannel("test:register"));
+ assertTrue(messenger.isReservedChannel("minecraft:unregister"));
+- assertFalse(messenger.isReservedChannel("test:nregister"));
+- assertTrue(messenger.isReservedChannel("minecraft:something"));
++ assertFalse(messenger.isReservedChannel("test:unregister")); // Paper - fix typo
++ assertFalse(messenger.isReservedChannel("minecraft:something")); // Paper - now less strict
+ assertFalse(messenger.isReservedChannel("minecraft:brand"));
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0166-Add-PlayerConnectionCloseEvent.patch b/Spigot-API-Patches-Unmapped/0166-Add-PlayerConnectionCloseEvent.patch
new file mode 100644
index 0000000000..f1a017f4fb
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0166-Add-PlayerConnectionCloseEvent.patch
@@ -0,0 +1,136 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Sun, 7 Oct 2018 12:05:06 -0700
+Subject: [PATCH] Add PlayerConnectionCloseEvent
+
+This event is invoked when a player has disconnected. It is guaranteed that,
+if the server is in online-mode, that the provided uuid and username have been
+validated.
+
+The event is invoked for players who have not yet logged into the world, whereas
+PlayerQuitEvent is only invoked on players who have logged into the world.
+
+The event is invoked for players who have already logged into the world,
+although whether or not the player exists in the world at the time of
+firing is undefined. (That is, whether the plugin can retrieve a Player object
+using the event parameters is undefined). However, it is guaranteed that this
+event is invoked AFTER PlayerQuitEvent, if the player has already logged into
+the world.
+
+This event is guaranteed to never fire unless AsyncPlayerPreLoginEvent has
+been called beforehand, and this event may not be called in parallel with
+AsyncPlayerPreLoginEvent for the same connection.
+
+Cancelling the AsyncPlayerPreLoginEvent guarantees the corresponding
+PlayerConnectionCloseEvent is never called.
+
+The event may be invoked asynchronously or synchronously. As it stands,
+it is never invoked asynchronously. However, plugins should check
+Event#isAsynchronous to be future-proof.
+
+On purpose, the deprecated PlayerPreLoginEvent event is left out of the
+API spec for this event. Plugins should not be using that event, and
+how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent
+is undefined.
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..12c1c6fe9dc8dc5f5faf6dcf99f6857219ef22b8
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java
+@@ -0,0 +1,95 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++
++import java.net.InetAddress;
++import java.util.UUID;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * <p>
++ * This event is invoked when a player has disconnected. It is guaranteed that,
++ * if the server is in online-mode, that the provided uuid and username have been
++ * validated.
++ * </p>
++ *
++ * <p>
++ * The event is invoked for players who have not yet logged into the world, whereas
++ * {@link org.bukkit.event.player.PlayerQuitEvent} is only invoked on players who have logged into the world.
++ * </p>
++ *
++ * <p>
++ * The event is invoked for players who have already logged into the world,
++ * although whether or not the player exists in the world at the time of
++ * firing is undefined. (That is, whether the plugin can retrieve a Player object
++ * using the event parameters is undefined). However, it is guaranteed that this
++ * event is invoked AFTER {@link org.bukkit.event.player.PlayerQuitEvent}, if the player has already logged into the world.
++ * </p>
++ *
++ * <p>
++ * This event is guaranteed to never fire unless {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} has
++ * been fired beforehand, and this event may not be called in parallel with
++ * {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} for the same connection.
++ * </p>
++ *
++ * <p>
++ * Cancelling the {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} guarantees the corresponding
++ * {@code PlayerConnectionCloseEvent} is never called.
++ * </p>
++ *
++ * <p>
++ * The event may be invoked asynchronously or synchronously. Plugins should check
++ * {@link Event#isAsynchronous()} and handle accordingly.
++ * </p>
++ */
++public class PlayerConnectionCloseEvent extends Event {
++
++ private static final HandlerList HANDLERS = new HandlerList();
++
++ @NotNull private final UUID playerUniqueId;
++ @NotNull private final String playerName;
++ @NotNull private final InetAddress ipAddress;
++
++ public PlayerConnectionCloseEvent(@NotNull final UUID playerUniqueId, @NotNull final String playerName, @NotNull final InetAddress ipAddress, final boolean async) {
++ super(async);
++ this.playerUniqueId = playerUniqueId;
++ this.playerName = playerName;
++ this.ipAddress = ipAddress;
++ }
++
++ /**
++ * Returns the {@code UUID} of the player disconnecting.
++ */
++ @NotNull
++ public UUID getPlayerUniqueId() {
++ return this.playerUniqueId;
++ }
++
++ /**
++ * Returns the name of the player disconnecting.
++ */
++ @NotNull
++ public String getPlayerName() {
++ return this.playerName;
++ }
++
++ /**
++ * Returns the player's IP address.
++ */
++ @NotNull
++ public InetAddress getIpAddress() {
++ return this.ipAddress;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLERS;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLERS;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0167-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch b/Spigot-API-Patches-Unmapped/0167-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch
new file mode 100644
index 0000000000..ec2b79af2d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0167-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zach Brown <[email protected]>
+Date: Wed, 2 Jan 2019 00:31:12 -0600
+Subject: [PATCH] Add APIs to replace OfflinePlayer#getLastPlayed
+
+Currently OfflinePlayer#getLastPlayed could more accurately be described
+as "OfflinePlayer#getLastTimeTheirDataWasSaved".
+
+The API doc says it should return the last time the server "witnessed"
+the player, whilst also saying it should return the last time they
+logged in. The current implementation does neither.
+
+Given this interesting contradiction in the API documentation and the
+current defacto implementation, I've elected to deprecate (with no
+intent to remove) and replace it with two new methods, clearly named and
+documented as to their purpose.
+
+diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java
+index 6cf05fed701c67a2c797a4e0839c795802a238a1..3afd5f5c0208a4ee93b5dbfc2aab2b9d2e8a7544 100644
+--- a/src/main/java/org/bukkit/OfflinePlayer.java
++++ b/src/main/java/org/bukkit/OfflinePlayer.java
+@@ -147,7 +147,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
+ * UTC.
+ *
+ * @return Date of last log-in for this player, or 0
++ * @deprecated The API contract is ambiguous and the implementation may or may not return the correct value given this API ambiguity. It is instead recommended use {@link #getLastLogin()} or {@link #getLastSeen()} depending on your needs.
+ */
++ @Deprecated
+ public long getLastPlayed();
+
+ /**
+@@ -165,6 +167,30 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
+ */
+ @Nullable
+ public Location getBedSpawnLocation();
++ // Paper start
++ /**
++ * Gets the last date and time that this player logged into the server.
++ * <p>
++ * If the player has never played before, this will return 0. Otherwise,
++ * it will be the amount of milliseconds since midnight, January 1, 1970
++ * UTC.
++ *
++ * @return last login time
++ */
++ public long getLastLogin();
++
++ /**
++ * Gets the last date and time that this player was seen on the server.
++ * <p>
++ * If the player has never played before, this will return 0. If the
++ * player is currently online, this will return the current time.
++ * Otherwise it will be the amount of milliseconds since midnight,
++ * January 1, 1970 UTC.
++ *
++ * @return last seen time
++ */
++ public long getLastSeen();
++ // Paper end
+
+ /**
+ * Increments the given statistic for this player.
diff --git a/Spigot-API-Patches-Unmapped/0168-BlockDestroyEvent.patch b/Spigot-API-Patches-Unmapped/0168-BlockDestroyEvent.patch
new file mode 100644
index 0000000000..4ad73d8ef1
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0168-BlockDestroyEvent.patch
@@ -0,0 +1,110 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 6 Feb 2019 00:19:33 -0500
+Subject: [PATCH] BlockDestroyEvent
+
+Adds an event for when the server is going to destroy a current block,
+potentially causing it to drop. This event can be cancelled to avoid
+the block destruction, such as preventing signs from popping when
+floating in the air.
+
+This can replace many uses of BlockPhysicsEvent
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/block/BlockDestroyEvent.java b/src/main/java/com/destroystokyo/paper/event/block/BlockDestroyEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..28255dc39eab5faf324d1fe556ab12daed527ff6
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/block/BlockDestroyEvent.java
+@@ -0,0 +1,92 @@
++package com.destroystokyo.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.block.data.BlockData;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired anytime the server intends to 'destroy' a block through some triggering reason.
++ * This does not fire anytime a block is set to air, but only with more direct triggers such
++ * as physics updates, pistons, Entities changing blocks, commands set to "Destroy".
++ *
++ * This event is associated with the game playing a sound effect at the block in question, when
++ * something can be described as "intend to destroy what is there",
++ *
++ * Events such as leaves decaying, pistons retracting (where the block is moving), does NOT fire this event.
++ *
++ */
++public class BlockDestroyEvent extends BlockEvent implements Cancellable {
++
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull private final BlockData newState;
++ private final boolean willDrop;
++ private boolean playEffect = true;
++
++ private boolean cancelled = false;
++
++ public BlockDestroyEvent(@NotNull Block block, @NotNull BlockData newState, boolean willDrop) {
++ super(block);
++ this.newState = newState;
++ this.willDrop = willDrop;
++ }
++
++ /**
++ * @return The new state of this block (Air, or a Fluid type)
++ */
++ @NotNull
++ public BlockData getNewState() {
++ return newState;
++ }
++
++ /**
++ * @return If the server is going to drop the block in question with this destroy event
++ */
++ public boolean willDrop() {
++ return this.willDrop;
++ }
++
++ /**
++ * @return If the server is going to play the sound effect for this destruction
++ */
++ public boolean playEffect() {
++ return this.playEffect;
++ }
++
++ /**
++ * @param playEffect If the server should play the sound effect for this destruction
++ */
++ public void setPlayEffect(boolean playEffect) {
++ this.playEffect = playEffect;
++ }
++
++ /**
++ * @return If the event is cancelled, meaning the block will not be destroyed
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * If the event is cancelled, the block will remain in its previous state.
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0169-Add-ItemStack-Recipe-API-helper-methods.patch b/Spigot-API-Patches-Unmapped/0169-Add-ItemStack-Recipe-API-helper-methods.patch
new file mode 100644
index 0000000000..9b2dfc0c72
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0169-Add-ItemStack-Recipe-API-helper-methods.patch
@@ -0,0 +1,70 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 28 Jan 2014 19:13:57 -0500
+Subject: [PATCH] Add ItemStack Recipe API helper methods
+
+Allows using ExactChoice Recipes with easier methodss
+
+diff --git a/src/main/java/org/bukkit/inventory/ShapedRecipe.java b/src/main/java/org/bukkit/inventory/ShapedRecipe.java
+index d742c4058ba9aed4fbe1591fd755a06608b06e98..222a12baa8e93ad686ab59426653f066d5876e38 100644
+--- a/src/main/java/org/bukkit/inventory/ShapedRecipe.java
++++ b/src/main/java/org/bukkit/inventory/ShapedRecipe.java
+@@ -145,6 +145,13 @@ public class ShapedRecipe implements Recipe, Keyed {
+ return this;
+ }
+
++ // Paper start
++ @NotNull
++ public ShapedRecipe setIngredient(char key, @NotNull ItemStack item) {
++ return setIngredient(key, new RecipeChoice.ExactChoice(item));
++ }
++ // Paper end
++
+ /**
+ * Get a copy of the ingredients map.
+ *
+diff --git a/src/main/java/org/bukkit/inventory/ShapelessRecipe.java b/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
+index 84062dd719cb8a6142dc8c806777cb208c6b42b2..7f6d3c71c5b3a9aa54c84a4c3b7c3614a0d477ce 100644
+--- a/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
++++ b/src/main/java/org/bukkit/inventory/ShapelessRecipe.java
+@@ -143,6 +143,40 @@ public class ShapelessRecipe implements Recipe, Keyed {
+ return this;
+ }
+
++ // Paper start
++ @NotNull
++ public ShapelessRecipe addIngredient(@NotNull ItemStack item) {
++ return addIngredient(item.getAmount(), item);
++ }
++
++ @NotNull
++ public ShapelessRecipe addIngredient(int count, @NotNull ItemStack item) {
++ Validate.isTrue(ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients");
++ while (count-- > 0) {
++ ingredients.add(new RecipeChoice.ExactChoice(item));
++ }
++ return this;
++ }
++
++ @NotNull
++ public ShapelessRecipe removeIngredient(@NotNull ItemStack item) {
++ return removeIngredient(1, item);
++ }
++
++ @NotNull
++ public ShapelessRecipe removeIngredient(int count, @NotNull ItemStack item) {
++ Iterator<RecipeChoice> iterator = ingredients.iterator();
++ while (count > 0 && iterator.hasNext()) {
++ ItemStack stack = iterator.next().getItemStack();
++ if (stack.equals(item)) {
++ iterator.remove();
++ count--;
++ }
++ }
++ return this;
++ }
++ // Paper end
++
+ /**
+ * Removes an ingredient from the list.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0170-Add-WhitelistToggleEvent.patch b/Spigot-API-Patches-Unmapped/0170-Add-WhitelistToggleEvent.patch
new file mode 100644
index 0000000000..4bb54b5c92
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0170-Add-WhitelistToggleEvent.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Wed, 13 Mar 2019 20:04:43 +0200
+Subject: [PATCH] Add WhitelistToggleEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/WhitelistToggleEvent.java b/src/main/java/com/destroystokyo/paper/event/server/WhitelistToggleEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fdd5eedb2b7401439912a3a4343a920f32edc860
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/WhitelistToggleEvent.java
+@@ -0,0 +1,40 @@
++package com.destroystokyo.paper.event.server;
++
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * This event is fired when whitelist is toggled
++ *
++ * @author Mark Vainomaa
++ */
++public class WhitelistToggleEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++
++ private boolean enabled;
++
++ public WhitelistToggleEvent(boolean enabled) {
++ this.enabled = enabled;
++ }
++
++ /**
++ * Gets whether whitelist is going to be enabled or not
++ *
++ * @return Whether whitelist is going to be enabled or not
++ */
++ public boolean isEnabled() {
++ return enabled;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0171-Annotation-Test-changes.patch b/Spigot-API-Patches-Unmapped/0171-Annotation-Test-changes.patch
new file mode 100644
index 0000000000..380b55d775
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0171-Annotation-Test-changes.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sun, 17 Mar 2019 23:04:30 +0000
+Subject: [PATCH] Annotation Test changes
+
+
+diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
+index c62919f18f318fec15a6c364d8b6d562c2b04762..2bb75e967d4250476a151599b68de80011d222b2 100644
+--- a/src/test/java/org/bukkit/AnnotationTest.java
++++ b/src/test/java/org/bukkit/AnnotationTest.java
+@@ -46,7 +46,17 @@ public class AnnotationTest {
+ "org/bukkit/util/io/Wrapper",
+ "org/bukkit/plugin/java/PluginClassLoader",
+ // Generic functional interface
+- "org/bukkit/util/Consumer"
++ "org/bukkit/util/Consumer",
++ // Paper start
++ // Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
++ "co/aikar/timings/TimingHistory$2",
++ "co/aikar/timings/TimingHistory$2$1",
++ "co/aikar/timings/TimingHistory$2$1$1",
++ "co/aikar/timings/TimingHistory$2$1$2",
++ "co/aikar/timings/TimingHistory$3",
++ "co/aikar/timings/TimingHistory$4",
++ "co/aikar/timings/TimingHistoryEntry$1"
++ // Paper end
+ };
+
+ @Test
diff --git a/Spigot-API-Patches-Unmapped/0172-Entity-getEntitySpawnReason.patch b/Spigot-API-Patches-Unmapped/0172-Entity-getEntitySpawnReason.patch
new file mode 100644
index 0000000000..cf940ce2fe
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0172-Entity-getEntitySpawnReason.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 24 Mar 2019 00:21:23 -0400
+Subject: [PATCH] Entity#getEntitySpawnReason
+
+Allows you to return the SpawnReason for why an Entity Spawned
+
+Pre existing entities will return NATURAL if it was a non
+persistenting Living Entity, SPAWNER for spawners,
+or DEFAULT since data was not stored.
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index c137199ed0537874010f1abf311a9cbee56831ac..2622a2f5c9e2cb43aff7ef9eac2dcdb3fd8fad04 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -662,5 +662,11 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ */
+ @NotNull
+ Chunk getChunk();
++
++ /**
++ * @return The {@link org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason} that spawned this entity.
++ */
++ @NotNull
++ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason getEntitySpawnReason();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0173-Add-GS4-Query-event.patch b/Spigot-API-Patches-Unmapped/0173-Add-GS4-Query-event.patch
new file mode 100644
index 0000000000..ffab49a92a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0173-Add-GS4-Query-event.patch
@@ -0,0 +1,424 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Sun, 17 Mar 2019 21:46:27 +0200
+Subject: [PATCH] Add GS4 Query event
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java b/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..77a19995f6792a182c5a43d6714e7bda0f42df5b
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java
+@@ -0,0 +1,412 @@
++package com.destroystokyo.paper.event.server;
++
++import com.google.common.base.Preconditions;
++import com.google.common.collect.ImmutableList;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++import java.net.InetAddress;
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Collection;
++import java.util.List;
++
++/**
++ * This event is fired if server is getting queried over GS4 Query protocol
++ *
++ * Adapted from Velocity's ProxyQueryEvent
++ *
++ * @author Mark Vainomaa
++ */
++public final class GS4QueryEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++
++ private final QueryType queryType;
++ private final InetAddress querierAddress;
++ private QueryResponse response;
++
++ public GS4QueryEvent(@NotNull QueryType queryType, @NotNull InetAddress querierAddress, @NotNull QueryResponse response) {
++ super(true); // should always be called async
++ this.queryType = Preconditions.checkNotNull(queryType, "queryType");
++ this.querierAddress = Preconditions.checkNotNull(querierAddress, "querierAddress");
++ this.response = Preconditions.checkNotNull(response, "response");
++ }
++
++ /**
++ * Get query type
++ * @return query type
++ */
++ @NotNull
++ public QueryType getQueryType() {
++ return queryType;
++ }
++
++ /**
++ * Get querier address
++ * @return querier address
++ */
++ @NotNull
++ public InetAddress getQuerierAddress() {
++ return querierAddress;
++ }
++
++ /**
++ * Get query response
++ * @return query response
++ */
++ @NotNull
++ public QueryResponse getResponse() {
++ return response;
++ }
++
++ /**
++ * Set query response
++ * @param response query response
++ */
++ public void setResponse(@NotNull QueryResponse response) {
++ this.response = Preconditions.checkNotNull(response, "response");
++ }
++
++ @Override
++ public String toString() {
++ return "GS4QueryEvent{" +
++ "queryType=" + queryType +
++ ", querierAddress=" + querierAddress +
++ ", response=" + response +
++ '}';
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ /**
++ * The type of query
++ */
++ public enum QueryType {
++ /**
++ * Basic query asks only a subset of information, such as motd, game type (hardcoded to <pre>MINECRAFT</pre>), map,
++ * current players, max players, server port and server motd
++ */
++ BASIC,
++
++ /**
++ * Full query asks pretty much everything present on this event (only hardcoded values cannot be modified here).
++ */
++ FULL
++ ;
++ }
++
++ public final static class QueryResponse {
++ private final String motd;
++ private final String gameVersion;
++ private final String map;
++ private final int currentPlayers;
++ private final int maxPlayers;
++ private final String hostname;
++ private final int port;
++ private final Collection<String> players;
++ private final String serverVersion;
++ private final Collection<PluginInformation> plugins;
++
++ private QueryResponse(String motd, String gameVersion, String map, int currentPlayers, int maxPlayers, String hostname, int port, Collection<String> players, String serverVersion, Collection<PluginInformation> plugins) {
++ this.motd = motd;
++ this.gameVersion = gameVersion;
++ this.map = map;
++ this.currentPlayers = currentPlayers;
++ this.maxPlayers = maxPlayers;
++ this.hostname = hostname;
++ this.port = port;
++ this.players = players;
++ this.serverVersion = serverVersion;
++ this.plugins = plugins;
++ }
++
++ /**
++ * Get motd which will be used to reply to the query. By default it is {@link org.bukkit.Server#getMotd()}.
++ * @return motd
++ */
++ @NotNull
++ public String getMotd() {
++ return motd;
++ }
++
++ /**
++ * Get game version which will be used to reply to the query. By default supported Minecraft versions range is sent.
++ * @return game version
++ */
++ @NotNull
++ public String getGameVersion() {
++ return gameVersion;
++ }
++
++ /**
++ * Get map name which will be used to reply to the query. By default {@code world} is sent.
++ * @return map name
++ */
++ @NotNull
++ public String getMap() {
++ return map;
++ }
++
++ /**
++ * Get current online player count which will be used to reply to the query.
++ * @return online player count
++ */
++ public int getCurrentPlayers() {
++ return currentPlayers;
++ }
++
++ /**
++ * Get max player count which will be used to reply to the query.
++ * @return max player count
++ */
++ public int getMaxPlayers() {
++ return maxPlayers;
++ }
++
++ /**
++ * Get server (public facing) hostname
++ * @return server hostname
++ */
++ @NotNull
++ public String getHostname() {
++ return hostname;
++ }
++
++ /**
++ * Get server (public facing) port
++ * @return server port
++ */
++ public int getPort() {
++ return port;
++ }
++
++ /**
++ * Get collection of players which will be used to reply to the query.
++ * @return collection of players
++ */
++ @NotNull
++ public Collection<String> getPlayers() {
++ return players;
++ }
++
++ /**
++ * Get server software (name and version) which will be used to reply to the query.
++ * @return server software
++ */
++ @NotNull
++ public String getServerVersion() {
++ return serverVersion;
++ }
++
++ /**
++ * Get list of plugins which will be used to reply to the query.
++ * @return collection of plugins
++ */
++ @NotNull
++ public Collection<PluginInformation> getPlugins() {
++ return plugins;
++ }
++
++
++ /**
++ * Creates a new {@link Builder} instance from data represented by this response
++ * @return {@link QueryResponse} builder
++ */
++ @NotNull
++ public Builder toBuilder() {
++ return QueryResponse.builder()
++ .motd(getMotd())
++ .gameVersion(getGameVersion())
++ .map(getMap())
++ .currentPlayers(getCurrentPlayers())
++ .maxPlayers(getMaxPlayers())
++ .hostname(getHostname())
++ .port(getPort())
++ .players(getPlayers())
++ .serverVersion(getServerVersion())
++ .plugins(getPlugins());
++ }
++
++ /**
++ * Creates a new {@link Builder} instance
++ * @return {@link QueryResponse} builder
++ */
++ @NotNull
++ public static Builder builder() {
++ return new Builder();
++ }
++
++ /**
++ * A builder for {@link QueryResponse} objects.
++ */
++ public static final class Builder {
++ private String motd;
++ private String gameVersion;
++ private String map;
++ private String hostname;
++ private String serverVersion;
++
++ private int currentPlayers;
++ private int maxPlayers;
++ private int port;
++
++ private List<String> players = new ArrayList<>();
++ private List<PluginInformation> plugins = new ArrayList<>();
++
++ private Builder() {}
++
++ @NotNull
++ public Builder motd(@NotNull String motd) {
++ this.motd = Preconditions.checkNotNull(motd, "motd");
++ return this;
++ }
++
++ @NotNull
++ public Builder gameVersion(@NotNull String gameVersion) {
++ this.gameVersion = Preconditions.checkNotNull(gameVersion, "gameVersion");
++ return this;
++ }
++
++ @NotNull
++ public Builder map(@NotNull String map) {
++ this.map = Preconditions.checkNotNull(map, "map");
++ return this;
++ }
++
++ @NotNull
++ public Builder currentPlayers(int currentPlayers) {
++ Preconditions.checkArgument(currentPlayers >= 0, "currentPlayers cannot be negative");
++ this.currentPlayers = currentPlayers;
++ return this;
++ }
++
++ @NotNull
++ public Builder maxPlayers(int maxPlayers) {
++ Preconditions.checkArgument(maxPlayers >= 0, "maxPlayers cannot be negative");
++ this.maxPlayers = maxPlayers;
++ return this;
++ }
++
++ @NotNull
++ public Builder hostname(@NotNull String hostname) {
++ this.hostname = Preconditions.checkNotNull(hostname, "hostname");
++ return this;
++ }
++
++ @NotNull
++ public Builder port(int port) {
++ Preconditions.checkArgument(port >= 1 && port <= 65535, "port must be between 1-65535");
++ this.port = port;
++ return this;
++ }
++
++ @NotNull
++ public Builder players(@NotNull Collection<String> players) {
++ this.players.addAll(Preconditions.checkNotNull(players, "players"));
++ return this;
++ }
++
++ @NotNull
++ public Builder players(@NotNull String... players) {
++ this.players.addAll(Arrays.asList(Preconditions.checkNotNull(players, "players")));
++ return this;
++ }
++
++ @NotNull
++ public Builder clearPlayers() {
++ this.players.clear();
++ return this;
++ }
++
++ @NotNull
++ public Builder serverVersion(@NotNull String serverVersion) {
++ this.serverVersion = Preconditions.checkNotNull(serverVersion, "serverVersion");
++ return this;
++ }
++
++ @NotNull
++ public Builder plugins(@NotNull Collection<PluginInformation> plugins) {
++ this.plugins.addAll(Preconditions.checkNotNull(plugins, "plugins"));
++ return this;
++ }
++
++ @NotNull
++ public Builder plugins(@NotNull PluginInformation... plugins) {
++ this.plugins.addAll(Arrays.asList(Preconditions.checkNotNull(plugins, "plugins")));
++ return this;
++ }
++
++ @NotNull
++ public Builder clearPlugins() {
++ this.plugins.clear();
++ return this;
++ }
++
++ /**
++ * Builds new {@link QueryResponse} with supplied data
++ * @return response
++ */
++ @NotNull
++ public QueryResponse build() {
++ return new QueryResponse(
++ Preconditions.checkNotNull(motd, "motd"),
++ Preconditions.checkNotNull(gameVersion, "gameVersion"),
++ Preconditions.checkNotNull(map, "map"),
++ currentPlayers,
++ maxPlayers,
++ Preconditions.checkNotNull(hostname, "hostname"),
++ port,
++ ImmutableList.copyOf(players),
++ Preconditions.checkNotNull(serverVersion, "serverVersion"),
++ ImmutableList.copyOf(plugins)
++ );
++ }
++ }
++
++ /**
++ * Plugin information
++ */
++ public static class PluginInformation {
++ private String name;
++ private String version;
++
++ public PluginInformation(@NotNull String name, @NotNull String version) {
++ this.name = Preconditions.checkNotNull(name, "name");
++ this.version = Preconditions.checkNotNull(version, "version");
++ }
++
++ @NotNull
++ public String getName() {
++ return name;
++ }
++
++ public void setName(@NotNull String name) {
++ this.name = name;
++ }
++
++ public void setVersion(@NotNull String version) {
++ this.version = version;
++ }
++
++ @NotNull
++ public String getVersion() {
++ return version;
++ }
++
++ @NotNull
++ public static PluginInformation of(@NotNull String name, @NotNull String version) {
++ return new PluginInformation(name, version);
++ }
++ }
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0174-Add-PlayerPostRespawnEvent.patch b/Spigot-API-Patches-Unmapped/0174-Add-PlayerPostRespawnEvent.patch
new file mode 100644
index 0000000000..2eb0647eac
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0174-Add-PlayerPostRespawnEvent.patch
@@ -0,0 +1,64 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MisterVector <[email protected]>
+Date: Fri, 26 Oct 2018 21:33:13 -0700
+Subject: [PATCH] Add PlayerPostRespawnEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerPostRespawnEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerPostRespawnEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..31f34b54801f6699ce43355fa2a0a51f1ad0c997
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerPostRespawnEvent.java
+@@ -0,0 +1,52 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.Location;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired after a player has respawned
++ */
++public class PlayerPostRespawnEvent extends PlayerEvent {
++ private final static HandlerList handlers = new HandlerList();
++ private final Location respawnedLocation;
++ private final boolean isBedSpawn;
++
++ public PlayerPostRespawnEvent(@NotNull final Player respawnPlayer, @NotNull final Location respawnedLocation, final boolean isBedSpawn) {
++ super(respawnPlayer);
++ this.respawnedLocation = respawnedLocation;
++ this.isBedSpawn = isBedSpawn;
++ }
++
++ /**
++ * Returns the location of the respawned player
++ *
++ * @return location of the respawned player
++ */
++ @NotNull
++ public Location getRespawnedLocation() {
++ return respawnedLocation.clone();
++ }
++
++ /**
++ * Checks if the player respawned to their bed
++ *
++ * @return whether the player respawned to their bed
++ */
++ public boolean isBedSpawn() {
++ return isBedSpawn;
++ }
++
++ @Override
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0175-Ignore-package-private-methods-for-nullability-annot.patch b/Spigot-API-Patches-Unmapped/0175-Ignore-package-private-methods-for-nullability-annot.patch
new file mode 100644
index 0000000000..28d9857af0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0175-Ignore-package-private-methods-for-nullability-annot.patch
@@ -0,0 +1,20 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 24 Mar 2019 18:44:26 -0400
+Subject: [PATCH] Ignore package-private methods for nullability annotations
+
+This isn't API
+
+diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
+index 2bb75e967d4250476a151599b68de80011d222b2..03229d5f4ec36a82197beb391356d791ff67fb2f 100644
+--- a/src/test/java/org/bukkit/AnnotationTest.java
++++ b/src/test/java/org/bukkit/AnnotationTest.java
+@@ -168,7 +168,7 @@ public class AnnotationTest {
+
+ private static boolean isMethodIncluded(@NotNull ClassNode clazz, @NotNull MethodNode method, @NotNull Map<String, ClassNode> allClasses) {
+ // Exclude private, synthetic and deprecated methods
+- if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0) {
++ if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0 || (method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0) { // Paper - ignore package-private
+ return false;
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0176-Flip-some-Spigot-API-null-annotations.patch b/Spigot-API-Patches-Unmapped/0176-Flip-some-Spigot-API-null-annotations.patch
new file mode 100644
index 0000000000..36b534410a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0176-Flip-some-Spigot-API-null-annotations.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sun, 24 Mar 2019 18:39:01 -0400
+Subject: [PATCH] Flip some Spigot API null annotations
+
+while some of these may of been true, they are extreme cases and cause
+a ton of noise to plugin developers.
+
+These do not help plugin developers if they bring moise noise than value.
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 908b75f0b9897ce830ed7c0a2c1dd0a458a872f1..1f5bcda86990d7c336db21d9c927bbf6b1b6d74d 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1377,7 +1377,7 @@ public final class Bukkit {
+ *
+ * @return the scoreboard manager or null if no worlds are loaded.
+ */
+- @Nullable
++ @NotNull // Paper
+ public static ScoreboardManager getScoreboardManager() {
+ return server.getScoreboardManager();
+ }
+@@ -1674,7 +1674,7 @@ public final class Bukkit {
+ * @param clazz the class of the tag entries
+ * @return the tag or null
+ */
+- @Nullable
++ @UndefinedNullability // Paper
+ public static <T extends Keyed> Tag<T> getTag(@NotNull String registry, @NotNull NamespacedKey tag, @NotNull Class<T> clazz) {
+ return server.getTag(registry, tag, clazz);
+ }
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 9c91c49ed7302c12fcb1d8e9bc58712efc199954..d5d67b3d84cd88ed0f858497e68535ec0162c700 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -46,7 +46,7 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ * @param y The y-coordinate of this new location
+ * @param z The z-coordinate of this new location
+ */
+- public Location(@Nullable final World world, final double x, final double y, final double z) {
++ public Location(@UndefinedNullability final World world, final double x, final double y, final double z) { // Paper
+ this(world, x, y, z, 0, 0);
+ }
+
+@@ -60,7 +60,7 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ * @param yaw The absolute rotation on the x-plane, in degrees
+ * @param pitch The absolute rotation on the y-plane, in degrees
+ */
+- public Location(@Nullable final World world, final double x, final double y, final double z, final float yaw, final float pitch) {
++ public Location(@UndefinedNullability final World world, final double x, final double y, final double z, final float yaw, final float pitch) {
+ if (world != null) {
+ this.world = new WeakReference<>(world);
+ }
+@@ -102,7 +102,7 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ * @throws IllegalArgumentException when world is unloaded
+ * @see #isWorldLoaded()
+ */
+- @Nullable
++ @UndefinedNullability
+ public World getWorld() {
+ if (this.world == null) {
+ return null;
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index a68b973e7574cae3ee7be7cfc51786589280408a..39188fcf95beff906c68a822f6aa5e19ad3ad08c 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1160,7 +1160,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ *
+ * @return the scoreboard manager or null if no worlds are loaded.
+ */
+- @Nullable
++ @NotNull // Paper
+ ScoreboardManager getScoreboardManager();
+
+ /**
+@@ -1430,7 +1430,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ * @param clazz the class of the tag entries
+ * @return the tag or null
+ */
+- @Nullable
++ @UndefinedNullability
+ <T extends Keyed> Tag<T> getTag(@NotNull String registry, @NotNull NamespacedKey tag, @NotNull Class<T> clazz);
+
+ /**
+diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
+index 3578f491a053154789ad696e93c70fdde74912e6..0654873eef22d1e35c7430f098ff9e8f00b870e3 100644
+--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
+@@ -3,6 +3,7 @@ package org.bukkit.inventory;
+ import org.bukkit.Color;
+ import org.bukkit.Material;
+ import org.bukkit.Server;
++import org.bukkit.UndefinedNullability;
+ import org.bukkit.inventory.meta.BookMeta;
+ import org.bukkit.inventory.meta.ItemMeta;
+ import org.bukkit.inventory.meta.SkullMeta;
+@@ -25,7 +26,7 @@ public interface ItemFactory {
+ * @return a new ItemMeta that could be applied to an item stack of the
+ * specified material
+ */
+- @Nullable
++ @UndefinedNullability // Paper
+ ItemMeta getItemMeta(@NotNull final Material material);
+
+ /**
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index ce9ee2d6d72069af518fc8d7d48a35c03b5f9f1f..3d63514729ddc30ff559a65815612be81e777892 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -8,6 +8,7 @@ import java.util.Set; // Paper
+ import org.apache.commons.lang.Validate;
+ import org.bukkit.Bukkit;
+ import org.bukkit.Material;
++import org.bukkit.UndefinedNullability;
+ import org.bukkit.Utility;
+ import org.bukkit.configuration.serialization.ConfigurationSerializable;
+ import org.bukkit.enchantments.Enchantment;
+@@ -546,7 +547,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ *
+ * @return a copy of the current ItemStack's ItemData
+ */
+- @Nullable
++ @UndefinedNullability // Paper
+ public ItemMeta getItemMeta() {
+ return this.meta == null ? Bukkit.getItemFactory().getItemMeta(this.type) : this.meta.clone();
+ }
diff --git a/Spigot-API-Patches-Unmapped/0177-Server-Tick-Events.patch b/Spigot-API-Patches-Unmapped/0177-Server-Tick-Events.patch
new file mode 100644
index 0000000000..9cb8b9a4c0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0177-Server-Tick-Events.patch
@@ -0,0 +1,110 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 27 Mar 2019 21:58:55 -0400
+Subject: [PATCH] Server Tick Events
+
+Fires event at start and end of a server tick
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java b/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9fd28e03649f66f71fb7f0536a137557ec32cd25
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/ServerTickEndEvent.java
+@@ -0,0 +1,59 @@
++package com.destroystokyo.paper.event.server;
++
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when the server has finished ticking the main loop
++ */
++public class ServerTickEndEvent extends Event {
++
++ private static final HandlerList handlers = new HandlerList();
++ private final int tickNumber;
++ private final double tickDuration;
++ private final long timeEnd;
++
++ public ServerTickEndEvent(int tickNumber, double tickDuration, long timeRemaining) {
++ this.tickNumber = tickNumber;
++ this.tickDuration = tickDuration;
++ this.timeEnd = System.nanoTime() + timeRemaining;
++ }
++
++ /**
++ * @return What tick this was since start (first tick = 1)
++ */
++ public int getTickNumber() {
++ return tickNumber;
++ }
++
++ /**
++ * @return Time in milliseconds of how long this tick took
++ */
++ public double getTickDuration() {
++ return tickDuration;
++ }
++
++ /**
++ * Amount of nanoseconds remaining before the next tick should start.
++ *
++ * If this value is negative, then that means the server has exceeded the tick time limit and TPS has been lost.
++ *
++ * Method will continously return the updated time remaining value. (return value is not static)
++ *
++ * @return Amount of nanoseconds remaining before the next tick should start
++ */
++ public long getTimeRemaining() {
++ return this.timeEnd - System.nanoTime();
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java b/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..eac85f1f49088bb71afb01eff4d5f53887306461
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/server/ServerTickStartEvent.java
+@@ -0,0 +1,32 @@
++package com.destroystokyo.paper.event.server;
++
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++public class ServerTickStartEvent extends Event {
++
++ private static final HandlerList handlers = new HandlerList();
++ private final int tickNumber;
++
++ public ServerTickStartEvent(int tickNumber) {
++ this.tickNumber = tickNumber;
++ }
++
++ /**
++ * @return What tick this is going be since start (first tick = 1)
++ */
++ public int getTickNumber() {
++ return tickNumber;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0178-PlayerDeathEvent-getItemsToKeep.patch b/Spigot-API-Patches-Unmapped/0178-PlayerDeathEvent-getItemsToKeep.patch
new file mode 100644
index 0000000000..e41136bc7f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0178-PlayerDeathEvent-getItemsToKeep.patch
@@ -0,0 +1,63 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Mon, 11 Mar 2013 20:04:34 -0400
+Subject: [PATCH] PlayerDeathEvent#getItemsToKeep
+
+Exposes a mutable array on items a player should keep on death
+
+Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4
+
+diff --git a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+index a01d4c21bedc7f1a54f5a330bb4c2909ce3a18e4..8c46eaebf004823c1c31eb2c7304181487cb1332 100644
+--- a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
++++ b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+@@ -36,7 +36,6 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ }
+ // Paper end
+
+- @Deprecated // Paper
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final String deathMessage) {
+ this(player, drops, droppedExp, 0, deathMessage);
+ }
+@@ -56,6 +55,41 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ this.adventure$deathMessage = deathMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(deathMessage) : net.kyori.adventure.text.Component.empty(); // Paper
+ }
+
++ @Deprecated // Paper
++ // Paper start
++ private List<ItemStack> itemsToKeep = new java.util.ArrayList<>();
++
++ /**
++ * A mutable collection to add items that the player should retain in their inventory on death (Similar to KeepInventory game rule)
++ *
++ * You <b>MUST</b> remove the item from the .getDrops() collection too or it will duplicate!
++ * <pre>{@code
++ * {@literal @EventHandler(ignoreCancelled = true)}
++ * public void onPlayerDeath(PlayerDeathEvent event) {
++ * for (Iterator<ItemStack> iterator = event.getDrops().iterator(); iterator.hasNext(); ) {
++ * ItemStack drop = iterator.next();
++ * List<String> lore = drop.getLore();
++ * if (lore != null && !lore.isEmpty()) {
++ * if (lore.get(0).contains("(SOULBOUND)")) {
++ * iterator.remove();
++ * event.getItemsToKeep().add(drop);
++ * }
++ * }
++ * }
++ * }
++ * }</pre>
++ *
++ * Adding an item to this list that the player did not previously have will give them the item on death.
++ * An example case could be a "Note" that "You died at X/Y/Z coordinates"
++ *
++ * @return The list to hold items to keep
++ */
++ @NotNull
++ public List<ItemStack> getItemsToKeep() {
++ return itemsToKeep;
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public Player getEntity() {
diff --git a/Spigot-API-Patches-Unmapped/0179-Add-Heightmap-API.patch b/Spigot-API-Patches-Unmapped/0179-Add-Heightmap-API.patch
new file mode 100644
index 0000000000..73e1e8d46f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0179-Add-Heightmap-API.patch
@@ -0,0 +1,196 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Sat, 1 Dec 2018 19:00:36 -0800
+Subject: [PATCH] Add Heightmap API
+
+Changed to use upstream's heightmap API - Machine_Maker
+
+diff --git a/src/main/java/com/destroystokyo/paper/HeightmapType.java b/src/main/java/com/destroystokyo/paper/HeightmapType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..709e44ea1b14ab6917501c928e689cc6cbdf4bb4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/HeightmapType.java
+@@ -0,0 +1,39 @@
++package com.destroystokyo.paper;
++
++import org.bukkit.*;
++
++/**
++ * Enumeration of different heightmap types maintained by the server. Generally using these maps is much faster
++ * than using an iterative search for a block in a given x, z coordinate.
++ *
++ * @deprecated Upstream has added their own API for using the game heightmaps. See {@link org.bukkit.HeightMap} and the
++ * non-deprecated getHighestBlock methods on World such as {@link org.bukkit.World#getHighestBlockAt(Location, HeightMap)}.
++ */
++@Deprecated
++public enum HeightmapType {
++
++ /**
++ * The highest block used for lighting in the world. Also the block returned by {@link World#getHighestBlockYAt(int, int)}}
++ */
++ LIGHT_BLOCKING,
++
++ /**
++ * References the highest block in the world.
++ */
++ ANY,
++
++ /**
++ * References the highest solid block in a world.
++ */
++ SOLID,
++
++ /**
++ * References the highest solid or liquid block in a world.
++ */
++ SOLID_OR_LIQUID,
++
++ /**
++ * References the highest solid or liquid block in a world, excluding leaves.
++ */
++ SOLID_OR_LIQUID_NO_LEAVES;
++}
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index d5d67b3d84cd88ed0f858497e68535ec0162c700..432d5711b7ec34eafeb27df82d367612dfe1fe54 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -638,6 +638,47 @@ public class Location implements Cloneable, ConfigurationSerializable {
+ return centerLoc;
+ }
+
++ // Paper start - Add heightmap api
++
++ /**
++ * Returns a copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ())
++ * @return A copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ())
++ * @throws NullPointerException if {{@link #getWorld()}} is {@code null}
++ */
++ @NotNull
++ public Location toHighestLocation() {
++ return this.toHighestLocation(HeightMap.WORLD_SURFACE);
++ }
++
++ /**
++ * Returns a copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightmap)
++ * @param heightmap The heightmap to use for finding the highest y location.
++ * @return A copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightmap)
++ * @throws NullPointerException if {{@link #getWorld()}} is {@code null}
++ * @throws UnsupportedOperationException if {@link World#getHighestBlockYAt(int, int, com.destroystokyo.paper.HeightmapType)} does not support the specified heightmap
++ * @deprecated Use {@link org.bukkit.Location#toHighestLocation(HeightMap)}
++ */
++ @NotNull
++ @Deprecated
++ public Location toHighestLocation(@NotNull final com.destroystokyo.paper.HeightmapType heightmap) {
++ final Location ret = this.clone();
++ ret.setY(this.getWorld().getHighestBlockYAt(this, heightmap));
++ return ret;
++ }
++
++ /**
++ * Returns a copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightMap)
++ * @param heightMap The heightmap to use for finding the highest y location.
++ * @return A copy of this location except with y = getWorld().getHighestBlockYAt(this.getBlockX(), this.getBlockZ(), heightMap)
++ */
++ @NotNull
++ public Location toHighestLocation(@NotNull final HeightMap heightMap) {
++ final Location ret = this.clone();
++ ret.setY(this.getWorld().getHighestBlockYAt(this, heightMap));
++ return ret;
++ }
++ // Paper end
++
+ /**
+ * Creates explosion at this location with given power
+ *
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 19060c7d9c74b9b2808103f3d5627444aece6ccb..f8e104d5e61e52c5fe658d7cda373f62424c7bea 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -160,6 +160,87 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public Block getHighestBlockAt(@NotNull Location location);
+
++ // Paper start - Add heightmap API
++ /**
++ * Returns the highest block's y-coordinate at the specified block coordinates that match the specified heightmap's conditions.
++ * <p>
++ * <b>implNote:</b> Implementations are recommended to use an iterative search as a fallback before resorting to
++ * throwing an {@code UnsupportedOperationException}.
++ * </p>
++ *
++ * @param x The block's x-coordinate.
++ * @param z The block's z-coordinate.
++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType}
++ * @return The highest block's y-coordinate at (x, z) that matches the specified heightmap's conditions.
++ * @throws UnsupportedOperationException If the heightmap type is not supported.
++ * @deprecated Upstream has added support for this, use {@link World#getHighestBlockYAt(int, int, HeightMap)}
++ *
++ * @see com.destroystokyo.paper.HeightmapType
++ */
++ @Deprecated
++ public int getHighestBlockYAt(int x, int z, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException;
++
++ /**
++ * Returns the highest block's y-coordinate at the specified block coordinates that match the specified heightmap's conditions.
++ * Note that the y-coordinate of the specified location is ignored.
++ * <p>
++ * <b>implNote:</b> Implementations are recommended to use an iterative search as a fallback before resorting to
++ * throwing an {@code UnsupportedOperationException}.
++ * </p>
++ *
++ * @param location The specified block coordinates.
++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType}
++ * @return The highest block's y-coordinate at {@code location} that matches the specified heightmap's conditions.
++ * @throws UnsupportedOperationException If the heightmap type is not supported.
++ * @deprecated Upstream has added support for this, use {@link World#getHighestBlockYAt(Location, HeightMap)}
++ * @see com.destroystokyo.paper.HeightmapType
++ */
++ @Deprecated
++ default int getHighestBlockYAt(@NotNull Location location, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
++ return this.getHighestBlockYAt(location.getBlockX(), location.getBlockZ(), heightmap);
++ }
++
++ /**
++ * Returns the highest {@link Block} at the specified block coordinates that match the specified heightmap's conditions.
++ * <p>
++ * <b>implNote:</b> Implementations are recommended to use an iterative search as a fallback before resorting to
++ * throwing an {@code UnsupportedOperationException}.
++ * </p>
++ * @param x The block's x-coordinate.
++ * @param z The block's z-coordinate.
++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType}
++ * @return The highest {@link Block} at (x, z) that matches the specified heightmap's conditions.
++ * @throws UnsupportedOperationException If the heightmap type is not supported.
++ * @deprecated Upstream has added support for this, use {@link World#getHighestBlockAt(int, int, HeightMap)}
++ * @see com.destroystokyo.paper.HeightmapType
++ */
++ @Deprecated
++ @NotNull
++ default Block getHighestBlockAt(int x, int z, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
++ return this.getBlockAt(x, this.getHighestBlockYAt(x, z, heightmap), z);
++ }
++
++ /**
++ * Returns the highest {@link Block} at the specified block coordinates that match the specified heightmap's conditions.
++ * Note that the y-coordinate of the specified location is ignored.
++ * <p>
++ * <b>implNote:</b> Implementations are recommended to use an iterative search as a fallback before resorting to
++ * throwing an {@code UnsupportedOperationException}.
++ * </p>
++ * @param location The specified block coordinates.
++ * @param heightmap The specified heightmap to use. See {@link com.destroystokyo.paper.HeightmapType}
++ * @return The highest {@link Block} at {@code location} that matches the specified heightmap's conditions.
++ * @throws UnsupportedOperationException If the heightmap type is not supported.
++ * @deprecated Upstream has added support for this, use {@link World#getHighestBlockAt(Location, HeightMap)}
++ * @see com.destroystokyo.paper.HeightmapType
++ */
++ @Deprecated
++ @NotNull
++ default Block getHighestBlockAt(@NotNull Location location, @NotNull com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
++ return this.getHighestBlockAt(location.getBlockX(), location.getBlockZ(), heightmap);
++ }
++ // Paper end
++
+ /**
+ * Gets the highest coordinate corresponding to the {@link HeightMap} at the
+ * given coordinates.
diff --git a/Spigot-API-Patches-Unmapped/0180-Mob-Spawner-API-Enhancements.patch b/Spigot-API-Patches-Unmapped/0180-Mob-Spawner-API-Enhancements.patch
new file mode 100644
index 0000000000..2bbe92cbe9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0180-Mob-Spawner-API-Enhancements.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Fri, 19 Apr 2019 12:41:19 -0500
+Subject: [PATCH] Mob Spawner API Enhancements
+
+
+diff --git a/src/main/java/org/bukkit/block/CreatureSpawner.java b/src/main/java/org/bukkit/block/CreatureSpawner.java
+index cb447a4ad5a9dce7c98999a5d7fcd6111fc9b10e..5bbae759ce39d42886994e500fd9454ec328f804 100644
+--- a/src/main/java/org/bukkit/block/CreatureSpawner.java
++++ b/src/main/java/org/bukkit/block/CreatureSpawner.java
+@@ -199,4 +199,30 @@ public interface CreatureSpawner extends TileState {
+ * @see #getSpawnRange()
+ */
+ public void setSpawnRange(int spawnRange);
++
++ // Paper start
++ /**
++ * Check if spawner is activated (a player is close enough)
++ *
++ * @return True if a player is close enough to activate it
++ */
++ public boolean isActivated();
++
++ /**
++ * Resets the spawn delay timer within the min/max range
++ */
++ public void resetTimer();
++
++ /**
++ * Sets the {@link EntityType} to {@link EntityType#DROPPED_ITEM} and sets the data to the given
++ * {@link org.bukkit.inventory.ItemStack ItemStack}.
++ * <p>
++ * {@link #setSpawnCount(int)} does not dictate the amount of items in the stack spawned, but rather how many
++ * stacks should be spawned.
++ *
++ * @param itemStack The item to spawn. Must not {@link org.bukkit.Material#isAir be air}.
++ * @see #setSpawnedType(EntityType)
++ */
++ void setSpawnedItem(@NotNull org.bukkit.inventory.ItemStack itemStack);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0181-Add-BlockSoundGroup-interface.patch b/Spigot-API-Patches-Unmapped/0181-Add-BlockSoundGroup-interface.patch
new file mode 100644
index 0000000000..428810f1d8
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0181-Add-BlockSoundGroup-interface.patch
@@ -0,0 +1,94 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: simpleauthority <[email protected]>
+Date: Tue, 28 May 2019 03:41:28 -0700
+Subject: [PATCH] Add BlockSoundGroup interface
+
+This PR adds the getSoundGroup() method in Block which returns a BlockSoundGroup
+
+diff --git a/src/main/java/com/destroystokyo/paper/block/BlockSoundGroup.java b/src/main/java/com/destroystokyo/paper/block/BlockSoundGroup.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8cf87d228a7006658d52ce0da16c2d74f4706545
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/block/BlockSoundGroup.java
+@@ -0,0 +1,52 @@
++package com.destroystokyo.paper.block;
++
++import org.bukkit.Sound;
++import org.bukkit.block.Block;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Represents the sounds that a {@link Block} makes in certain situations
++ * <p>
++ * The sound group includes break, step, place, hit, and fall sounds.
++ */
++public interface BlockSoundGroup {
++ /**
++ * Gets the sound that plays when breaking this block
++ *
++ * @return The break sound
++ */
++ @NotNull
++ Sound getBreakSound();
++
++ /**
++ * Gets the sound that plays when stepping on this block
++ *
++ * @return The step sound
++ */
++ @NotNull
++ Sound getStepSound();
++
++ /**
++ * Gets the sound that plays when placing this block
++ *
++ * @return The place sound
++ */
++ @NotNull
++ Sound getPlaceSound();
++
++ /**
++ * Gets the sound that plays when hitting this block
++ *
++ * @return The hit sound
++ */
++ @NotNull
++ Sound getHitSound();
++
++ /**
++ * Gets the sound that plays when this block falls
++ *
++ * @return The fall sound
++ */
++ @NotNull
++ Sound getFallSound();
++}
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index b090938f883c486e703cb7c036c47925f3016704..5e2aa4fb8cf8130df21d3172dd94e857317f7653 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -1,6 +1,7 @@
+ package org.bukkit.block;
+
+ import java.util.Collection;
++
+ import org.bukkit.Chunk;
+ import org.bukkit.FluidCollisionMode;
+ import org.bukkit.Location;
+@@ -560,4 +561,16 @@ public interface Block extends Metadatable {
+ */
+ @NotNull
+ BoundingBox getBoundingBox();
++
++ // Paper start
++ /**
++ * Gets the {@link com.destroystokyo.paper.block.BlockSoundGroup} for this block.
++ * <p>
++ * This object contains the block, step, place, hit, and fall sounds.
++ *
++ * @return the sound group for this block
++ */
++ @NotNull
++ com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0182-Amend-PlayerInteractAtEntityEvent-javadoc-for-ArmorS.patch b/Spigot-API-Patches-Unmapped/0182-Amend-PlayerInteractAtEntityEvent-javadoc-for-ArmorS.patch
new file mode 100644
index 0000000000..b5148c5574
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0182-Amend-PlayerInteractAtEntityEvent-javadoc-for-ArmorS.patch
@@ -0,0 +1,20 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: simpleauthority <[email protected]>
+Date: Thu, 11 Jul 2019 10:35:56 -0700
+Subject: [PATCH] Amend PlayerInteractAtEntityEvent javadoc for ArmorStands
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java b/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java
+index 1075dbb8135d2fd2fd69d1e821dd1ba7f90824d5..3f24d302e28170f7f6e5885b5b9abb22cbbb7d66 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java
+@@ -13,6 +13,9 @@ import org.jetbrains.annotations.NotNull;
+ * <br>
+ * Note that the client may sometimes spuriously send this packet in addition to {@link PlayerInteractEntityEvent}.
+ * Users are advised to listen to this (parent) class unless specifically required.
++ * <br>
++ * Note that interacting with Armor Stands fires this event only and not its parent and as such users are expressly required
++ * to listen to this event for that scenario.
+ */
+ public class PlayerInteractAtEntityEvent extends PlayerInteractEntityEvent {
+ private static final HandlerList handlers = new HandlerList();
diff --git a/Spigot-API-Patches-Unmapped/0183-Increase-custom-payload-channel-message-size.patch b/Spigot-API-Patches-Unmapped/0183-Increase-custom-payload-channel-message-size.patch
new file mode 100644
index 0000000000..24aacd2b29
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0183-Increase-custom-payload-channel-message-size.patch
@@ -0,0 +1,21 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Fri, 18 Oct 2019 17:39:05 +0100
+Subject: [PATCH] Increase custom payload channel message size
+
+Doubles the custom payload size limit imposed by bukkit, also creates a system
+property to allow customizing the size `paper.maxCustomChannelName`
+
+diff --git a/src/main/java/org/bukkit/plugin/messaging/Messenger.java b/src/main/java/org/bukkit/plugin/messaging/Messenger.java
+index 9d2c68c826f3b867d407e7f13c6394a899cc8ee8..682c77188436d696d4dafbc70cf131d5c921e94d 100644
+--- a/src/main/java/org/bukkit/plugin/messaging/Messenger.java
++++ b/src/main/java/org/bukkit/plugin/messaging/Messenger.java
+@@ -24,7 +24,7 @@ public interface Messenger {
+ /**
+ * Represents the largest size that a Plugin Channel may be.
+ */
+- public static final int MAX_CHANNEL_SIZE = 64;
++ public static final int MAX_CHANNEL_SIZE = Integer.getInteger("paper.maxCustomChannelName", 64); // Paper
+
+ /**
+ * Checks if the specified channel is a reserved name.
diff --git a/Spigot-API-Patches-Unmapped/0184-Expose-the-internal-current-tick.patch b/Spigot-API-Patches-Unmapped/0184-Expose-the-internal-current-tick.patch
new file mode 100644
index 0000000000..1370e6830d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0184-Expose-the-internal-current-tick.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Sat, 20 Apr 2019 19:47:29 -0500
+Subject: [PATCH] Expose the internal current tick
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 1f5bcda86990d7c336db21d9c927bbf6b1b6d74d..3e2d644806233bb72f2f6637ec6edde4e3bcfdc9 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1823,6 +1823,10 @@ public final class Bukkit {
+ public static com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
+ return server.createProfile(uuid, name);
+ }
++
++ public static int getCurrentTick() {
++ return server.getCurrentTick();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 39188fcf95beff906c68a822f6aa5e19ad3ad08c..f107b5658b60e59e9d98bc86a0313f4da201c190 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1598,5 +1598,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ */
+ @NotNull
+ com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name);
++
++ /**
++ * Get the current internal server tick
++ *
++ * @return Current tick
++ */
++ int getCurrentTick();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0185-PlayerDeathEvent-shouldDropExperience.patch b/Spigot-API-Patches-Unmapped/0185-PlayerDeathEvent-shouldDropExperience.patch
new file mode 100644
index 0000000000..f19bdcd569
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0185-PlayerDeathEvent-shouldDropExperience.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Tue, 24 Dec 2019 00:35:31 +0000
+Subject: [PATCH] PlayerDeathEvent#shouldDropExperience
+
+
+diff --git a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+index 7b78afe9281681cb9262fa044c1069a6121358eb..46917615ac4734bf5fa4ddea497132466eb5cc35 100644
+--- a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
++++ b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java
+@@ -1,6 +1,8 @@
+ package org.bukkit.event.entity;
+
+ import java.util.List;
++
++import org.bukkit.GameMode;
+ import org.bukkit.entity.Player;
+ import org.bukkit.inventory.ItemStack;
+ import org.jetbrains.annotations.NotNull;
+@@ -18,6 +20,8 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ private boolean keepLevel = false;
+ private boolean keepInventory = false;
+ // Paper start
++ private boolean doExpDrop;
++
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage) {
+ this(player, drops, droppedExp, 0, adventure$deathMessage, null);
+ }
+@@ -27,12 +31,17 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ }
+
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage) {
++ this(player, drops, droppedExp, newExp, newTotalExp, newLevel, adventure$deathMessage, deathMessage, true);
++ }
++
++ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final net.kyori.adventure.text.Component adventure$deathMessage, @Nullable String deathMessage, boolean doExpDrop) {
+ super(player, drops, droppedExp);
+ this.newExp = newExp;
+ this.newTotalExp = newTotalExp;
+ this.newLevel = newLevel;
+ this.deathMessage = deathMessage;
+ this.adventure$deathMessage = adventure$deathMessage;
++ this.doExpDrop = doExpDrop;
+ }
+ // Paper end
+
+@@ -47,6 +56,11 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+
+ @Deprecated // Paper
+ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final String deathMessage) {
++ this(player, drops, droppedExp, newExp, newTotalExp, newLevel, deathMessage, true);
++ }
++
++ @Deprecated // Paper
++ public PlayerDeathEvent(@NotNull final Player player, @NotNull final List<ItemStack> drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, @Nullable final String deathMessage, boolean doExpDrop) {
+ super(player, drops, droppedExp);
+ this.newExp = newExp;
+ this.newTotalExp = newTotalExp;
+@@ -88,6 +102,20 @@ public class PlayerDeathEvent extends EntityDeathEvent {
+ public List<ItemStack> getItemsToKeep() {
+ return itemsToKeep;
+ }
++
++ /**
++ * @return should experience be dropped from this death
++ */
++ public boolean shouldDropExperience() {
++ return doExpDrop;
++ }
++
++ /**
++ * @param doExpDrop sets if experience should be dropped from this death
++ */
++ public void setShouldDropExperience(boolean doExpDrop) {
++ this.doExpDrop = doExpDrop;
++ }
+ // Paper end
+
+ @NotNull
diff --git a/Spigot-API-Patches-Unmapped/0186-Add-effect-to-block-break-naturally.patch b/Spigot-API-Patches-Unmapped/0186-Add-effect-to-block-break-naturally.patch
new file mode 100644
index 0000000000..7c52aaa446
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0186-Add-effect-to-block-break-naturally.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Thu, 2 Jan 2020 12:25:16 -0600
+Subject: [PATCH] Add effect to block break naturally
+
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index d4ba9c2b858204825d47fd6e91dab8c003df085a..f8c599718143fe638de422fd4625f353ee6c54ae 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -469,6 +469,18 @@ public interface Block extends Metadatable {
+ */
+ boolean breakNaturally(@Nullable ItemStack tool);
+
++ // Paper start
++ /**
++ * Breaks the block and spawns items as if a player had digged it with a
++ * specific tool
++ *
++ * @param tool The tool or item in hand used for digging
++ * @param triggerEffect Play the block break particle effect and sound
++ * @return true if the block was destroyed
++ */
++ boolean breakNaturally(@NotNull ItemStack tool, boolean triggerEffect);
++ // Paper end
++
+ /**
+ * Simulate bone meal application to this block (if possible).
+ *
diff --git a/Spigot-API-Patches-Unmapped/0187-Add-ThrownEggHatchEvent.patch b/Spigot-API-Patches-Unmapped/0187-Add-ThrownEggHatchEvent.patch
new file mode 100644
index 0000000000..f605a2a11b
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0187-Add-ThrownEggHatchEvent.patch
@@ -0,0 +1,129 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Sun, 9 Feb 2020 00:19:08 -0600
+Subject: [PATCH] Add ThrownEggHatchEvent
+
+Adds a new event similar to PlayerEggThrowEvent, but without the Player requirement
+(dispensers can throw eggs to hatch them, too).
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/ThrownEggHatchEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/ThrownEggHatchEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..085d77dde83d6ed13eb83f23cf3e51d380187c9c
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/ThrownEggHatchEvent.java
+@@ -0,0 +1,115 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.Egg;
++import org.bukkit.entity.EntityType;
++import org.bukkit.event.Event;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a thrown egg might hatch.
++ * <p>
++ * This event fires for all thrown eggs that may hatch, players, dispensers, etc.
++ */
++public class ThrownEggHatchEvent extends Event {
++ private static final HandlerList handlers = new HandlerList();
++ private final Egg egg;
++ private boolean hatching;
++ private EntityType hatchType;
++ private byte numHatches;
++
++ public ThrownEggHatchEvent(@NotNull final Egg egg, final boolean hatching, final byte numHatches, @NotNull final EntityType hatchingType) {
++ this.egg = egg;
++ this.hatching = hatching;
++ this.numHatches = numHatches;
++ this.hatchType = hatchingType;
++ }
++
++ /**
++ * Gets the egg involved in this event.
++ *
++ * @return the egg involved in this event
++ */
++ @NotNull
++ public Egg getEgg() {
++ return egg;
++ }
++
++ /**
++ * Gets whether the egg is hatching or not. Will be what the server
++ * would've done without interaction.
++ *
++ * @return boolean Whether the egg is going to hatch or not
++ */
++ public boolean isHatching() {
++ return hatching;
++ }
++
++ /**
++ * Sets whether the egg will hatch or not.
++ *
++ * @param hatching true if you want the egg to hatch, false if you want it
++ * not to
++ */
++ public void setHatching(boolean hatching) {
++ this.hatching = hatching;
++ }
++
++ /**
++ * Get the type of the mob being hatched (EntityType.CHICKEN by default)
++ *
++ * @return The type of the mob being hatched by the egg
++ */
++ @NotNull
++ public EntityType getHatchingType() {
++ return hatchType;
++ }
++
++ /**
++ * Change the type of mob being hatched by the egg
++ *
++ * @param hatchType The type of the mob being hatched by the egg
++ */
++ public void setHatchingType(@NotNull EntityType hatchType) {
++ if (!hatchType.isSpawnable()) throw new IllegalArgumentException("Can't spawn that entity type from an egg!");
++ this.hatchType = hatchType;
++ }
++
++ /**
++ * Get the number of mob hatches from the egg. By default the number will
++ * be the number the server would've done
++ * <ul>
++ * <li>7/8 chance of being 0
++ * <li>31/256 ~= 1/8 chance to be 1
++ * <li>1/256 chance to be 4
++ * </ul>
++ *
++ * @return The number of mobs going to be hatched by the egg
++ */
++ public byte getNumHatches() {
++ return numHatches;
++ }
++
++ /**
++ * Change the number of mobs coming out of the hatched egg
++ * <p>
++ * The boolean hatching will override this number. Ie. If hatching =
++ * false, this number will not matter
++ *
++ * @param numHatches The number of mobs coming out of the egg
++ */
++ public void setNumHatches(byte numHatches) {
++ this.numHatches = numHatches;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0188-Entity-Jump-API.patch b/Spigot-API-Patches-Unmapped/0188-Entity-Jump-API.patch
new file mode 100644
index 0000000000..0841810517
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0188-Entity-Jump-API.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sat, 8 Feb 2020 23:26:18 -0600
+Subject: [PATCH] Entity Jump API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityJumpEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityJumpEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f0067c2e953d18e1a33536980071ba3f0152ecb4
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityJumpEvent.java
+@@ -0,0 +1,46 @@
++package com.destroystokyo.paper.event.entity;
++
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when an entity jumps
++ * <p>
++ * Cancelling the event will stop the entity from jumping
++ */
++public class EntityJumpEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean canceled;
++
++ public EntityJumpEvent(@NotNull LivingEntity entity) {
++ super(entity);
++ }
++
++ @NotNull
++ @Override
++ public LivingEntity getEntity() {
++ return (LivingEntity) entity;
++ }
++
++ public boolean isCancelled() {
++ return canceled;
++ }
++
++ public void setCancelled(boolean cancel) {
++ canceled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 561db9d594633e3909fd6d69dad1dc2976928d58..a2f5639904881d9bef7d1550dbed810e4b17c8de 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -785,5 +785,25 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @return Whether or not this entity is using or charging an attack (Bow pulled back, drinking potion, eating food)
+ */
+ boolean isHandRaised();
++
++ /**
++ * Get entity jump state.
++ * <p>
++ * Jump state will be true when the entity has been marked to jump.
++ *
++ * @return entity jump state.
++ */
++ boolean isJumping();
++
++ /**
++ * Set entity jump state
++ * <p>
++ * Setting to true will mark the entity to jump.
++ * <p>
++ * Setting to false will unmark the entity to jump but will not stop a jump already in-progress.
++ *
++ * @param jumping entity jump state
++ */
++ void setJumping(boolean jumping);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0189-add-hand-to-BlockMultiPlaceEvent.patch b/Spigot-API-Patches-Unmapped/0189-add-hand-to-BlockMultiPlaceEvent.patch
new file mode 100644
index 0000000000..f61bddb960
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0189-add-hand-to-BlockMultiPlaceEvent.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Trigary <[email protected]>
+Date: Sun, 1 Mar 2020 22:43:34 +0100
+Subject: [PATCH] add hand to BlockMultiPlaceEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java b/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java
+index fe2ec79dd6084509cb8cbb8155a356f4162466fd..8460aa4f5e211fb0a6ab11f7487d38dba927e1da 100644
+--- a/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java
++++ b/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java
+@@ -18,9 +18,17 @@ import org.jetbrains.annotations.NotNull;
+ public class BlockMultiPlaceEvent extends BlockPlaceEvent {
+ private final List<BlockState> states;
+
++ @Deprecated // Paper
+ public BlockMultiPlaceEvent(@NotNull List<BlockState> states, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild) {
+- super(states.get(0).getBlock(), states.get(0), clicked, itemInHand, thePlayer, canBuild);
++ //Paper start - add hand to BlockMultiPlaceEvent
++ this(states, clicked, itemInHand, thePlayer, canBuild, org.bukkit.inventory.EquipmentSlot.HAND);
++ }
++
++
++ public BlockMultiPlaceEvent(@NotNull List<BlockState> states, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild, @NotNull org.bukkit.inventory.EquipmentSlot hand) {
++ super(states.get(0).getBlock(), states.get(0), clicked, itemInHand, thePlayer, canBuild, hand);
+ this.states = ImmutableList.copyOf(states);
++ //Paper end
+ }
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0190-Add-tick-times-API.patch b/Spigot-API-Patches-Unmapped/0190-Add-tick-times-API.patch
new file mode 100644
index 0000000000..17977550d7
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0190-Add-tick-times-API.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Sun, 5 Apr 2020 22:22:58 -0500
+Subject: [PATCH] Add tick times API
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 3e2d644806233bb72f2f6637ec6edde4e3bcfdc9..296c8fbb9239ab28f5ebbd4c322a3b9226a3ce11 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1578,6 +1578,25 @@ public final class Bukkit {
+ public static double[] getTPS() {
+ return server.getTPS();
+ }
++
++ /**
++ * Get a sample of the servers last tick times (in nanos)
++ *
++ * @return A sample of the servers last tick times (in nanos)
++ */
++ @NotNull
++ public static long[] getTickTimes() {
++ return server.getTickTimes();
++ }
++
++ /**
++ * Get the average tick time (in millis)
++ *
++ * @return Average tick time (in millis)
++ */
++ public static double getAverageTickTime() {
++ return server == null ? 0D : server.getAverageTickTime();
++ }
+ // Paper end
+
+ /**
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index f107b5658b60e59e9d98bc86a0313f4da201c190..7195b480a16aa67f8e06fd3064b1072eafbb27b2 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1334,6 +1334,21 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ */
+ @NotNull
+ public double[] getTPS();
++
++ /**
++ * Get a sample of the servers last tick times (in nanos)
++ *
++ * @return A sample of the servers last tick times (in nanos)
++ */
++ @NotNull
++ long[] getTickTimes();
++
++ /**
++ * Get the average tick time (in millis)
++ *
++ * @return Average tick time (in millis)
++ */
++ double getAverageTickTime();
+ // Paper end
+
+ // Paper start
diff --git a/Spigot-API-Patches-Unmapped/0191-Expose-MinecraftServer-isRunning.patch b/Spigot-API-Patches-Unmapped/0191-Expose-MinecraftServer-isRunning.patch
new file mode 100644
index 0000000000..f98e0d9d7a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0191-Expose-MinecraftServer-isRunning.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: JRoy <[email protected]>
+Date: Fri, 10 Apr 2020 21:24:35 -0400
+Subject: [PATCH] Expose MinecraftServer#isRunning
+
+This allows for plugins to detect if the server is actually turning off in onDisable rather than just plugins reloading.
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 296c8fbb9239ab28f5ebbd4c322a3b9226a3ce11..86ac6702b3aeab1126b2b2879b87ef3883793d44 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1846,6 +1846,15 @@ public final class Bukkit {
+ public static int getCurrentTick() {
+ return server.getCurrentTick();
+ }
++
++ /**
++ * Checks if the server is in the process of being shutdown.
++ *
++ * @return true if server is in the process of being shutdown
++ */
++ public static boolean isStopping() {
++ return server.isStopping();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 7195b480a16aa67f8e06fd3064b1072eafbb27b2..16a74b834c4d4b907f9b11ccf9ef9804514df224 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1620,5 +1620,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ * @return Current tick
+ */
+ int getCurrentTick();
++
++ /**
++ * Checks if the server is in the process of being shutdown.
++ *
++ * @return true if server is in the process of being shutdown
++ */
++ boolean isStopping();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0192-Disable-Sync-Events-firing-Async-errors-during-shutd.patch b/Spigot-API-Patches-Unmapped/0192-Disable-Sync-Events-firing-Async-errors-during-shutd.patch
new file mode 100644
index 0000000000..5ca0264ed8
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0192-Disable-Sync-Events-firing-Async-errors-during-shutd.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 11 Apr 2020 21:38:59 -0400
+Subject: [PATCH] Disable Sync Events firing Async errors during shutdown
+
+This is how it use to behave on Paper, and this is totally destroying
+the ability to try to shut the server down gracefully during the
+shutdown process as events firing on the watchdog thread are throwing
+errors.
+
+This isn't an issue on Spigot
+
+diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+index a1a805004941d67abb0b9aa1721e0370c45b5289..26685f59b235ea5b4c4fb7ae21acb5149edaa2b3 100644
+--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+@@ -591,7 +591,7 @@ public final class SimplePluginManager implements PluginManager {
+ // Paper - replace callEvent by merging to below method
+ if (event.isAsynchronous() && server.isPrimaryThread()) {
+ throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
+- } else if (!event.isAsynchronous() && !server.isPrimaryThread()) {
++ } else if (!event.isAsynchronous() && !server.isPrimaryThread() && !server.isStopping() ) {
+ throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0193-Make-JavaPluginLoader-thread-safe.patch b/Spigot-API-Patches-Unmapped/0193-Make-JavaPluginLoader-thread-safe.patch
new file mode 100644
index 0000000000..21dbe589a4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0193-Make-JavaPluginLoader-thread-safe.patch
@@ -0,0 +1,59 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Trigary <[email protected]>
+Date: Wed, 15 Apr 2020 01:24:55 -0400
+Subject: [PATCH] Make JavaPluginLoader thread-safe
+
+
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index de44d850d7b3ab3e528eb6f2de375a6c3e0e5cf9..9d1f7cdf12029c8198792fd299f92be476040222 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -52,6 +52,8 @@ public final class JavaPluginLoader implements PluginLoader {
+ final Server server;
+ private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
+ private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
++ private final Map<String, java.util.concurrent.locks.ReentrantReadWriteLock> classLoadLock = new java.util.HashMap<String, java.util.concurrent.locks.ReentrantReadWriteLock>(); // Paper
++ private final Map<String, Integer> classLoadLockCount = new java.util.HashMap<String, Integer>(); // Paper
+ private final List<PluginClassLoader> loaders = new CopyOnWriteArrayList<PluginClassLoader>();
+
+ /**
+@@ -191,7 +193,19 @@ public final class JavaPluginLoader implements PluginLoader {
+
+ @Nullable
+ Class<?> getClassByName(final String name) {
++ // Paper start - make MT safe
+ Class<?> cachedClass = classes.get(name);
++ if (cachedClass != null) {
++ return cachedClass;
++ }
++ java.util.concurrent.locks.ReentrantReadWriteLock lock;
++ synchronized (classLoadLock) {
++ lock = classLoadLock.computeIfAbsent(name, (x) -> new java.util.concurrent.locks.ReentrantReadWriteLock());
++ classLoadLockCount.compute(name, (x, prev) -> prev != null ? prev + 1 : 1);
++ }
++ lock.writeLock().lock();try {
++ cachedClass = classes.get(name);
++ // Paper end
+
+ if (cachedClass != null) {
+ return cachedClass;
+@@ -205,6 +219,19 @@ public final class JavaPluginLoader implements PluginLoader {
+ }
+ }
+ }
++ // Paper start - make MT safe
++ } finally {
++ synchronized (classLoadLock) {
++ lock.writeLock().unlock();
++ if (classLoadLockCount.get(name) == 1) {
++ classLoadLock.remove(name);
++ classLoadLockCount.remove(name);
++ } else {
++ classLoadLockCount.compute(name, (x, prev) -> prev - 1);
++ }
++ }
++ }
++ // Paper end
+ return null;
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0194-Add-Player-Client-Options-API.patch b/Spigot-API-Patches-Unmapped/0194-Add-Player-Client-Options-API.patch
new file mode 100644
index 0000000000..19f52fdf39
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0194-Add-Player-Client-Options-API.patch
@@ -0,0 +1,202 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MiniDigger <[email protected]>
+Date: Mon, 20 Jan 2020 21:38:34 +0100
+Subject: [PATCH] Add Player Client Options API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/ClientOption.java b/src/main/java/com/destroystokyo/paper/ClientOption.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9dad814cf51bc59ec5dfbf14474fea6557de38aa
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/ClientOption.java
+@@ -0,0 +1,33 @@
++package com.destroystokyo.paper;
++
++import org.jetbrains.annotations.NotNull;
++
++import org.bukkit.inventory.MainHand;
++
++public final class ClientOption<T> {
++
++ public static final ClientOption<SkinParts> SKIN_PARTS = new ClientOption<>(SkinParts.class);
++ public static final ClientOption<Boolean> CHAT_COLORS_ENABLED = new ClientOption<>(Boolean.class);
++ public static final ClientOption<ChatVisibility> CHAT_VISIBILITY = new ClientOption<>(ChatVisibility.class);
++ public static final ClientOption<String> LOCALE = new ClientOption<>(String.class);
++ public static final ClientOption<MainHand> MAIN_HAND = new ClientOption<>(MainHand.class);
++ public static final ClientOption<Integer> VIEW_DISTANCE = new ClientOption<>(Integer.class);
++
++ private final Class<T> type;
++
++ private ClientOption(@NotNull Class<T> type) {
++ this.type = type;
++ }
++
++ @NotNull
++ public Class<T> getType() {
++ return type;
++ }
++
++ public enum ChatVisibility {
++ FULL,
++ SYSTEM,
++ HIDDEN,
++ UNKNOWN
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/SkinParts.java b/src/main/java/com/destroystokyo/paper/SkinParts.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4a0c39405d4fbed457787e3c6ded4cc6591bc8c2
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/SkinParts.java
+@@ -0,0 +1,20 @@
++package com.destroystokyo.paper;
++
++public interface SkinParts {
++
++ boolean hasCapeEnabled();
++
++ boolean hasJacketEnabled();
++
++ boolean hasLeftSleeveEnabled();
++
++ boolean hasRightSleeveEnabled();
++
++ boolean hasLeftPantsEnabled();
++
++ boolean hasRightPantsEnabled();
++
++ boolean hasHatsEnabled();
++
++ int getRaw();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f7f171c4ee0b8339b2f8fbe82442d65f17202f28
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java
+@@ -0,0 +1,100 @@
++package com.destroystokyo.paper.event.player;
++
++import com.destroystokyo.paper.ClientOption;
++import com.destroystokyo.paper.ClientOption.ChatVisibility;
++import com.destroystokyo.paper.SkinParts;
++
++import org.jetbrains.annotations.NotNull;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.MainHand;
++
++/**
++ * Called when the player changes his client settings
++ */
++public class PlayerClientOptionsChangeEvent extends PlayerEvent {
++
++ private static final HandlerList handlers = new HandlerList();
++
++ private final String locale;
++ private final int viewDistance;
++ private final ChatVisibility chatVisibility;
++ private final boolean chatColors;
++ private final SkinParts skinparts;
++ private final MainHand mainHand;
++
++ public PlayerClientOptionsChangeEvent(@NotNull Player player, @NotNull String locale, int viewDistance, @NotNull ChatVisibility chatVisibility, boolean chatColors, @NotNull SkinParts skinParts, @NotNull MainHand mainHand) {
++ super(player);
++ this.locale = locale;
++ this.viewDistance = viewDistance;
++ this.chatVisibility = chatVisibility;
++ this.chatColors = chatColors;
++ this.skinparts = skinParts;
++ this.mainHand = mainHand;
++ }
++
++ @NotNull
++ public String getLocale() {
++ return locale;
++ }
++
++ public boolean hasLocaleChanged() {
++ return !locale.equals(player.getClientOption(ClientOption.LOCALE));
++ }
++
++ public int getViewDistance() {
++ return viewDistance;
++ }
++
++ public boolean hasViewDistanceChanged() {
++ return viewDistance != player.getClientOption(ClientOption.VIEW_DISTANCE);
++ }
++
++ @NotNull
++ public ChatVisibility getChatVisibility() {
++ return chatVisibility;
++ }
++
++ public boolean hasChatVisibilityChanged() {
++ return chatVisibility != player.getClientOption(ClientOption.CHAT_VISIBILITY);
++ }
++
++ public boolean hasChatColorsEnabled() {
++ return chatColors;
++ }
++
++ public boolean hasChatColorsEnabledChanged() {
++ return chatColors != player.getClientOption(ClientOption.CHAT_COLORS_ENABLED);
++ }
++
++ @NotNull
++ public SkinParts getSkinParts() {
++ return skinparts;
++ }
++
++ public boolean hasSkinPartsChanged() {
++ return skinparts.getRaw() != player.getClientOption(ClientOption.SKIN_PARTS).getRaw();
++ }
++
++ @NotNull
++ public MainHand getMainHand() {
++ return mainHand;
++ }
++
++ public boolean hasMainHandChanged() {
++ return mainHand != player.getClientOption(ClientOption.MAIN_HAND);
++ }
++
++ @Override
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 6253b43490cac138edbb59ce0647d3f37314e435..e6b5969df34f096cec84ebb46061cb956eba4bab 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -2,6 +2,7 @@ package org.bukkit.entity;
+
+ import java.net.InetSocketAddress;
+ import java.util.UUID;
++import com.destroystokyo.paper.ClientOption; // Paper
+ import com.destroystokyo.paper.Title; // Paper
+ import net.kyori.adventure.text.Component;
+ import com.destroystokyo.paper.profile.PlayerProfile; // Paper
+@@ -1894,6 +1895,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * Reset the cooldown counter to 0, effectively starting the cooldown period.
+ */
+ void resetCooldown();
++
++ /**
++ * @return the client option value of the player
++ */
++ @NotNull
++ <T> T getClientOption(@NotNull ClientOption<T> option);
+ // Paper end
+
+ // Spigot start
diff --git a/Spigot-API-Patches-Unmapped/0195-Add-PlayerAttackEntityCooldownResetEvent.patch b/Spigot-API-Patches-Unmapped/0195-Add-PlayerAttackEntityCooldownResetEvent.patch
new file mode 100644
index 0000000000..15fa2db392
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0195-Add-PlayerAttackEntityCooldownResetEvent.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: nossr50 <[email protected]>
+Date: Thu, 26 Mar 2020 19:30:58 -0700
+Subject: [PATCH] Add PlayerAttackEntityCooldownResetEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerAttackEntityCooldownResetEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerAttackEntityCooldownResetEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ebdebe7b6ec6ed5aadc7ee925ba0147e61e6bc84
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerAttackEntityCooldownResetEvent.java
+@@ -0,0 +1,76 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.entity.Entity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when processing a player's attack on an entity when the player's attack strength cooldown is reset
++ */
++public class PlayerAttackEntityCooldownResetEvent extends PlayerEvent implements Cancellable {
++
++ private final float cooledAttackStrength;
++ private boolean cancel = false;
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull private final Entity attackedEntity;
++
++ public PlayerAttackEntityCooldownResetEvent(@NotNull Player who, @NotNull Entity attackedEntity, float cooledAttackStrength) {
++ super(who);
++ this.attackedEntity = attackedEntity;
++ this.cooledAttackStrength = cooledAttackStrength;
++ }
++
++ @Override
++ public @NotNull HandlerList getHandlers() {
++ return handlers;
++ }
++
++ public static @NotNull HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ /**
++ * Gets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ * <p>
++ * If an attack cooldown event is cancelled, the players attack strength will remain at the same value instead of being reset.
++ *
++ * @return true if this event is cancelled
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ /**
++ * Cancelling this event will prevent the target player from having their cooldown reset from attacking this entity
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ /**
++ * Get the value of the players cooldown attack strength when they initiated the attack
++ *
++ * @return returns the original player cooldown value
++ */
++ public float getCooledAttackStrength() {
++ return cooledAttackStrength;
++ }
++
++ /**
++ * Returns the entity attacked by the player
++ *
++ * @return the entity attacked by the player
++ */
++ @NotNull
++ public Entity getAttackedEntity() {
++ return attackedEntity;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0196-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch b/Spigot-API-Patches-Unmapped/0196-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch
new file mode 100644
index 0000000000..800d5364e2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0196-Fix-Potion-toItemStack-swapping-the-extended-and-upg.patch
@@ -0,0 +1,21 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wesley Smith <[email protected]>
+Date: Fri, 24 Apr 2020 18:30:26 -0400
+Subject: [PATCH] Fix Potion#toItemStack swapping the extended and upgraded
+ constructor values.
+
+While the Potion class is deprecated, it is still used in some plugins for cross-version potion handling. This issue has existed for a long time, and has caused many heaches along the way.
+
+diff --git a/src/main/java/org/bukkit/potion/Potion.java b/src/main/java/org/bukkit/potion/Potion.java
+index b9dbbfd07dea643d7ac749822548571968adaa94..ac02ae4fc179483b4ac3d1adc41684a8426197eb 100644
+--- a/src/main/java/org/bukkit/potion/Potion.java
++++ b/src/main/java/org/bukkit/potion/Potion.java
+@@ -267,7 +267,7 @@ public class Potion {
+ }
+ ItemStack itemStack = new ItemStack(material, amount);
+ PotionMeta meta = (PotionMeta) itemStack.getItemMeta();
+- meta.setBasePotionData(new PotionData(type, level == 2, extended));
++ meta.setBasePotionData(new PotionData(type, extended, level == 2)); // Paper - fix swapped values
+ itemStack.setItemMeta(meta);
+ return itemStack;
+ }
diff --git a/Spigot-API-Patches-Unmapped/0197-Villager-Restocks-API.patch b/Spigot-API-Patches-Unmapped/0197-Villager-Restocks-API.patch
new file mode 100644
index 0000000000..e2dcfc3144
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0197-Villager-Restocks-API.patch
@@ -0,0 +1,31 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: zbk <[email protected]>
+Date: Sun, 26 Apr 2020 23:49:03 -0400
+Subject: [PATCH] Villager Restocks API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java
+index ef48ad9b28750ab7b33071f6b8e354e922731909..d1579153092c1b80350155110f1b9926b1a1ef57 100644
+--- a/src/main/java/org/bukkit/entity/Villager.java
++++ b/src/main/java/org/bukkit/entity/Villager.java
+@@ -77,6 +77,20 @@ public interface Villager extends AbstractVillager {
+ */
+ public void setVillagerExperience(int experience);
+
++ // Paper start
++ /**
++ * Gets the amount of times a villager has restocked their trades today
++ * @return The amount of trade restocks.
++ */
++ public int getRestocksToday();
++
++ /**
++ * Sets the amount of times a villager has restocked their trades today
++ * @param restocksToday new restock count
++ */
++ public void setRestocksToday(int restocksToday);
++ // Paper end
++
+ /**
+ * Attempts to make this villager sleep at the given location.
+ * <br>
diff --git a/Spigot-API-Patches-Unmapped/0198-Expose-game-version.patch b/Spigot-API-Patches-Unmapped/0198-Expose-game-version.patch
new file mode 100644
index 0000000000..560d2370a1
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0198-Expose-game-version.patch
@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Fri, 1 May 2020 17:39:02 +0300
+Subject: [PATCH] Expose game version
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 86ac6702b3aeab1126b2b2879b87ef3883793d44..12214ce2af7363d40cf44652e46f05c5c1f2fe5a 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -118,6 +118,18 @@ public final class Bukkit {
+ return server.getBukkitVersion();
+ }
+
++ // Paper start - expose game version
++ /**
++ * Gets the version of game this server implements
++ *
++ * @return version of game
++ */
++ @NotNull
++ public static String getMinecraftVersion() {
++ return server.getMinecraftVersion();
++ }
++ // Paper end
++
+ /**
+ * Gets a view of all currently logged in players. This {@linkplain
+ * Collections#unmodifiableCollection(Collection) view} is a reused
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 16a74b834c4d4b907f9b11ccf9ef9804514df224..360decea2eb6de4c567fa4cceea8f19bceff6823 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -97,6 +97,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @NotNull
+ public String getBukkitVersion();
+
++ // Paper start - expose game version
++ /**
++ * Gets the version of game this server implements
++ *
++ * @return version of game
++ */
++ @NotNull
++ String getMinecraftVersion();
++ // Paper end
++
+ /**
+ * Gets a view of all currently logged in players. This {@linkplain
+ * Collections#unmodifiableCollection(Collection) view} is a reused
diff --git a/Spigot-API-Patches-Unmapped/0199-Add-item-slot-convenience-methods.patch b/Spigot-API-Patches-Unmapped/0199-Add-item-slot-convenience-methods.patch
new file mode 100644
index 0000000000..3b3612713f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0199-Add-item-slot-convenience-methods.patch
@@ -0,0 +1,283 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: KennyTV <[email protected]>
+Date: Sat, 25 Apr 2020 23:31:28 +0200
+Subject: [PATCH] Add item slot convenience methods
+
+
+diff --git a/src/main/java/org/bukkit/inventory/AnvilInventory.java b/src/main/java/org/bukkit/inventory/AnvilInventory.java
+index 4af562426aa38faeb6822abb0c878a3ac346b383..b95e563b5454306a9188ae3295309ee86a756477 100644
+--- a/src/main/java/org/bukkit/inventory/AnvilInventory.java
++++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java
+@@ -49,4 +49,64 @@ public interface AnvilInventory extends Inventory {
+ * @param levels the maximum experience cost
+ */
+ void setMaximumRepairCost(int levels);
++
++ // Paper start
++ /**
++ * Gets the item in the left input slot.
++ *
++ * @return item in the first slot
++ */
++ @Nullable
++ default ItemStack getFirstItem() {
++ return getItem(0);
++ }
++
++ /**
++ * Sets the item in the left input slot.
++ *
++ * @param firstItem item to set
++ */
++ default void setFirstItem(@Nullable ItemStack firstItem) {
++ setItem(0, firstItem);
++ }
++
++ /**
++ * Gets the item in the right input slot.
++ *
++ * @return item in the second slot
++ */
++ @Nullable
++ default ItemStack getSecondItem() {
++ return getItem(1);
++ }
++
++ /**
++ * Sets the item in the right input slot.
++ *
++ * @param secondItem item to set
++ */
++ default void setSecondItem(@Nullable ItemStack secondItem) {
++ setItem(1, secondItem);
++ }
++
++ /**
++ * Gets the item in the result slot.
++ *
++ * @return item in the result slot
++ */
++ @Nullable
++ default ItemStack getResult() {
++ return getItem(2);
++ }
++
++ /**
++ * Sets the item in the result slot.
++ * Note that the client might not be able to take out the item if it does not match the input items.
++ *
++ * @param result item to set
++ */
++ default void setResult(@Nullable ItemStack result) {
++ setItem(2, result);
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/GrindstoneInventory.java b/src/main/java/org/bukkit/inventory/GrindstoneInventory.java
+index 9048892c8768c6b4d6cea03da73339f13bfbe82e..1c750108f55a0a31ad23433b333e0ea486a63ff2 100644
+--- a/src/main/java/org/bukkit/inventory/GrindstoneInventory.java
++++ b/src/main/java/org/bukkit/inventory/GrindstoneInventory.java
+@@ -1,6 +1,68 @@
+ package org.bukkit.inventory;
+
++import org.jetbrains.annotations.Nullable; // Paper
++
+ /**
+ * Interface to the inventory of a Grindstone.
+ */
+-public interface GrindstoneInventory extends Inventory { }
++public interface GrindstoneInventory extends Inventory {
++
++ // Paper start
++ /**
++ * Gets the upper input item.
++ *
++ * @return upper input item
++ */
++ @Nullable
++ default ItemStack getUpperItem() {
++ return getItem(0);
++ }
++
++ /**
++ * Sets the upper input item.
++ *
++ * @param upperItem item to set
++ */
++ default void setUpperItem(@Nullable ItemStack upperItem) {
++ setItem(0, upperItem);
++ }
++
++ /**
++ * Gets the lower input item.
++ *
++ * @return lower input item
++ */
++ @Nullable
++ default ItemStack getLowerItem() {
++ return getItem(1);
++ }
++
++ /**
++ * Sets the lower input item.
++ *
++ * @param lowerItem item to set
++ */
++ default void setLowerItem(@Nullable ItemStack lowerItem) {
++ setItem(1, lowerItem);
++ }
++
++ /**
++ * Gets the result.
++ *
++ * @return result
++ */
++ @Nullable
++ default ItemStack getResult() {
++ return getItem(2);
++ }
++
++ /**
++ * Sets the result.
++ *
++ * @param result item to set
++ */
++ default void setResult(@Nullable ItemStack result) {
++ setItem(2, result);
++ }
++ // Paper end
++}
+diff --git a/src/main/java/org/bukkit/inventory/LecternInventory.java b/src/main/java/org/bukkit/inventory/LecternInventory.java
+index 4a0c43acc2714e095973eb78536041bb1a179ddc..acf2244f77133df53eb5f862c8e713c85192f13d 100644
+--- a/src/main/java/org/bukkit/inventory/LecternInventory.java
++++ b/src/main/java/org/bukkit/inventory/LecternInventory.java
+@@ -11,4 +11,25 @@ public interface LecternInventory extends Inventory {
+ @Nullable
+ @Override
+ public Lectern getHolder();
++
++ // Paper start
++ /**
++ * Gets the lectern's held book.
++ *
++ * @return book set in the lectern
++ */
++ @Nullable
++ default ItemStack getBook() {
++ return getItem(0);
++ }
++
++ /**
++ * Sets the lectern's held book.
++ *
++ * @param book the new book
++ */
++ default void setBook(@Nullable ItemStack book) {
++ setItem(0, book);
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/SmithingInventory.java b/src/main/java/org/bukkit/inventory/SmithingInventory.java
+index 96d526b7b153e56c9a97de42ce3270b6638510e4..a41ca6bd2672db2810dd70c4925b84a4f081af05 100644
+--- a/src/main/java/org/bukkit/inventory/SmithingInventory.java
++++ b/src/main/java/org/bukkit/inventory/SmithingInventory.java
+@@ -30,4 +30,44 @@ public interface SmithingInventory extends Inventory {
+ */
+ @Nullable
+ Recipe getRecipe();
++
++ // Paper start
++ /**
++ * Gets the input equipment (first slot).
++ *
++ * @return input equipment item
++ */
++ @Nullable
++ default ItemStack getInputEquipment() {
++ return getItem(0);
++ }
++
++ /**
++ * Sets the input equipment (first slot).
++ *
++ * @param itemStack item to set
++ */
++ default void setInputEquipment(@Nullable ItemStack itemStack) {
++ setItem(0, itemStack);
++ }
++
++ /**
++ * Gets the input mineral (second slot).
++ *
++ * @return input mineral item
++ */
++ @Nullable
++ default ItemStack getInputMineral() {
++ return getItem(1);
++ }
++
++ /**
++ * Sets the input mineral (second slot).
++ *
++ * @param itemStack item to set
++ */
++ default void setInputMineral(@Nullable ItemStack itemStack) {
++ setItem(1, itemStack);
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/StonecutterInventory.java b/src/main/java/org/bukkit/inventory/StonecutterInventory.java
+index dbb034fae3b8bfaf40e6341460e274c21e321a3b..e7a8e7188bf8b9840de56dc80c2b79d64a9389cb 100644
+--- a/src/main/java/org/bukkit/inventory/StonecutterInventory.java
++++ b/src/main/java/org/bukkit/inventory/StonecutterInventory.java
+@@ -1,6 +1,49 @@
+ package org.bukkit.inventory;
+
++import org.jetbrains.annotations.Nullable; // Paper
++
+ /**
+ * Interface to the inventory of a Stonecutter.
+ */
+-public interface StonecutterInventory extends Inventory { }
++public interface StonecutterInventory extends Inventory {
++
++ // Paper start
++ /**
++ * Gets the input item.
++ *
++ * @return input item
++ */
++ @Nullable
++ default ItemStack getInputItem() {
++ return getItem(0);
++ }
++
++ /**
++ * Sets the input item.
++ *
++ * @param itemStack item to set
++ */
++ default void setInputItem(@Nullable ItemStack itemStack) {
++ setItem(0, itemStack);
++ }
++
++ /**
++ * Gets the result item.
++ *
++ * @return result
++ */
++ @Nullable
++ default ItemStack getResult() {
++ return getItem(1);
++ }
++
++ /**
++ * Sets the result item.
++ *
++ * @param itemStack item to set
++ */
++ default void setResult(@Nullable ItemStack itemStack) {
++ setItem(1, itemStack);
++ }
++ // Paper end
++}
diff --git a/Spigot-API-Patches-Unmapped/0200-Add-Mob-Goal-API.patch b/Spigot-API-Patches-Unmapped/0200-Add-Mob-Goal-API.patch
new file mode 100644
index 0000000000..a93d7da4fe
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0200-Add-Mob-Goal-API.patch
@@ -0,0 +1,481 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MiniDigger <[email protected]>
+Date: Fri, 3 Jan 2020 16:24:46 +0100
+Subject: [PATCH] Add Mob Goal API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/Goal.java b/src/main/java/com/destroystokyo/paper/entity/ai/Goal.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c57c5416c88e2070a082403ab0dda9d7f08d2a57
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/ai/Goal.java
+@@ -0,0 +1,66 @@
++package com.destroystokyo.paper.entity.ai;
++
++import org.jetbrains.annotations.NotNull;
++
++import java.util.EnumSet;
++
++import org.bukkit.entity.Mob;
++
++/**
++ * Represents an AI goal of an entity
++ */
++public interface Goal<T extends Mob> {
++
++ /**
++ * Checks if this goal should be activated
++ *
++ * @return if this goal should be activated
++ */
++ boolean shouldActivate();
++
++ /**
++ * Checks if this goal should stay active, defaults to {@link Goal#shouldActivate()}
++ *
++ * @return if this goal should stay active
++ */
++ default boolean shouldStayActive() {
++ return shouldActivate();
++ }
++
++ /**
++ * Called when this goal gets activated
++ */
++ default void start() {
++ }
++
++ /**
++ * Called when this goal gets stopped
++ */
++ default void stop() {
++ }
++
++ /**
++ * Called each tick the goal is activated
++ */
++ default void tick() {
++ }
++
++ /**
++ * A unique key that identifies this type of goal. Plugins should use their own namespace, not the minecraft
++ * namespace. Additionally, this key also specifies to what mobs this goal can be applied to
++ *
++ * @return the goal key
++ */
++ @NotNull
++ GoalKey<T> getKey();
++
++ /**
++ * Returns a list of all applicable flags for this goal.<br>
++ *
++ * This method is only called on construction.
++ *
++ * @return the subtypes.
++ */
++ @NotNull
++ EnumSet<GoalType> getTypes();
++}
+diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/GoalKey.java b/src/main/java/com/destroystokyo/paper/entity/ai/GoalKey.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9cd98c6fcfa3eb439d9013ef76ef4661175a0e5a
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/ai/GoalKey.java
+@@ -0,0 +1,64 @@
++package com.destroystokyo.paper.entity.ai;
++
++import com.google.common.base.Objects;
++
++import org.jetbrains.annotations.NotNull;
++
++import java.util.StringJoiner;
++
++import org.bukkit.NamespacedKey;
++import org.bukkit.entity.Mob;
++
++/**
++ *
++ * Used to identify a Goal. Consists of a {@link NamespacedKey} and the type of mob the goal can be applied to
++ *
++ * @param <T> the type of mob the goal can be applied to
++ */
++public class GoalKey<T extends Mob> {
++
++ private final Class<T> entityClass;
++ private final NamespacedKey namespacedKey;
++
++ private GoalKey(@NotNull Class<T> entityClass, @NotNull NamespacedKey namespacedKey) {
++ this.entityClass = entityClass;
++ this.namespacedKey = namespacedKey;
++ }
++
++ @NotNull
++ public Class<T> getEntityClass() {
++ return entityClass;
++ }
++
++ @NotNull
++ public NamespacedKey getNamespacedKey() {
++ return namespacedKey;
++ }
++
++ @Override
++ public boolean equals(Object o) {
++ if (this == o) return true;
++ if (o == null || getClass() != o.getClass()) return false;
++ GoalKey<?> goalKey = (GoalKey<?>) o;
++ return Objects.equal(entityClass, goalKey.entityClass) &&
++ Objects.equal(namespacedKey, goalKey.namespacedKey);
++ }
++
++ @Override
++ public int hashCode() {
++ return Objects.hashCode(entityClass, namespacedKey);
++ }
++
++ @Override
++ public String toString() {
++ return new StringJoiner(", ", GoalKey.class.getSimpleName() + "[", "]")
++ .add("entityClass=" + entityClass)
++ .add("namespacedKey=" + namespacedKey)
++ .toString();
++ }
++
++ @NotNull
++ public static <A extends Mob> GoalKey<A> of(@NotNull Class<A> entityClass, @NotNull NamespacedKey namespacedKey) {
++ return new GoalKey<>(entityClass, namespacedKey);
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/GoalType.java b/src/main/java/com/destroystokyo/paper/entity/ai/GoalType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7024c8f484d2460abf3abfe65a29771d814105ec
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/ai/GoalType.java
+@@ -0,0 +1,17 @@
++package com.destroystokyo.paper.entity.ai;
++
++/**
++ * Represents the subtype of a goal. Used by minecraft to disable certain types of goals if needed.
++ */
++public enum GoalType {
++
++ MOVE,
++ LOOK,
++ JUMP,
++ TARGET,
++ /**
++ * Used to map vanilla goals, that are a behavior goal but don't have a type set...
++ */
++ UNKNOWN_BEHAVIOR,
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoals.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoals.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e21f7574763dd4f13794f91bbef192ef66a8f5e9
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoals.java
+@@ -0,0 +1,50 @@
++package com.destroystokyo.paper.entity.ai;
++
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++import java.util.Collection;
++
++import org.bukkit.entity.Mob;
++
++/**
++ * Represents a part of the "brain" of a mob. It tracks all tasks (running or not), allows adding and removing goals
++ */
++public interface MobGoals {
++
++ <T extends Mob> void addGoal(@NotNull T mob, int priority, @NotNull Goal<T> goal);
++
++ <T extends Mob> void removeGoal(@NotNull T mob, @NotNull Goal<T> goal);
++
++ <T extends Mob> void removeAllGoals(@NotNull T mob);
++
++ <T extends Mob> void removeAllGoals(@NotNull T mob, @NotNull GoalType type);
++
++ <T extends Mob> void removeGoal(@NotNull T mob, @NotNull GoalKey<T> key);
++
++ <T extends Mob> boolean hasGoal(@NotNull T mob, @NotNull GoalKey<T> key);
++
++ @Nullable
++ <T extends Mob> Goal<T> getGoal(@NotNull T mob, @NotNull GoalKey<T> key);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getGoals(@NotNull T mob, @NotNull GoalKey<T> key);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getAllGoals(@NotNull T mob);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getAllGoals(@NotNull T mob, @NotNull GoalType type);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getAllGoalsWithout(@NotNull T mob, @NotNull GoalType type);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getRunningGoals(@NotNull T mob);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getRunningGoals(@NotNull T mob, @NotNull GoalType type);
++
++ @NotNull
++ <T extends Mob> Collection<Goal<T>> getRunningGoalsWithout(@NotNull T mob, @NotNull GoalType type);
++}
+diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b42091752981a1f309ab350e9a394092cb334824
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java
+@@ -0,0 +1,209 @@
++package com.destroystokyo.paper.entity.ai;
++
++import com.destroystokyo.paper.entity.RangedEntity;
++
++import org.bukkit.NamespacedKey;
++import org.bukkit.entity.*;
++
++/**
++ * Represents a vanilla goal. Plugins should never implement this.<br>
++ * Generated by VanillaPathfinderTest in paper-server
++ */
++public interface VanillaGoal<T extends Mob> extends Goal<T> {
++
++ GoalKey<Bee> BEE_ATTACK = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_attack"));
++ GoalKey<Bee> BEE_BECOME_ANGRY = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_become_angry"));
++ GoalKey<Bee> BEE_ENTER_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_enter_hive"));
++ GoalKey<Bee> BEE_GO_TO_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_go_to_hive"));
++ GoalKey<Bee> BEE_GO_TO_KNOWN_FLOWER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_go_to_known_flower"));
++ GoalKey<Bee> BEE_GROW_CROP = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_grow_crop"));
++ GoalKey<Bee> BEE_HURT_BY_OTHER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_hurt_by_other"));
++ GoalKey<Bee> BEE_LOCATE_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_locate_hive"));
++ GoalKey<Bee> BEE_POLLINATE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_pollinate"));
++ GoalKey<Bee> BEE_WANDER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_wander"));
++ GoalKey<Blaze> BLAZE_FIREBALL = GoalKey.of(Blaze.class, NamespacedKey.minecraft("blaze_fireball"));
++ GoalKey<Cat> TEMPT_CHANCE = GoalKey.of(Cat.class, NamespacedKey.minecraft("tempt_chance"));
++ GoalKey<Cat> CAT_AVOID_ENTITY = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_avoid_entity"));
++ GoalKey<Cat> CAT_RELAX_ON_OWNER = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_relax_on_owner"));
++ GoalKey<Dolphin> DOLPHIN_SWIM_TO_TREASURE = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_swim_to_treasure"));
++ GoalKey<Dolphin> DOLPHIN_SWIM_WITH_PLAYER = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_swim_with_player"));
++ GoalKey<Dolphin> DOLPHIN_PLAY_WITH_ITEMS = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_play_with_items"));
++ GoalKey<Drowned> DROWNED_ATTACK = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack"));
++ GoalKey<Drowned> DROWNED_GOTO_BEACH = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_goto_beach"));
++ GoalKey<Creature> DROWNED_GOTO_WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("drowned_goto_water"));
++ GoalKey<Drowned> DROWNED_SWIM_UP = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_swim_up"));
++ GoalKey<RangedEntity> DROWNED_TRIDENT_ATTACK = GoalKey.of(RangedEntity.class, NamespacedKey.minecraft("drowned_trident_attack"));
++ GoalKey<Enderman> ENDERMAN_PICKUP_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_pickup_block"));
++ GoalKey<Enderman> ENDERMAN_PLACE_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_place_block"));
++ GoalKey<Enderman> PLAYER_WHO_LOOKED_AT_TARGET = GoalKey.of(Enderman.class, NamespacedKey.minecraft("player_who_looked_at_target"));
++ GoalKey<Enderman> ENDERMAN_FREEZE_WHEN_LOOKED_AT = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_freeze_when_looked_at"));
++ GoalKey<Evoker> EVOKER_ATTACK_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_attack_spell"));
++ GoalKey<Evoker> EVOKER_CAST_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_cast_spell"));
++ GoalKey<Evoker> EVOKER_SUMMON_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_summon_spell"));
++ GoalKey<Evoker> EVOKER_WOLOLO_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_wololo_spell"));
++ GoalKey<Fish> FISH_SWIM = GoalKey.of(Fish.class, NamespacedKey.minecraft("fish_swim"));
++ GoalKey<Fox> FOX_DEFEND_TRUSTED = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_defend_trusted"));
++ GoalKey<Fox> FOX_FACEPLANT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_faceplant"));
++ GoalKey<Fox> FOX_BREED = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_breed"));
++ GoalKey<Fox> FOX_EAT_BERRIES = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_eat_berries"));
++ GoalKey<Fox> FOX_FLOAT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_float"));
++ GoalKey<Fox> FOX_FOLLOW_PARENT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_follow_parent"));
++ GoalKey<Fox> FOX_LOOK_AT_PLAYER = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_look_at_player"));
++ GoalKey<Fox> FOX_MELEE_ATTACK = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_melee_attack"));
++ GoalKey<Fox> FOX_PANIC = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_panic"));
++ GoalKey<Fox> FOX_PERCH_AND_SEARCH = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_perch_and_search"));
++ GoalKey<Fox> FOX_POUNCE = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_pounce"));
++ GoalKey<Fox> FOX_SEARCH_FOR_ITEMS = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_search_for_items"));
++ GoalKey<Fox> FOX_SLEEP = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_sleep"));
++ GoalKey<Fox> FOX_STROLL_THROUGH_VILLAGE = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_stroll_through_village"));
++ GoalKey<Fox> FOX_SEEK_SHELTER = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_seek_shelter"));
++ GoalKey<Fox> FOX_STALK_PREY = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_stalk_prey"));
++ GoalKey<Ghast> GHAST_ATTACK_TARGET = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_attack_target"));
++ GoalKey<Ghast> GHAST_IDLE_MOVE = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_idle_move"));
++ GoalKey<Ghast> GHAST_MOVE_TOWARDS_TARGET = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_move_towards_target"));
++ GoalKey<Guardian> GUARDIAN_ATTACK = GoalKey.of(Guardian.class, NamespacedKey.minecraft("guardian_attack"));
++ GoalKey<Illager> RAIDER_OPEN_DOOR = GoalKey.of(Illager.class, NamespacedKey.minecraft("raider_open_door"));
++ GoalKey<Illusioner> ILLUSIONER_BLINDNESS_SPELL = GoalKey.of(Illusioner.class, NamespacedKey.minecraft("illusioner_blindness_spell"));
++ GoalKey<Illusioner> ILLUSIONER_MIRROR_SPELL = GoalKey.of(Illusioner.class, NamespacedKey.minecraft("illusioner_mirror_spell"));
++ GoalKey<Spellcaster> SPELLCASTER_CAST_SPELL = GoalKey.of(Spellcaster.class, NamespacedKey.minecraft("spellcaster_cast_spell"));
++ GoalKey<Llama> LLAMA_ATTACK_WOLF = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_attack_wolf"));
++ GoalKey<Llama> LLAMA_HURT_BY = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_hurt_by"));
++ GoalKey<TraderLlama> LLAMATRADER_DEFENDED_WANDERING_TRADER = GoalKey.of(TraderLlama.class, NamespacedKey.minecraft("llamatrader_defended_wandering_trader"));
++ GoalKey<Monster> LONG_DISTANCE_PATROL = GoalKey.of(Monster.class, NamespacedKey.minecraft("long_distance_patrol"));
++ GoalKey<Ocelot> OCELOT_AVOID_ENTITY = GoalKey.of(Ocelot.class, NamespacedKey.minecraft("ocelot_avoid_entity"));
++ GoalKey<Ocelot> OCELOT_TEMPT = GoalKey.of(Ocelot.class, NamespacedKey.minecraft("ocelot_tempt"));
++ GoalKey<Panda> PANDA_ATTACK = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_attack"));
++ GoalKey<Panda> PANDA_AVOID = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_avoid"));
++ GoalKey<Panda> PANDA_BREED = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_breed"));
++ GoalKey<Panda> PANDA_HURT_BY_TARGET = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_hurt_by_target"));
++ GoalKey<Panda> PANDA_LIE_ON_BACK = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_lie_on_back"));
++ GoalKey<Panda> PANDA_LOOK_AT_PLAYER = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_look_at_player"));
++ GoalKey<Panda> PANDA_PANIC = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_panic"));
++ GoalKey<Panda> PANDA_ROLL = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_roll"));
++ GoalKey<Panda> PANDA_SIT = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_sit"));
++ GoalKey<Panda> PANDA_SNEEZE = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_sneeze"));
++ GoalKey<Phantom> PHANTOM_ATTACK_PLAYER = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_attack_player"));
++ GoalKey<Phantom> PHANTOM_ATTACK_STRATEGY = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_attack_strategy"));
++ GoalKey<Phantom> PHANTOM_CIRCLE_AROUND_ANCHOR = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_circle_around_anchor"));
++ GoalKey<Phantom> PHANTOM_SWEEP_ATTACK = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_sweep_attack"));
++ /**
++ * @deprecated removed in 1.16
++ */
++ @Deprecated
++ GoalKey<PigZombie> ANGER = GoalKey.of(PigZombie.class, NamespacedKey.minecraft("anger"));
++ /**
++ * @deprecated removed in 1.16
++ */
++ @Deprecated
++ GoalKey<PigZombie> ANGER_OTHER = GoalKey.of(PigZombie.class, NamespacedKey.minecraft("anger_other"));
++ GoalKey<PolarBear> POLARBEAR_ATTACK_PLAYERS = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_attack_players"));
++ GoalKey<PolarBear> POLARBEAR_HURT_BY = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_hurt_by"));
++ GoalKey<PolarBear> POLARBEAR_MELEE = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_melee"));
++ GoalKey<PolarBear> POLARBEAR_PANIC = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_panic"));
++ GoalKey<PufferFish> PUFFERFISH_PUFF = GoalKey.of(PufferFish.class, NamespacedKey.minecraft("pufferfish_puff"));
++ GoalKey<Rabbit> EAT_CARROTS = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("eat_carrots"));
++ GoalKey<Rabbit> KILLER_RABBIT_MELEE_ATTACK = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("killer_rabbit_melee_attack"));
++ GoalKey<Rabbit> RABBIT_AVOID_TARGET = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("rabbit_avoid_target"));
++ GoalKey<Rabbit> RABBIT_PANIC = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("rabbit_panic"));
++ GoalKey<Raider> RAIDER_HOLD_GROUND = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_hold_ground"));
++ GoalKey<Raider> RAIDER_OBTAIN_BANNER = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_obtain_banner"));
++ GoalKey<Raider> RAIDER_CELEBRATION = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_celebration"));
++ GoalKey<Raider> RAIDER_MOVE_THROUGH_VILLAGE = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_move_through_village"));
++ GoalKey<Ravager> RAVAGER_MELEE_ATTACK = GoalKey.of(Ravager.class, NamespacedKey.minecraft("ravager_melee_attack"));
++ GoalKey<Shulker> SHULKER_ATTACK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_attack"));
++ GoalKey<Shulker> SHULKER_DEFENSE = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_defense"));
++ GoalKey<Shulker> SHULKER_NEAREST = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_nearest"));
++ GoalKey<Shulker> SHULKER_PEEK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_peek"));
++ GoalKey<Silverfish> SILVERFISH_HIDE_IN_BLOCK = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_hide_in_block"));
++ GoalKey<Silverfish> SILVERFISH_WAKE_OTHERS = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_wake_others"));
++ GoalKey<Skeleton> SKELETON_MELEE = GoalKey.of(Skeleton.class, NamespacedKey.minecraft("skeleton_melee"));
++ GoalKey<Slime> SLIME_IDLE = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_idle"));
++ GoalKey<Slime> SLIME_NEAREST_PLAYER = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_nearest_player"));
++ GoalKey<Slime> SLIME_RANDOM_DIRECTION = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_random_direction"));
++ GoalKey<Slime> SLIME_RANDOM_JUMP = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_random_jump"));
++ GoalKey<Spider> SPIDER_MELEE_ATTACK = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider_melee_attack"));
++ GoalKey<Spider> SPIDER_NEAREST_ATTACKABLE_TARGET = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider_nearest_attackable_target"));
++ GoalKey<Strider> STRIDER_GO_TO_LAVA = GoalKey.of(Strider.class, NamespacedKey.minecraft("strider_go_to_lava"));
++ GoalKey<Squid> SQUID = GoalKey.of(Squid.class, NamespacedKey.minecraft("squid"));
++ GoalKey<Squid> SQUID_FLEE = GoalKey.of(Squid.class, NamespacedKey.minecraft("squid_flee"));
++ GoalKey<Turtle> TURTLE_BREED = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_breed"));
++ GoalKey<Turtle> TURTLE_GO_HOME = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_go_home"));
++ GoalKey<Turtle> TURTLE_GOTO_WATER = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_goto_water"));
++ GoalKey<Turtle> TURTLE_LAY_EGG = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_lay_egg"));
++ GoalKey<Turtle> TURTLE_PANIC = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_panic"));
++ GoalKey<Turtle> TURTLE_RANDOM_STROLL = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_random_stroll"));
++ GoalKey<Turtle> TURTLE_TEMPT = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_tempt"));
++ GoalKey<Turtle> TURTLE_TRAVEL = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_travel"));
++ GoalKey<Vex> VEX_CHARGE_ATTACK = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_charge_attack"));
++ GoalKey<Vex> VEX_COPY_TARGET_OF_OWNER = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_copy_target_of_owner"));
++ GoalKey<Vex> VEX_RANDOM_MOVE = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_random_move"));
++ GoalKey<WanderingTrader> VILLAGERTRADER_WANDER_TO_POSITION = GoalKey.of(WanderingTrader.class, NamespacedKey.minecraft("villagertrader_wander_to_position"));
++ GoalKey<Mob> VINDICATOR_BREAK_DOOR = GoalKey.of(Mob.class, NamespacedKey.minecraft("vindicator_break_door"));
++ GoalKey<Vindicator> VINDICATOR_JOHNNY_ATTACK = GoalKey.of(Vindicator.class, NamespacedKey.minecraft("vindicator_johnny_attack"));
++ GoalKey<Vindicator> VINDICATOR_MELEE_ATTACK = GoalKey.of(Vindicator.class, NamespacedKey.minecraft("vindicator_melee_attack"));
++ GoalKey<Wither> WITHER_DO_NOTHING = GoalKey.of(Wither.class, NamespacedKey.minecraft("wither_do_nothing"));
++ GoalKey<Wolf> WOLF_AVOID_ENTITY = GoalKey.of(Wolf.class, NamespacedKey.minecraft("wolf_avoid_entity"));
++ GoalKey<Zombie> ZOMBIE_ATTACK_TURTLE_EGG = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_turtle_egg"));
++ GoalKey<RangedEntity> ARROW_ATTACK = GoalKey.of(RangedEntity.class, NamespacedKey.minecraft("arrow_attack"));
++ GoalKey<Creature> AVOID_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("avoid_target"));
++ GoalKey<Wolf> BEG = GoalKey.of(Wolf.class, NamespacedKey.minecraft("beg"));
++ GoalKey<Monster> BOW_SHOOT = GoalKey.of(Monster.class, NamespacedKey.minecraft("bow_shoot"));
++ GoalKey<Mob> BREAK_DOOR = GoalKey.of(Mob.class, NamespacedKey.minecraft("break_door"));
++ GoalKey<Creature> BREATH = GoalKey.of(Creature.class, NamespacedKey.minecraft("breath"));
++ GoalKey<Animals> BREED = GoalKey.of(Animals.class, NamespacedKey.minecraft("breed"));
++ GoalKey<Cat> CAT_SIT_ON_BED = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_sit_on_bed"));
++ GoalKey<Monster> CROSSBOW_ATTACK = GoalKey.of(Monster.class, NamespacedKey.minecraft("crossbow_attack"));
++ GoalKey<IronGolem> DEFEND_VILLAGE = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("defend_village"));
++ GoalKey<Mob> DOOR_OPEN = GoalKey.of(Mob.class, NamespacedKey.minecraft("door_open"));
++ GoalKey<Mob> EAT_TILE = GoalKey.of(Mob.class, NamespacedKey.minecraft("eat_tile"));
++ GoalKey<Fish> FISH_SCHOOL = GoalKey.of(Fish.class, NamespacedKey.minecraft("fish_school"));
++ GoalKey<Creature> FLEE_SUN = GoalKey.of(Creature.class, NamespacedKey.minecraft("flee_sun"));
++ GoalKey<Mob> FLOAT = GoalKey.of(Mob.class, NamespacedKey.minecraft("float"));
++ GoalKey<Creature> FOLLOW_BOAT = GoalKey.of(Creature.class, NamespacedKey.minecraft("follow_boat"));
++ GoalKey<Mob> FOLLOW_ENTITY = GoalKey.of(Mob.class, NamespacedKey.minecraft("follow_entity"));
++ GoalKey<Tameable> FOLLOW_OWNER = GoalKey.of(Tameable.class, NamespacedKey.minecraft("follow_owner"));
++ GoalKey<Animals> FOLLOW_PARENT = GoalKey.of(Animals.class, NamespacedKey.minecraft("follow_parent"));
++ GoalKey<SkeletonHorse> HORSE_TRAP = GoalKey.of(SkeletonHorse.class, NamespacedKey.minecraft("horse_trap"));
++ GoalKey<Creature> HURT_BY_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("hurt_by_target"));
++ GoalKey<Mob> INTERACT = GoalKey.of(Mob.class, NamespacedKey.minecraft("interact"));
++ GoalKey<Cat> JUMP_ON_BLOCK = GoalKey.of(Cat.class, NamespacedKey.minecraft("jump_on_block"));
++ GoalKey<Mob> LEAP_AT_TARGET = GoalKey.of(Mob.class, NamespacedKey.minecraft("leap_at_target"));
++ GoalKey<Llama> LLAMA_FOLLOW = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_follow"));
++ GoalKey<Mob> LOOK_AT_PLAYER = GoalKey.of(Mob.class, NamespacedKey.minecraft("look_at_player"));
++ GoalKey<AbstractVillager> LOOK_AT_TRADING_PLAYER = GoalKey.of(AbstractVillager.class, NamespacedKey.minecraft("look_at_trading_player"));
++ GoalKey<Creature> MELEE_ATTACK = GoalKey.of(Creature.class, NamespacedKey.minecraft("melee_attack"));
++ GoalKey<Creature> MOVE_THROUGH_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_through_village"));
++ GoalKey<Creature> MOVE_TOWARDS_RESTRICTION = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_towards_restriction"));
++ GoalKey<Creature> MOVE_TOWARDS_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_towards_target"));
++ GoalKey<Mob> NEAREST_ATTACKABLE_TARGET = GoalKey.of(Mob.class, NamespacedKey.minecraft("nearest_attackable_target"));
++ GoalKey<Raider> NEAREST_ATTACKABLE_TARGET_WITCH = GoalKey.of(Raider.class, NamespacedKey.minecraft("nearest_attackable_target_witch"));
++ GoalKey<Raider> NEAREST_HEALABLE_RAIDER = GoalKey.of(Raider.class, NamespacedKey.minecraft("nearest_healable_raider"));
++ GoalKey<Creature> NEAREST_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("nearest_village"));
++ GoalKey<Mob> OCELOT_ATTACK = GoalKey.of(Mob.class, NamespacedKey.minecraft("ocelot_attack"));
++ GoalKey<IronGolem> OFFER_FLOWER = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("offer_flower"));
++ GoalKey<Tameable> OWNER_HURT_BY_TARGET = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt_by_target"));
++ GoalKey<Tameable> OWNER_HURT_TARGET = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt_target"));
++ GoalKey<Creature> PANIC = GoalKey.of(Creature.class, NamespacedKey.minecraft("panic"));
++ GoalKey<Parrot> PERCH = GoalKey.of(Parrot.class, NamespacedKey.minecraft("perch"));
++ GoalKey<Raider> RAID = GoalKey.of(Raider.class, NamespacedKey.minecraft("raid"));
++ GoalKey<Creature> RANDOM_FLY = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_fly"));
++ GoalKey<Mob> RANDOM_LOOKAROUND = GoalKey.of(Mob.class, NamespacedKey.minecraft("random_lookaround"));
++ GoalKey<Creature> RANDOM_STROLL = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_stroll"));
++ GoalKey<Creature> RANDOM_STROLL_LAND = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_stroll_land"));
++ GoalKey<Creature> RANDOM_SWIM = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_swim"));
++ GoalKey<Tameable> RANDOM_TARGET_NON_TAMED = GoalKey.of(Tameable.class, NamespacedKey.minecraft("random_target_non_tamed"));
++ GoalKey<Creature> REMOVE_BLOCK = GoalKey.of(Creature.class, NamespacedKey.minecraft("remove_block"));
++ GoalKey<Creature> RESTRICT_SUN = GoalKey.of(Creature.class, NamespacedKey.minecraft("restrict_sun"));
++ GoalKey<Tameable> SIT = GoalKey.of(Tameable.class, NamespacedKey.minecraft("sit"));
++ GoalKey<Creature> STROLL_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("stroll_village"));
++ GoalKey<Creeper> SWELL = GoalKey.of(Creeper.class, NamespacedKey.minecraft("swell"));
++ GoalKey<AbstractHorse> TAME = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("tame"));
++ GoalKey<Creature> TEMPT = GoalKey.of(Creature.class, NamespacedKey.minecraft("tempt"));
++ GoalKey<AbstractVillager> TRADE_WITH_PLAYER = GoalKey.of(AbstractVillager.class, NamespacedKey.minecraft("trade_with_player"));
++ GoalKey<Mob> USE_ITEM = GoalKey.of(Mob.class, NamespacedKey.minecraft("use_item"));
++ GoalKey<Creature> WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("water"));
++ GoalKey<Dolphin> WATER_JUMP = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("water_jump"));
++ GoalKey<Zombie> ZOMBIE_ATTACK = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack"));
++ GoalKey<Creature> STROLL_VILLAGE_GOLEM = GoalKey.of(Creature.class, NamespacedKey.minecraft("stroll_village_golem"));
++ GoalKey<Mob> UNIVERSAL_ANGER_RESET = GoalKey.of(Mob.class, NamespacedKey.minecraft("universal_anger_reset"));
++}
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 12214ce2af7363d40cf44652e46f05c5c1f2fe5a..77d352450f6bc5293efec3b75e08b857e2626fe7 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1867,6 +1867,16 @@ public final class Bukkit {
+ public static boolean isStopping() {
+ return server.isStopping();
+ }
++
++ /**
++ * Returns the {@link com.destroystokyo.paper.entity.ai.MobGoals} manager
++ *
++ * @return the mob goals manager
++ */
++ @NotNull
++ public static com.destroystokyo.paper.entity.ai.MobGoals getMobGoals() {
++ return server.getMobGoals();
++ }
+ // Paper end
+
+ @NotNull
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 360decea2eb6de4c567fa4cceea8f19bceff6823..4b1f515f2228f16c1ce793bd45510243d758236f 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1637,5 +1637,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ * @return true if server is in the process of being shutdown
+ */
+ boolean isStopping();
++
++ /**
++ * Returns the {@link com.destroystokyo.paper.entity.ai.MobGoals} manager
++ *
++ * @return the mob goals manager
++ */
++ @NotNull
++ com.destroystokyo.paper.entity.ai.MobGoals getMobGoals();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0201-World-view-distance-api.patch b/Spigot-API-Patches-Unmapped/0201-World-view-distance-api.patch
new file mode 100644
index 0000000000..17a9a94f12
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0201-World-view-distance-api.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Tue, 5 May 2020 21:28:01 -0700
+Subject: [PATCH] World view distance api
+
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 7bf85151efddcbd561afb0fb2d423aa97ac176c1..61dfb057d94d89477d11b9e8d4be7c16032e25a9 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -3447,6 +3447,34 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ int getViewDistance();
+ // Spigot end
+
++ // Paper start - view distance api
++ /**
++ * Sets the view distance for this world.
++ * @param viewDistance view distance in [2, 32]
++ */
++ void setViewDistance(int viewDistance);
++
++ /**
++ * Returns the no-tick view distance for this world.
++ * <p>
++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
++ * be set to tick.
++ * </p>
++ * @return The no-tick view distance for this world.
++ */
++ int getNoTickViewDistance();
++
++ /**
++ * Sets the no-tick view distance for this world.
++ * <p>
++ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
++ * be set to tick.
++ * </p>
++ * @param viewDistance view distance in [2, 32]
++ */
++ void setNoTickViewDistance(int viewDistance);
++ // Paper end - view distance api
++
+ // Spigot start
+ public class Spigot {
+
diff --git a/Spigot-API-Patches-Unmapped/0202-Add-villager-reputation-API.patch b/Spigot-API-Patches-Unmapped/0202-Add-villager-reputation-API.patch
new file mode 100644
index 0000000000..cef4ea6027
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0202-Add-villager-reputation-API.patch
@@ -0,0 +1,177 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Wed, 22 Apr 2020 23:13:49 +0200
+Subject: [PATCH] Add villager reputation API
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java b/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..1cc9ef255df888cb7dd7f7f2c5014e818d1be613
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java
+@@ -0,0 +1,54 @@
++package com.destroystokyo.paper.entity.villager;
++
++import com.google.common.base.Preconditions;
++import java.util.Map;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A reputation score for a player on a villager.
++ */
++public final class Reputation {
++ private static final ReputationType[] REPUTATION_TYPES = ReputationType.values(); // Avoid allocation
++ @NotNull
++ private final int[] reputation;
++
++ public Reputation() {
++ this(new int[REPUTATION_TYPES.length]);
++ }
++
++ // Package level to avoid plugins creating reputations with "magic values".
++ Reputation(@NotNull int[] reputation) {
++ this.reputation = reputation;
++ }
++
++ public Reputation(@NotNull final Map<ReputationType, Integer> reputation) {
++ this();
++ Preconditions.checkNotNull(reputation, "reputation cannot be null");
++
++ for (Map.Entry<ReputationType, Integer> entry : reputation.entrySet()) {
++ setReputation(entry.getKey(), entry.getValue());
++ }
++ }
++
++ /**
++ * Gets the reputation value for a specific {@link ReputationType}.
++ *
++ * @param type The {@link ReputationType type} of reputation to get.
++ * @return The value of the {@link ReputationType type}.
++ */
++ public int getReputation(@NotNull ReputationType type) {
++ Preconditions.checkNotNull(type, "the reputation type cannot be null");
++ return reputation[type.ordinal()];
++ }
++
++ /**
++ * Sets the reputation value for a specific {@link ReputationType}.
++ *
++ * @param type The {@link ReputationType type} of reputation to set.
++ * @param value The value of the {@link ReputationType type}.
++ */
++ public void setReputation(@NotNull ReputationType type, int value) {
++ Preconditions.checkNotNull(type, "the reputation type cannot be null");
++ reputation[type.ordinal()] = value;
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/entity/villager/ReputationType.java b/src/main/java/com/destroystokyo/paper/entity/villager/ReputationType.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5600fcdc9795a9f49091db48d73bbd4964b8b737
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/entity/villager/ReputationType.java
+@@ -0,0 +1,36 @@
++package com.destroystokyo.paper.entity.villager;
++
++/**
++ * A type of reputation gained with a {@link org.bukkit.entity.Villager Villager}.
++ * <p>
++ * All types but {@link #MAJOR_POSITIVE} are shared to other villagers.
++ */
++public enum ReputationType {
++ /**
++ * A gossip with a majorly negative effect. This is only gained through killing a nearby
++ * villager.
++ */
++ MAJOR_NEGATIVE,
++
++ /**
++ * A gossip with a minor negative effect. This is only gained through damaging a villager.
++ */
++ MINOR_NEGATIVE,
++
++ /**
++ * A gossip with a minor positive effect. This is only gained through curing a zombie
++ * villager.
++ */
++ MINOR_POSITIVE,
++
++ /**
++ * A gossip with a major positive effect. This is only gained through curing a zombie
++ * villager.
++ */
++ MAJOR_POSITIVE,
++
++ /**
++ * A gossip with a minor positive effect. This is only gained through trading with a villager.
++ */
++ TRADING,
++}
+diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java
+index d1579153092c1b80350155110f1b9926b1a1ef57..c8777a476e38ef5e72b6709761990a339eb43d2b 100644
+--- a/src/main/java/org/bukkit/entity/Villager.java
++++ b/src/main/java/org/bukkit/entity/Villager.java
+@@ -1,10 +1,13 @@
+ package org.bukkit.entity;
+
+ import java.util.Locale;
++import java.util.Map; // Paper
++import java.util.UUID; // Paper
+ import org.bukkit.Keyed;
+ import org.bukkit.Location;
+ import org.bukkit.NamespacedKey;
+ import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable; // Paper
+
+ /**
+ * Represents a villager NPC
+@@ -224,4 +227,50 @@ public interface Villager extends AbstractVillager {
+ return key;
+ }
+ }
++
++ // Paper start - Add villager reputation API
++ /**
++ * Get the {@link com.destroystokyo.paper.entity.villager.Reputation reputation}
++ * for a specific player by {@link UUID}.
++ *
++ * @param uniqueId The {@link UUID} of the player to get the reputation of.
++ * @return The player's copied reputation with this villager.
++ */
++ @Nullable
++ public com.destroystokyo.paper.entity.villager.Reputation getReputation(@NotNull UUID uniqueId);
++
++ /**
++ * Get all {@link com.destroystokyo.paper.entity.villager.Reputation reputations}
++ * for all players mapped by their {@link UUID unique IDs}.
++ *
++ * @return All {@link com.destroystokyo.paper.entity.villager.Reputation reputations} for all players
++ * in a copied map.
++ */
++ @NotNull
++ public Map<UUID, com.destroystokyo.paper.entity.villager.Reputation> getReputations();
++
++ /**
++ * Set the {@link com.destroystokyo.paper.entity.villager.Reputation reputation}
++ * for a specific player by {@link UUID}.
++ *
++ * @param uniqueId The {@link UUID} of the player to set the reputation of.
++ * @param reputation The {@link com.destroystokyo.paper.entity.villager.Reputation reputation} to set.
++ */
++ public void setReputation(@NotNull UUID uniqueId, @NotNull com.destroystokyo.paper.entity.villager.Reputation reputation);
++
++ /**
++ * Set all {@link com.destroystokyo.paper.entity.villager.Reputation reputations}
++ * for all players mapped by their {@link UUID unique IDs}.
++ *
++ * @param reputations All {@link com.destroystokyo.paper.entity.villager.Reputation reputations}
++ * for all players mapped by their {@link UUID unique IDs}.
++ */
++ public void setReputations(@NotNull Map<UUID, com.destroystokyo.paper.entity.villager.Reputation> reputations);
++
++ /**
++ * Clear all reputations from this villager. This removes every single
++ * reputation regardless of its impact and the player associated.
++ */
++ public void clearReputations();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0203-Spawn-Reason-API.patch b/Spigot-API-Patches-Unmapped/0203-Spawn-Reason-API.patch
new file mode 100644
index 0000000000..0ee08ee996
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0203-Spawn-Reason-API.patch
@@ -0,0 +1,62 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Thu, 10 Apr 2014 23:18:28 -0400
+Subject: [PATCH] Spawn Reason API
+
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 476184db904d8a2e1347e1219e8ba196bf4da5cb..c1010e314144a65e12eaf5514d639a87f45891a9 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -1,6 +1,8 @@
+ package org.bukkit;
+
+ import java.io.File;
++
++import org.bukkit.event.entity.CreatureSpawnEvent;
+ import org.bukkit.generator.ChunkGenerator;
+
+ import java.util.ArrayList;
+@@ -2249,6 +2251,12 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @NotNull
+ public <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz) throws IllegalArgumentException;
+
++ // Paper start
++ @NotNull
++ public default <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz, @NotNull CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException {
++ return spawn(location, clazz, reason, null);
++ }
++
+ /**
+ * Spawn an entity of a specific class at the given {@link Location}, with
+ * the supplied function run before the entity is added to the world.
+@@ -2266,7 +2274,28 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ * {@link Entity} requested cannot be spawned
+ */
+ @NotNull
+- public <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz, @Nullable Consumer<T> function) throws IllegalArgumentException;
++ public default <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz, @Nullable Consumer<T> function) throws IllegalArgumentException {
++ return spawn(location, clazz, CreatureSpawnEvent.SpawnReason.CUSTOM, function);
++ }
++
++ @NotNull
++ public default <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz, @NotNull CreatureSpawnEvent.SpawnReason reason, @Nullable Consumer<T> function) throws IllegalArgumentException {
++ return spawn(location, clazz, function, reason);
++ }
++
++ @NotNull
++ public default Entity spawnEntity(@NotNull Location loc, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason) {
++ return spawn(loc, (Class<Entity>) type.getEntityClass(), reason, null);
++ }
++
++ @NotNull
++ public default Entity spawnEntity(@NotNull Location loc, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason, @Nullable Consumer<Entity> function) {
++ return spawn(loc, (Class<Entity>) type.getEntityClass(), reason, function);
++ }
++
++ @NotNull
++ public <T extends Entity> T spawn(@NotNull Location location, @NotNull Class<T> clazz, @Nullable Consumer<T> function, @NotNull CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException;
++ // Paper end
+
+ /**
+ * Spawn a {@link FallingBlock} entity at the given {@link Location} of
diff --git a/Spigot-API-Patches-Unmapped/0204-Potential-bed-API.patch b/Spigot-API-Patches-Unmapped/0204-Potential-bed-API.patch
new file mode 100644
index 0000000000..b8b1e2519a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0204-Potential-bed-API.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: JRoy <[email protected]>
+Date: Sun, 10 May 2020 23:06:41 -0400
+Subject: [PATCH] Potential bed API
+
+Adds a new method to fetch the location of a player's bed without generating any sync loads.
+
+getPotentialBedLocation - Gets the last known location of a player's bed. This does not preform any check if the bed is still valid and does not load any chunks.
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index 3418133d07250a7fd50caad8d97924b86fb30bad..b09d12390d5f77330ac84452e0fee63a169bd01f 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -240,6 +240,19 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ */
+ public int getSleepTicks();
+
++
++ // Paper start - Potential bed api
++ /**
++ * Gets the Location of the player's bed, null if they have not slept
++ * in one. This method will not attempt to validate if the current bed
++ * is still valid.
++ *
++ * @return Bed Location if has slept in one, otherwise null.
++ */
++ @Nullable
++ public Location getPotentialBedLocation();
++ // Paper end
++
+ /**
+ * Attempts to make the entity sleep at the given location.
+ * <br>
diff --git a/Spigot-API-Patches-Unmapped/0205-Prioritise-own-classes-where-possible.patch b/Spigot-API-Patches-Unmapped/0205-Prioritise-own-classes-where-possible.patch
new file mode 100644
index 0000000000..fad10c31db
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0205-Prioritise-own-classes-where-possible.patch
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Mon, 27 Apr 2020 12:31:59 +0200
+Subject: [PATCH] Prioritise own classes where possible
+
+This adds the server property `Paper.DisableClassPrioritization` to disable
+prioritization of own classes for plugins' classloaders.
+
+This value is by default not present, and this will therefore break any
+plugins which abuse behaviour related to not using their own classes
+while still loading their own. This is often an issue with failing to
+relocate or shade properly, such as when shading plugin APIs like Vault.
+
+A plugin's classloader will first look in the same jar as it is loading
+in for a requested class, then load it. It does not re-use other
+plugins' classes if it has the chance to avoid doing so.
+
+If a class is not found in the same jar as it is loading for and it does
+find it elsewhere, it will still choose the class elsewhere. This is
+intended behaviour, as it will only prioritise classes it has in its own
+jar, no other plugins' classes will be prioritised in any other order
+than the one they were registered in.
+
+The patch in general terms just loads the class in the plugin's jar
+before it starts looking elsewhere for it.
+
+diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+index 9d1f7cdf12029c8198792fd299f92be476040222..384edf9890dfbd1cddfdcac4db1ebe9a4d761f78 100644
+--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+@@ -50,6 +50,7 @@ import org.yaml.snakeyaml.error.YAMLException;
+ */
+ public final class JavaPluginLoader implements PluginLoader {
+ final Server server;
++ private static final boolean DISABLE_CLASS_PRIORITIZATION = Boolean.getBoolean("Paper.DisableClassPrioritization"); // Paper
+ private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
+ private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
+ private final Map<String, java.util.concurrent.locks.ReentrantReadWriteLock> classLoadLock = new java.util.HashMap<String, java.util.concurrent.locks.ReentrantReadWriteLock>(); // Paper
+@@ -193,6 +194,11 @@ public final class JavaPluginLoader implements PluginLoader {
+
+ @Nullable
+ Class<?> getClassByName(final String name) {
++ // Paper start - prioritize self
++ return getClassByName(name, null);
++ }
++ Class<?> getClassByName(final String name, PluginClassLoader requester) {
++ // Paper end
+ // Paper start - make MT safe
+ Class<?> cachedClass = classes.get(name);
+ if (cachedClass != null) {
+@@ -204,6 +210,16 @@ public final class JavaPluginLoader implements PluginLoader {
+ classLoadLockCount.compute(name, (x, prev) -> prev != null ? prev + 1 : 1);
+ }
+ lock.writeLock().lock();try {
++ // Paper start - prioritize self
++ if (!DISABLE_CLASS_PRIORITIZATION && requester != null) {
++ try {
++ cachedClass = requester.findClass(name, false);
++ } catch (ClassNotFoundException cnfe) {}
++ if (cachedClass != null) {
++ return cachedClass;
++ }
++ }
++ // Paper end-
+ cachedClass = classes.get(name);
+ // Paper end
+
+diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+index 3a02dbe9d183bc907dcce081d8338d5716ed5242..28e5c6591fb594ca79ee92480cedc8f549e3fe01 100644
+--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+@@ -108,7 +108,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+
+ if (result == null) {
+ if (checkGlobal) {
+- result = loader.getClassByName(name);
++ result = loader.getClassByName(name, this); // Paper - prioritize self
+
+ if (result != null) {
+ PluginDescriptionFile provider = ((PluginClassLoader) result.getClassLoader()).description;
diff --git a/Spigot-API-Patches-Unmapped/0206-Add-Raw-Byte-ItemStack-Serialization.patch b/Spigot-API-Patches-Unmapped/0206-Add-Raw-Byte-ItemStack-Serialization.patch
new file mode 100644
index 0000000000..8c4535128e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0206-Add-Raw-Byte-ItemStack-Serialization.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Thu, 30 Apr 2020 16:56:31 +0200
+Subject: [PATCH] Add Raw Byte ItemStack Serialization
+
+Serializes using NBT which is safer for server data migrations than bukkits format.
+
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index d6897f43a0692e031bed8a212d9a637ef548cc60..e348034288c74ab80360086d71f0b7f61551df24 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -97,5 +97,9 @@ public interface UnsafeValues {
+ static boolean isLegacyPlugin(org.bukkit.plugin.Plugin plugin) {
+ return !Bukkit.getUnsafe().isSupportedApiVersion(plugin.getDescription().getAPIVersion());
+ }
++
++ byte[] serializeItem(ItemStack item);
++
++ ItemStack deserializeItem(byte[] data);
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
+index 3d63514729ddc30ff559a65815612be81e777892..58f99e3ebac9a01ebffe4d208e16cbee474d4aa3 100644
+--- a/src/main/java/org/bukkit/inventory/ItemStack.java
++++ b/src/main/java/org/bukkit/inventory/ItemStack.java
+@@ -619,6 +619,30 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, net.kyor
+ return Bukkit.getServer().getItemFactory().ensureServerConversions(this);
+ }
+
++ /**
++ * Deserializes this itemstack from raw NBT bytes. NBT is safer for data migrations as it will
++ * use the built in data converter instead of bukkits dangerous serialization system.
++ *
++ * This expects that the DataVersion was stored on the root of the Compound, as saved from
++ * the {@link #serializeAsBytes()} API returned.
++ * @param bytes bytes representing an item in NBT
++ * @return ItemStack migrated to this version of Minecraft if needed.
++ */
++ @NotNull
++ public static ItemStack deserializeBytes(@NotNull byte[] bytes) {
++ return org.bukkit.Bukkit.getUnsafe().deserializeItem(bytes);
++ }
++
++ /**
++ * Serializes this itemstack to raw bytes in NBT. NBT is safer for data migrations as it will
++ * use the built in data converter instead of bukkits dangerous serialization system.
++ * @return bytes representing this item in NBT.
++ */
++ @NotNull
++ public byte[] serializeAsBytes() {
++ return org.bukkit.Bukkit.getUnsafe().serializeItem(this);
++ }
++
+ /**
+ * Gets the Display name as seen in the Client.
+ * Currently the server only supports the English language. To override this,
diff --git a/Spigot-API-Patches-Unmapped/0207-Provide-a-useful-PluginClassLoader-toString.patch b/Spigot-API-Patches-Unmapped/0207-Provide-a-useful-PluginClassLoader-toString.patch
new file mode 100644
index 0000000000..056a908da9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0207-Provide-a-useful-PluginClassLoader-toString.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sun, 31 May 2020 15:26:17 +0100
+Subject: [PATCH] Provide a useful PluginClassLoader#toString
+
+There are several cases where the plugin classloader may be dumped to the logs,
+however, this provides no indication of the owner of the classloader, making
+these messages effectively useless, this patch rectifies this
+
+diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+index 28e5c6591fb594ca79ee92480cedc8f549e3fe01..62f7a6817da079513f471e36cd79739d36a36d86 100644
+--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+@@ -209,4 +209,16 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+ javaPlugin.logger = this.logger; // Paper - set logger
+ javaPlugin.init(loader, loader.server, description, dataFolder, file, this);
+ }
++
++ // Paper start
++ @Override
++ public String toString() {
++ JavaPlugin currPlugin = plugin != null ? plugin : pluginInit;
++ return "PluginClassLoader{" +
++ "plugin=" + currPlugin +
++ ", pluginEnabled=" + (currPlugin == null ? "uninitialized" : currPlugin.isEnabled()) +
++ ", url=" + file +
++ '}';
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0208-Inventory-getHolder-method-without-block-snapshot.patch b/Spigot-API-Patches-Unmapped/0208-Inventory-getHolder-method-without-block-snapshot.patch
new file mode 100644
index 0000000000..5b633e50af
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0208-Inventory-getHolder-method-without-block-snapshot.patch
@@ -0,0 +1,51 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Phoenix616 <[email protected]>
+Date: Wed, 10 Jun 2020 23:55:16 +0100
+Subject: [PATCH] Inventory getHolder method without block snapshot
+
+
+diff --git a/src/main/java/org/bukkit/block/DoubleChest.java b/src/main/java/org/bukkit/block/DoubleChest.java
+index 83a4642119c3f33749e04c774cf2b39839f797e2..a39d2f1acbbd84ae0e2cf29f85594e09e55e9355 100644
+--- a/src/main/java/org/bukkit/block/DoubleChest.java
++++ b/src/main/java/org/bukkit/block/DoubleChest.java
+@@ -34,6 +34,18 @@ public class DoubleChest implements InventoryHolder {
+ return inventory.getRightSide().getHolder();
+ }
+
++ // Paper start - getHolder without snapshot
++ @Nullable
++ public InventoryHolder getLeftSide(boolean useSnapshot) {
++ return inventory.getLeftSide().getHolder(useSnapshot);
++ }
++
++ @Nullable
++ public InventoryHolder getRightSide(boolean useSnapshot) {
++ return inventory.getRightSide().getHolder(useSnapshot);
++ }
++ // Paper end
++
+ @NotNull
+ public Location getLocation() {
+ return getInventory().getLocation();
+diff --git a/src/main/java/org/bukkit/inventory/Inventory.java b/src/main/java/org/bukkit/inventory/Inventory.java
+index 9c6a5bdac8c3ab682bbfae04ff24b76a62bc2883..6386206188e820206bb1a9f516b5e194fdc9d952 100644
+--- a/src/main/java/org/bukkit/inventory/Inventory.java
++++ b/src/main/java/org/bukkit/inventory/Inventory.java
+@@ -384,6 +384,17 @@ public interface Inventory extends Iterable<ItemStack> {
+ @Nullable
+ public InventoryHolder getHolder();
+
++ // Paper start - getHolder without snapshot
++ /**
++ * Gets the block or entity belonging to the open inventory
++ *
++ * @param useSnapshot Create a snapshot if the holder is a tile entity
++ * @return The holder of the inventory; null if it has no holder.
++ */
++ @Nullable
++ public InventoryHolder getHolder(boolean useSnapshot);
++ // Paper end
++
+ @NotNull
+ @Override
+ public ListIterator<ItemStack> iterator();
diff --git a/Spigot-API-Patches-Unmapped/0209-Expose-Arrow-getItemStack.patch b/Spigot-API-Patches-Unmapped/0209-Expose-Arrow-getItemStack.patch
new file mode 100644
index 0000000000..e718e0e598
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0209-Expose-Arrow-getItemStack.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Nesaak <[email protected]>
+Date: Fri, 22 May 2020 13:35:21 -0400
+Subject: [PATCH] Expose Arrow getItemStack
+
+
+diff --git a/src/main/java/org/bukkit/entity/AbstractArrow.java b/src/main/java/org/bukkit/entity/AbstractArrow.java
+index e8e56e89e32d84af0639fe2e9b0eeabd747b6007..b1d8007eed489aa061c1a6813bcdafc101231e56 100644
+--- a/src/main/java/org/bukkit/entity/AbstractArrow.java
++++ b/src/main/java/org/bukkit/entity/AbstractArrow.java
+@@ -143,6 +143,14 @@ public interface AbstractArrow extends Projectile {
+ }
+
+ // Paper start
++ /**
++ * Gets the ItemStack for this arrow.
++ *
++ * @return The ItemStack, as if a player picked up the arrow
++ */
++ @NotNull
++ org.bukkit.inventory.ItemStack getItemStack();
++
+ /**
+ * Gets the {@link PickupRule} for this arrow.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0210-Add-and-implement-PlayerRecipeBookClickEvent.patch b/Spigot-API-Patches-Unmapped/0210-Add-and-implement-PlayerRecipeBookClickEvent.patch
new file mode 100644
index 0000000000..93bd233b85
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0210-Add-and-implement-PlayerRecipeBookClickEvent.patch
@@ -0,0 +1,96 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: LordKorea <[email protected]>
+Date: Mon, 11 May 2020 22:38:10 -0400
+Subject: [PATCH] Add and implement PlayerRecipeBookClickEvent
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerRecipeBookClickEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerRecipeBookClickEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7fa937d339ee98ad308deebb523fead6522eb262
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerRecipeBookClickEvent.java
+@@ -0,0 +1,84 @@
++package com.destroystokyo.paper.event.player;
++
++import org.bukkit.NamespacedKey;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player clicks a recipe in the recipe book
++ */
++public class PlayerRecipeBookClickEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancel = false;
++ @NotNull private NamespacedKey recipe;
++ private boolean makeAll;
++
++ public PlayerRecipeBookClickEvent(@NotNull Player player, @NotNull NamespacedKey recipe, boolean makeAll) {
++ super(player);
++ this.recipe = recipe;
++ this.makeAll = makeAll;
++ }
++
++ /**
++ * Gets the namespaced key of the recipe that was clicked by the player
++ *
++ * @return The namespaced key of the recipe
++ */
++ @NotNull
++ public NamespacedKey getRecipe() {
++ return recipe;
++ }
++
++ /**
++ * Changes what recipe is requested. This sets the requested recipe to the recipe with the given key
++ *
++ * @param recipe The key of the recipe that should be requested
++ */
++ public void setRecipe(@NotNull NamespacedKey recipe) {
++ this.recipe = recipe;
++ }
++
++ /**
++ * Gets a boolean which indicates whether or not the player requested to make the maximum amount of results. This is
++ * true if shift is pressed while the recipe is clicked in the recipe book
++ *
++ * @return {@code true} if shift is pressed while the recipe is clicked
++ */
++ public boolean isMakeAll() {
++ return makeAll;
++ }
++
++ /**
++ * Sets whether or not the maximum amount of results should be made. If this is true, the request is handled as if
++ * the player had pressed shift while clicking on the recipe
++ *
++ * @param makeAll {@code true} if the request should attempt to make the maximum amount of results
++ */
++ public void setMakeAll(boolean makeAll) {
++ this.makeAll = makeAll;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0211-Support-components-in-ItemMeta.patch b/Spigot-API-Patches-Unmapped/0211-Support-components-in-ItemMeta.patch
new file mode 100644
index 0000000000..1b51f21c69
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0211-Support-components-in-ItemMeta.patch
@@ -0,0 +1,93 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MiniDigger <[email protected]>
+Date: Sat, 6 Jun 2020 18:13:16 +0200
+Subject: [PATCH] Support components in ItemMeta
+
+
+diff --git a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+index 01b462fccce71cef3398dd43944046f322b8e57e..f093f991f1fedd20fcef041b093398250b7fb286 100644
+--- a/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
++++ b/src/main/java/org/bukkit/inventory/meta/ItemMeta.java
+@@ -5,6 +5,7 @@ import java.util.Collection;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
++import net.kyori.adventure.text.Component;
+ import org.bukkit.attribute.Attribute;
+ import org.bukkit.attribute.AttributeModifier;
+ import org.bukkit.configuration.serialization.ConfigurationSerializable;
+@@ -62,6 +63,20 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @NotNull
+ String getDisplayName();
+
++ // Paper start
++ /**
++ * Gets the display name that is set.
++ * <p>
++ * Plugins should check that hasDisplayName() returns <code>true</code>
++ * before calling this method.
++ *
++ * @return the display name that is set
++ * @deprecated use {@link #displayName()}
++ */
++ @NotNull
++ @Deprecated
++ net.md_5.bungee.api.chat.BaseComponent[] getDisplayNameComponent();
++ // Paper end
+ /**
+ * Sets the display name.
+ *
+@@ -71,6 +86,16 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @Deprecated // Paper
+ void setDisplayName(@Nullable String name);
+
++ // Paper start
++ /**
++ * Sets the display name.
++ *
++ * @param component the name component to set
++ * @deprecated use {@link #displayName(Component)}
++ */
++ @Deprecated
++ void setDisplayNameComponent(@Nullable net.md_5.bungee.api.chat.BaseComponent[] component);
++ // Paper end
+ /**
+ * Checks for existence of a localized name.
+ *
+@@ -134,6 +159,19 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @Nullable
+ List<String> getLore();
+
++ /**
++ * Gets the lore that is set.
++ * <p>
++ * Plugins should check if hasLore() returns <code>true</code> before
++ * calling this method.
++ *
++ * @return a list of lore that is set
++ * @deprecated use {@link #lore()}
++ */
++ @Nullable
++ @Deprecated
++ List<net.md_5.bungee.api.chat.BaseComponent[]> getLoreComponents();
++
+ /**
+ * Sets the lore for this item.
+ * Removes lore when given null.
+@@ -144,6 +182,16 @@ public interface ItemMeta extends Cloneable, ConfigurationSerializable, Persiste
+ @Deprecated // Paper
+ void setLore(@Nullable List<String> lore);
+
++ /**
++ * Sets the lore for this item.
++ * Removes lore when given null.
++ *
++ * @param lore the lore that will be set
++ * @deprecated use {@link #lore(List)}
++ */
++ @Deprecated
++ void setLoreComponents(@Nullable List<net.md_5.bungee.api.chat.BaseComponent[]> lore);
++
+ /**
+ * Checks for existence of custom model data.
+ * <p>
diff --git a/Spigot-API-Patches-Unmapped/0212-added-2-new-TargetReasons-for-1.16-mob-behavior.patch b/Spigot-API-Patches-Unmapped/0212-added-2-new-TargetReasons-for-1.16-mob-behavior.patch
new file mode 100644
index 0000000000..747470a546
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0212-added-2-new-TargetReasons-for-1.16-mob-behavior.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Fri, 3 Jul 2020 15:05:54 -0700
+Subject: [PATCH] added 2 new TargetReasons for 1.16 mob behavior
+
+
+diff --git a/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java b/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java
+index dee186e99463a56394bbc2039d1e763d109125b9..601904150156d475c18286b485f3409307a75950 100644
+--- a/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java
++++ b/src/main/java/org/bukkit/event/entity/EntityTargetEvent.java
+@@ -159,6 +159,14 @@ public class EntityTargetEvent extends EntityEvent implements Cancellable {
+ * as wheat in it's hand.
+ */
+ TEMPT,
++ /**
++ * When the target is in a different dimension
++ */
++ TARGET_OTHER_LEVEL, // Paper
++ /**
++ * When the target is in creative or spectator mode, or the gamemode is peaceful, or other reasons
++ */
++ TARGET_INVALID, // Paper
+ /**
+ * A currently unknown reason for the entity changing target.
+ */
diff --git a/Spigot-API-Patches-Unmapped/0213-Add-entity-liquid-API.patch b/Spigot-API-Patches-Unmapped/0213-Add-entity-liquid-API.patch
new file mode 100644
index 0000000000..c13d4c2cb3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0213-Add-entity-liquid-API.patch
@@ -0,0 +1,46 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Thu, 2 Jul 2020 18:11:33 -0500
+Subject: [PATCH] Add entity liquid API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 2622a2f5c9e2cb43aff7ef9eac2dcdb3fd8fad04..75cebfca7b436ef30b718bba7e0566d047e5c61a 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -668,5 +668,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ */
+ @NotNull
+ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason getEntitySpawnReason();
++
++ /**
++ * Check if entity is in rain
++ */
++ public boolean isInRain();
++
++ /**
++ * Check if entity is in bubble column
++ */
++ public boolean isInBubbleColumn();
++
++ /**
++ * Check if entity is in water or rain
++ */
++ public boolean isInWaterOrRain();
++
++ /**
++ * Check if entity is in water or bubble column
++ */
++ public boolean isInWaterOrBubbleColumn();
++
++ /**
++ * Check if entity is in water or rain or bubble column
++ */
++ public boolean isInWaterOrRainOrBubbleColumn();
++
++ /**
++ * Check if entity is in lava
++ */
++ public boolean isInLava();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0214-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch b/Spigot-API-Patches-Unmapped/0214-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch
new file mode 100644
index 0000000000..5dc61d601d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0214-Add-PrepareResultEvent-PrepareGrindstoneEvent.patch
@@ -0,0 +1,211 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Fri, 3 Jul 2020 11:58:56 -0500
+Subject: [PATCH] Add PrepareResultEvent / PrepareGrindstoneEvent
+
+Adds a new event for all crafting stations that generate a result slot item
+
+Anvil, Grindstone and Smithing now extend this event
+
+Grindstone is a backwards compat from a previous PrepareGrindstoneEvent
+
+diff --git a/src/main/java/com/destroystokyo/paper/event/inventory/PrepareGrindstoneEvent.java b/src/main/java/com/destroystokyo/paper/event/inventory/PrepareGrindstoneEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..449e8c06f8434b59d49a76481fa60c5c49e80579
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/inventory/PrepareGrindstoneEvent.java
+@@ -0,0 +1,28 @@
++package com.destroystokyo.paper.event.inventory;
++
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.inventory.InventoryEvent;
++import org.bukkit.inventory.GrindstoneInventory;
++import org.bukkit.inventory.InventoryView;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when an item is put in a slot for grinding in a Grindstone
++ * @see com.destroystokyo.paper.event.inventory.PrepareResultEvent
++ */
++@Deprecated
++public class PrepareGrindstoneEvent extends PrepareResultEvent {
++
++ public PrepareGrindstoneEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
++ super(inventory, result);
++ }
++
++ @NotNull
++ @Override
++ public GrindstoneInventory getInventory() {
++ return (GrindstoneInventory) super.getInventory();
++ }
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/event/inventory/PrepareResultEvent.java b/src/main/java/com/destroystokyo/paper/event/inventory/PrepareResultEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..045ce9ec3c9134aced5f5235b760ac85599d16c6
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/event/inventory/PrepareResultEvent.java
+@@ -0,0 +1,48 @@
++package com.destroystokyo.paper.event.inventory;
++
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.inventory.InventoryEvent;
++import org.bukkit.event.inventory.InventoryType;
++import org.bukkit.inventory.InventoryView;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when an item is put in an inventory containing a result slot
++ */
++public class PrepareResultEvent extends InventoryEvent {
++
++ private static final HandlerList handlers = new HandlerList();
++ private ItemStack result;
++
++ public PrepareResultEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
++ super(inventory);
++ this.result = result;
++ }
++
++ /**
++ * Get result item, may be null.
++ *
++ * @return result item
++ */
++ @Nullable
++ public ItemStack getResult() {
++ return result;
++ }
++
++ public void setResult(@Nullable ItemStack result) {
++ this.result = result;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/org/bukkit/event/inventory/PrepareAnvilEvent.java b/src/main/java/org/bukkit/event/inventory/PrepareAnvilEvent.java
+index 77109a07f07aa6985106dc1a9ad5218f6c7f360f..f1f6f4ab4f81a3f21e757fef4a30b00e94371f8d 100644
+--- a/src/main/java/org/bukkit/event/inventory/PrepareAnvilEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/PrepareAnvilEvent.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.event.inventory;
+
++import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
+ import org.bukkit.event.HandlerList;
+ import org.bukkit.inventory.AnvilInventory;
+ import org.bukkit.inventory.InventoryView;
+@@ -10,14 +11,16 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Called when an item is put in a slot for repair by an anvil.
+ */
+-public class PrepareAnvilEvent extends InventoryEvent {
++// Paper start - extend PrepareResultEvent
++public class PrepareAnvilEvent extends PrepareResultEvent {
+
+- private static final HandlerList handlers = new HandlerList();
+- private ItemStack result;
++ //private static final HandlerList handlers = new HandlerList();
++ //private ItemStack result;
+
+ public PrepareAnvilEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
+- super(inventory);
+- this.result = result;
++ super(inventory, result);
++ //this.result = result;
++ // Paper end
+ }
+
+ @NotNull
+@@ -33,13 +36,14 @@ public class PrepareAnvilEvent extends InventoryEvent {
+ */
+ @Nullable
+ public ItemStack getResult() {
+- return result;
++ return super.getResult(); // Paper
+ }
+
+ public void setResult(@Nullable ItemStack result) {
+- this.result = result;
++ super.setResult(result); // Paper
+ }
+
++ /* // Paper - comment out
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+@@ -50,4 +54,5 @@ public class PrepareAnvilEvent extends InventoryEvent {
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
++ */ // Paper
+ }
+diff --git a/src/main/java/org/bukkit/event/inventory/PrepareSmithingEvent.java b/src/main/java/org/bukkit/event/inventory/PrepareSmithingEvent.java
+index 99af1540324c4d68c5890ac40b591c5cbdd2e870..0bc0ca4f96c800e9c46c61710f44446691d8b93f 100644
+--- a/src/main/java/org/bukkit/event/inventory/PrepareSmithingEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/PrepareSmithingEvent.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.event.inventory;
+
++import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
+ import org.bukkit.event.HandlerList;
+ import org.bukkit.inventory.InventoryView;
+ import org.bukkit.inventory.ItemStack;
+@@ -10,14 +11,16 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Called when an item is put in a slot for upgrade by a Smithing Table.
+ */
+-public class PrepareSmithingEvent extends InventoryEvent {
++// Paper start - extend PrepareResultEvent
++public class PrepareSmithingEvent extends PrepareResultEvent {
+
+- private static final HandlerList handlers = new HandlerList();
+- private ItemStack result;
++ //private static final HandlerList handlers = new HandlerList();
++ //private ItemStack result;
+
+ public PrepareSmithingEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
+- super(inventory);
+- this.result = result;
++ super(inventory, result);
++ //this.result = result;
++ // Paper end
+ }
+
+ @NotNull
+@@ -33,13 +36,14 @@ public class PrepareSmithingEvent extends InventoryEvent {
+ */
+ @Nullable
+ public ItemStack getResult() {
+- return result;
++ return super.getResult(); // Paper
+ }
+
+ public void setResult(@Nullable ItemStack result) {
+- this.result = result;
++ super.setResult(result); // Paper
+ }
+
++ /* // Paper - comment out
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+@@ -50,4 +54,5 @@ public class PrepareSmithingEvent extends InventoryEvent {
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
++ */ // Paper
+ }
diff --git a/Spigot-API-Patches-Unmapped/0215-Allow-delegation-to-vanilla-chunk-gen.patch b/Spigot-API-Patches-Unmapped/0215-Allow-delegation-to-vanilla-chunk-gen.patch
new file mode 100644
index 0000000000..1f3141b243
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0215-Allow-delegation-to-vanilla-chunk-gen.patch
@@ -0,0 +1,85 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MiniDigger <[email protected]>
+Date: Wed, 29 Apr 2020 02:09:17 +0200
+Subject: [PATCH] Allow delegation to vanilla chunk gen
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 77d352450f6bc5293efec3b75e08b857e2626fe7..1eaf6aae1e13c48a7f911e523015cb9b8cca8638 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -1478,6 +1478,22 @@ public final class Bukkit {
+ return server.createChunkData(world);
+ }
+
++ // Paper start
++ /**
++ * Create a ChunkData for use in a generator, that is populated by the vanilla generator for that world
++ *
++ * @param world the world to create the ChunkData for
++ * @param x the x coordinate of the chunk
++ * @param z the z coordinate of the chunk
++ * @return a new ChunkData for the world
++ *
++ */
++ @NotNull
++ public static ChunkGenerator.ChunkData createVanillaChunkData(@NotNull World world, int x, int z) {
++ return server.createVanillaChunkData(world, x, z);
++ }
++ // Paper stop
++
+ /**
+ * Creates a boss bar instance to display to players. The progress
+ * defaults to 1.0
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 4b1f515f2228f16c1ce793bd45510243d758236f..8ee02c3d6cc8751704e5993fecd05293714e492f 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -1245,6 +1245,20 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @NotNull
+ public ChunkGenerator.ChunkData createChunkData(@NotNull World world);
+
++ // Paper start
++ /**
++ * Create a ChunkData for use in a generator, that is populated by the vanilla generator for that world
++ *
++ * @param world the world to create the ChunkData for
++ * @param x the x coordinate of the chunk
++ * @param z the z coordinate of the chunk
++ * @return a new ChunkData for the world
++ *
++ */
++ @NotNull
++ ChunkGenerator.ChunkData createVanillaChunkData(@NotNull World world, int x, int z);
++ // Paper end
++
+ /**
+ * Creates a boss bar instance to display to players. The progress
+ * defaults to 1.0
+diff --git a/src/main/java/org/bukkit/generator/ChunkGenerator.java b/src/main/java/org/bukkit/generator/ChunkGenerator.java
+index 7caef27682f22a77de283dd6f391ec8bc0b0312b..5ba77d40a38e5e592ee265e4fbd510043a0b4345 100644
+--- a/src/main/java/org/bukkit/generator/ChunkGenerator.java
++++ b/src/main/java/org/bukkit/generator/ChunkGenerator.java
+@@ -227,6 +227,22 @@ public abstract class ChunkGenerator {
+ return false;
+ }
+
++ // Paper start
++ /**
++ * Create a ChunkData for use in a generator, that is populated by the vanilla generator for that world
++ *
++ * @param world the world to create the ChunkData for
++ * @param x the x coordinate of the chunk
++ * @param z the z coordinate of the chunk
++ * @return a new ChunkData for the world
++ *
++ */
++ @NotNull
++ public ChunkData createVanillaChunkData(@NotNull World world, int x, int z) {
++ return Bukkit.getServer().createVanillaChunkData(world, x, z);
++ }
++ // Paper end
++
+ /**
+ * Data for a Chunk.
+ */
diff --git a/Spigot-API-Patches-Unmapped/0216-Support-hex-colors-in-getLastColors.patch b/Spigot-API-Patches-Unmapped/0216-Support-hex-colors-in-getLastColors.patch
new file mode 100644
index 0000000000..a3117a4377
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0216-Support-hex-colors-in-getLastColors.patch
@@ -0,0 +1,34 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Gerrygames <[email protected]>
+Date: Thu, 16 Jul 2020 10:40:10 +0200
+Subject: [PATCH] Support hex colors in getLastColors
+
+
+diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java
+index 44d597d7a6f66a18b8037e971170ff7cea5e825f..4594701d77c5d0f744bece871b98d9f6f73eb5a7 100644
+--- a/src/main/java/org/bukkit/ChatColor.java
++++ b/src/main/java/org/bukkit/ChatColor.java
+@@ -363,6 +363,7 @@ public enum ChatColor {
+ return new String(b);
+ }
+
++ private static final Pattern HEX_COLOR_PATTERN = Pattern.compile(COLOR_CHAR + "x(?>" + COLOR_CHAR + "[0-9a-f]){6}", Pattern.CASE_INSENSITIVE); // Paper - Support hex colors in getLastColors
+ /**
+ * Gets the ChatColors used at the end of the given input string.
+ *
+@@ -380,6 +381,15 @@ public enum ChatColor {
+ for (int index = length - 1; index > -1; index--) {
+ char section = input.charAt(index);
+ if (section == COLOR_CHAR && index < length - 1) {
++ // Paper start - Support hex colors
++ if (index > 11 && input.charAt(index - 12) == COLOR_CHAR && (input.charAt(index - 11) == 'x' || input.charAt(index - 11) == 'X')) {
++ String color = input.substring(index - 12, index + 2);
++ if (HEX_COLOR_PATTERN.matcher(color).matches()) {
++ result = color + result;
++ break;
++ }
++ }
++ // Paper end
+ char c = input.charAt(index + 1);
+ ChatColor color = getByChar(c);
+
diff --git a/Spigot-API-Patches-Unmapped/0217-Add-setMaxPlayers-API.patch b/Spigot-API-Patches-Unmapped/0217-Add-setMaxPlayers-API.patch
new file mode 100644
index 0000000000..17b9d923f2
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0217-Add-setMaxPlayers-API.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Sat, 22 Aug 2020 23:59:25 +0200
+Subject: [PATCH] Add #setMaxPlayers API
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 1eaf6aae1e13c48a7f911e523015cb9b8cca8638..6228d7eca85fba52296c8d63d32804f32af1b421 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -171,6 +171,17 @@ public final class Bukkit {
+ return server.getMaxPlayers();
+ }
+
++ // Paper start
++ /**
++ * Set the maximum amount of players which can login to this server.
++ *
++ * @param maxPlayers the amount of players this server allows
++ */
++ public static void setMaxPlayers(int maxPlayers) {
++ server.setMaxPlayers(maxPlayers);
++ }
++ // Paper end
++
+ /**
+ * Get the game port that the server runs on.
+ *
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 8ee02c3d6cc8751704e5993fecd05293714e492f..6237578b373002c009efde4fb4c1864f0bf4f19e 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -144,6 +144,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ */
+ public int getMaxPlayers();
+
++ // Paper start
++ /**
++ * Set the maximum amount of players which can login to this server.
++ *
++ * @param maxPlayers the amount of players this server allows
++ */
++ public void setMaxPlayers(int maxPlayers);
++ // Paper end
++
+ /**
+ * Get the game port that the server runs on.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0218-Add-moon-phase-API.patch b/Spigot-API-Patches-Unmapped/0218-Add-moon-phase-API.patch
new file mode 100644
index 0000000000..c9e9827954
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0218-Add-moon-phase-API.patch
@@ -0,0 +1,65 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 23 Aug 2020 16:32:03 +0200
+Subject: [PATCH] Add moon phase API
+
+
+diff --git a/src/main/java/io/papermc/paper/world/MoonPhase.java b/src/main/java/io/papermc/paper/world/MoonPhase.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..df05153397b42930cd53d37b30824c7e5f008f7e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/world/MoonPhase.java
+@@ -0,0 +1,36 @@
++package io.papermc.paper.world;
++
++import org.jetbrains.annotations.NotNull;
++
++import java.util.HashMap;
++import java.util.Map;
++
++public enum MoonPhase {
++ FULL_MOON(0L),
++ WANING_GIBBOUS(1L),
++ LAST_QUARTER(2L),
++ WANING_CRESCENT(3L),
++ NEW_MOON(4L),
++ WAXING_CRESCENT(5L),
++ FIRST_QUARTER(6L),
++ WAXING_GIBBOUS(7L);
++
++ private final long day;
++
++ MoonPhase(long day) {
++ this.day = day;
++ }
++
++ private static final Map<Long, MoonPhase> BY_DAY = new HashMap<>();
++
++ static {
++ for (MoonPhase phase : values()) {
++ BY_DAY.put(phase.day, phase);
++ }
++ }
++
++ @NotNull
++ public static MoonPhase getPhase(long day) {
++ return BY_DAY.get(day % 8L);
++ }
++}
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 6441d4e45e5d5f008f95233cdc34048b8be38592..e20d863d1308b470a294cb7ab022aac4b9a91f71 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -70,6 +70,12 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ * @return The amount of Players in this world
+ */
+ int getPlayerCount();
++
++ /**
++ * @return the current moon phase at the current time in the world
++ */
++ @NotNull
++ io.papermc.paper.world.MoonPhase getMoonPhase();
+ // Paper end
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0219-Add-playPickupItemAnimation-to-LivingEntity.patch b/Spigot-API-Patches-Unmapped/0219-Add-playPickupItemAnimation-to-LivingEntity.patch
new file mode 100644
index 0000000000..6550b233c5
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0219-Add-playPickupItemAnimation-to-LivingEntity.patch
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Sun, 23 Aug 2020 19:36:08 +0200
+Subject: [PATCH] Add playPickupItemAnimation to LivingEntity
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index a2f5639904881d9bef7d1550dbed810e4b17c8de..9bf525b795ff1d88d2596b1f2bc787ce0df047bb 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -805,5 +805,28 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @param jumping entity jump state
+ */
+ void setJumping(boolean jumping);
++
++ /**
++ * Plays pickup item animation towards this entity.
++ * <p>
++ * <b>This will remove the item on the client.</b>
++ * <p>
++ * Quantity is inferred to be that of the {@link Item}.
++ *
++ * @param item item to pickup
++ */
++ default void playPickupItemAnimation(@NotNull Item item) {
++ playPickupItemAnimation(item, item.getItemStack().getAmount());
++ }
++
++ /**
++ * Plays pickup item animation towards this entity.
++ * <p>
++ * <b>This will remove the item on the client.</b>
++ *
++ * @param item item to pickup
++ * @param quantity quantity of item
++ */
++ void playPickupItemAnimation(@NotNull Item item, int quantity);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0220-Add-BellRingEvent.patch b/Spigot-API-Patches-Unmapped/0220-Add-BellRingEvent.patch
new file mode 100644
index 0000000000..7862ad8c88
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0220-Add-BellRingEvent.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Eearslya Sleiarion <[email protected]>
+Date: Mon, 24 Jun 2019 21:27:39 -0700
+Subject: [PATCH] Add BellRingEvent
+
+Add a new event, BellRingEvent, to trigger whenever a player rings a
+village bell. Passes along the bell block and the player who rang it.
+
+diff --git a/src/main/java/io/papermc/paper/event/block/BellRingEvent.java b/src/main/java/io/papermc/paper/event/block/BellRingEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..881e545df51409e6101b4bb49f699655a744f13f
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/BellRingEvent.java
+@@ -0,0 +1,55 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Entity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.bukkit.potion.PotionEffect;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when a bell is rung.
++ */
++public class BellRingEvent extends BlockEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ private final Entity entity;
++
++ public BellRingEvent(@NotNull Block block, @Nullable Entity entity) {
++ super(block);
++ this.entity = entity;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ /**
++ * Gets the entity that rang the bell.
++ *
++ * @return the ringer or null if none
++ */
++ @Nullable
++ public Entity getEntity() {
++ return entity;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0221-Brand-support.patch b/Spigot-API-Patches-Unmapped/0221-Brand-support.patch
new file mode 100644
index 0000000000..adab82515a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0221-Brand-support.patch
@@ -0,0 +1,27 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: DigitalRegent <[email protected]>
+Date: Mon, 6 Apr 2020 20:30:09 +0200
+Subject: [PATCH] Brand support
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index e6b5969df34f096cec84ebb46061cb956eba4bab..61c572fe69cf08aaa36a26ebed21127c14309bf4 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -2029,6 +2029,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ // Paper end
+ }
+
++ // Paper start - brand support
++ /**
++ * Returns player's client brand name. If the client didn't send this information, the brand name will be null.<br>
++ * For the Notchian client this name defaults to <code>vanilla</code>. Some modified clients report other names such as <code>forge</code>.<br>
++ * @return client brand name
++ */
++ @Nullable
++ String getClientBrandName();
++ // Paper end
++
+ @NotNull
+ @Override
+ Spigot spigot();
diff --git a/Spigot-API-Patches-Unmapped/0222-Add-more-Evoker-API.patch b/Spigot-API-Patches-Unmapped/0222-Add-more-Evoker-API.patch
new file mode 100644
index 0000000000..0d6f0f7328
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0222-Add-more-Evoker-API.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Sun, 23 Aug 2020 15:22:44 +0200
+Subject: [PATCH] Add more Evoker API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Evoker.java b/src/main/java/org/bukkit/entity/Evoker.java
+index f8d173adc09197418883dc7bc66dd8bfff8c5c72..76f81cd124090337876c9e5e469862a1c8da4ec8 100644
+--- a/src/main/java/org/bukkit/entity/Evoker.java
++++ b/src/main/java/org/bukkit/entity/Evoker.java
+@@ -64,4 +64,19 @@ public interface Evoker extends Spellcaster {
+ */
+ @Deprecated
+ void setCurrentSpell(@Nullable Spell spell);
++
++ // Paper start
++ /**
++ * @return the sheep being targeted by the {@link Spell#WOLOLO wololo spell}, or {@code null} if none
++ */
++ @Nullable
++ Sheep getWololoTarget();
++
++ /**
++ * Set the sheep to be the target of the {@link Spell#WOLOLO wololo spell}, or {@code null} to clear.
++ *
++ * @param sheep new wololo target
++ */
++ void setWololoTarget(@Nullable Sheep sheep);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0223-Add-a-way-to-get-translation-keys-for-blocks-entitie.patch b/Spigot-API-Patches-Unmapped/0223-Add-a-way-to-get-translation-keys-for-blocks-entitie.patch
new file mode 100644
index 0000000000..bfe84791b9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0223-Add-a-way-to-get-translation-keys-for-blocks-entitie.patch
@@ -0,0 +1,98 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MeFisto94 <[email protected]>
+Date: Tue, 11 Aug 2020 19:17:46 +0200
+Subject: [PATCH] Add a way to get translation keys for blocks, entities and
+ materials
+
+
+diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
+index 2b53e68e96ea346a6f2b5cadcf9f81b2c231c408..e453e5eb7245aad3ecbb19652ebb34abe030c0a9 100644
+--- a/src/main/java/org/bukkit/Material.java
++++ b/src/main/java/org/bukkit/Material.java
+@@ -3578,6 +3578,16 @@ public enum Material implements Keyed {
+ }
+ return false;
+ }
++
++ /**
++ * Return the translation key for the Material, so the client can translate it into the active
++ * locale when using a TranslatableComponent.
++ * @return the translation key
++ */
++ @NotNull
++ public String getTranslationKey() {
++ return Bukkit.getUnsafe().getTranslationKey(this);
++ }
+ // Paper end
+
+ /**
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index e348034288c74ab80360086d71f0b7f61551df24..a604b7e00e64912a2103d9af845eddff6835e825 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -101,5 +101,27 @@ public interface UnsafeValues {
+ byte[] serializeItem(ItemStack item);
+
+ ItemStack deserializeItem(byte[] data);
++
++ /**
++ * Return the translation key for the Material, so the client can translate it into the active
++ * locale when using a TranslatableComponent.
++ * @return the translation key
++ */
++ String getTranslationKey(Material mat);
++
++ /**
++ * Return the translation key for the Block, so the client can translate it into the active
++ * locale when using a TranslatableComponent.
++ * @return the translation key
++ */
++ String getTranslationKey(org.bukkit.block.Block block);
++
++ /**
++ * Return the translation key for the EntityType, so the client can translate it into the active
++ * locale when using a TranslatableComponent.<br>
++ * This is <code>null</code>, when the EntityType isn't known to NMS (custom entities)
++ * @return the translation key
++ */
++ String getTranslationKey(org.bukkit.entity.EntityType type);
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index e1cc36fbe808973227c0e8ca7166453235c90279..e6647c45f65bae916759cd899256f8130790d242 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -584,5 +584,13 @@ public interface Block extends Metadatable {
+ */
+ @NotNull
+ com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup();
++
++ /**
++ * Return the translation key for the Block, so the client can translate it into the active
++ * locale when using a TranslatableComponent.
++ * @return the translation key
++ */
++ @NotNull
++ String getTranslationKey();
+ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/entity/EntityType.java b/src/main/java/org/bukkit/entity/EntityType.java
+index 774363a8186b3861f10c0452ac63726cae365169..692b75eb78405874077c850bfc72e247ccc80860 100644
+--- a/src/main/java/org/bukkit/entity/EntityType.java
++++ b/src/main/java/org/bukkit/entity/EntityType.java
+@@ -414,4 +414,15 @@ public enum EntityType implements Keyed {
+ public boolean isAlive() {
+ return living;
+ }
++
++ /**
++ * Return the translation key for the EntityType, so the client can translate it into the active
++ * locale when using a TranslatableComponent.<br>
++ * This is <code>null</code>, when the EntityType isn't known to NMS (custom entities)
++ * @return the translation key
++ */
++ @Nullable
++ String getTranslationKey() {
++ return org.bukkit.Bukkit.getUnsafe().getTranslationKey(this);
++ }
+ }
diff --git a/Spigot-API-Patches-Unmapped/0224-Create-HoverEvent-from-ItemStack-Entity.patch b/Spigot-API-Patches-Unmapped/0224-Create-HoverEvent-from-ItemStack-Entity.patch
new file mode 100644
index 0000000000..6a9da8a8df
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0224-Create-HoverEvent-from-ItemStack-Entity.patch
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: ysl3000 <[email protected]>
+Date: Mon, 6 Jul 2020 22:17:37 +0200
+Subject: [PATCH] Create HoverEvent from ItemStack Entity
+
+
+diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java
+index 0654873eef22d1e35c7430f098ff9e8f00b870e3..eab52f8615b329a795b3fe3a3719e5687e105061 100644
+--- a/src/main/java/org/bukkit/inventory/ItemFactory.java
++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java
+@@ -175,5 +175,62 @@ public interface ItemFactory {
+ */
+ @Nullable
+ String getI18NDisplayName(@Nullable ItemStack item);
++
++ /**
++ * Creates a {@link net.md_5.bungee.api.chat.hover.content.Content} of that ItemStack for displaying.
++ *
++ * @param itemStack
++ * @return the {@link net.md_5.bungee.api.chat.hover.content.Content} of that ItemStack
++ */
++ @NotNull
++ net.md_5.bungee.api.chat.hover.content.Content hoverContentOf(@NotNull ItemStack itemStack);
++
++ /**
++ * Creates a {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity} for displaying.
++ * Uses the display name of the entity, if present.
++ *
++ * @param entity Entity to create the HoverEvent for
++ * @return the {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity}
++ * @deprecated use {@link org.bukkit.entity.Entity#asHoverEvent()}
++ */
++ @NotNull
++ @Deprecated
++ net.md_5.bungee.api.chat.hover.content.Content hoverContentOf(@NotNull org.bukkit.entity.Entity entity);
++
++ /**
++ * Creates a {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity} for displaying.
++ *
++ * @param entity Entity to create the HoverEvent for
++ * @param customName a custom name that should be displayed, if not passed entity name will be displayed
++ * @return the {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity}
++ * @deprecated use {@link org.bukkit.entity.Entity#asHoverEvent(java.util.function.UnaryOperator)}
++ */
++ @NotNull
++ @Deprecated
++ net.md_5.bungee.api.chat.hover.content.Content hoverContentOf(@NotNull org.bukkit.entity.Entity entity, @Nullable String customName);
++
++ /**
++ * Creates a {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity} for displaying.
++ *
++ * @param entity Entity to create the HoverEvent for
++ * @param customName a custom name that should be displayed, if not passed entity name will be displayed
++ * @return the {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity}
++ * @deprecated use {@link org.bukkit.entity.Entity#asHoverEvent(java.util.function.UnaryOperator)}
++ */
++ @NotNull
++ @Deprecated
++ net.md_5.bungee.api.chat.hover.content.Content hoverContentOf(@NotNull org.bukkit.entity.Entity entity, @Nullable net.md_5.bungee.api.chat.BaseComponent customName);
++
++ /**
++ * Creates a {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity} for displaying.
++ *
++ * @param entity Entity to create the HoverEvent for
++ * @param customName a custom name that should be displayed, if not passed entity name will be displayed
++ * @return the {@link net.md_5.bungee.api.chat.hover.content.Content} of that {@link org.bukkit.entity.Entity}
++ * @deprecated use {@link org.bukkit.entity.Entity#asHoverEvent(java.util.function.UnaryOperator)}
++ */
++ @NotNull
++ @Deprecated
++ net.md_5.bungee.api.chat.hover.content.Content hoverContentOf(@NotNull org.bukkit.entity.Entity entity, @NotNull net.md_5.bungee.api.chat.BaseComponent[] customName);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0225-Add-additional-open-container-api-to-HumanEntity.patch b/Spigot-API-Patches-Unmapped/0225-Add-additional-open-container-api-to-HumanEntity.patch
new file mode 100644
index 0000000000..53584ff633
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0225-Add-additional-open-container-api-to-HumanEntity.patch
@@ -0,0 +1,103 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: JRoy <[email protected]>
+Date: Wed, 26 Aug 2020 02:11:58 -0400
+Subject: [PATCH] Add additional open container api to HumanEntity
+
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index b09d12390d5f77330ac84452e0fee63a169bd01f..77bff8fb6bfdf739e413084e13677a83e723c71e 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -148,6 +148,92 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ @Nullable
+ public InventoryView openMerchant(@NotNull Merchant merchant, boolean force);
+
++ // Paper start - Add additional containers
++ /**
++ * Opens an empty anvil inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no anvil block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openAnvil(@Nullable Location location, boolean force);
++
++ /**
++ * Opens an empty cartography table inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no cartography table block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openCartographyTable(@Nullable Location location, boolean force);
++
++ /**
++ * Opens an empty grindstone inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no grindstone block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openGrindstone(@Nullable Location location, boolean force);
++
++ /**
++ * Opens an empty loom inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no loom block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openLoom(@Nullable Location location, boolean force);
++
++ /**
++ * Opens an empty smithing table inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no smithing table block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openSmithingTable(@Nullable Location location, boolean force);
++
++ /**
++ * Opens an empty stonecutter inventory window with the player's inventory
++ * on the bottom.
++ *
++ * @param location The location to attach it to. If null, the player's
++ * location is used.
++ * @param force If false, and there is no stonecutter block at the location,
++ * no inventory will be opened and null will be returned.
++ * @return The newly opened inventory view, or null if it could not be
++ * opened.
++ */
++ @Nullable
++ public InventoryView openStonecutter(@Nullable Location location, boolean force);
++ // Paper end
++
+ /**
+ * Force-closes the currently open inventory view for this player, if any.
+ */
diff --git a/Spigot-API-Patches-Unmapped/0226-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch b/Spigot-API-Patches-Unmapped/0226-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch
new file mode 100644
index 0000000000..c708b4709d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0226-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MeFisto94 <[email protected]>
+Date: Fri, 28 Aug 2020 01:41:31 +0200
+Subject: [PATCH] Expose the Entity Counter to allow plugins to use valid and
+ non-conflicting Entity Ids
+
+
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index a604b7e00e64912a2103d9af845eddff6835e825..fafc4d63b6202b00a133c50cd38dec54db9b3576 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -123,5 +123,13 @@ public interface UnsafeValues {
+ * @return the translation key
+ */
+ String getTranslationKey(org.bukkit.entity.EntityType type);
++
++ /**
++ * Creates and returns the next EntityId available.
++ * <p>
++ * Use this when sending custom packets, so that there are no collisions on the client or server.
++ */
++ public int nextEntityId();
++
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0227-Entity-isTicking.patch b/Spigot-API-Patches-Unmapped/0227-Entity-isTicking.patch
new file mode 100644
index 0000000000..3856ba234e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0227-Entity-isTicking.patch
@@ -0,0 +1,21 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Sat, 3 Oct 2020 21:39:07 -0500
+Subject: [PATCH] Entity#isTicking
+
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 75cebfca7b436ef30b718bba7e0566d047e5c61a..2cc501b74741bdbdc40d1b135725f18c4d36dc2b 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -698,5 +698,10 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ * Check if entity is in lava
+ */
+ public boolean isInLava();
++
++ /**
++ * Check if entity is inside a ticking chunk
++ */
++ public boolean isTicking();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0228-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch b/Spigot-API-Patches-Unmapped/0228-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch
new file mode 100644
index 0000000000..7798b89d21
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0228-Clarify-the-Javadocs-for-Entity.getEntitySpawnReason.patch
@@ -0,0 +1,19 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aurora <[email protected]>
+Date: Sat, 3 Oct 2020 16:28:41 +0200
+Subject: [PATCH] Clarify the Javadocs for Entity.getEntitySpawnReason()
+
+
+diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
+index 2cc501b74741bdbdc40d1b135725f18c4d36dc2b..428daeb04d0a35a443467e2f657d2356bcfdd7d7 100644
+--- a/src/main/java/org/bukkit/entity/Entity.java
++++ b/src/main/java/org/bukkit/entity/Entity.java
+@@ -664,7 +664,7 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+ Chunk getChunk();
+
+ /**
+- * @return The {@link org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason} that spawned this entity.
++ * @return The {@link org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason} that initially spawned this entity. <!-- Paper - added "initially" to clarify that the SpawnReason doesn't change after the Entity was initially spawned" -->
+ */
+ @NotNull
+ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason getEntitySpawnReason();
diff --git a/Spigot-API-Patches-Unmapped/0229-Villager-resetOffers.patch b/Spigot-API-Patches-Unmapped/0229-Villager-resetOffers.patch
new file mode 100644
index 0000000000..60fedfba0f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0229-Villager-resetOffers.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Mon, 7 Oct 2019 00:15:28 -0500
+Subject: [PATCH] Villager#resetOffers
+
+
+diff --git a/src/main/java/org/bukkit/entity/AbstractVillager.java b/src/main/java/org/bukkit/entity/AbstractVillager.java
+index d2b0c08554dba4d34b37b440f1d77ae0e64cb99e..7fbe31c4fd69d4fca7ef96c0a56b0e0204d60cf4 100644
+--- a/src/main/java/org/bukkit/entity/AbstractVillager.java
++++ b/src/main/java/org/bukkit/entity/AbstractVillager.java
+@@ -21,4 +21,11 @@ public interface AbstractVillager extends Breedable, NPC, InventoryHolder, Merch
+ @NotNull
+ @Override
+ Inventory getInventory();
++
++ // Paper start
++ /**
++ * Reset this villager's trade offers
++ */
++ public void resetOffers();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0230-Player-elytra-boost-API.patch b/Spigot-API-Patches-Unmapped/0230-Player-elytra-boost-API.patch
new file mode 100644
index 0000000000..adf57f7f4f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0230-Player-elytra-boost-API.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Trigary <[email protected]>
+Date: Tue, 14 Apr 2020 12:06:14 +0200
+Subject: [PATCH] Player elytra boost API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 61c572fe69cf08aaa36a26ebed21127c14309bf4..5bdc48b533833f626a012b675c9c58f8b00b2400 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1901,6 +1901,19 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ @NotNull
+ <T> T getClientOption(@NotNull ClientOption<T> option);
++
++ /**
++ * Boost a Player that's {@link #isGliding()} using a {@link Firework}.
++ * If the creation of the entity is cancelled, no boosting is done.
++ * This method does not fire {@link com.destroystokyo.paper.event.player.PlayerElytraBoostEvent}.
++ *
++ * @param firework The {@link Material#FIREWORK_ROCKET} to boost the player with
++ * @return The {@link Firework} boosting the Player or null if the spawning of the entity was cancelled
++ * @throws IllegalArgumentException if {@link #isGliding()} is false
++ * or if the {@code firework} isn't a {@link Material#FIREWORK_ROCKET}
++ */
++ @Nullable
++ Firework boostElytra(@NotNull ItemStack firework);
+ // Paper end
+
+ // Spigot start
diff --git a/Spigot-API-Patches-Unmapped/0231-Add-getOfflinePlayerIfCached-String.patch b/Spigot-API-Patches-Unmapped/0231-Add-getOfflinePlayerIfCached-String.patch
new file mode 100644
index 0000000000..86d947866a
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0231-Add-getOfflinePlayerIfCached-String.patch
@@ -0,0 +1,68 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: oxygencraft <[email protected]>
+Date: Sun, 25 Oct 2020 18:35:58 +1100
+Subject: [PATCH] Add getOfflinePlayerIfCached(String)
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 6228d7eca85fba52296c8d63d32804f32af1b421..68101a322ffab8ec28843386b79b8079576fa720 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -950,6 +950,27 @@ public final class Bukkit {
+ return server.getOfflinePlayer(name);
+ }
+
++ // Paper start
++ /**
++ * Gets the player by the given name, regardless if they are offline or
++ * online.
++ * <p>
++ * This will not make a web request to get the UUID for the given name,
++ * thus this method will not block. However this method will return
++ * {@code null} if the player is not cached.
++ * </p>
++ *
++ * @param name the name of the player to retrieve
++ * @return an offline player if cached, {@code null} otherwise
++ * @see #getOfflinePlayer(String)
++ * @see #getOfflinePlayer(java.util.UUID)
++ */
++ @Nullable
++ public static OfflinePlayer getOfflinePlayerIfCached(@NotNull String name) {
++ return server.getOfflinePlayerIfCached(name);
++ }
++ // Paper end
++
+ /**
+ * Gets the player by the given UUID, regardless if they are offline or
+ * online.
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index 6237578b373002c009efde4fb4c1864f0bf4f19e..a79fa08b9e6fb924b2da933eb6e4b365d14d938d 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -797,6 +797,25 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @NotNull
+ public OfflinePlayer getOfflinePlayer(@NotNull String name);
+
++ // Paper start
++ /**
++ * Gets the player by the given name, regardless if they are offline or
++ * online.
++ * <p>
++ * This will not make a web request to get the UUID for the given name,
++ * thus this method will not block. However this method will return
++ * {@code null} if the player is not cached.
++ * </p>
++ *
++ * @param name the name of the player to retrieve
++ * @return an offline player if cached, {@code null} otherwise
++ * @see #getOfflinePlayer(String)
++ * @see #getOfflinePlayer(java.util.UUID)
++ */
++ @Nullable
++ public OfflinePlayer getOfflinePlayerIfCached(@NotNull String name);
++ // Paper end
++
+ /**
+ * Gets the player by the given UUID, regardless if they are offline or
+ * online.
diff --git a/Spigot-API-Patches-Unmapped/0232-Add-ignore-discounts-API.patch b/Spigot-API-Patches-Unmapped/0232-Add-ignore-discounts-API.patch
new file mode 100644
index 0000000000..492c998c91
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0232-Add-ignore-discounts-API.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Mon, 9 Nov 2020 20:33:38 +0100
+Subject: [PATCH] Add ignore discounts API
+
+
+diff --git a/src/main/java/org/bukkit/inventory/MerchantRecipe.java b/src/main/java/org/bukkit/inventory/MerchantRecipe.java
+index 1fb4a1c53791776f9c5a952a592f15fc35cb2703..2be2f3fe655c417bfc8f8e840f9e9415d168f37e 100644
+--- a/src/main/java/org/bukkit/inventory/MerchantRecipe.java
++++ b/src/main/java/org/bukkit/inventory/MerchantRecipe.java
+@@ -28,6 +28,7 @@ public class MerchantRecipe implements Recipe {
+ private boolean experienceReward;
+ private int villagerExperience;
+ private float priceMultiplier;
++ private boolean ignoreDiscounts; // Paper
+
+ public MerchantRecipe(@NotNull ItemStack result, int maxUses) {
+ this(result, 0, maxUses, false);
+@@ -38,6 +39,12 @@ public class MerchantRecipe implements Recipe {
+ }
+
+ public MerchantRecipe(@NotNull ItemStack result, int uses, int maxUses, boolean experienceReward, int villagerExperience, float priceMultiplier) {
++ // Paper start - add ignoreDiscounts param
++ this(result, uses, maxUses, experienceReward, villagerExperience, priceMultiplier, false);
++ }
++ public MerchantRecipe(@NotNull ItemStack result, int uses, int maxUses, boolean experienceReward, int villagerExperience, float priceMultiplier, boolean ignoreDiscounts) {
++ this.ignoreDiscounts = ignoreDiscounts;
++ // Paper end
+ this.result = result;
+ this.uses = uses;
+ this.maxUses = maxUses;
+@@ -172,4 +179,20 @@ public class MerchantRecipe implements Recipe {
+ public void setPriceMultiplier(float priceMultiplier) {
+ this.priceMultiplier = priceMultiplier;
+ }
++
++ // Paper start
++ /**
++ * @return Whether all discounts on this trade should be ignored.
++ */
++ public boolean shouldIgnoreDiscounts() {
++ return ignoreDiscounts;
++ }
++
++ /**
++ * @param ignoreDiscounts Whether all discounts on this trade should be ignored.
++ */
++ public void setIgnoreDiscounts(boolean ignoreDiscounts) {
++ this.ignoreDiscounts = ignoreDiscounts;
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0233-Item-no-age-no-player-pickup.patch b/Spigot-API-Patches-Unmapped/0233-Item-no-age-no-player-pickup.patch
new file mode 100644
index 0000000000..142a8b5202
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0233-Item-no-age-no-player-pickup.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alfie Smith <[email protected]>
+Date: Sat, 7 Nov 2020 01:20:27 +0000
+Subject: [PATCH] Item no age & no player pickup
+
+
+diff --git a/src/main/java/org/bukkit/entity/Item.java b/src/main/java/org/bukkit/entity/Item.java
+index c404a5b8efea7c780db5ddae19456753808abb3d..0ee072645ecf1bf5feb74de6960947ef76db366e 100644
+--- a/src/main/java/org/bukkit/entity/Item.java
++++ b/src/main/java/org/bukkit/entity/Item.java
+@@ -90,5 +90,34 @@ public interface Item extends Entity {
+ * @param canMobPickup True to allow non-player entity pickup
+ */
+ public void setCanMobPickup(boolean canMobPickup);
++
++ /**
++ * Gets whether the player can pickup the item or not
++ *
++ * @return True if a player can pickup the item
++ */
++ public boolean canPlayerPickup();
++
++ /**
++ * Sets whether the item can be picked up or not. Modifies the pickup delay value to do so.
++ *
++ * @param canPlayerPickup True if the player can pickup the item
++ */
++ public void setCanPlayerPickup(boolean canPlayerPickup);
++
++ /**
++ * Gets whether the item will age and despawn from being on the ground too long
++ *
++ * @return True if the item will age
++ */
++ public boolean willAge();
++
++ /**
++ * Sets whether the item will age or not. If the item is not ageing, it will not despawn
++ * by being on the ground for too long.
++ *
++ * @param willAge True if the item should age
++ */
++ public void setWillAge(boolean willAge);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0234-Beacon-API-custom-effect-ranges.patch b/Spigot-API-Patches-Unmapped/0234-Beacon-API-custom-effect-ranges.patch
new file mode 100644
index 0000000000..ff56b99ebf
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0234-Beacon-API-custom-effect-ranges.patch
@@ -0,0 +1,37 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 24 Jun 2020 12:38:15 -0600
+Subject: [PATCH] Beacon API - custom effect ranges
+
+
+diff --git a/src/main/java/org/bukkit/block/Beacon.java b/src/main/java/org/bukkit/block/Beacon.java
+index 6349fa9da3f96df3553fb9552c1cab95338cecb0..78475fc6faff0f295828d7b53792001d51aa2889 100644
+--- a/src/main/java/org/bukkit/block/Beacon.java
++++ b/src/main/java/org/bukkit/block/Beacon.java
+@@ -64,4 +64,26 @@ public interface Beacon extends TileState, Lockable, Nameable {
+ * @param effect desired secondary effect
+ */
+ void setSecondaryEffect(@Nullable PotionEffectType effect);
++
++ // Paper start - Custom effect ranges
++ /**
++ * Gets the effect range of this beacon.
++ * A negative range value means the beacon is using its default range based on tier.
++ * @return Either the custom range set with {@link #setEffectRange(double)} or the range based on the beacon tier.
++ */
++ double getEffectRange();
++
++ /**
++ * Sets the effect range of the beacon
++ * A negative range value means the beacon is using its default range based on tier.
++ * @param range Radius of effect range.
++ */
++ void setEffectRange(double range);
++
++ /**
++ * Resets the custom range from this beacon and falls back to the range based on the the beacon tier.
++ * Shortcut for setting the effect range to a negative number.
++ */
++ void resetEffectRange();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0235-Add-API-for-quit-reason.patch b/Spigot-API-Patches-Unmapped/0235-Add-API-for-quit-reason.patch
new file mode 100644
index 0000000000..63d864224b
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0235-Add-API-for-quit-reason.patch
@@ -0,0 +1,79 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Sat, 14 Nov 2020 16:19:58 +0100
+Subject: [PATCH] Add API for quit reason
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
+index 849e8f10dd77e9fb46aab17752b8f1ff79e9d42e..b6016aa1e91863efc252eecab69ade6f54c89f27 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerQuitEvent.java
+@@ -11,16 +11,28 @@ import org.jetbrains.annotations.Nullable;
+ public class PlayerQuitEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+ private net.kyori.adventure.text.Component quitMessage; // Paper
++ private final QuitReason reason; // Paper
+
+ @Deprecated // Paper
+ public PlayerQuitEvent(@NotNull final Player who, @Nullable final String quitMessage) {
++ // Paper start
++ this(who, quitMessage, null);
++ }
++ @Deprecated // Paper
++ public PlayerQuitEvent(@NotNull final Player who, @Nullable final String quitMessage, @Nullable QuitReason quitReason) {
+ super(who);
+ this.quitMessage = quitMessage != null ? org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().deserialize(quitMessage) : null; // Paper
++ this.reason = quitReason == null ? QuitReason.DISCONNECTED : quitReason;
+ }
+ // Paper start
++ @Deprecated
+ public PlayerQuitEvent(@NotNull final Player who, @Nullable final net.kyori.adventure.text.Component quitMessage) {
++ this(who, quitMessage, null);
++ }
++ public PlayerQuitEvent(@NotNull final Player who, @Nullable final net.kyori.adventure.text.Component quitMessage, @Nullable QuitReason quitReason) {
+ super(who);
+ this.quitMessage = quitMessage;
++ this.reason = quitReason == null ? QuitReason.DISCONNECTED : quitReason;
+ }
+
+ /**
+@@ -75,4 +87,39 @@ public class PlayerQuitEvent extends PlayerEvent {
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
++
++ // Paper start
++ @NotNull
++ public QuitReason getReason() {
++ return this.reason;
++ }
++
++ public enum QuitReason {
++ /**
++ * The player left on their own behalf.
++ * <p>
++ * This does not mean they pressed the disconnect button in their client, but rather that the client severed the
++ * connection themselves. This may occur if no keep-alive packet is received on their side, among other things.
++ */
++ DISCONNECTED,
++
++ /**
++ * The player was kicked from the server.
++ */
++ KICKED,
++
++ /**
++ * The player has timed out.
++ */
++ TIMED_OUT,
++
++ /**
++ * The player's connection has entered an erroneous state.
++ * <p>
++ * Reasons for this may include invalid packets, invalid data, and uncaught exceptions in the packet handler,
++ * among others.
++ */
++ ERRONEOUS_STATE,
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0236-Add-Destroy-Speed-API.patch b/Spigot-API-Patches-Unmapped/0236-Add-Destroy-Speed-API.patch
new file mode 100644
index 0000000000..bdf3dd2bbe
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0236-Add-Destroy-Speed-API.patch
@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ineusia <[email protected]>
+Date: Mon, 26 Oct 2020 11:37:48 -0500
+Subject: [PATCH] Add Destroy Speed API
+
+Co-authored-by: Jake Potrebic <[email protected]>
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index e6647c45f65bae916759cd899256f8130790d242..e4e3be0ee9c557e04d9ed1ab6f1569bd36a0e846 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -592,5 +592,29 @@ public interface Block extends Metadatable {
+ */
+ @NotNull
+ String getTranslationKey();
++
++ /**
++ * Gets the speed at which this block will be destroyed by a given {@link ItemStack}
++ *
++ * <p>Default value is 1.0</p>
++ *
++ * @param itemStack {@link ItemStack} used to mine this Block
++ * @return the speed that this Block will be mined by the given {@link ItemStack}
++ */
++ @NotNull
++ public default float getDestroySpeed(@NotNull ItemStack itemStack) {
++ return getDestroySpeed(itemStack, false);
++ }
++
++ /**
++ * Gets the speed at which this blook will be destroyed by a given {@link org.bukkit.inventory.ItemStack}
++ * <p>
++ * Default value is 1.0
++ * @param itemStack {@link org.bukkit.inventory.ItemStack} used to mine this Block
++ * @param considerEnchants true to look at enchants on the itemstack
++ * @return the speed that this Block will be mined by the given {@link org.bukkit.inventory.ItemStack}
++ */
++ @NotNull
++ float getDestroySpeed(@NotNull ItemStack itemStack, boolean considerEnchants);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0237-Add-LivingEntity-clearActiveItem.patch b/Spigot-API-Patches-Unmapped/0237-Add-LivingEntity-clearActiveItem.patch
new file mode 100644
index 0000000000..bb5b5e0b5e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0237-Add-LivingEntity-clearActiveItem.patch
@@ -0,0 +1,24 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Anrza <[email protected]>
+Date: Wed, 15 Jul 2020 12:07:58 +0200
+Subject: [PATCH] Add LivingEntity#clearActiveItem
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index 9bf525b795ff1d88d2596b1f2bc787ce0df047bb..e535750d01a6c1bf4b1fe94df518166213da9b08 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -766,6 +766,13 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ @Nullable
+ ItemStack getActiveItem();
+
++ // Paper start
++ /**
++ * Interrupts any ongoing active "usage" or consumption or an item.
++ */
++ void clearActiveItem();
++ // Paper end
++
+ /**
+ * Get's remaining time a player needs to keep hands raised with an item to finish using it.
+ * @return Remaining ticks to use the item
diff --git a/Spigot-API-Patches-Unmapped/0238-Add-PlayerItemCooldownEvent.patch b/Spigot-API-Patches-Unmapped/0238-Add-PlayerItemCooldownEvent.patch
new file mode 100644
index 0000000000..3bf6084e14
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0238-Add-PlayerItemCooldownEvent.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: KennyTV <[email protected]>
+Date: Tue, 25 Aug 2020 13:45:15 +0200
+Subject: [PATCH] Add PlayerItemCooldownEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerItemCooldownEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerItemCooldownEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..58d18f05af13d836ddc62fcd30befcb06f07c57c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerItemCooldownEvent.java
+@@ -0,0 +1,77 @@
++package io.papermc.paper.event.player;
++
++import com.google.common.base.Preconditions;
++import org.bukkit.Material;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Fired when a player receives an item cooldown.
++ */
++public class PlayerItemCooldownEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ @NotNull
++ private final Material type;
++ private boolean cancelled;
++ private int cooldown;
++
++ public PlayerItemCooldownEvent(@NotNull Player player, @NotNull Material type, int cooldown) {
++ super(player);
++ this.type = type;
++ this.cooldown = cooldown;
++ }
++
++ /**
++ * Get the material affected by the cooldown.
++ *
++ * @return material affected by the cooldown
++ */
++ @NotNull
++ public Material getType() {
++ return type;
++ }
++
++ /**
++ * Gets the cooldown in ticks.
++ *
++ * @return cooldown in ticks
++ */
++ public int getCooldown() {
++ return cooldown;
++ }
++
++ /**
++ * Sets the cooldown of the material in ticks.
++ * Setting the cooldown to 0 results in removing an already existing cooldown for the material.
++ *
++ * @param cooldown cooldown in ticks, has to be a positive number
++ */
++ public void setCooldown(int cooldown) {
++ Preconditions.checkArgument(cooldown >= 0, "The cooldown has to be equal to or greater than 0!");
++ this.cooldown = cooldown;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0239-More-lightning-API.patch b/Spigot-API-Patches-Unmapped/0239-More-lightning-API.patch
new file mode 100644
index 0000000000..1b6f512299
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0239-More-lightning-API.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: KennyTV <[email protected]>
+Date: Sun, 26 Jul 2020 14:44:16 +0200
+Subject: [PATCH] More lightning API
+
+
+diff --git a/src/main/java/org/bukkit/entity/LightningStrike.java b/src/main/java/org/bukkit/entity/LightningStrike.java
+index be347c3d0291f44036bae29a4e7e4645d6a4cdf6..2c81a3f685588431a3c7675c84b35a28975232af 100644
+--- a/src/main/java/org/bukkit/entity/LightningStrike.java
++++ b/src/main/java/org/bukkit/entity/LightningStrike.java
+@@ -31,4 +31,38 @@ public interface LightningStrike extends Entity {
+ @Override
+ Spigot spigot();
+ // Spigot end
++
++ // Paper start
++ /**
++ * Returns the amount of flash iterations that will be done before the lightning dies.
++ *
++ * @see #getLifeTicks() for how long the current flash will last
++ * @return amount of flashes that will be shown before the lightning dies
++ */
++ int getFlashCount();
++
++ /**
++ * Sets the amount of life iterations that will be done before the lightning dies.
++ * Default number of flashes on creation is between 1-3.
++ *
++ * @param flashes amount of iterations that will be done before the lightning dies, must to be a positive number
++ */
++ void setFlashCount(int flashes);
++
++ /**
++ * Returns the amount of ticks the current flash will do damage for.
++ * Starts with 2 by default, will damage while it is equal to or above 0, with the next flash beginning somewhere between 0 and -9.
++ *
++ * @return ticks the current flash will do damage for
++ */
++ int getLifeTicks();
++
++ /**
++ * Sets the amount of ticks the current flash will do damage/fire for.
++ * Default is 2 for each flash, on which the sound and effect will also be played.
++ *
++ * @param lifeTicks ticks the current flash will do damage for
++ */
++ void setLifeTicks(int lifeTicks);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0240-Add-PlayerShearBlockEvent.patch b/Spigot-API-Patches-Unmapped/0240-Add-PlayerShearBlockEvent.patch
new file mode 100644
index 0000000000..051544b4a9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0240-Add-PlayerShearBlockEvent.patch
@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: JRoy <[email protected]>
+Date: Thu, 27 Aug 2020 12:32:35 -0400
+Subject: [PATCH] Add PlayerShearBlockEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/block/PlayerShearBlockEvent.java b/src/main/java/io/papermc/paper/event/block/PlayerShearBlockEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..aa7d440b797eac9e62678d03cc87f42838758bfd
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/PlayerShearBlockEvent.java
+@@ -0,0 +1,108 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.EquipmentSlot;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++import java.util.List;
++
++/**
++ * Called when a player uses sheers on a block.
++ * <p>
++ * This event is <b>not</b> called when breaking blocks with shears but instead only when a
++ * player uses the sheer item on a block to garner drops from said block and/or change its state.
++ * <p>
++ * Examples include shearing a pumpkin to turn it into a carved pumpkin or shearing a beehive to get honeycomb.
++ */
++public class PlayerShearBlockEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++ private final Block block;
++ private final ItemStack item;
++ private final EquipmentSlot hand;
++ private final List<ItemStack> drops;
++
++ public PlayerShearBlockEvent(@NotNull Player who, @NotNull Block block, @NotNull ItemStack item, @NotNull EquipmentSlot hand, @NotNull List<ItemStack> drops) {
++ super(who);
++ this.block = block;
++ this.item = item;
++ this.hand = hand;
++ this.drops = drops;
++ }
++
++ /**
++ * Gets the block being sheared in this event.
++ *
++ * @return The {@link Block} which block is being sheared in this event.
++ */
++ @NotNull
++ public Block getBlock() {
++ return block;
++ }
++
++ /**
++ * Gets the item used to shear the block.
++ *
++ * @return The {@link ItemStack} of the shears.
++ */
++ @NotNull
++ public ItemStack getItem() {
++ return item;
++ }
++
++ /**
++ * Gets the hand used to shear the block.
++ *
++ * @return Either {@link EquipmentSlot#HAND} OR {@link EquipmentSlot#OFF_HAND}.
++ */
++ @NotNull
++ public EquipmentSlot getHand() {
++ return hand;
++ }
++
++ /**
++ * Gets the resulting drops of this event.
++ *
++ * @return A {@link List list} of {@link ItemStack items} that will be dropped as result of this event.
++ */
++ @NotNull
++ public List<ItemStack> getDrops() {
++ return drops;
++ }
++
++ /**
++ * Gets whether the shearing of the block should be cancelled or not.
++ *
++ * @return Whether the shearing of the block should be cancelled or not.
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Sets whether the shearing of the block should be cancelled or not.
++ *
++ * @param cancel whether the shearing of the block should be cancelled or not.
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0241-Enable-multi-release-plugin-jars.patch b/Spigot-API-Patches-Unmapped/0241-Enable-multi-release-plugin-jars.patch
new file mode 100644
index 0000000000..892d1d4874
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0241-Enable-multi-release-plugin-jars.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Kyle Wood <[email protected]>
+Date: Fri, 4 Dec 2020 15:53:19 -0800
+Subject: [PATCH] Enable multi-release plugin jars
+
+
+diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+index 62f7a6817da079513f471e36cd79739d36a36d86..7760be3e34fa20825faf145d9fb5b2855c1a4602 100644
+--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+@@ -56,7 +56,18 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
+ this.description = description;
+ this.dataFolder = dataFolder;
+ this.file = file;
+- this.jar = new JarFile(file);
++ // Paper - enable multi-release jars for Java 9+
++ JarFile jarFile;
++ try {
++ final java.lang.reflect.Method runtimeVersionMethod = JarFile.class.getMethod("runtimeVersion");
++ final Object runtimeVersion = runtimeVersionMethod.invoke(null);
++ @SuppressWarnings("JavaReflectionMemberAccess") final java.lang.reflect.Constructor<JarFile> constructor = JarFile.class.getConstructor(File.class, boolean.class, int.class, runtimeVersion.getClass());
++ jarFile = constructor.newInstance(file, true, java.util.zip.ZipFile.OPEN_READ, runtimeVersion);
++ } catch (Exception ignored) {
++ jarFile = new JarFile(file);
++ }
++ this.jar = jarFile;
++ // Paper end
+ this.manifest = jar.getManifest();
+ this.url = file.toURI().toURL();
+
diff --git a/Spigot-API-Patches-Unmapped/0242-Player-Chunk-Load-Unload-Events.patch b/Spigot-API-Patches-Unmapped/0242-Player-Chunk-Load-Unload-Events.patch
new file mode 100644
index 0000000000..e2ce87ec9d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0242-Player-Chunk-Load-Unload-Events.patch
@@ -0,0 +1,100 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: ysl3000 <[email protected]>
+Date: Mon, 5 Oct 2020 21:24:45 +0200
+Subject: [PATCH] Player Chunk Load/Unload Events
+
+
+diff --git a/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java b/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2c1cda1126e577a88f19071e958eddb5a38785af
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java
+@@ -0,0 +1,42 @@
++package io.papermc.paper.event.packet;
++
++import org.bukkit.Chunk;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.world.ChunkEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Is called when a {@link org.bukkit.entity.Player} receives a {@link org.bukkit.Chunk}
++ * <p>
++ * Can for example be used for spawning a fake entity when the player receives a chunk.
++ *
++ * Should only be used for packet/clientside related stuff.
++ * Not intended for modifying server side state.
++ */
++public class PlayerChunkLoadEvent extends ChunkEvent {
++
++ private static final HandlerList handlers = new HandlerList();
++ private final Player player;
++
++ public PlayerChunkLoadEvent(@NotNull Chunk chunk, @NotNull Player player) {
++ super(chunk);
++ this.player = player;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public Player getPlayer() {
++ return player;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/packet/PlayerChunkUnloadEvent.java b/src/main/java/io/papermc/paper/event/packet/PlayerChunkUnloadEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..12163a7b0591a7d022dc7eb9ee6608a1b6c39d9b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/packet/PlayerChunkUnloadEvent.java
+@@ -0,0 +1,40 @@
++package io.papermc.paper.event.packet;
++
++import org.bukkit.Chunk;
++import org.bukkit.entity.Player;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.world.ChunkEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Is called when a {@link Player} receives a chunk unload packet.
++ *
++ * Should only be used for packet/clientside related stuff.
++ * Not intended for modifying server side.
++ */
++public class PlayerChunkUnloadEvent extends ChunkEvent {
++
++ private static final HandlerList handlers = new HandlerList();
++ private final Player player;
++
++ public PlayerChunkUnloadEvent(@NotNull Chunk chunk, @NotNull Player player) {
++ super(chunk);
++ this.player = player;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public Player getPlayer() {
++ return player;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0243-Expose-LivingEntity-hurt-direction.patch b/Spigot-API-Patches-Unmapped/0243-Expose-LivingEntity-hurt-direction.patch
new file mode 100644
index 0000000000..3ca885228d
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0243-Expose-LivingEntity-hurt-direction.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mark Vainomaa <[email protected]>
+Date: Sun, 13 Dec 2020 05:32:12 +0200
+Subject: [PATCH] Expose LivingEntity hurt direction
+
+
+diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
+index e535750d01a6c1bf4b1fe94df518166213da9b08..9f0645dc5f76ee9ef73d88f768025429e5a9edf7 100644
+--- a/src/main/java/org/bukkit/entity/LivingEntity.java
++++ b/src/main/java/org/bukkit/entity/LivingEntity.java
+@@ -835,5 +835,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
+ * @param quantity quantity of item
+ */
+ void playPickupItemAnimation(@NotNull Item item, int quantity);
++
++ /**
++ * Gets player hurt direction
++ *
++ * @return hurt direction
++ */
++ float getHurtDirection();
++
++ /**
++ * Sets player hurt direction
++ *
++ * @param hurtDirection hurt direction
++ */
++ void setHurtDirection(float hurtDirection);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0244-added-PlayerTradeEvent.patch b/Spigot-API-Patches-Unmapped/0244-added-PlayerTradeEvent.patch
new file mode 100644
index 0000000000..5288a11205
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0244-added-PlayerTradeEvent.patch
@@ -0,0 +1,133 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 2 Jul 2020 16:10:10 -0700
+Subject: [PATCH] added PlayerTradeEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java
+new file mode 100755
+index 0000000000000000000000000000000000000000..198e5464eae6b961c83148a57c18f91a4bb33cf6
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerTradeEvent.java
+@@ -0,0 +1,121 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.entity.AbstractVillager;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.MerchantRecipe;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when a player trades with a villager or wandering trader
++ */
++public class PlayerTradeEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++
++ private boolean increaseTradeUses;
++ private boolean rewardExp;
++ private final AbstractVillager villager;
++ private MerchantRecipe trade;
++
++ public PlayerTradeEvent(@NotNull Player player, @NotNull AbstractVillager villager, @NotNull MerchantRecipe trade, boolean rewardExp, boolean increaseTradeUses) {
++ super(player);
++ this.villager = villager;
++ this.trade = trade;
++ this.rewardExp = rewardExp;
++ this.increaseTradeUses = increaseTradeUses;
++ }
++
++ /**
++ * Gets the Villager or Wandering trader associated with this event
++ * @return the villager or wandering trader
++ */
++ @NotNull
++ public AbstractVillager getVillager() {
++ return villager;
++ }
++
++ /**
++ * Gets the associated trade with this event
++ * @return the trade
++ */
++ @NotNull
++ public MerchantRecipe getTrade() {
++ return trade;
++ }
++
++ /**
++ * Sets the trade. This is then used to determine the next prices
++ * @param trade the trade to use
++ */
++ public void setTrade(@Nullable MerchantRecipe trade) {
++ this.trade = trade;
++ }
++
++ /**
++ * @return will trade try to reward exp
++ */
++ public boolean isRewardingExp() {
++ return rewardExp;
++ }
++
++ /**
++ * Sets whether the trade will try to reward exp
++ * @param rewardExp try to reward exp
++ */
++ public void setRewardExp(boolean rewardExp) {
++ this.rewardExp = rewardExp;
++ }
++
++ /**
++ * @return whether or not the trade will count as a use of the trade
++ */
++ public boolean willIncreaseTradeUses() {
++ return increaseTradeUses;
++ }
++
++ /**
++ * Sets whether or not the trade will count as a use
++ * @param increaseTradeUses true to count/false to not count
++ */
++ public void setIncreaseTradeUses(boolean increaseTradeUses) {
++ this.increaseTradeUses = increaseTradeUses;
++ }
++
++ /**
++ * Gets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ *
++ * @return true if this event is cancelled
++ */
++ @Override
++ public boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ /**
++ * Sets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins.
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch b/Spigot-API-Patches-Unmapped/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch
new file mode 100644
index 0000000000..eed087f1f6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0245-Add-OBSTRUCTED-reason-to-BedEnterResult.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 24 Dec 2020 12:43:30 -0800
+Subject: [PATCH] Add OBSTRUCTED reason to BedEnterResult
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java b/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java
+index c7d57e286c11eaa578ff6fd457b02a9ff829aa71..24b371b11347abf31fda4dadde8e0a7af60b181b 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerBedEnterEvent.java
+@@ -37,6 +37,12 @@ public class PlayerBedEnterEvent extends PlayerEvent implements Cancellable {
+ * Entering the bed is prevented due to the player being too far away.
+ */
+ TOO_FAR_AWAY,
++ // Paper start
++ /**
++ * Bed was obstructed.
++ */
++ OBSTRUCTED,
++ // Paper end
+ /**
+ * Entering the bed is prevented due to there being monsters nearby.
+ */
diff --git a/Spigot-API-Patches-Unmapped/0246-Add-TargetHitEvent-API.patch b/Spigot-API-Patches-Unmapped/0246-Add-TargetHitEvent-API.patch
new file mode 100644
index 0000000000..ddc1efeb9e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0246-Add-TargetHitEvent-API.patch
@@ -0,0 +1,81 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: jmp <[email protected]>
+Date: Wed, 25 Nov 2020 23:21:32 -0800
+Subject: [PATCH] Add TargetHitEvent API
+
+
+diff --git a/src/main/java/io/papermc/paper/event/block/TargetHitEvent.java b/src/main/java/io/papermc/paper/event/block/TargetHitEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..cc067ae118af9957b1b9f5c8d45f63f9154f4942
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/TargetHitEvent.java
+@@ -0,0 +1,69 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.block.BlockFace;
++import org.bukkit.entity.Projectile;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.ProjectileHitEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a Target Block is hit by a projectile.
++ * <p>
++ * Cancelling this event will stop the Target from emitting a redstone signal,
++ * and in the case that the shooter is a player, will stop them from receiving
++ * advancement criteria.
++ */
++public class TargetHitEvent extends ProjectileHitEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled;
++ private int signalStrength;
++
++ public TargetHitEvent(@NotNull Projectile projectile, @NotNull Block block, @NotNull BlockFace blockFace, int signalStrength) {
++ super(projectile, null, block, blockFace);
++ this.signalStrength = signalStrength;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ /**
++ * Gets the strength of the redstone signal to be emitted by the Target block
++ *
++ * @return the strength of the redstone signal to be emitted
++ */
++ public int getSignalStrength() {
++ return signalStrength;
++ }
++
++ /**
++ * Sets the strength of the redstone signal to be emitted by the Target block
++ *
++ * @param signalStrength the strength of the redstone signal to be emitted
++ */
++ public void setSignalStrength(int signalStrength) {
++ if (signalStrength < 0 || signalStrength > 15) {
++ throw new IllegalArgumentException("Signal strength out of range (" + signalStrength + "), must be in range [0,15]");
++ }
++ this.signalStrength = signalStrength;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0247-Additional-Block-Material-API-s.patch b/Spigot-API-Patches-Unmapped/0247-Additional-Block-Material-API-s.patch
new file mode 100644
index 0000000000..87283b0984
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0247-Additional-Block-Material-API-s.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Wed, 30 Dec 2020 17:27:27 -0500
+Subject: [PATCH] Additional Block Material API's
+
+Faster version for isSolid() that utilizes NMS's state for isSolid instead of the slower
+process to do this in the Bukkit API
+
+Adds API for buildable, replaceable, burnable too.
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index 6933fd6ad353a2d008c4a64c52a64bf36bd8035c..0c72d00ad238ab69d7ae0941e3ecb6c86e71624d 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -427,6 +427,42 @@ public interface Block extends Metadatable {
+ */
+ boolean isLiquid();
+
++ // Paper start
++ /**
++ * Check if this block is solid
++ * <p>
++ * Determined by Minecraft, typically a block a player can use to place a new block to build things.
++ * An example of a non buildable block would be liquids, flowers, or fire
++ *
++ * @return true if block is buildable
++ */
++ boolean isBuildable();
++ /**
++ * Check if this block is burnable
++ * <p>
++ * Determined by Minecraft, typically a block that fire can destroy (Wool, Wood)
++ *
++ * @return true if block is burnable
++ */
++ boolean isBurnable();
++ /**
++ * Check if this block is replaceable
++ * <p>
++ * Determined by Minecraft, representing a block that is not AIR that you can still place a new block at, such as flowers.
++ * @return true if block is replaceable
++ */
++ boolean isReplaceable();
++ /**
++ * Check if this block is solid
++ * <p>
++ * Determined by Minecraft, typically a block a player can stand on and can't be passed through.
++ *
++ * This API is faster than accessing Material#isSolid as it avoids a material lookup and switch statement.
++ * @return true if block is solid
++ */
++ boolean isSolid();
++ // Paper end
++
+ /**
+ * Gets the temperature of this block.
+ * <p>
diff --git a/Spigot-API-Patches-Unmapped/0248-Add-API-to-get-Material-from-Boats-and-Minecarts.patch b/Spigot-API-Patches-Unmapped/0248-Add-API-to-get-Material-from-Boats-and-Minecarts.patch
new file mode 100644
index 0000000000..d6cdb8862b
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0248-Add-API-to-get-Material-from-Boats-and-Minecarts.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Miller <[email protected]>
+Date: Thu, 31 Dec 2020 12:48:38 +1000
+Subject: [PATCH] Add API to get Material from Boats and Minecarts
+
+
+diff --git a/src/main/java/org/bukkit/entity/Boat.java b/src/main/java/org/bukkit/entity/Boat.java
+index 24751b5c4e3bc24bdfa85af8f6fcba37413aa002..e0d0537606d4f9a3fe588ebf7d02f314c0359335 100644
+--- a/src/main/java/org/bukkit/entity/Boat.java
++++ b/src/main/java/org/bukkit/entity/Boat.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.entity;
+
++import org.bukkit.Material;
+ import org.bukkit.TreeSpecies;
+ import org.jetbrains.annotations.NotNull;
+
+@@ -103,4 +104,14 @@ public interface Boat extends Vehicle {
+ */
+ @Deprecated
+ public void setWorkOnLand(boolean workOnLand);
++
++ // Paper start
++ /**
++ * Gets the {@link Material} that represents this Boat type.
++ *
++ * @return the boat material.
++ */
++ @NotNull
++ public Material getBoatMaterial();
++ // Paper end
+ }
+diff --git a/src/main/java/org/bukkit/entity/Minecart.java b/src/main/java/org/bukkit/entity/Minecart.java
+index 95c79c5fa0c4e30201f887da6467ce5f81c8a255..53b042f8ebbbf6ee77435b93d4e89371375cc515 100644
+--- a/src/main/java/org/bukkit/entity/Minecart.java
++++ b/src/main/java/org/bukkit/entity/Minecart.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.entity;
+
++import org.bukkit.Material;
+ import org.bukkit.block.data.BlockData;
+ import org.bukkit.material.MaterialData;
+ import org.bukkit.util.Vector;
+@@ -143,4 +144,14 @@ public interface Minecart extends Vehicle {
+ * @return the current block offset for this minecart.
+ */
+ public int getDisplayBlockOffset();
++
++ // Paper start
++ /**
++ * Gets the {@link Material} that represents this Minecart type.
++ *
++ * @return the minecart material.
++ */
++ @NotNull
++ public Material getMinecartMaterial();
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0249-Add-PlayerFlowerPotManipulateEvent.patch b/Spigot-API-Patches-Unmapped/0249-Add-PlayerFlowerPotManipulateEvent.patch
new file mode 100644
index 0000000000..d9cd5a9f01
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0249-Add-PlayerFlowerPotManipulateEvent.patch
@@ -0,0 +1,94 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: MisterVector <[email protected]>
+Date: Tue, 13 Aug 2019 19:44:19 -0700
+Subject: [PATCH] Add PlayerFlowerPotManipulateEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerFlowerPotManipulateEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerFlowerPotManipulateEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4c141f3d8f668cdf9c75865a8e3ecbd012d9e521
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerFlowerPotManipulateEvent.java
+@@ -0,0 +1,82 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player places an item in or takes an item out of a flowerpot.
++ */
++public class PlayerFlowerPotManipulateEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++
++ @NotNull
++ private final Block flowerpot;
++ @NotNull
++ private final ItemStack item;
++ private final boolean placing;
++
++ private boolean cancel = false;
++
++ public PlayerFlowerPotManipulateEvent(@NotNull final Player player, @NotNull final Block flowerpot, @NotNull final ItemStack item, final boolean placing) {
++ super(player);
++ this.flowerpot = flowerpot;
++ this.item = item;
++ this.placing = placing;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancel;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancel = cancel;
++ }
++
++ /**
++ * Gets the flowerpot that is involved in this event.
++ *
++ * @return the flowerpot that is involved with this event
++ */
++ @NotNull
++ public Block getFlowerpot() {
++ return flowerpot;
++ }
++
++ /**
++ * Gets the item being placed, or taken from, the flower pot.
++ * Check if placing with {@link #isPlacing()}.
++ *
++ * @return the item placed, or taken from, the flowerpot
++ */
++ @NotNull
++ public ItemStack getItem() {
++ return item;
++ }
++
++ /**
++ * Gets if the item is being placed into the flowerpot.
++ *
++ * @return if the item is being placed into the flowerpot
++ */
++ public boolean isPlacing() {
++ return placing;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0250-Zombie-API-breaking-doors.patch b/Spigot-API-Patches-Unmapped/0250-Zombie-API-breaking-doors.patch
new file mode 100644
index 0000000000..e2a1a8350e
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0250-Zombie-API-breaking-doors.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 18 Nov 2020 11:32:15 -0800
+Subject: [PATCH] Zombie API - breaking doors
+
+
+diff --git a/src/main/java/org/bukkit/entity/Zombie.java b/src/main/java/org/bukkit/entity/Zombie.java
+index 1217576e6f08abf0175ab800cfca058d5deda116..a39fbc9fa0903be8ed8e89f3ef39f93c02dfc90b 100644
+--- a/src/main/java/org/bukkit/entity/Zombie.java
++++ b/src/main/java/org/bukkit/entity/Zombie.java
+@@ -140,5 +140,19 @@ public interface Zombie extends Monster, Ageable {
+ * @param shouldBurnInDay True to burn in sunlight
+ */
+ void setShouldBurnInDay(boolean shouldBurnInDay);
++
++ /**
++ * Check if this zombie can break doors
++ *
++ * @return True if zombie can break doors
++ */
++ boolean canBreakDoors();
++
++ /**
++ * Sets if this zombie can break doors
++ *
++ * @param canBreakDoors True if zombie can break doors
++ */
++ void setCanBreakDoors(boolean canBreakDoors);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0251-Add-EntityLoadCrossbowEvent.patch b/Spigot-API-Patches-Unmapped/0251-Add-EntityLoadCrossbowEvent.patch
new file mode 100644
index 0000000000..f484d40cca
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0251-Add-EntityLoadCrossbowEvent.patch
@@ -0,0 +1,113 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: JRoy <[email protected]>
+Date: Wed, 7 Oct 2020 12:04:17 -0400
+Subject: [PATCH] Add EntityLoadCrossbowEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/entity/EntityLoadCrossbowEvent.java b/src/main/java/io/papermc/paper/event/entity/EntityLoadCrossbowEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..287f487b266d5c12fcf6f028452735e314d55636
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/entity/EntityLoadCrossbowEvent.java
+@@ -0,0 +1,101 @@
++package io.papermc.paper.event.entity;
++
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.bukkit.inventory.EquipmentSlot;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when a LivingEntity loads a crossbow with a projectile.
++ */
++public class EntityLoadCrossbowEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private final ItemStack crossbow;
++ private final EquipmentSlot hand;
++ private boolean cancelled;
++ private boolean consumeItem = true;
++
++ public EntityLoadCrossbowEvent(@NotNull LivingEntity entity, @Nullable ItemStack crossbow, @NotNull EquipmentSlot hand) {
++ super(entity);
++ this.crossbow = crossbow;
++ this.hand = hand;
++ }
++
++ @NotNull
++ @Override
++ public LivingEntity getEntity() {
++ return (LivingEntity) entity;
++ }
++
++ /**
++ * Gets the crossbow {@link ItemStack} being loaded.
++ *
++ * @return the crossbow involved in this event
++ */
++ @Nullable
++ public ItemStack getCrossbow() {
++ return crossbow;
++ }
++
++ /**
++ * Gets the hand from which the crossbow was loaded.
++ *
++ * @return the hand
++ */
++ @NotNull
++ public EquipmentSlot getHand() {
++ return hand;
++ }
++
++ /**
++ *
++ * @return should the itemstack be consumed
++ */
++ public boolean shouldConsumeItem() {
++ return consumeItem;
++ }
++
++ /**
++ *
++ * @param consume should the item be consumed
++ */
++ public void setConsumeItem(boolean consume) {
++ this.consumeItem = consume;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Set whether or not to cancel the crossbow being loaded. If canceled, the
++ * projectile that would be loaded into the crossbow will not be consumed.
++ *
++ * If set to false, and this event is pertaining to a player entity,
++ * it's recommended that a call to {@link Player#updateInventory()} is made
++ * as the client may think the server still loaded an item into the crossbow.
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0252-Added-WorldGameRuleChangeEvent.patch b/Spigot-API-Patches-Unmapped/0252-Added-WorldGameRuleChangeEvent.patch
new file mode 100644
index 0000000000..8ef5f7efcd
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0252-Added-WorldGameRuleChangeEvent.patch
@@ -0,0 +1,103 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Sun, 20 Dec 2020 16:41:44 -0800
+Subject: [PATCH] Added WorldGameRuleChangeEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java b/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..20c25a0f9d65188402e8bb3981348bc6462904bf
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java
+@@ -0,0 +1,91 @@
++package io.papermc.paper.event.world;
++
++import org.bukkit.GameRule;
++import org.bukkit.World;
++import org.bukkit.command.CommandSender;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.world.WorldEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when a world's gamerule is changed, either by command or by api.
++ */
++public class WorldGameRuleChangeEvent extends WorldEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private final CommandSender commandSender;
++ private final GameRule<?> gameRule;
++ private String value;
++ private boolean cancelled;
++
++ public WorldGameRuleChangeEvent(@NotNull World world, @Nullable CommandSender commandSender, @NotNull GameRule<?> gameRule, @NotNull String value) {
++ super(world);
++ this.commandSender = commandSender;
++ this.gameRule = gameRule;
++ this.value = value;
++ }
++
++ /**
++ * Gets the command sender associated with this event.
++ *
++ * @return {@code null} if the gamerule was changed via api, otherwise the {@link CommandSender}.
++ */
++ @Nullable
++ public CommandSender getCommandSender() {
++ return commandSender;
++ }
++
++ /**
++ * Gets the game rule associated with this event.
++ *
++ * @return the gamerule being changed.
++ */
++ @NotNull
++ public GameRule<?> getGameRule() {
++ return gameRule;
++ }
++
++ /**
++ * Gets the new value of the gamerule.
++ *
++ * @return the new value of the gamerule.
++ */
++ @NotNull
++ public String getValue() {
++ return value;
++ }
++
++ /**
++ * Sets the new value of this gamerule.
++ *
++ * @param value the new value of the gamerule.
++ */
++ public void setValue(@NotNull String value) {
++ this.value = value;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0253-Added-ServerResourcesReloadedEvent.patch b/Spigot-API-Patches-Unmapped/0253-Added-ServerResourcesReloadedEvent.patch
new file mode 100644
index 0000000000..de081bbae7
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0253-Added-ServerResourcesReloadedEvent.patch
@@ -0,0 +1,60 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 2 Dec 2020 20:04:16 -0800
+Subject: [PATCH] Added ServerResourcesReloadedEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/server/ServerResourcesReloadedEvent.java b/src/main/java/io/papermc/paper/event/server/ServerResourcesReloadedEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7a8d6815c17a107039399298f7ac9f0612faee02
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/server/ServerResourcesReloadedEvent.java
+@@ -0,0 +1,48 @@
++package io.papermc.paper.event.server;
++
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.server.ServerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when resources such as datapacks are reloaded (e.g. /minecraft:reload)
++ * <p>
++ * Intended for use to re-register custom recipes, advancements that may be lost during a reload like this.
++ * </p>
++ */
++public class ServerResourcesReloadedEvent extends ServerEvent {
++
++ public static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private final Cause cause;
++
++ public ServerResourcesReloadedEvent(@NotNull Cause cause) {
++ this.cause = cause;
++ }
++
++ /**
++ * Gets the cause of the resource reload.
++ *
++ * @return the reload cause
++ */
++ @NotNull
++ public Cause getCause() {
++ return cause;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ public enum Cause {
++ COMMAND,
++ PLUGIN,
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0254-Add-BlockFailedDispenseEvent.patch b/Spigot-API-Patches-Unmapped/0254-Add-BlockFailedDispenseEvent.patch
new file mode 100644
index 0000000000..126714b965
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0254-Add-BlockFailedDispenseEvent.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: TheViperShow <[email protected]>
+Date: Wed, 22 Apr 2020 09:40:23 +0200
+Subject: [PATCH] Add BlockFailedDispenseEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/block/BlockFailedDispenseEvent.java b/src/main/java/io/papermc/paper/event/block/BlockFailedDispenseEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..dab794341170ed10d5a05c1b4c180d164e0f70e2
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/BlockFailedDispenseEvent.java
+@@ -0,0 +1,57 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a block tries to dispense an item, but its inventory is empty.
++ */
++public class BlockFailedDispenseEvent extends BlockEvent {
++ private static final HandlerList handlers = new HandlerList();
++
++ private boolean shouldPlayEffect = true;
++
++ public BlockFailedDispenseEvent(@NotNull Block theBlock) {
++ super(theBlock);
++ }
++
++ /**
++ * @return if the effect should be played
++ */
++ public boolean shouldPlayEffect() {
++ return this.shouldPlayEffect;
++ }
++
++ /**
++ * Sets if the effect for empty dispensers should be played
++ *
++ * @param playEffect if the effect should be played
++ */
++ public void shouldPlayEffect(boolean playEffect) {
++ this.shouldPlayEffect = playEffect;
++ }
++
++ /**
++ * {@inheritDoc}
++ *
++ * @return {@link #shouldPlayEffect()}
++ */
++ @Override
++ public boolean callEvent() {
++ super.callEvent();
++ return this.shouldPlayEffect();
++ }
++
++ @Override
++ public @NotNull
++ HandlerList getHandlers() {
++ return handlers;
++ }
++
++ public static @NotNull
++ HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0255-Added-PlayerLecternPageChangeEvent.patch b/Spigot-API-Patches-Unmapped/0255-Added-PlayerLecternPageChangeEvent.patch
new file mode 100644
index 0000000000..2b65c3a245
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0255-Added-PlayerLecternPageChangeEvent.patch
@@ -0,0 +1,127 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Mon, 23 Nov 2020 12:58:16 -0800
+Subject: [PATCH] Added PlayerLecternPageChangeEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerLecternPageChangeEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerLecternPageChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..26370e46e4a12e3470e9bb747fac5786a7305810
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerLecternPageChangeEvent.java
+@@ -0,0 +1,115 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.block.Lectern;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class PlayerLecternPageChangeEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private boolean cancelled;
++ private final Lectern lectern;
++ private final ItemStack book;
++ private final PageChangeDirection pageChangeDirection;
++ private final int oldPage;
++ private int newPage;
++
++ public PlayerLecternPageChangeEvent(@NotNull Player player, @NotNull Lectern lectern, @NotNull ItemStack book, @NotNull PageChangeDirection pageChangeDirection, int oldPage, int newPage) {
++ super(player);
++ this.lectern = lectern;
++ this.book = book;
++ this.pageChangeDirection = pageChangeDirection;
++ this.oldPage = oldPage;
++ this.newPage = newPage;
++ }
++
++ /**
++ * Gets the lectern involved.
++ *
++ * @return the Lectern
++ */
++ @NotNull
++ public Lectern getLectern() {
++ return lectern;
++ }
++
++ /**
++ * Gets the current ItemStack on the lectern.
++ *
++ * @return the ItemStack on the Lectern
++ */
++ @NotNull
++ public ItemStack getBook() {
++ return this.book;
++ }
++
++ /**
++ * Gets the page change direction. This is essentially returns which button the player clicked, left or right.
++ *
++ * @return the page change direction
++ */
++ @NotNull
++ public PageChangeDirection getPageChangeDirection() {
++ return pageChangeDirection;
++ }
++
++ /**
++ * Gets the page changed from. <i>Pages are 0-indexed.</i>
++ *
++ * @return the page changed from
++ */
++ public int getOldPage() {
++ return oldPage;
++ }
++
++ /**
++ * Gets the page changed to. <i>Pages are 0-indexed.</i>
++ *
++ * @return the page changed to
++ */
++ public int getNewPage() {
++ return newPage;
++ }
++
++ /**
++ * Sets the page changed to. <i>Pages are 0-indexed.</i>
++ * Page indices that are greater than the number of pages will show the last page.
++ *
++ * @param newPage the new paged changed to
++ */
++ public void setNewPage(int newPage) {
++ this.newPage = newPage;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++
++ public enum PageChangeDirection {
++ LEFT,
++ RIGHT,
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0256-Added-PlayerLoomPatternSelectEvent.patch b/Spigot-API-Patches-Unmapped/0256-Added-PlayerLoomPatternSelectEvent.patch
new file mode 100644
index 0000000000..5677026956
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0256-Added-PlayerLoomPatternSelectEvent.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 25 Nov 2020 16:33:42 -0800
+Subject: [PATCH] Added PlayerLoomPatternSelectEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerLoomPatternSelectEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerLoomPatternSelectEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8cb05709f7cb5dee993ff6fea1626c41b90a7d8b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerLoomPatternSelectEvent.java
+@@ -0,0 +1,77 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.block.banner.PatternType;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.LoomInventory;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player selects a banner patten in a loom inventory.
++ */
++public class PlayerLoomPatternSelectEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private boolean cancelled;
++ private final LoomInventory loomInventory;
++ private PatternType patternType;
++
++ public PlayerLoomPatternSelectEvent(@NotNull Player player, @NotNull LoomInventory loomInventory, @NotNull PatternType patternType) {
++ super(player);
++ this.loomInventory = loomInventory;
++ this.patternType = patternType;
++ }
++
++ /**
++ * Gets the loom inventory involved.
++ *
++ * @return the loom inventory
++ */
++ @NotNull
++ public LoomInventory getLoomInventory() {
++ return loomInventory;
++ }
++
++ /**
++ * Gets the pattern type selected.
++ *
++ * @return the pattern type
++ */
++ @NotNull
++ public PatternType getPatternType() {
++ return patternType;
++ }
++
++ /**
++ * Sets the pattern type selected.
++ *
++ * @param patternType the pattern type
++ */
++ public void setPatternType(@NotNull PatternType patternType) {
++ this.patternType = patternType;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0257-Better-AnnotationTest-printout.patch b/Spigot-API-Patches-Unmapped/0257-Better-AnnotationTest-printout.patch
new file mode 100644
index 0000000000..0d2e5026a4
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0257-Better-AnnotationTest-printout.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 3 Dec 2020 14:04:57 -0800
+Subject: [PATCH] Better AnnotationTest printout
+
+
+diff --git a/pom.xml b/pom.xml
+index de6a5b697535b9532fee1521363432c76c233952..e66661eb84308dc13faa0d39b7487f40c1180443 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -237,6 +237,19 @@
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ </configuration>
+ </plugin>
++ <plugin>
++ <groupId>org.apache.maven.plugins</groupId>
++ <artifactId>maven-surefire-plugin</artifactId>
++ <version>2.22.2</version>
++ <configuration>
++ <properties>
++ <property>
++ <name>listener</name>
++ <value>io.papermc.paper.JunitEventListener</value>
++ </property>
++ </properties>
++ </configuration>
++ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+diff --git a/src/test/java/io/papermc/paper/JunitEventListener.java b/src/test/java/io/papermc/paper/JunitEventListener.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..072ac1a96394b8d494f42fca8dfe08115eaed3fe
+--- /dev/null
++++ b/src/test/java/io/papermc/paper/JunitEventListener.java
+@@ -0,0 +1,6 @@
++package io.papermc.paper;
++
++import org.junit.runner.notification.RunListener;
++
++public class JunitEventListener extends RunListener {
++}
+diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
+index 03229d5f4ec36a82197beb391356d791ff67fb2f..19271057cf24329757c9419fa6c97848e008a96c 100644
+--- a/src/test/java/org/bukkit/AnnotationTest.java
++++ b/src/test/java/org/bukkit/AnnotationTest.java
+@@ -107,13 +107,18 @@ public class AnnotationTest {
+
+ Collections.sort(errors);
+
+- System.out.println(errors.size() + " missing annotation(s):");
++ StringBuilder builder = new StringBuilder()
++ .append("There ")
++ .append(errors.size() != 1 ? "are " : "is ")
++ .append(errors.size())
++ .append(" missing annotation")
++ .append(errors.size() != 1 ? "s:\n" : ":\n");
++
+ for (String message : errors) {
+- System.out.print("\t");
+- System.out.println(message);
++ builder.append("\t").append(message).append("\n");
+ }
+
+- Assert.fail("There " + errors.size() + " are missing annotation(s)");
++ Assert.fail(builder.toString());
+ }
+
+ private static void collectClasses(@NotNull File from, @NotNull Map<String, ClassNode> to) throws IOException {
diff --git a/Spigot-API-Patches-Unmapped/0258-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch b/Spigot-API-Patches-Unmapped/0258-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch
new file mode 100644
index 0000000000..197d00ca14
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0258-Add-API-to-get-exact-interaction-point-in-PlayerInte.patch
@@ -0,0 +1,67 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Miller <[email protected]>
+Date: Mon, 4 Jan 2021 16:40:55 +1000
+Subject: [PATCH] Add API to get exact interaction point in PlayerInteractEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java b/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java
+index 1208e1f8c2163d83c5b12bbb9b7ac044c72380e0..a01f86e6aba8b66ecc713da0787cd861e2930a2a 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java
+@@ -1,5 +1,6 @@
+ package org.bukkit.event.player;
+
++import org.bukkit.Location;
+ import org.bukkit.Material;
+ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockFace;
+@@ -34,22 +35,30 @@ public class PlayerInteractEvent extends PlayerEvent implements Cancellable {
+ private Result useClickedBlock;
+ private Result useItemInHand;
+ private EquipmentSlot hand;
++ private Location interactionPoint; // Paper
+
+ public PlayerInteractEvent(@NotNull final Player who, @NotNull final Action action, @Nullable final ItemStack item, @Nullable final Block clickedBlock, @NotNull final BlockFace clickedFace) {
+ this(who, action, item, clickedBlock, clickedFace, EquipmentSlot.HAND);
+ }
+
+ public PlayerInteractEvent(@NotNull final Player who, @NotNull final Action action, @Nullable final ItemStack item, @Nullable final Block clickedBlock, @NotNull final BlockFace clickedFace, @Nullable final EquipmentSlot hand) {
++ // Paper start - Add interactionPoint
++ this(who, action, item, clickedBlock, clickedFace, hand, null);
++ }
++
++ public PlayerInteractEvent(@NotNull final Player who, @NotNull final Action action, @Nullable final ItemStack item, @Nullable final Block clickedBlock, @NotNull final BlockFace clickedFace, @Nullable final EquipmentSlot hand, @Nullable final Location interactionPoint) {
+ super(who);
+ this.action = action;
+ this.item = item;
+ this.blockClicked = clickedBlock;
+ this.blockFace = clickedFace;
+ this.hand = hand;
++ this.interactionPoint = interactionPoint;
+
+ useItemInHand = Result.DEFAULT;
+ useClickedBlock = clickedBlock == null ? Result.DENY : Result.ALLOW;
+ }
++ // Paper end
+
+ /**
+ * Returns the action type
+@@ -221,6 +230,18 @@ public class PlayerInteractEvent extends PlayerEvent implements Cancellable {
+ return hand;
+ }
+
++ // Paper start
++ /**
++ * The exact point at which the interaction occurred. May be null.
++ *
++ * @return the exact interaction point. May be null.
++ */
++ @Nullable
++ public Location getInteractionPoint() {
++ return interactionPoint;
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
diff --git a/Spigot-API-Patches-Unmapped/0259-Add-sendOpLevel-API.patch b/Spigot-API-Patches-Unmapped/0259-Add-sendOpLevel-API.patch
new file mode 100644
index 0000000000..b825d1f4f0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0259-Add-sendOpLevel-API.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Mariell Hoversholm <[email protected]>
+Date: Tue, 29 Dec 2020 15:02:57 +0100
+Subject: [PATCH] Add sendOpLevel API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 5bdc48b533833f626a012b675c9c58f8b00b2400..8a1e35952ae84ae1e41a2b7783e88eab52bd274c 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1914,6 +1914,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ */
+ @Nullable
+ Firework boostElytra(@NotNull ItemStack firework);
++
++ /**
++ * Send a packet to the player indicating its operator status level.
++ * <p>
++ * <b>Note:</b> This will not persist across more than the current connection, and setting the player's operator
++ * status as a later point <i>will</i> override the effects of this.
++ *
++ * @param level The level to send to the player. Must be in {@code [0, 4]}.
++ * @throws IllegalArgumentException If the level is negative or greater than {@code 4} (i.e. not within {@code [0, 4]}).
++ */
++ void sendOpLevel(byte level);
+ // Paper end
+
+ // Spigot start
diff --git a/Spigot-API-Patches-Unmapped/0260-Add-StructureLocateEvent.patch b/Spigot-API-Patches-Unmapped/0260-Add-StructureLocateEvent.patch
new file mode 100644
index 0000000000..88c14d8ea6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0260-Add-StructureLocateEvent.patch
@@ -0,0 +1,167 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: dfsek <[email protected]>
+Date: Tue, 15 Sep 2020 21:59:16 -0700
+Subject: [PATCH] Add StructureLocateEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..45b6694fc5741831e2df638b1f760a3ca28a4907
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/StructureLocateEvent.java
+@@ -0,0 +1,155 @@
++package io.papermc.paper.event.world;
++
++import org.bukkit.Location;
++import org.bukkit.StructureType;
++import org.bukkit.World;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.world.WorldEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called <b>before</b> a structure/feature is located.
++ * This happens when:
++ * <ul>
++ * <li>The /locate command is used.<br></li>
++ * <li>An Eye of Ender is used.</li>
++ * <li>An Explorer/Treasure Map is activated.</li>
++ * <li>{@link World#locateNearestStructure(Location, StructureType, int, boolean)} is invoked.</li>
++ * </ul>
++ */
++public class StructureLocateEvent extends WorldEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private final Location origin;
++ private Location result = null;
++ private StructureType type;
++ private int radius;
++ private boolean findUnexplored;
++ private boolean cancelled = false;
++
++ public StructureLocateEvent(@NotNull World world, @NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored) {
++ super(world);
++ this.origin = origin;
++ this.type = structureType;
++ this.radius = radius;
++ this.findUnexplored = findUnexplored;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ /**
++ * Gets the location set as the structure location, if it was defined.
++ * <p>
++ * Returns {@code null} if it has not been set by {@link StructureLocateEvent#setResult(Location)}.
++ * Since this event fires <i>before</i> the search is done, the actual location is unknown at this point.
++ *
++ * @return The result location, if it has been set. null if it has not.
++ * @see World#locateNearestStructure(Location, StructureType, int, boolean)
++ */
++ @Nullable
++ public Location getResult() {
++ return result;
++ }
++
++ /**
++ * Sets the result {@link Location}. This causes the search to be skipped, and the location passed here to be used as the result.
++ *
++ * @param result the {@link Location} of the structure.
++ */
++ public void setResult(@Nullable Location result) {
++ this.result = result;
++ }
++
++ /**
++ * Gets the {@link StructureType} that is to be located.
++ *
++ * @return the structure type.
++ */
++ @NotNull
++ public StructureType getType() {
++ return type;
++ }
++
++ /**
++ * Sets the {@link StructureType} that is to be located.
++ *
++ * @param type the structure type.
++ */
++ public void setType(@NotNull StructureType type) {
++ this.type = type;
++ }
++
++ /**
++ * Gets the {@link Location} from which the search is to be conducted.
++ *
++ * @return {@link Location} where search begins
++ */
++ @NotNull
++ public Location getOrigin() {
++ return origin;
++ }
++
++ /**
++ * Gets the search radius in which to attempt locating the structure.
++ * <p>
++ * This radius may not always be obeyed during the structure search!
++ *
++ * @return the search radius.
++ */
++ public int getRadius() {
++ return radius;
++ }
++
++ /**
++ * Sets the search radius in which to attempt locating the structure.
++ * <p>
++ * This radius may not always be obeyed during the structure search!
++ *
++ * @param radius the search radius.
++ */
++ public void setRadius(int radius) {
++ this.radius = radius;
++ }
++
++ /**
++ * Gets whether to search exclusively for unexplored structures.
++ * <p>
++ * As with the search radius, this value is not always obeyed.
++ *
++ * @return Whether to search for only unexplored structures.
++ */
++ public boolean shouldFindUnexplored() {
++ return findUnexplored;
++ }
++
++ /**
++ * Sets whether to search exclusively for unexplored structures.
++ * <p>
++ * As with the search radius, this value is not always obeyed.
++ *
++ * @param findUnexplored Whether to search for only unexplored structures.
++ */
++ public void setFindUnexplored(boolean findUnexplored) {
++ this.findUnexplored = findUnexplored;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0261-Make-ProjectileHitEvent-Cancellable.patch b/Spigot-API-Patches-Unmapped/0261-Make-ProjectileHitEvent-Cancellable.patch
new file mode 100644
index 0000000000..010006a4e6
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0261-Make-ProjectileHitEvent-Cancellable.patch
@@ -0,0 +1,49 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 16 Jan 2021 14:00:16 -0500
+Subject: [PATCH] Make ProjectileHitEvent Cancellable
+
+Allows cancelling things like detonating TNT from Fire Arrows
+
+diff --git a/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java b/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java
+index 809c1098c16fe52eda47ae2335afab30e36746b2..000c9967e008d3340a08c4ee727a5d2de27dedcc 100644
+--- a/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java
++++ b/src/main/java/org/bukkit/event/entity/ProjectileHitEvent.java
+@@ -4,6 +4,7 @@ import org.bukkit.block.Block;
+ import org.bukkit.block.BlockFace;
+ import org.bukkit.entity.Entity;
+ import org.bukkit.entity.Projectile;
++import org.bukkit.event.Cancellable;
+ import org.bukkit.event.HandlerList;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+@@ -11,11 +12,28 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Called when a projectile hits an object
+ */
+-public class ProjectileHitEvent extends EntityEvent {
++public class ProjectileHitEvent extends EntityEvent implements Cancellable { // Paper
+ private static final HandlerList handlers = new HandlerList();
+ private final Entity hitEntity;
+ private final Block hitBlock;
+ private final BlockFace hitFace;
++ // Paper start - make cancellable
++ private boolean canceled;
++ /**
++ * @return If the arrow activating a block should be cancelled
++ */
++ public boolean isCancelled() {
++ return canceled;
++ }
++
++ /**
++ * Whether or not to cancel any behavior that would occur from the arrow hitting the block
++ * @param cancel true if you wish to cancel this event
++ */
++ public void setCancelled(boolean cancel) {
++ canceled = cancel;
++ }
++ // Paper end
+
+ public ProjectileHitEvent(@NotNull final Projectile projectile) {
+ this(projectile, null, null);
diff --git a/Spigot-API-Patches-Unmapped/0262-Return-chat-component-with-empty-text-instead-of-thr.patch b/Spigot-API-Patches-Unmapped/0262-Return-chat-component-with-empty-text-instead-of-thr.patch
new file mode 100644
index 0000000000..ae5787045f
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0262-Return-chat-component-with-empty-text-instead-of-thr.patch
@@ -0,0 +1,20 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: CDFN <[email protected]>
+Date: Tue, 7 Jul 2020 17:53:23 +0200
+Subject: [PATCH] Return chat component with empty text instead of throwing
+ exception
+
+
+diff --git a/src/main/java/org/bukkit/inventory/InventoryView.java b/src/main/java/org/bukkit/inventory/InventoryView.java
+index a4e3d526db2d17dc923cbe82e53d3c902d61e1f3..2448e70d75ae7a678c6befac4506c103edb78875 100644
+--- a/src/main/java/org/bukkit/inventory/InventoryView.java
++++ b/src/main/java/org/bukkit/inventory/InventoryView.java
+@@ -450,7 +450,7 @@ public abstract class InventoryView {
+ /**
+ * Get the title of this inventory window.
+ *
+- * @return The title.
++ * @return The title or empty string when title is {@code null}. <!-- Paper -->
+ */
+ @NotNull
+ public /*abstract*/ net.kyori.adventure.text.Component title() {
diff --git a/Spigot-API-Patches-Unmapped/0263-Add-BlockPreDispenseEvent.patch b/Spigot-API-Patches-Unmapped/0263-Add-BlockPreDispenseEvent.patch
new file mode 100644
index 0000000000..5850391175
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0263-Add-BlockPreDispenseEvent.patch
@@ -0,0 +1,72 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Matthew Miller <[email protected]>
+Date: Sun, 17 Jan 2021 13:15:54 +1000
+Subject: [PATCH] Add BlockPreDispenseEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/block/BlockPreDispenseEvent.java b/src/main/java/io/papermc/paper/event/block/BlockPreDispenseEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..07aad3f4ff60a6a6de69634b0d31926e9c00e77b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/BlockPreDispenseEvent.java
+@@ -0,0 +1,60 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.block.Block;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockEvent;
++import org.bukkit.inventory.ItemStack;
++import org.jetbrains.annotations.NotNull;
++
++public class BlockPreDispenseEvent extends BlockEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean cancelled = false;
++ private final ItemStack itemStack;
++ private final int slot;
++
++ public BlockPreDispenseEvent(@NotNull Block block, @NotNull ItemStack itemStack, int slot) {
++ super(block);
++ this.itemStack = itemStack;
++ this.slot = slot;
++ }
++
++ /**
++ * Gets the {@link ItemStack} to be dispensed.
++ *
++ * @return The item to be dispensed
++ */
++ @NotNull
++ public ItemStack getItemStack() {
++ return itemStack;
++ }
++
++ /**
++ * Gets the inventory slot of the dispenser to dispense from.
++ *
++ * @return The inventory slot
++ */
++ public int getSlot() {
++ return slot;
++ }
++
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return this.cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0264-Added-Vanilla-Entity-Tags.patch b/Spigot-API-Patches-Unmapped/0264-Added-Vanilla-Entity-Tags.patch
new file mode 100644
index 0000000000..9916b1726b
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0264-Added-Vanilla-Entity-Tags.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Sun, 3 Jan 2021 20:03:40 -0800
+Subject: [PATCH] Added Vanilla Entity Tags
+
+
+diff --git a/src/main/java/org/bukkit/Tag.java b/src/main/java/org/bukkit/Tag.java
+index 3c2a6a2167eab43097f5d6ccf1550e12795fc0b6..c1ec099448d57f2a5f973ac5b4e4a25b79ea2112 100644
+--- a/src/main/java/org/bukkit/Tag.java
++++ b/src/main/java/org/bukkit/Tag.java
+@@ -425,6 +425,32 @@ public interface Tag<T extends Keyed> extends Keyed {
+ * Vanilla fluid tag representing water and flowing water.
+ */
+ Tag<Fluid> FLUIDS_WATER = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("water"), Fluid.class);
++ // Paper start
++ /**
++ * Key for the build in entity registry
++ */
++ String REGISTRY_ENTITIES = "entities";
++ /**
++ * Vanilla entity tag representing arrow entities.
++ */
++ Tag<org.bukkit.entity.EntityType> ARROWS = Bukkit.getTag(REGISTRY_ENTITIES, NamespacedKey.minecraft("arrows"), org.bukkit.entity.EntityType.class);
++ /**
++ * Vanilla entity tag representing entities that live in beehives
++ */
++ Tag<org.bukkit.entity.EntityType> BEEHIVE_INHABITORS = Bukkit.getTag(REGISTRY_ENTITIES, NamespacedKey.minecraft("beehive_inhabitors"), org.bukkit.entity.EntityType.class);
++ /**
++ * Vanilla entity tag representing projectiles that impact
++ */
++ Tag<org.bukkit.entity.EntityType> IMPACT_PROJECTILES = Bukkit.getTag(REGISTRY_ENTITIES, NamespacedKey.minecraft("impact_projectiles"), org.bukkit.entity.EntityType.class);
++ /**
++ * Vanilla entity tag for village raiders
++ */
++ Tag<org.bukkit.entity.EntityType> RAIDERS = Bukkit.getTag(REGISTRY_ENTITIES, NamespacedKey.minecraft("raiders"), org.bukkit.entity.EntityType.class);
++ /**
++ * Vanilla entity tag for skeleton types
++ */
++ Tag<org.bukkit.entity.EntityType> SKELETONS = Bukkit.getTag(REGISTRY_ENTITIES, NamespacedKey.minecraft("skeletons"), org.bukkit.entity.EntityType.class);
++ // Paper end
+
+ /**
+ * Returns whether or not this tag has an entry for the specified item.
diff --git a/Spigot-API-Patches-Unmapped/0265-added-Wither-API.patch b/Spigot-API-Patches-Unmapped/0265-added-Wither-API.patch
new file mode 100644
index 0000000000..dbfc15a42c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0265-added-Wither-API.patch
@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Sun, 5 Jul 2020 15:39:40 -0700
+Subject: [PATCH] added Wither API
+
+
+diff --git a/src/main/java/org/bukkit/entity/Wither.java b/src/main/java/org/bukkit/entity/Wither.java
+index 426d3693317cd303d35d8203026b528d87e401d5..8c95cd6933f11076de936854f379e6fc8600b525 100644
+--- a/src/main/java/org/bukkit/entity/Wither.java
++++ b/src/main/java/org/bukkit/entity/Wither.java
+@@ -6,4 +6,34 @@ import com.destroystokyo.paper.entity.RangedEntity;
+ * Represents a Wither boss
+ */
+ public interface Wither extends Monster, Boss, RangedEntity { // Paper
++ // Paper start
++ /**
++ * @return whether the wither is charged
++ */
++ boolean isCharged();
++
++ /**
++ * @return ticks the wither is invulnerable for
++ */
++ int getInvulnerableTicks();
++
++ /**
++ * Sets for how long in the future, the wither should be invulnerable.
++ *
++ * @param ticks ticks the wither is invulnerable for
++ */
++ void setInvulnerableTicks(int ticks);
++
++ /**
++ * @return whether the wither can travel through portals
++ */
++ boolean canTravelThroughPortals();
++
++ /**
++ * Sets whether the wither can travel through portals.
++ *
++ * @param value whether the wither can travel through portals
++ */
++ void setCanTravelThroughPortals(boolean value);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0266-Added-PlayerChangeBeaconEffectEvent.patch b/Spigot-API-Patches-Unmapped/0266-Added-PlayerChangeBeaconEffectEvent.patch
new file mode 100644
index 0000000000..4331e880bb
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0266-Added-PlayerChangeBeaconEffectEvent.patch
@@ -0,0 +1,153 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 24 Jun 2020 15:12:18 -0600
+Subject: [PATCH] Added PlayerChangeBeaconEffectEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerChangeBeaconEffectEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerChangeBeaconEffectEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c80183a79713b1e73549911e474a8c585cfdeb52
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerChangeBeaconEffectEvent.java
+@@ -0,0 +1,141 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.block.Block;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.potion.PotionEffectType;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when a player sets the effect for a beacon
++ */
++public class PlayerChangeBeaconEffectEvent extends PlayerEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++
++ private PotionEffectType primary;
++ private PotionEffectType secondary;
++ private final Block beacon;
++ private boolean consumeItem = true;
++
++ private boolean isCancelled;
++
++ public PlayerChangeBeaconEffectEvent(@NotNull Player player, @Nullable PotionEffectType primary, @Nullable PotionEffectType secondary, @Nullable Block beacon) {
++ super(player);
++ this.primary = primary;
++ this.secondary = secondary;
++ this.isCancelled = false;
++ this.beacon = beacon;
++ }
++
++ /**
++ * @return the primary effect
++ */
++ @Nullable public PotionEffectType getPrimary() {
++ return primary;
++ }
++
++ /**
++ * Sets the primary effect
++ * <p>
++ * NOTE: The primary effect still has to be one of the valid effects for a beacon.
++ *
++ * @param primary the primary effect
++ */
++ public void setPrimary(@Nullable PotionEffectType primary) {
++ this.primary = primary;
++ }
++
++ /**
++ * @return the secondary effect
++ */
++ @Nullable public PotionEffectType getSecondary() {
++ return secondary;
++ }
++
++ /**
++ * Sets the secondary effect
++ * <p>
++ * This only has an effect when the beacon is able to accept a secondary effect.
++ * NOTE: The secondary effect still has to be a valid effect for a beacon.
++ *
++ * @param secondary the secondary effect
++ */
++ public void setSecondary(@Nullable PotionEffectType secondary) {
++ this.secondary = secondary;
++ }
++
++ /**
++ * @return the beacon block associated with this event, or null if not found
++ */
++ @Nullable
++ public Block getBeacon() {
++ return beacon;
++ }
++
++ /**
++ * Gets if the item used to change the beacon will be consume.
++ * <p>
++ * Independant of {@link #isCancelled()}. If the event is cancelled
++ * the item will <b>NOT</b> be consumed.
++ *
++ * @return true if item will be consumed
++ */
++ public boolean willConsumeItem() {
++ return consumeItem;
++ }
++
++ /**
++ * Sets if the item used to change the beacon should be consumed.
++ * <p>
++ * Independant of {@link #isCancelled()}. If the event is cancelled
++ * the item will <b>NOT</b> be consumed.
++ *
++ * @param consumeItem true if item should be consumed
++ */
++ public void setConsumeItem(boolean consumeItem) {
++ this.consumeItem = consumeItem;
++ }
++
++ /**
++ * Gets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ * <p>
++ * If a {@link PlayerChangeBeaconEffectEvent} is cancelled, the changes will
++ * not take effect
++ *
++ * @return true if this event is cancelled
++ */
++ @Override
++ public boolean isCancelled() {
++ return this.isCancelled;
++ }
++
++ /**
++ * Sets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ * <p>
++ * If cancelled, the item will <b>NOT</b> be consumed regardless of what {@link #willConsumeItem()} says
++ * <p>
++ * If a {@link PlayerChangeBeaconEffectEvent} is cancelled, the changes will not be applied
++ * or saved.
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.isCancelled = cancel;
++ }
++
++ @Override
++ public @NotNull HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0267-Add-dropLeash-variable-to-EntityUnleashEvent.patch b/Spigot-API-Patches-Unmapped/0267-Add-dropLeash-variable-to-EntityUnleashEvent.patch
new file mode 100644
index 0000000000..1a81930699
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0267-Add-dropLeash-variable-to-EntityUnleashEvent.patch
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: KennyTV <[email protected]>
+Date: Fri, 29 Jan 2021 15:13:04 +0100
+Subject: [PATCH] Add dropLeash variable to EntityUnleashEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java b/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java
+index a33986a0c437a673435206fc337031a7eebdab3b..e0e068799a1868c8e561869015f41f553ef4fbdb 100644
+--- a/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java
++++ b/src/main/java/org/bukkit/event/entity/EntityUnleashEvent.java
+@@ -10,10 +10,19 @@ import org.jetbrains.annotations.NotNull;
+ public class EntityUnleashEvent extends EntityEvent {
+ private static final HandlerList handlers = new HandlerList();
+ private final UnleashReason reason;
++ private boolean dropLeash; // Paper
+
++ // Paper start - drop leash variable
++ @Deprecated
+ public EntityUnleashEvent(@NotNull Entity entity, @NotNull UnleashReason reason) {
++ this(entity, reason, false);
++ }
++
++ public EntityUnleashEvent(@NotNull Entity entity, @NotNull UnleashReason reason, boolean dropLeash) {
+ super(entity);
++ // Paper end
+ this.reason = reason;
++ this.dropLeash = dropLeash; // Paper
+ }
+
+ /**
+@@ -26,6 +35,26 @@ public class EntityUnleashEvent extends EntityEvent {
+ return reason;
+ }
+
++ // Paper start
++ /**
++ * Returns whether a leash item will be dropped.
++ *
++ * @return Whether the leash item will be dropped
++ */
++ public boolean isDropLeash() {
++ return dropLeash;
++ }
++
++ /**
++ * Sets whether a leash item should be dropped.
++ *
++ * @param dropLeash Whether the leash item should be dropped
++ */
++ public void setDropLeash(boolean dropLeash) {
++ this.dropLeash = dropLeash;
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+diff --git a/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java b/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java
+index cf78950b56d4977f6c4d9d98d183bfc5ba3bacc0..68eab1563caba1ee4f52b308f390e4e172667fc5 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java
+@@ -13,8 +13,15 @@ public class PlayerUnleashEntityEvent extends EntityUnleashEvent implements Canc
+ private final Player player;
+ private boolean cancelled = false;
+
++ // Paper start - drop leash variable
++ @Deprecated
+ public PlayerUnleashEntityEvent(@NotNull Entity entity, @NotNull Player player) {
+- super(entity, UnleashReason.PLAYER_UNLEASH);
++ this(entity, player, false);
++ }
++
++ public PlayerUnleashEntityEvent(@NotNull Entity entity, @NotNull Player player, boolean dropLeash) {
++ super(entity, UnleashReason.PLAYER_UNLEASH, dropLeash);
++ // Paper end
+ this.player = player;
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0268-Added-PlayerStonecutterRecipeSelectEvent.patch b/Spigot-API-Patches-Unmapped/0268-Added-PlayerStonecutterRecipeSelectEvent.patch
new file mode 100644
index 0000000000..ce446c46fe
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0268-Added-PlayerStonecutterRecipeSelectEvent.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Fri, 27 Nov 2020 17:13:59 -0800
+Subject: [PATCH] Added PlayerStonecutterRecipeSelectEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerStonecutterRecipeSelectEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerStonecutterRecipeSelectEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..fb1bca3a9d12096c9a2b2663f466a8ff5f2b4319
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerStonecutterRecipeSelectEvent.java
+@@ -0,0 +1,59 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.bukkit.inventory.StonecutterInventory;
++import org.bukkit.inventory.StonecuttingRecipe;
++import org.jetbrains.annotations.NotNull;
++
++public class PlayerStonecutterRecipeSelectEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private boolean cancelled;
++ private final StonecutterInventory stonecutterInventory;
++ private StonecuttingRecipe stonecuttingRecipe;
++
++ public PlayerStonecutterRecipeSelectEvent(@NotNull Player player, @NotNull StonecutterInventory stonecutterInventory, @NotNull StonecuttingRecipe stonecuttingRecipe) {
++ super(player);
++ this.stonecutterInventory = stonecutterInventory;
++ this.stonecuttingRecipe = stonecuttingRecipe;
++ }
++
++ @NotNull
++ public StonecutterInventory getStonecutterInventory() {
++ return stonecutterInventory;
++ }
++
++ @NotNull
++ public StonecuttingRecipe getStonecuttingRecipe() {
++ return stonecuttingRecipe;
++ }
++
++ public void setStonecuttingRecipe(@NotNull StonecuttingRecipe stonecuttingRecipe) {
++ this.stonecuttingRecipe = stonecuttingRecipe;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0269-EntityMoveEvent.patch b/Spigot-API-Patches-Unmapped/0269-EntityMoveEvent.patch
new file mode 100644
index 0000000000..9470211635
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0269-EntityMoveEvent.patch
@@ -0,0 +1,107 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: William Blake Galbreath <[email protected]>
+Date: Tue, 11 Feb 2020 21:56:38 -0600
+Subject: [PATCH] EntityMoveEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java b/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..299ff57b3001b4be41cf2f0eea29ed82b8fb8ec7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/entity/EntityMoveEvent.java
+@@ -0,0 +1,95 @@
++package io.papermc.paper.event.entity;
++
++import com.google.common.base.Preconditions;
++import org.bukkit.Location;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.entity.EntityEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Holds information for living entity movement events
++ */
++public class EntityMoveEvent extends EntityEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private boolean canceled;
++ private Location from;
++ private Location to;
++
++ public EntityMoveEvent(@NotNull LivingEntity entity, @NotNull Location from, @NotNull Location to) {
++ super(entity);
++ this.from = from;
++ this.to = to;
++ }
++
++ @Override
++ @NotNull
++ public LivingEntity getEntity() {
++ return (LivingEntity) entity;
++ }
++
++ public boolean isCancelled() {
++ return canceled;
++ }
++
++ public void setCancelled(boolean cancel) {
++ canceled = cancel;
++ }
++
++ /**
++ * Gets the location this entity moved from
++ *
++ * @return Location the entity moved from
++ */
++ @NotNull
++ public Location getFrom() {
++ return from;
++ }
++
++ /**
++ * Sets the location to mark as where the entity moved from
++ *
++ * @param from New location to mark as the entity's previous location
++ */
++ public void setFrom(@NotNull Location from) {
++ validateLocation(from);
++ this.from = from;
++ }
++
++ /**
++ * Gets the location this entity moved to
++ *
++ * @return Location the entity moved to
++ */
++ @NotNull
++ public Location getTo() {
++ return to;
++ }
++
++ /**
++ * Sets the location that this entity will move to
++ *
++ * @param to New Location this entity will move to
++ */
++ public void setTo(@NotNull Location to) {
++ validateLocation(to);
++ this.to = to;
++ }
++
++ private void validateLocation(@NotNull Location loc) {
++ Preconditions.checkArgument(loc != null, "Cannot use null location!");
++ Preconditions.checkArgument(loc.getWorld() != null, "Cannot use null location with null world!");
++ }
++
++ @Override
++ @NotNull
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0270-add-DragonEggFormEvent.patch b/Spigot-API-Patches-Unmapped/0270-add-DragonEggFormEvent.patch
new file mode 100644
index 0000000000..c465295e88
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0270-add-DragonEggFormEvent.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Trigary <[email protected]>
+Date: Mon, 25 Jan 2021 14:53:49 +0100
+Subject: [PATCH] add DragonEggFormEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/block/DragonEggFormEvent.java b/src/main/java/io/papermc/paper/event/block/DragonEggFormEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..5495b87330518363498e1ac5d8f0a832be35fefb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/block/DragonEggFormEvent.java
+@@ -0,0 +1,63 @@
++package io.papermc.paper.event.block;
++
++import org.bukkit.Material;
++import org.bukkit.block.Block;
++import org.bukkit.block.BlockState;
++import org.bukkit.boss.DragonBattle;
++import org.bukkit.entity.EnderDragon;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.block.BlockFormEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when the {@link EnderDragon} is defeated (killed) in a {@link DragonBattle},
++ * causing a {@link Material#DRAGON_EGG} (more formally: {@link #getNewState()})
++ * to possibly appear depending on {@link #isCancelled()}.
++ * <b>This event might be cancelled by default depending on
++ * eg. {@link DragonBattle#hasBeenPreviouslyKilled()} and server configuration.</b>
++ */
++public class DragonEggFormEvent extends BlockFormEvent implements Cancellable {
++ private static final HandlerList handlers = new HandlerList();
++ private final DragonBattle dragonBattle;
++ private boolean cancelled;
++
++ public DragonEggFormEvent(@NotNull Block block, @NotNull BlockState newState,
++ @NotNull DragonBattle dragonBattle) {
++ super(block, newState);
++ this.dragonBattle = dragonBattle;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancelled) {
++ this.cancelled = cancelled;
++ }
++
++ /**
++ * Gets the {@link DragonBattle} associated with this event.
++ * Keep in mind that the {@link EnderDragon} is already dead
++ * when this event is called.
++ *
++ * @return the dragon battle
++ */
++ @NotNull
++ public DragonBattle getDragonBattle() {
++ return dragonBattle;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return handlers;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return handlers;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0271-Allow-adding-items-to-BlockDropItemEvent.patch b/Spigot-API-Patches-Unmapped/0271-Allow-adding-items-to-BlockDropItemEvent.patch
new file mode 100644
index 0000000000..984da00f52
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0271-Allow-adding-items-to-BlockDropItemEvent.patch
@@ -0,0 +1,19 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: BillyGalbreath <[email protected]>
+Date: Wed, 20 Jan 2021 14:25:26 -0600
+Subject: [PATCH] Allow adding items to BlockDropItemEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java b/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java
+index a0f6f1af304190b4c5db4b284d460f625eeb7801..3dd4bd38e72c04e74e5787fb38ca9abd10bad06b 100644
+--- a/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java
++++ b/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java
+@@ -64,7 +64,7 @@ public class BlockDropItemEvent extends BlockEvent implements Cancellable {
+ * Gets list of the Item drops caused by the block break.
+ *
+ * This list is mutable - removing an item from it will cause it to not
+- * drop. It is not legal however to add new items to the list.
++ * drop. Adding to the list is allowed.
+ *
+ * @return The Item the block caused to drop
+ */
diff --git a/Spigot-API-Patches-Unmapped/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/Spigot-API-Patches-Unmapped/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch
new file mode 100644
index 0000000000..6de1a3b396
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0272-Add-getMainThreadExecutor-to-BukkitScheduler.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aleksander Jagiello <[email protected]>
+Date: Sun, 24 Jan 2021 22:17:29 +0100
+Subject: [PATCH] Add getMainThreadExecutor to BukkitScheduler
+
+
+diff --git a/src/main/java/org/bukkit/scheduler/BukkitScheduler.java b/src/main/java/org/bukkit/scheduler/BukkitScheduler.java
+index ac140fc2c638e22e06b2920db3e376ab9e8c3733..f5e3bfd22d4d38182065b5215e5f78d9bb13381e 100644
+--- a/src/main/java/org/bukkit/scheduler/BukkitScheduler.java
++++ b/src/main/java/org/bukkit/scheduler/BukkitScheduler.java
+@@ -458,4 +458,15 @@ public interface BukkitScheduler {
+ @Deprecated
+ @NotNull
+ public BukkitTask runTaskTimerAsynchronously(@NotNull Plugin plugin, @NotNull BukkitRunnable task, long delay, long period) throws IllegalArgumentException;
++
++ // Paper start - add getMainThreadExecutor
++ /**
++ * Returns an executor that will run tasks on the next server tick.
++ *
++ * @param plugin the reference to the plugin scheduling tasks
++ * @return an executor associated with the given plugin
++ */
++ @NotNull
++ public java.util.concurrent.Executor getMainThreadExecutor(@NotNull Plugin plugin);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0273-living-entity-allow-attribute-registration.patch b/Spigot-API-Patches-Unmapped/0273-living-entity-allow-attribute-registration.patch
new file mode 100644
index 0000000000..e7ded6dabb
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0273-living-entity-allow-attribute-registration.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: ysl3000 <[email protected]>
+Date: Sat, 24 Oct 2020 16:37:21 +0200
+Subject: [PATCH] living entity allow attribute registration
+
+
+diff --git a/src/main/java/org/bukkit/attribute/Attributable.java b/src/main/java/org/bukkit/attribute/Attributable.java
+index 0ed96b5af0e6e93590882e0ad8239221bcc3f688..474ed1df364a5ca18661d0fbc29901760e39cb07 100644
+--- a/src/main/java/org/bukkit/attribute/Attributable.java
++++ b/src/main/java/org/bukkit/attribute/Attributable.java
+@@ -17,4 +17,14 @@ public interface Attributable {
+ */
+ @Nullable
+ AttributeInstance getAttribute(@NotNull Attribute attribute);
++
++ // Paper start
++ /**
++ * Registers a generic attribute to that attributable instance.
++ * Allows it to add attributes not registered by default to that entity.
++ *
++ * @param attribute the generic attribute to register
++ */
++ void registerAttribute(@NotNull Attribute attribute);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0274-Add-missing-effects.patch b/Spigot-API-Patches-Unmapped/0274-Add-missing-effects.patch
new file mode 100644
index 0000000000..57086bea9c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0274-Add-missing-effects.patch
@@ -0,0 +1,76 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Ivan Pekov <[email protected]>
+Date: Tue, 5 Jan 2021 10:19:11 +0200
+Subject: [PATCH] Add missing effects
+
+
+diff --git a/src/main/java/org/bukkit/Effect.java b/src/main/java/org/bukkit/Effect.java
+index d0c812c72967469122f7f276ced31cfb0e9482dc..0f598fa0560d14b44dcc65205364870c0bbfa0d8 100644
+--- a/src/main/java/org/bukkit/Effect.java
++++ b/src/main/java/org/bukkit/Effect.java
+@@ -201,6 +201,65 @@ public enum Effect {
+ * The sound of an enderdragon growling
+ */
+ ENDERDRAGON_GROWL(3001, Type.SOUND),
++ // Paper start - add missing effects
++ /**
++ * The sound of a wither spawning
++ */
++ WITHER_SPAWNED(1023, Type.SOUND),
++ /**
++ * The sound of an ender dragon dying
++ */
++ ENDER_DRAGON_DEATH(1028, Type.SOUND),
++ /**
++ * The sound of an ender portal being created in the overworld
++ */
++ END_PORTAL_CREATED_IN_OVERWORLD(1038, Type.SOUND),
++ /**
++ * The sound of phantom's bites
++ */
++ PHANTOM_BITES(1039, Type.SOUND),
++ /**
++ * The sound of zombie converting to drowned zombie
++ */
++ ZOMBIE_CONVERTS_TO_DROWNED(1040, Type.SOUND),
++ /**
++ * The sound of a husk converting to zombie by drowning
++ */
++ HUSK_CONVERTS_TO_ZOMBIE(1041, Type.SOUND),
++ /**
++ * The sound of a grindstone being used
++ */
++ GRINDSTONE_USED(1042, Type.SOUND),
++ /**
++ * The sound of a book page being turned
++ */
++ BOOK_PAGE_TURNED(1043, Type.SOUND),
++ /**
++ * Particles displayed when a composter composts
++ */
++ COMPOSTER_COMPOSTS(1500, Type.VISUAL),
++ /**
++ * Particles displayed when lava converts a block (either water to stone, or
++ * removing existing blocks such as torches)
++ */
++ LAVA_CONVERTS_BLOCK(1501, Type.VISUAL),
++ /**
++ * Particles displayd when a redstone torch burns out
++ */
++ REDSTONE_TORCH_BURNS_OUT(1502, Type.VISUAL),
++ /**
++ * Particles displayed when an ender eye is placed
++ */
++ ENDER_EYE_PLACED(1503, Type.VISUAL),
++ /**
++ * Particles displayed when an ender dragon destroys block
++ */
++ ENDER_DRAGON_DESTROYS_BLOCK(2008, Type.VISUAL),
++ /**
++ * Particles displayed when a wet sponge vaporizes in nether.
++ */
++ WET_SPONGE_VAPORIZES_IN_NETHER(2009, Type.VISUAL)
++ // Paper end
+ ;
+
+ private final int id;
diff --git a/Spigot-API-Patches-Unmapped/0275-Expose-Tracked-Players.patch b/Spigot-API-Patches-Unmapped/0275-Expose-Tracked-Players.patch
new file mode 100644
index 0000000000..1f1fce583c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0275-Expose-Tracked-Players.patch
@@ -0,0 +1,33 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Tom <[email protected]>
+Date: Fri, 26 Feb 2021 16:24:25 -0600
+Subject: [PATCH] Expose Tracked Players
+
+
+diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
+index 8a1e35952ae84ae1e41a2b7783e88eab52bd274c..2ea531eaef8c455fdd503f0c0258813fe9136085 100644
+--- a/src/main/java/org/bukkit/entity/Player.java
++++ b/src/main/java/org/bukkit/entity/Player.java
+@@ -1,6 +1,7 @@
+ package org.bukkit.entity;
+
+ import java.net.InetSocketAddress;
++import java.util.Set; // Paper
+ import java.util.UUID;
+ import com.destroystokyo.paper.ClientOption; // Paper
+ import com.destroystokyo.paper.Title; // Paper
+@@ -1927,6 +1928,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ void sendOpLevel(byte level);
+ // Paper end
+
++ // Paper start
++ /**
++ * @return Returns a set of Players within this player's tracking range (that the player's client can "see")
++ */
++ @NotNull
++ Set<Player> getTrackedPlayers();
++ // Paper end
++
+ // Spigot start
+ public class Spigot extends Entity.Spigot {
+
diff --git a/Spigot-API-Patches-Unmapped/0276-Cache-the-result-of-Material-isBlock.patch b/Spigot-API-Patches-Unmapped/0276-Cache-the-result-of-Material-isBlock.patch
new file mode 100644
index 0000000000..eaa498baea
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0276-Cache-the-result-of-Material-isBlock.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: jmp <[email protected]>
+Date: Tue, 2 Mar 2021 15:24:58 -0800
+Subject: [PATCH] Cache the result of Material#isBlock
+
+
+diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
+index e453e5eb7245aad3ecbb19652ebb34abe030c0a9..112c3f035ec7e7a7cae939264e0af4c6f4450abd 100644
+--- a/src/main/java/org/bukkit/Material.java
++++ b/src/main/java/org/bukkit/Material.java
+@@ -3522,6 +3522,7 @@ public enum Material implements Keyed {
+ public final Class<?> data;
+ private final boolean legacy;
+ private final NamespacedKey key;
++ private boolean isBlock; // Paper
+
+ private Material(final int id) {
+ this(id, 64);
+@@ -3719,6 +3720,11 @@ public enum Material implements Keyed {
+ * @return true if this material is a block
+ */
+ public boolean isBlock() {
++ // Paper start - cache isBlock
++ return this.isBlock;
++ }
++ private boolean isBlock0() {
++ // Paper end
+ switch (this) {
+ //<editor-fold defaultstate="collapsed" desc="isBlock">
+ case ACACIA_BUTTON:
+@@ -4664,6 +4670,7 @@ public enum Material implements Keyed {
+ static {
+ for (Material material : values()) {
+ BY_NAME.put(material.name(), material);
++ material.isBlock = material.isBlock0(); // Paper
+ }
+ }
+
diff --git a/Spigot-API-Patches-Unmapped/0277-Add-worldborder-events.patch b/Spigot-API-Patches-Unmapped/0277-Add-worldborder-events.patch
new file mode 100644
index 0000000000..09c3770e16
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0277-Add-worldborder-events.patch
@@ -0,0 +1,308 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Mon, 4 Jan 2021 22:40:26 -0800
+Subject: [PATCH] Add worldborder events
+
+
+diff --git a/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java b/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..126fe50b519a8d7cd158f799058cb235f9c4cbdb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java
+@@ -0,0 +1,114 @@
++package io.papermc.paper.event.world.border;
++
++import org.bukkit.World;
++import org.bukkit.WorldBorder;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a world border changes its bounds, either over time, or instantly.
++ */
++public class WorldBorderBoundsChangeEvent extends WorldBorderEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private Type type;
++ private final double oldSize;
++ private double newSize;
++ private long duration;
++ private boolean cancelled;
++
++ public WorldBorderBoundsChangeEvent(@NotNull World world, @NotNull WorldBorder worldBorder, @NotNull Type type, double oldSize, double newSize, long duration) {
++ super(world, worldBorder);
++ this.type = type;
++ this.oldSize = oldSize;
++ this.newSize = newSize;
++ this.duration = duration;
++ }
++
++ /**
++ * Gets if this change is an instant change or over-time change.
++ *
++ * @return the change type
++ */
++ @NotNull
++ public Type getType() {
++ return type;
++ }
++
++ /**
++ * Gets the old size or the world border.
++ *
++ * @return the old size
++ */
++ public double getOldSize() {
++ return oldSize;
++ }
++
++ /**
++ * Gets the new size of the world border.
++ *
++ * @return the new size
++ */
++ public double getNewSize() {
++ return newSize;
++ }
++
++ /**
++ * Sets the new size of the world border.
++ *
++ * @param newSize the new size
++ */
++ public void setNewSize(double newSize) {
++ // PAIL: TODO: Magic Values
++ this.newSize = Math.min(6.0E7D, Math.max(1.0D, newSize));
++ }
++
++ /**
++ * Gets the time in milliseconds for the change. Will be 0 if instant.
++ *
++ * @return the time in milliseconds for the change
++ */
++ public long getDuration() {
++ return duration;
++ }
++
++ /**
++ * Sets the time in milliseconds for the change. Will change {@link #getType()} to return
++ * {@link Type#STARTED_MOVE}.
++ *
++ * @param duration the time in milliseconds for the change
++ */
++ public void setDuration(long duration) {
++ // PAIL: TODO: Magic Values
++ this.duration = Math.min(9223372036854775L, Math.max(0L, duration));
++ if (duration >= 0 && type == Type.INSTANT_MOVE) type = Type.STARTED_MOVE;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++
++ public enum Type {
++ STARTED_MOVE,
++ INSTANT_MOVE
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeFinishEvent.java b/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeFinishEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c3d578ae2c5615b0ebace99d9bacf100b086c971
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeFinishEvent.java
+@@ -0,0 +1,65 @@
++package io.papermc.paper.event.world.border;
++
++import org.bukkit.World;
++import org.bukkit.WorldBorder;
++import org.bukkit.event.HandlerList;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a moving world border has finished it's move.
++ */
++public class WorldBorderBoundsChangeFinishEvent extends WorldBorderEvent {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private final double oldSize;
++ private final double newSize;
++ private final double duration;
++
++ public WorldBorderBoundsChangeFinishEvent(@NotNull World world, @NotNull WorldBorder worldBorder, double oldSize, double newSize, double duration) {
++ super(world, worldBorder);
++ this.oldSize = oldSize;
++ this.newSize = newSize;
++ this.duration = duration;
++ }
++
++ /**
++ * Gets the old size of the worldborder.
++ *
++ * @return the old size
++ */
++ public double getOldSize() {
++ return oldSize;
++ }
++
++ /**
++ * Gets the new size of the worldborder.
++ *
++ * @return the new size
++ */
++ public double getNewSize() {
++ return newSize;
++ }
++
++ /**
++ * Gets the duration this worldborder took to make the change.
++ * <p>
++ * Can be 0 if handlers for {@link io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent} set the duration to 0.
++ *
++ * @return the duration of the transition
++ */
++ public double getDuration() {
++ return duration;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/world/border/WorldBorderCenterChangeEvent.java b/src/main/java/io/papermc/paper/event/world/border/WorldBorderCenterChangeEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..4a10c773a8d05a596066e63306dead74c1363fd7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/border/WorldBorderCenterChangeEvent.java
+@@ -0,0 +1,77 @@
++package io.papermc.paper.event.world.border;
++
++import org.bukkit.Location;
++import org.bukkit.World;
++import org.bukkit.WorldBorder;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.world.WorldEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a world border's center is changed.
++ */
++public class WorldBorderCenterChangeEvent extends WorldBorderEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private final Location oldCenter;
++ private Location newCenter;
++ private boolean cancelled;
++
++ public WorldBorderCenterChangeEvent(@NotNull World world, @NotNull WorldBorder worldBorder, @NotNull Location oldCenter, @NotNull Location newCenter) {
++ super(world, worldBorder);
++ this.oldCenter = oldCenter;
++ this.newCenter = newCenter;
++ }
++
++ /**
++ * Gets the original center location of the world border.
++ *
++ * @return the old center
++ */
++ @NotNull
++ public Location getOldCenter() {
++ return oldCenter;
++ }
++
++ /**
++ * Gets the new center location for the world border.
++ *
++ * @return the new center
++ */
++ @NotNull
++ public Location getNewCenter() {
++ return newCenter;
++ }
++
++ /**
++ * Sets the new center location for the world border. Y coordinate is ignored.
++ *
++ * @param newCenter the new center
++ */
++ public void setNewCenter(@NotNull Location newCenter) {
++ this.newCenter = newCenter;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/event/world/border/WorldBorderEvent.java b/src/main/java/io/papermc/paper/event/world/border/WorldBorderEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..67bd469d3680c9554ce6c1d5493826a252682835
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/world/border/WorldBorderEvent.java
+@@ -0,0 +1,22 @@
++package io.papermc.paper.event.world.border;
++
++import org.bukkit.World;
++import org.bukkit.WorldBorder;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.world.WorldEvent;
++import org.jetbrains.annotations.NotNull;
++
++public abstract class WorldBorderEvent extends WorldEvent {
++
++ private final WorldBorder worldBorder;
++
++ public WorldBorderEvent(@NotNull World world, @NotNull WorldBorder worldBorder) {
++ super(world);
++ this.worldBorder = worldBorder;
++ }
++
++ @NotNull
++ public WorldBorder getWorldBorder() {
++ return worldBorder;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0278-added-PlayerNameEntityEvent.patch b/Spigot-API-Patches-Unmapped/0278-added-PlayerNameEntityEvent.patch
new file mode 100644
index 0000000000..a38082f8e8
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0278-added-PlayerNameEntityEvent.patch
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Sun, 5 Jul 2020 00:34:24 -0700
+Subject: [PATCH] added PlayerNameEntityEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerNameEntityEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerNameEntityEvent.java
+new file mode 100755
+index 0000000000000000000000000000000000000000..ef9e53a73eff469bbaa8fb20c634297acb9d1986
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerNameEntityEvent.java
+@@ -0,0 +1,118 @@
++package io.papermc.paper.event.player;
++
++import net.kyori.adventure.text.Component;
++import org.bukkit.entity.LivingEntity;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++/**
++ * Called when the player is attempting to rename a mob
++ */
++public class PlayerNameEntityEvent extends PlayerEvent implements Cancellable {
++
++ private LivingEntity entity;
++ private Component name;
++ private boolean persistent;
++ private boolean cancelled;
++
++ public PlayerNameEntityEvent(@NotNull Player player, @NotNull LivingEntity entity, @NotNull Component name, boolean persistent) {
++ super(player);
++ this.entity = entity;
++ this.name = name;
++ this.persistent = persistent;
++ }
++
++ /**
++ * Gets the name to be given to the entity.
++ * @return the name
++ */
++ @Nullable
++ public Component getName() {
++ return name;
++ }
++
++ /**
++ * Sets the name to be given to the entity.
++ *
++ * @param name the name
++ */
++ public void setName(@Nullable Component name) {
++ this.name = name;
++ }
++
++ /**
++ * Gets the entity involved in this event.
++ *
++ * @return the entity
++ */
++ @NotNull
++ public LivingEntity getEntity() {
++ return entity;
++ }
++
++ /**
++ * Sets the entity involved in this event.
++ *
++ * @param entity the entity
++ */
++ public void setEntity(@NotNull LivingEntity entity) {
++ this.entity = entity;
++ }
++
++ /**
++ * Gets whether this will set the mob to be persistent.
++ *
++ * @return persistent
++ */
++ public boolean isPersistent() {
++ return persistent;
++ }
++
++ /**
++ * Sets whether this will set the mob to be persistent.
++ *
++ * @param persistent persistent
++ */
++ public void setPersistent(boolean persistent) {
++ this.persistent = persistent;
++ }
++
++ /**
++ * Gets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins
++ *
++ * @return true if this event is cancelled
++ */
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Sets the cancellation state of this event. A cancelled event will not
++ * be executed in the server, but will still pass to other plugins.
++ *
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0279-Add-recipe-to-cook-events.patch b/Spigot-API-Patches-Unmapped/0279-Add-recipe-to-cook-events.patch
new file mode 100644
index 0000000000..b0b34773c0
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0279-Add-recipe-to-cook-events.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Thonk <[email protected]>
+Date: Wed, 6 Jan 2021 12:05:29 -0800
+Subject: [PATCH] Add recipe to cook events
+
+
+diff --git a/src/main/java/org/bukkit/event/block/BlockCookEvent.java b/src/main/java/org/bukkit/event/block/BlockCookEvent.java
+index be7af5440bf9923f0c9c84efa4d70a89337a2f96..a3f1c9cb36c9069ed622985a525bfc2a7a27ab91 100644
+--- a/src/main/java/org/bukkit/event/block/BlockCookEvent.java
++++ b/src/main/java/org/bukkit/event/block/BlockCookEvent.java
+@@ -14,12 +14,21 @@ public class BlockCookEvent extends BlockEvent implements Cancellable {
+ private final ItemStack source;
+ private ItemStack result;
+ private boolean cancelled;
++ private final org.bukkit.inventory.CookingRecipe<?> recipe; // Paper
+
++ @Deprecated // Paper
+ public BlockCookEvent(@NotNull final Block block, @NotNull final ItemStack source, @NotNull final ItemStack result) {
++ // Paper start
++ this(block, source, result, null);
++ }
++
++ public BlockCookEvent(@NotNull final Block block, @NotNull final ItemStack source, @NotNull final ItemStack result, @org.jetbrains.annotations.Nullable org.bukkit.inventory.CookingRecipe<?> recipe) {
++ // Paper end
+ super(block);
+ this.source = source;
+ this.result = result;
+ this.cancelled = false;
++ this.recipe = recipe; // Paper
+ }
+
+ /**
+@@ -61,6 +70,18 @@ public class BlockCookEvent extends BlockEvent implements Cancellable {
+ this.cancelled = cancel;
+ }
+
++ // Paper start
++ /**
++ * Gets the cooking recipe associated with this event.
++ *
++ * @return the recipe
++ */
++ @org.jetbrains.annotations.Nullable
++ public org.bukkit.inventory.CookingRecipe<?> getRecipe() {
++ return recipe;
++ }
++ // Paper end
++
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+diff --git a/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java b/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java
+index 066e7dd9a34d35c8b643a5efcf95d6a5ef47c7ee..16b3ab8f525c4e863f804cc8460a330407d85478 100644
+--- a/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/FurnaceSmeltEvent.java
+@@ -10,7 +10,13 @@ import org.jetbrains.annotations.NotNull;
+ */
+ public class FurnaceSmeltEvent extends BlockCookEvent {
+
++ @Deprecated // Paper
+ public FurnaceSmeltEvent(@NotNull final Block furnace, @NotNull final ItemStack source, @NotNull final ItemStack result) {
+ super(furnace, source, result);
+ }
++ // Paper start
++ public FurnaceSmeltEvent(@NotNull final Block furnace, @NotNull final ItemStack source, @NotNull final ItemStack result, @org.jetbrains.annotations.Nullable org.bukkit.inventory.CookingRecipe<?> recipe) {
++ super(furnace, source, result, recipe);
++ }
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0280-Add-Block-isValidTool.patch b/Spigot-API-Patches-Unmapped/0280-Add-Block-isValidTool.patch
new file mode 100644
index 0000000000..859c5c36bc
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0280-Add-Block-isValidTool.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Mon, 6 Jul 2020 12:44:23 -0700
+Subject: [PATCH] Add Block#isValidTool
+
+
+diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java
+index a7bc431ea8e25abdc1bc575eb73d26b37a2049b3..85ff6ce3bf75ab78f066a508d130def5913fff0e 100644
+--- a/src/main/java/org/bukkit/block/Block.java
++++ b/src/main/java/org/bukkit/block/Block.java
+@@ -218,6 +218,15 @@ public interface Block extends Metadatable {
+ public static int getBlockKeyZ(long packed) {
+ return (int) ((packed << 10) >> 37);
+ }
++
++ /**
++ * Checks if the itemstack is a valid tool to
++ * break the block with
++ *
++ * @param itemStack The (tool) itemstack
++ * @return whether the block will drop items
++ */
++ boolean isValidTool(@NotNull ItemStack itemStack);
+ // Paper End
+
+ /**
diff --git a/Spigot-API-Patches-Unmapped/0281-Implement-Keyed-on-World.patch b/Spigot-API-Patches-Unmapped/0281-Implement-Keyed-on-World.patch
new file mode 100644
index 0000000000..b901f8bf97
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0281-Implement-Keyed-on-World.patch
@@ -0,0 +1,164 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 6 Jan 2021 00:34:10 -0800
+Subject: [PATCH] Implement Keyed on World
+
+
+diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
+index 68101a322ffab8ec28843386b79b8079576fa720..5f7208196684d9c8373df28b7cfb5f9e21baa41e 100644
+--- a/src/main/java/org/bukkit/Bukkit.java
++++ b/src/main/java/org/bukkit/Bukkit.java
+@@ -639,6 +639,18 @@ public final class Bukkit {
+ public static World getWorld(@NotNull UUID uid) {
+ return server.getWorld(uid);
+ }
++ // Paper start
++ /**
++ * Gets the world from the given NamespacedKey
++ *
++ * @param worldKey the NamespacedKey of the world to retrieve
++ * @return a world with the given NamespacedKey, or null if none exists
++ */
++ @Nullable
++ public static World getWorld(@NotNull NamespacedKey worldKey) {
++ return server.getWorld(worldKey);
++ }
++ // Paper end
+
+ /**
+ * Gets the map from the given item ID.
+diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
+index a79fa08b9e6fb924b2da933eb6e4b365d14d938d..f3e27d2d02a9407bb1b091b8c1125ad5abf99e55 100644
+--- a/src/main/java/org/bukkit/Server.java
++++ b/src/main/java/org/bukkit/Server.java
+@@ -541,6 +541,17 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
+ @Nullable
+ public World getWorld(@NotNull UUID uid);
+
++ // Paper start
++ /**
++ * Gets the world from the given NamespacedKey
++ *
++ * @param worldKey the NamespacedKey of the world to retrieve
++ * @return a world with the given NamespacedKey, or null if none exists
++ */
++ @Nullable
++ public World getWorld(@NotNull NamespacedKey worldKey);
++ // Paper end
++
+ /**
+ * Gets the map from the given item ID.
+ *
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 3c50a10fda033c139993ed5465a92f95d5b10c17..064497506e6a5ab89ca43b99968ca79d51d67c46 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -43,7 +43,7 @@ import org.jetbrains.annotations.Nullable;
+ /**
+ * Represents a world, which may contain entities, chunks and blocks
+ */
+-public interface World extends PluginMessageRecipient, Metadatable, net.kyori.adventure.audience.ForwardingAudience { // Paper
++public interface World extends PluginMessageRecipient, Metadatable, net.kyori.adventure.audience.ForwardingAudience, Keyed { // Paper
+
+ // Paper start
+ /**
+@@ -829,6 +829,15 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+
+ @NotNull
+ java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent);
++
++ /**
++ * Get the world's key
++ *
++ * @return the world's key
++ */
++ @NotNull
++ @Override
++ NamespacedKey getKey();
+ // Paper end
+
+ /**
+diff --git a/src/main/java/org/bukkit/WorldCreator.java b/src/main/java/org/bukkit/WorldCreator.java
+index 6e6945dd4c770be04ec09da3958fae751717527a..e6a83252f42da31ad38f8dc1beccc7aa2c3f54b8 100644
+--- a/src/main/java/org/bukkit/WorldCreator.java
++++ b/src/main/java/org/bukkit/WorldCreator.java
+@@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
+ * Represents various types of options that may be used to create a world.
+ */
+ public class WorldCreator {
++ private final NamespacedKey key; // Paper
+ private final String name;
+ private long seed;
+ private World.Environment environment = World.Environment.NORMAL;
+@@ -26,13 +27,67 @@ public class WorldCreator {
+ * @param name Name of the world that will be created
+ */
+ public WorldCreator(@NotNull String name) {
+- if (name == null) {
+- throw new IllegalArgumentException("World name cannot be null");
+- }
++ // Paper start
++ this(name, NamespacedKey.minecraft(name.toLowerCase(java.util.Locale.ENGLISH).replace(" ", "_")));
++ }
+
+- this.name = name;
++ /**
++ * Creates an empty WorldCreator for the given world name and key
++ *
++ * @param levelName LevelName of the world that will be created
++ * @param worldKey NamespacedKey of the world that will be created
++ */
++ public WorldCreator(@NotNull String levelName, @NotNull NamespacedKey worldKey) {
++ if (levelName == null || worldKey == null) {
++ throw new IllegalArgumentException("World name and key cannot be null");
++ }
++ this.name = levelName;
+ this.seed = (new Random()).nextLong();
++ this.key = worldKey;
++ }
++
++ /**
++ * Creates an empty WorldCreator for the given key.
++ * LevelName will be the Key part of the NamespacedKey.
++ *
++ * @param worldKey NamespacedKey of the world that will be created
++ */
++ public WorldCreator(@NotNull NamespacedKey worldKey) {
++ this(worldKey.getKey(), worldKey);
++ }
++
++ /**
++ * Gets the key for this WorldCreator
++ *
++ * @return the key
++ */
++ @NotNull
++ public NamespacedKey key() {
++ return key;
++ }
++
++ /**
++ * Creates an empty WorldCreator for the given world name and key
++ *
++ * @param levelName LevelName of the world that will be created
++ * @param worldKey NamespacedKey of the world that will be created
++ */
++ @NotNull
++ public static WorldCreator ofNameAndKey(@NotNull String levelName, @NotNull NamespacedKey worldKey) {
++ return new WorldCreator(levelName, worldKey);
++ }
++
++ /**
++ * Creates an empty WorldCreator for the given key.
++ * LevelName will be the Key part of the NamespacedKey.
++ *
++ * @param worldKey NamespacedKey of the world that will be created
++ */
++ @NotNull
++ public static WorldCreator ofKey(@NotNull NamespacedKey worldKey) {
++ return new WorldCreator(worldKey);
+ }
++ // Paper end
+
+ /**
+ * Copies the options from the specified world
diff --git a/Spigot-API-Patches-Unmapped/0282-fix-Inventory-getContents-null-annotations.patch b/Spigot-API-Patches-Unmapped/0282-fix-Inventory-getContents-null-annotations.patch
new file mode 100644
index 0000000000..4d216020ec
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0282-fix-Inventory-getContents-null-annotations.patch
@@ -0,0 +1,22 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: CDFN <[email protected]>
+Date: Fri, 12 Mar 2021 18:31:31 +0100
+Subject: [PATCH] fix Inventory#getContents null annotations
+
+
+diff --git a/src/main/java/org/bukkit/inventory/Inventory.java b/src/main/java/org/bukkit/inventory/Inventory.java
+index 6386206188e820206bb1a9f516b5e194fdc9d952..7956aebcb390379677dccf7c9561866cf94c024c 100644
+--- a/src/main/java/org/bukkit/inventory/Inventory.java
++++ b/src/main/java/org/bukkit/inventory/Inventory.java
+@@ -158,9 +158,8 @@ public interface Inventory extends Iterable<ItemStack> {
+ *
+ * @return An array of ItemStacks from the inventory. Individual items may be null.
+ */
+- @NotNull
+- public ItemStack[] getContents();
+-
++ public @org.checkerframework.checker.nullness.qual.Nullable ItemStack @org.checkerframework.checker.nullness.qual.NonNull [] getContents(); // Paper - make array elements nullable instead array
++
+ /**
+ * Completely replaces the inventory's contents. Removes all existing
+ * contents and replaces it with the ItemStacks given in the array.
diff --git a/Spigot-API-Patches-Unmapped/0283-Item-Rarity-API.patch b/Spigot-API-Patches-Unmapped/0283-Item-Rarity-API.patch
new file mode 100644
index 0000000000..12b81466e5
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0283-Item-Rarity-API.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Fri, 12 Mar 2021 17:09:40 -0800
+Subject: [PATCH] Item Rarity API
+
+
+diff --git a/src/main/java/io/papermc/paper/inventory/ItemRarity.java b/src/main/java/io/papermc/paper/inventory/ItemRarity.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..74ef8395cc040ce488c2acaa416db20272cc2734
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/inventory/ItemRarity.java
+@@ -0,0 +1,28 @@
++package io.papermc.paper.inventory;
++
++import net.kyori.adventure.text.format.NamedTextColor;
++import net.kyori.adventure.text.format.TextColor;
++import org.jetbrains.annotations.NotNull;
++
++public enum ItemRarity {
++
++ COMMON(NamedTextColor.WHITE),
++ UNCOMMON(NamedTextColor.YELLOW),
++ RARE(NamedTextColor.AQUA),
++ EPIC(NamedTextColor.LIGHT_PURPLE);
++
++ TextColor color;
++
++ ItemRarity(TextColor color) {
++ this.color = color;
++ }
++
++ /**
++ * Gets the color formatting associated with the rarity.
++ * @return
++ */
++ @NotNull
++ public TextColor getColor() {
++ return color;
++ }
++}
+diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
+index 112c3f035ec7e7a7cae939264e0af4c6f4450abd..9b1c9e60dba9ea3ef8d8e164f13dd76daf57db8e 100644
+--- a/src/main/java/org/bukkit/Material.java
++++ b/src/main/java/org/bukkit/Material.java
+@@ -3589,6 +3589,17 @@ public enum Material implements Keyed {
+ public String getTranslationKey() {
+ return Bukkit.getUnsafe().getTranslationKey(this);
+ }
++
++ /**
++ * Returns the item rarity for the item. The Material <b>MUST</b> be an Item not a block.
++ * Use {@link #isItem()} before this.
++ *
++ * @return the item rarity
++ */
++ @NotNull
++ public io.papermc.paper.inventory.ItemRarity getItemRarity() {
++ return Bukkit.getUnsafe().getItemRarity(this);
++ }
+ // Paper end
+
+ /**
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index fafc4d63b6202b00a133c50cd38dec54db9b3576..6db8c3bae9c9bb10eedf8102b3ac4c6eb288b77b 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -131,5 +131,21 @@ public interface UnsafeValues {
+ */
+ public int nextEntityId();
+
++ /**
++ * Gets the item rarity of a material. The material <b>MUST</b> be an item.
++ * Use {@link Material#isItem()} before this.
++ *
++ * @param material the material to get the rarity of
++ * @return the item rarity
++ */
++ public io.papermc.paper.inventory.ItemRarity getItemRarity(Material material);
++
++ /**
++ * Gets the item rarity of the itemstack. The rarity can change based on enchantements.
++ *
++ * @param itemStack the itemstack to get the rarity of
++ * @return the itemstack rarity
++ */
++ public io.papermc.paper.inventory.ItemRarity getItemStackRarity(ItemStack itemStack);
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0284-Expose-protocol-version.patch b/Spigot-API-Patches-Unmapped/0284-Expose-protocol-version.patch
new file mode 100644
index 0000000000..39ddd2f7b3
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0284-Expose-protocol-version.patch
@@ -0,0 +1,23 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: KennyTV <[email protected]>
+Date: Fri, 26 Mar 2021 11:23:27 +0100
+Subject: [PATCH] Expose protocol version
+
+
+diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
+index 6db8c3bae9c9bb10eedf8102b3ac4c6eb288b77b..3bf6e58b2351cee935e23abec1cea289e31943dc 100644
+--- a/src/main/java/org/bukkit/UnsafeValues.java
++++ b/src/main/java/org/bukkit/UnsafeValues.java
+@@ -147,5 +147,12 @@ public interface UnsafeValues {
+ * @return the itemstack rarity
+ */
+ public io.papermc.paper.inventory.ItemRarity getItemStackRarity(ItemStack itemStack);
++
++ /**
++ * Returns the server's protocol version.
++ *
++ * @return the server's protocol version
++ */
++ int getProtocolVersion();
+ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0285-add-isDeeplySleeping-to-HumanEntity.patch b/Spigot-API-Patches-Unmapped/0285-add-isDeeplySleeping-to-HumanEntity.patch
new file mode 100644
index 0000000000..b0f198f0e1
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0285-add-isDeeplySleeping-to-HumanEntity.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 8 Apr 2021 17:36:15 -0700
+Subject: [PATCH] add isDeeplySleeping to HumanEntity
+
+
+diff --git a/src/main/java/org/bukkit/entity/HumanEntity.java b/src/main/java/org/bukkit/entity/HumanEntity.java
+index f0e0710fef5a3e0b722ece7ccf89c3d0f88f8f0f..2ce774c81a93260a1464183d435b4c418ed61648 100644
+--- a/src/main/java/org/bukkit/entity/HumanEntity.java
++++ b/src/main/java/org/bukkit/entity/HumanEntity.java
+@@ -319,6 +319,15 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
+ */
+ public void setCooldown(@NotNull Material material, int ticks);
+
++ // Paper start
++ /**
++ * If the player has slept enough to count towards passing the night.
++ *
++ * @return true if the player has slept enough
++ */
++ public boolean isDeeplySleeping();
++ // Paper end
++
+ /**
+ * Get the sleep ticks of the player. This value may be capped.
+ *
diff --git a/Spigot-API-Patches-Unmapped/0286-add-consumeFuel-to-FurnaceBurnEvent.patch b/Spigot-API-Patches-Unmapped/0286-add-consumeFuel-to-FurnaceBurnEvent.patch
new file mode 100644
index 0000000000..6c8b681cd9
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0286-add-consumeFuel-to-FurnaceBurnEvent.patch
@@ -0,0 +1,44 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 22 Apr 2021 16:45:15 -0700
+Subject: [PATCH] add consumeFuel to FurnaceBurnEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java b/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java
+index bc71bc2d3ace0d19d730c09f05f9e0655bcee8f5..caef53d0f6546516fa7aabb2cb3abed70808b3ba 100644
+--- a/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/FurnaceBurnEvent.java
+@@ -16,6 +16,7 @@ public class FurnaceBurnEvent extends BlockEvent implements Cancellable {
+ private int burnTime;
+ private boolean cancelled;
+ private boolean burning;
++ private boolean consumeFuel = true; // Paper
+
+ public FurnaceBurnEvent(@NotNull final Block furnace, @NotNull final ItemStack fuel, final int burnTime) {
+ super(furnace);
+@@ -70,6 +71,25 @@ public class FurnaceBurnEvent extends BlockEvent implements Cancellable {
+ public void setBurning(boolean burning) {
+ this.burning = burning;
+ }
++ // Paper start
++ /**
++ * Gets whether the furnace's fuel will be consumed or not.
++ *
++ * @return whether the furnace's fuel will be consumed
++ */
++ public boolean willConsumeFuel() {
++ return consumeFuel;
++ }
++
++ /**
++ * Sets whether the furnace's fuel will be consumed or not.
++ *
++ * @param consumeFuel true to consume the fuel
++ */
++ public void setConsumeFuel(boolean consumeFuel) {
++ this.consumeFuel = consumeFuel;
++ }
++ // Paper end
+
+ @Override
+ public boolean isCancelled() {
diff --git a/Spigot-API-Patches-Unmapped/0287-add-get-set-drop-chance-to-EntityEquipment.patch b/Spigot-API-Patches-Unmapped/0287-add-get-set-drop-chance-to-EntityEquipment.patch
new file mode 100644
index 0000000000..447a45db06
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0287-add-get-set-drop-chance-to-EntityEquipment.patch
@@ -0,0 +1,43 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 22 Apr 2021 00:28:20 -0700
+Subject: [PATCH] add get-set drop chance to EntityEquipment
+
+
+diff --git a/src/main/java/org/bukkit/inventory/EntityEquipment.java b/src/main/java/org/bukkit/inventory/EntityEquipment.java
+index f905bf7a28a42d8bd2aecd42030d2b2092696fc3..58cfd450973f56bfbdd20f9dca8c1e7455260a55 100644
+--- a/src/main/java/org/bukkit/inventory/EntityEquipment.java
++++ b/src/main/java/org/bukkit/inventory/EntityEquipment.java
+@@ -405,4 +405,32 @@ public interface EntityEquipment {
+ */
+ @Nullable
+ Entity getHolder();
++ // Paper start
++ /**
++ * Gets the drop chance of specified slot.
++ *
++ * <ul>
++ * <li>A drop chance of 0.0F will never drop
++ * <li>A drop chance of 1.0F will always drop
++ * </ul>
++ *
++ * @param slot the slot to get the drop chance of
++ * @return the drop chance for the slot
++ */
++ float getDropChance(@NotNull EquipmentSlot slot);
++
++ /**
++ * Sets the drop chance of the specified slot.
++ *
++ * <ul>
++ * <li>A drop chance of 0.0F will never drop
++ * <li>A drop chance of 1.0F will always drop
++ * </ul>
++ *
++ * @param slot the slot to set the drop chance of
++ * @param chance the drop chance for the slot
++ * @throws UnsupportedOperationException when called on players
++ */
++ void setDropChance(@NotNull EquipmentSlot slot, float chance);
++ // Paper end
+ }
diff --git a/Spigot-API-Patches-Unmapped/0288-Added-PlayerDeepSleepEvent.patch b/Spigot-API-Patches-Unmapped/0288-Added-PlayerDeepSleepEvent.patch
new file mode 100644
index 0000000000..d8dccca755
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0288-Added-PlayerDeepSleepEvent.patch
@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Wed, 21 Apr 2021 15:58:25 -0700
+Subject: [PATCH] Added PlayerDeepSleepEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerDeepSleepEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerDeepSleepEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e3ee540bb0a5bc578b148fbcf8b5e39ab9c8575c
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerDeepSleepEvent.java
+@@ -0,0 +1,46 @@
++package io.papermc.paper.event.player;
++
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * Called when a player has slept long enough
++ * to count as passing the night/storm.
++ * <p>
++ * Cancelling this event will prevent the player from being counted as deeply sleeping
++ * unless they exit and re-enter the bed.
++ */
++public class PlayerDeepSleepEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private boolean cancelled;
++
++ public PlayerDeepSleepEvent(@NotNull Player player) {
++ super(player);
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ @Override
++ public void setCancelled(boolean cancel) {
++ this.cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++}
diff --git a/Spigot-API-Patches-Unmapped/0289-More-World-API.patch b/Spigot-API-Patches-Unmapped/0289-More-World-API.patch
new file mode 100644
index 0000000000..1a3c73561c
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0289-More-World-API.patch
@@ -0,0 +1,131 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Tue, 7 Jul 2020 10:53:22 -0700
+Subject: [PATCH] More World API
+
+
+diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
+index 98512bddbb0c8bd6a3f487c60b1ec77b274b991e..a1496fe00a2d5ba6c1af054d4327f868b2cd7344 100644
+--- a/src/main/java/org/bukkit/World.java
++++ b/src/main/java/org/bukkit/World.java
+@@ -3482,6 +3482,120 @@ public interface World extends PluginMessageRecipient, Metadatable, net.kyori.ad
+ @Nullable
+ public Location locateNearestStructure(@NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored);
+
++ // Paper start
++ /**
++ * Locates the nearest biome based on an origin, biome type, and radius to search.
++ * Step defaults to {@code 8}.
++ *
++ * @param origin Origin location
++ * @param biome Biome to find
++ * @param radius radius to search
++ * @return Location of biome or null if not found in specified radius
++ */
++ @Nullable
++ Location locateNearestBiome(@NotNull Location origin, @NotNull Biome biome, int radius);
++
++ /**
++ * Locates the nearest biome based on an origin, biome type, and radius to search
++ * and step
++ *
++ * @param origin Origin location
++ * @param biome Biome to find
++ * @param radius radius to search
++ * @param step Search step 1 would mean checking every block, 8 would be every 8th block
++ * @return Location of biome or null if not found in specified radius
++ */
++ @Nullable
++ Location locateNearestBiome(@NotNull Location origin, @NotNull Biome biome, int radius, int step);
++
++ /**
++ * Checks if the world:
++ * <ul>
++ * <li>evaporates water</li>
++ * <li>dries sponges</li>
++ * <li>has lava spread faster and further</li>
++ * </ul>
++ *
++ * @return true if ultrawarm, false if not
++ */
++ boolean isUltrawarm();
++
++ /**
++ * Checks if the world is natural.
++ * <p>
++ * If {@code false}, compasses will spin randomly in the world.
++ * If {@code true}, nether portals will spawn zombified piglins.
++ * </p>
++ *
++ * @return true or false
++ */
++ boolean isNatural();
++
++ /**
++ * Gets the coordinate scaling of this world.
++ *
++ * @return the coordinate scale
++ */
++ double getCoordinateScale();
++
++ /**
++ * Checks if the world has skylight access
++ *
++ * @return whether there is skylight
++ */
++ boolean hasSkylight();
++
++ /**
++ * Checks if the world has a bedrock ceiling
++ *
++ * @return whether the world has a bedrock ceiling
++ */
++ boolean hasBedrockCeiling();
++
++ /**
++ * Checks if piglins will turn into Zombified Piglins in this world
++ *
++ * @return whether Piglins will <i>not</i> transform
++ */
++ boolean isPiglinSafe();
++
++ /**
++ * Checks if beds work
++ *
++ * @return whether beds work
++ */
++ boolean doesBedWork();
++
++ /**
++ * Checks if respawn anchors work
++ *
++ * @return whether respawn anchors work
++ */
++ boolean doesRespawnAnchorWork();
++
++ /**
++ * Checks if this world supports raids
++ *
++ * @return whether this world supports raids
++ */
++ boolean hasRaids();
++
++ /**
++ * Checks if this world has a fixed time
++ *
++ * @return whether this world has fixed time
++ */
++ boolean isFixedTime();
++
++ /**
++ * Gets the collection of materials that burn infinitely in this world.
++ *
++ * @return the materials that will forever stay lit by fire
++ */
++ @NotNull
++ Collection<Material> getInfiniburn();
++ // Paper end
++
+ // Spigot start
+ /**
+ * Returns the view distance used for this world.
diff --git a/Spigot-API-Patches-Unmapped/0290-Added-PlayerBedFailEnterEvent.patch b/Spigot-API-Patches-Unmapped/0290-Added-PlayerBedFailEnterEvent.patch
new file mode 100644
index 0000000000..dde6c8f9de
--- /dev/null
+++ b/Spigot-API-Patches-Unmapped/0290-Added-PlayerBedFailEnterEvent.patch
@@ -0,0 +1,131 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Thu, 24 Dec 2020 12:27:49 -0800
+Subject: [PATCH] Added PlayerBedFailEnterEvent
+
+
+diff --git a/src/main/java/io/papermc/paper/event/player/PlayerBedFailEnterEvent.java b/src/main/java/io/papermc/paper/event/player/PlayerBedFailEnterEvent.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b7a8c8f0a5ba1b4d8e3d7a6f9381cb9a81315336
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/event/player/PlayerBedFailEnterEvent.java
+@@ -0,0 +1,119 @@
++package io.papermc.paper.event.player;
++
++import net.kyori.adventure.text.Component;
++import org.bukkit.block.Block;
++import org.bukkit.entity.Player;
++import org.bukkit.event.Cancellable;
++import org.bukkit.event.HandlerList;
++import org.bukkit.event.player.PlayerEvent;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++
++public class PlayerBedFailEnterEvent extends PlayerEvent implements Cancellable {
++
++ private static final HandlerList HANDLER_LIST = new HandlerList();
++
++ private final FailReason failReason;
++ private final Block bed;
++ private boolean willExplode;
++ private Component message;
++ private boolean cancelled;
++
++ public PlayerBedFailEnterEvent(@NotNull Player player, @NotNull FailReason failReason, @NotNull Block bed, boolean willExplode, @Nullable Component message) {
++ super(player);
++ this.failReason = failReason;
++ this.bed = bed;
++ this.willExplode = willExplode;
++ this.message = message;
++ }
++
++ @NotNull
++ public FailReason getFailReason() {
++ return failReason;
++ }
++
++ @NotNull
++ public Block getBed() {
++ return bed;
++ }
++
++ public boolean getWillExplode() {
++ return willExplode;
++ }
++
++ public void setWillExplode(boolean willExplode) {
++ this.willExplode = willExplode;
++ }
++
++ @Nullable
++ public Component getMessage() {
++ return message;
++ }
++
++ public void setMessage(@Nullable Component message) {
++ this.message = message;
++ }
++
++ @Override
++ public boolean isCancelled() {
++ return cancelled;
++ }
++
++ /**
++ * Cancel this event.
++ * <p>
++ * <b>NOTE: This does not cancel the player getting in the bed, but any messages/explosions
++ * that may occur because of the interaction.</b>
++ * @param cancel true if you wish to cancel this event
++ */
++ @Override
++ public void setCancelled(boolean cancel) {
++ cancelled = cancel;
++ }
++
++ @NotNull
++ @Override
++ public HandlerList getHandlers() {
++ return HANDLER_LIST;
++ }
++
++ @NotNull
++ public static HandlerList getHandlerList() {
++ return HANDLER_LIST;
++ }
++
++ public static enum FailReason {
++ /**
++ * The world doesn't allow sleeping (ex. Nether or The End). Entering
++ * the bed is prevented and the bed explodes.
++ */
++ NOT_POSSIBLE_HERE,
++ /**
++ * Entering the bed is prevented due to it not being night nor
++ * thundering currently.
++ * <p>
++ * If the event is forcefully allowed during daytime, the player will
++ * enter the bed (and set its bed location), but might get immediately
++ * thrown out again.
++ */
++ NOT_POSSIBLE_NOW,
++ /**
++ * Entering the bed is prevented due to the player being too far away.
++ */
++ TOO_FAR_AWAY,
++ /**
++ * Bed is obstructed.
++ */
++ OBSTRUCTED,
++ /**
++ * Entering the bed is prevented due to there being some other problem.
++ */
++ OTHER_PROBLEM,
++ /**
++ * Entering the bed is prevented due to there being monsters nearby.
++ */
++ NOT_SAFE;
++
++ public static final FailReason[] VALUES = values();
++ }
++}