diff --git a/paper-api/src/main/java/io/papermc/paper/event/block/VaultChangeStateEvent.java b/paper-api/src/main/java/io/papermc/paper/event/block/VaultChangeStateEvent.java
new file mode 100644
index 000000000..2fc049377
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/event/block/VaultChangeStateEvent.java
@@ -0,0 +1,79 @@
+package io.papermc.paper.event.block;
+
+import com.google.common.base.Preconditions;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.type.Vault;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.block.BlockEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Called when a vault block changes state.
+ */
+@NullMarked
+public class VaultChangeStateEvent extends BlockEvent implements Cancellable {
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final @Nullable Player player;
+ private final Vault.State currentState;
+ private final Vault.State newState;
+ private boolean cancelled = false;
+
+ @ApiStatus.Internal
+ public VaultChangeStateEvent(final Block vaultBlock, final @Nullable Player player, final Vault.State currentState, final Vault.State newState) {
+ super(vaultBlock);
+ this.player = player;
+ this.currentState = currentState;
+ this.newState = newState;
+ }
+
+ /**
+ * Gets the player associated with this state change, if applicable.
+ *
+ * @return The associated player, or {@code null} if not known.
+ */
+ public @Nullable Player getPlayer() {
+ return this.player;
+ }
+
+ /**
+ * Gets the state the vault is currently in.
+ *
+ * @return The current vault state.
+ */
+ public Vault.State getCurrentState() {
+ return currentState;
+ }
+
+ /**
+ * Gets the state the vault is attempting to transition to.
+ *
+ * @return The new vault state.
+ */
+ public Vault.State getNewState() {
+ return newState;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java
index 7dbfff8e0..5f649c86c 100644
--- a/paper-api/src/main/java/org/bukkit/Server.java
+++ b/paper-api/src/main/java/org/bukkit/Server.java
@@ -830,9 +830,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @param id the id of the map to get
* @return a map view if it exists, or null otherwise
*/
- // @Deprecated(since = "1.6.2") // Paper - Not a magic value
@Nullable
- public MapView getMap(int id);
+ MapView getMap(int id);
/**
* Create a new map with an automatically assigned ID.
diff --git a/paper-api/src/main/java/org/bukkit/entity/FishHook.java b/paper-api/src/main/java/org/bukkit/entity/FishHook.java
index 470443e3e..1839195eb 100644
--- a/paper-api/src/main/java/org/bukkit/entity/FishHook.java
+++ b/paper-api/src/main/java/org/bukkit/entity/FishHook.java
@@ -1,5 +1,6 @@
package org.bukkit.entity;
+import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -323,7 +324,6 @@ public interface FishHook extends Projectile {
BOBBING;
}
- // Paper start - More FishHook API
/**
* Get the number of ticks the hook needs to wait for a fish to bite.
*
@@ -367,5 +367,18 @@ public interface FishHook extends Projectile {
* enchantment.
*/
void resetFishingState();
- // Paper end
+
+ /**
+ * Retrieve this fishhook back to the casting player.
+ *
+ * This method will trigger and respect API events, which may be subject to cancellation.
+ * Plugins listening to {@link org.bukkit.event.player.PlayerFishEvent} might for example cancel this action.
+ *
+ * @param slot Slot holding the fishing rod (must be HAND/OFF_HAND)
+ * @return The amount of damage which would be applied to the itemstack
+ * @throws IllegalStateException if the fish hook does not have a player casting it.
+ * @throws IllegalStateException if the player casting it is not holding a
+ * {@link org.bukkit.inventory.ItemType#FISHING_ROD} in the specified equipment slot.
+ */
+ int retrieve(@NotNull EquipmentSlot slot);
}
diff --git a/paper-api/src/main/java/org/bukkit/entity/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java
index 7b9324d12..9ccb7e901 100644
--- a/paper-api/src/main/java/org/bukkit/entity/Player.java
+++ b/paper-api/src/main/java/org/bukkit/entity/Player.java
@@ -2109,6 +2109,8 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*
* @param other The other {@link Player} to list.
* @return True if the {@code other} player was not listed.
+ * @throws IllegalStateException if this player can't see the other player
+ * @see #canSee(Player)
*/
boolean listPlayer(Player other);
// Paper end
diff --git a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java
index 7ef5fbd88..8730daf02 100644
--- a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java
+++ b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryClickEvent.java
@@ -203,10 +203,10 @@ public class InventoryClickEvent extends InventoryInteractEvent {
/**
* If the ClickType is NUMBER_KEY, this method will return the index of
- * the pressed key (0-8).
+ * the pressed key (0-8) and -1 if player swapped with off-hand (or the action is not NUMBER_KEY).
*
- * @return the number on the key minus 1 (range 0-8); or -1 if not
- * a NUMBER_KEY action
+ * @return the number on the key minus 1 (range 0-8);
+ * or -1 if ClickType is NUMBER_KEY and player did an off-hand swap. Is also -1 if ClickType is not NUMBER_KEY
*/
public int getHotbarButton() {
return this.hotbarKey;
diff --git a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch
index 10729a44c..0a8480644 100644
--- a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch
+++ b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch
@@ -28202,10 +28202,10 @@ index b30f56fbc1fd17259a1d05dc9155fffcab292ca1..11fed81a4696ba18440e755c3b8a5ca3
this.generatingStep = generatingStep;
this.cache = cache;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
-index 73717609fccd9af12e2cc39824106f49426b581c..72524ff3399a4477dfa3db2f4e79bb14f6519a8b 100644
+index 5ec9e3b37e575e9805bf9f0ce5cae5c1284461d8..78201407a37eced73998b97d5d5c412eaba69af1 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
-@@ -1321,7 +1321,7 @@ public abstract class PlayerList {
+@@ -1320,7 +1320,7 @@ public abstract class PlayerList {
public void setViewDistance(int viewDistance) {
this.viewDistance = viewDistance;
@@ -28214,7 +28214,7 @@ index 73717609fccd9af12e2cc39824106f49426b581c..72524ff3399a4477dfa3db2f4e79bb14
for (ServerLevel serverLevel : this.server.getAllLevels()) {
if (serverLevel != null) {
-@@ -1332,7 +1332,7 @@ public abstract class PlayerList {
+@@ -1331,7 +1331,7 @@ public abstract class PlayerList {
public void setSimulationDistance(int simulationDistance) {
this.simulationDistance = simulationDistance;
diff --git a/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch b/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch
index 4cdfa5a7a..ba97d07c8 100644
--- a/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch
+++ b/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Incremental chunk and player saving
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
-index 00f7f4356d6bffdd31f58b9d798c755edd9cd3ff..ea85cac4a41075efe8525c40755e7ebac6ca9dea 100644
+index 094ef7f54ad71795a2d8c2a8d03a32bef6ff2164..79bc1b7d9f640d2322814177eb3e921da8671e87 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -952,7 +952,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop {
++ server.executeBlocking(() -> { // Paper - Broadcast chat session update sync
+ this.player.setChatSession(chatSession);
this.server
.getPlayerList()
.broadcastAll(
- new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT), List.of(this.player))
+ new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT), List.of(this.player)), this.player // Paper - Use single player info update packet on join
);
++ });
}
);
-@@ -2013,11 +_,13 @@
+ }
@Override
public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch
index 114e7eac7..04c85d3a4 100644
--- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch
+++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch
@@ -756,7 +756,7 @@
return serverPlayer;
}
-@@ -488,24 +_,60 @@
+@@ -488,24 +_,59 @@
}
public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl connection) {
@@ -800,12 +800,11 @@
+ // CraftBukkit start - add a world/entity limited version
+ public void broadcastAll(Packet packet, net.minecraft.world.entity.player.Player entityhuman) {
-+ for (int i = 0; i < this.players.size(); ++i) {
-+ ServerPlayer entityplayer = this.players.get(i);
++ for (ServerPlayer entityplayer : this.players) { // Paper - replace for i with for each for thread safety
+ if (entityhuman != null && !entityplayer.getBukkitEntity().canSee(entityhuman.getBukkitEntity())) {
+ continue;
+ }
-+ ((ServerPlayer) this.players.get(i)).connection.send(packet);
++ ((ServerPlayer) entityplayer).connection.send(packet); // Paper - replace for i with for each for thread safety
+ }
+ }
+
diff --git a/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch b/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch
new file mode 100644
index 000000000..5471cd718
--- /dev/null
+++ b/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch
@@ -0,0 +1,40 @@
+--- a/net/minecraft/world/InteractionResult.java
++++ b/net/minecraft/world/InteractionResult.java
+@@ -30,18 +_,34 @@
+ public record Pass() implements InteractionResult {
+ }
+
+- public record Success(InteractionResult.SwingSource swingSource, InteractionResult.ItemContext itemContext) implements InteractionResult {
++ // Paper start - track more context in interaction result
++ public record PaperSuccessContext(net.minecraft.core.@org.jspecify.annotations.Nullable BlockPos placedBlockPosition) {
++ static PaperSuccessContext DEFAULT = new PaperSuccessContext(null);
++
++ public PaperSuccessContext placedBlockAt(final net.minecraft.core.BlockPos blockPos) {
++ return new PaperSuccessContext(blockPos);
++ }
++ }
++ public record Success(InteractionResult.SwingSource swingSource, InteractionResult.ItemContext itemContext, PaperSuccessContext paperSuccessContext) implements InteractionResult {
++ public InteractionResult.Success configurePaper(final java.util.function.UnaryOperator edit) {
++ return new InteractionResult.Success(this.swingSource, this.itemContext, edit.apply(this.paperSuccessContext));
++ }
++
++ public Success(final net.minecraft.world.InteractionResult.SwingSource swingSource, final net.minecraft.world.InteractionResult.ItemContext itemContext) {
++ this(swingSource, itemContext, PaperSuccessContext.DEFAULT);
++ }
++ // Paper end - track more context in interaction result
+ @Override
+ public boolean consumesAction() {
+ return true;
+ }
+
+ public InteractionResult.Success heldItemTransformedTo(ItemStack stack) {
+- return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, stack));
++ return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, stack), this.paperSuccessContext); // Paper - track more context in interaction result
+ }
+
+ public InteractionResult.Success withoutItem() {
+- return new InteractionResult.Success(this.swingSource, InteractionResult.ItemContext.NONE);
++ return new InteractionResult.Success(this.swingSource, InteractionResult.ItemContext.NONE, this.paperSuccessContext); // Paper - track more context in interaction result
+ }
+
+ public boolean wasItemInteraction() {
diff --git a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch
index bbcf05ba0..a529dd9ad 100644
--- a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch
@@ -56,6 +56,15 @@
level.playSound(
player,
clickedPos,
+@@ -88,7 +_,7 @@
+ );
+ level.gameEvent(GameEvent.BLOCK_PLACE, clickedPos, GameEvent.Context.of(player, blockState));
+ itemInHand.consume(1, player);
+- return InteractionResult.SUCCESS;
++ return InteractionResult.SUCCESS.configurePaper(e -> e.placedBlockAt(clickedPos.immutable())); // Paper - track placed block position from block item
+ }
+ }
+ }
@@ -137,8 +_,19 @@
protected boolean canPlace(BlockPlaceContext context, BlockState state) {
diff --git a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch
index 6d3893213..443385a23 100644
--- a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch
@@ -23,7 +23,7 @@
}
}
};
-@@ -373,10 +_,167 @@
+@@ -373,10 +_,166 @@
return InteractionResult.PASS;
} else {
Item item = this.getItem();
@@ -175,10 +175,9 @@
+ }
+
+ // SPIGOT-1288 - play sound stripped from BlockItem
-+ if (this.item instanceof BlockItem) {
++ if (this.item instanceof BlockItem && success.paperSuccessContext().placedBlockPosition() != null) {
+ // Paper start - Fix spigot sound playing for BlockItem ItemStacks
-+ BlockPos pos = new net.minecraft.world.item.context.BlockPlaceContext(context).getClickedPos();
-+ net.minecraft.world.level.block.state.BlockState state = serverLevel.getBlockState(pos);
++ net.minecraft.world.level.block.state.BlockState state = serverLevel.getBlockState(success.paperSuccessContext().placedBlockPosition());
+ net.minecraft.world.level.block.SoundType soundType = state.getSoundType();
+ // Paper end - Fix spigot sound playing for BlockItem ItemStacks
+ serverLevel.playSound(player, clickedPos, soundType.getPlaceSound(), net.minecraft.sounds.SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F);
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch
index a84630639..024d2b4bd 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch
@@ -1,17 +1,52 @@
--- a/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java
-@@ -260,6 +_,11 @@
+@@ -260,7 +_,12 @@
if (!list.isEmpty()) {
player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
stack.consume(config.keyItem().getCount(), player);
+- unlock(level, state, pos, config, serverData, sharedData, list);
+ // CraftBukkit start
+ org.bukkit.event.block.BlockDispenseLootEvent vaultDispenseLootEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDispenseLootEvent(level, pos, player, list);
+ if (vaultDispenseLootEvent.isCancelled()) return;
+ list = vaultDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList();
+ // CraftBukkit end
- unlock(level, state, pos, config, serverData, sharedData, list);
++ unlock(level, state, pos, config, serverData, sharedData, list, player); // Paper - Vault API
serverData.addToRewardedPlayers(player);
sharedData.updateConnectedPlayersWithinRange(level, pos, serverData, config, config.deactivationRange());
+ }
+@@ -269,8 +_,30 @@
+ }
+
+ static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData) {
+- VaultState vaultState = oldState.getValue(VaultBlock.STATE);
+- VaultState vaultState1 = newState.getValue(VaultBlock.STATE);
++ // Paper start - Vault API
++ setVaultState(level, pos, oldState, newState, config,sharedData, null);
++ }
++
++ static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData, @Nullable Player associatedPlayer) {
++ VaultState vaultState = oldState.getValue(VaultBlock.STATE); final VaultState oldVaultState = vaultState;
++ VaultState vaultState1 = newState.getValue(VaultBlock.STATE); final VaultState newVaultState = vaultState1;
++ org.bukkit.entity.Player apiAssociatedPlayer = null;
++ if (associatedPlayer != null) {
++ apiAssociatedPlayer = (org.bukkit.entity.Player) associatedPlayer.getBukkitEntity();
++ } else if (newVaultState == VaultState.ACTIVE) {
++ final Set connectedPlayers = sharedData.getConnectedPlayers();
++ if (!connectedPlayers.isEmpty()) { // Used over sharedData#hasConnectedPlayers to ensure belows iterator#next is always okay.
++ apiAssociatedPlayer = level.getCraftServer().getPlayer(connectedPlayers.iterator().next());
++ }
++ }
++ final io.papermc.paper.event.block.VaultChangeStateEvent event = new io.papermc.paper.event.block.VaultChangeStateEvent(
++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos),
++ apiAssociatedPlayer,
++ org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(oldVaultState, org.bukkit.block.data.type.Vault.State.class),
++ org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(newVaultState, org.bukkit.block.data.type.Vault.State.class)
++ );
++ if (!event.callEvent()) return;
++ // Paper end - Vault API
+ level.setBlock(pos, newState, 3);
+ vaultState.onTransition(level, pos, vaultState1, config, sharedData, newState.getValue(VaultBlock.OMINOUS));
+ }
@@ -282,6 +_,11 @@
ItemStack randomDisplayItemFromLootTable = getRandomDisplayItemFromLootTable(
level, pos, config.overrideLootTableToDisplay().orElse(config.lootTable())
@@ -24,3 +59,29 @@
sharedData.setDisplayItem(randomDisplayItemFromLootTable);
}
}
+@@ -304,10 +_,24 @@
+ VaultSharedData sharedData,
+ List itemsToEject
+ ) {
++ // Paper start - Vault API
++ unlock(level, state, pos, config, serverData, sharedData, itemsToEject, null);
++ }
++ private static void unlock(
++ ServerLevel level,
++ BlockState state,
++ BlockPos pos,
++ VaultConfig config,
++ VaultServerData serverData,
++ VaultSharedData sharedData,
++ List itemsToEject,
++ final @Nullable Player associatedPlayer
++ ) {
++ // Paper end - Vault API
+ serverData.setItemsToEject(itemsToEject);
+ sharedData.setDisplayItem(serverData.getNextItemToEject());
+ serverData.pauseStateUpdatingUntil(level.getGameTime() + 14L);
+- setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData);
++ setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData, associatedPlayer); // Paper - Vault API
+ }
+
+ private static List resolveItemsToEject(ServerLevel level, VaultConfig config, BlockPos pos, Player player, ItemStack key) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 5fb7b3a76..6ce00157d 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -40,6 +40,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
+import net.minecraft.Optionull;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
@@ -1943,12 +1944,13 @@ public final class CraftServer implements Server {
}
@Override
- @Deprecated
public CraftMapView getMap(int id) {
- MapItemSavedData mapData = this.console.getLevel(net.minecraft.world.level.Level.OVERWORLD).getMapData(new MapId(id));
- if (mapData == null) {
- return null;
- }
+ final net.minecraft.world.level.Level overworld = this.console.overworld();
+ if (overworld == null) return null;
+
+ final MapItemSavedData mapData = overworld.getMapData(new MapId(id));
+ if (mapData == null) return null;
+
return mapData.mapView;
}
@@ -2261,7 +2263,11 @@ public final class CraftServer implements Server {
@Override
public GameMode getDefaultGameMode() {
- return GameMode.getByValue(this.console.getLevel(net.minecraft.world.level.Level.OVERWORLD).serverLevelData.getGameType().getId());
+ return GameMode.getByValue(Optionull.mapOrDefault(
+ this.console.getLevel(net.minecraft.world.level.Level.OVERWORLD),
+ l -> l.serverLevelData.getGameType(),
+ this.console.getProperties().gamemode
+ ).getId());
}
@Override
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
index 328773b37..34a2e07da 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
@@ -165,7 +165,7 @@ public class CraftBlockData implements BlockData {
* @throws IllegalStateException if the Enum could not be converted
*/
@SuppressWarnings("unchecked")
- private static > B toBukkit(Enum> nms, Class bukkit) {
+ public static > B toBukkit(Enum> nms, Class bukkit) {
if (nms instanceof Direction) {
return (B) CraftBlock.notchToBlockFace((Direction) nms);
}
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
index 94f07ebc2..5f7225f18 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
@@ -2,10 +2,16 @@ package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions;
import net.minecraft.core.BlockPos;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FishingHook;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.FishHook;
+import org.bukkit.inventory.EquipmentSlot;
public class CraftFishHook extends CraftProjectile implements FishHook {
private double biteChance = -1;
@@ -233,4 +239,18 @@ public class CraftFishHook extends CraftProjectile implements FishHook {
hook.resetTimeUntilLured();
hook.timeUntilHooked = 0; // Reset time until hooked, will be repopulated once lured time is ticked down.
}
+
+ @Override
+ public int retrieve(EquipmentSlot slot) {
+ Preconditions.checkArgument(slot == EquipmentSlot.HAND || slot == EquipmentSlot.OFF_HAND, "Equipment slot must be HAND or OFF_HAND");
+ final FishingHook fishingHook = getHandle();
+ final Player playerOwner = fishingHook.getPlayerOwner();
+ Preconditions.checkState(playerOwner != null, "Player owner cannot be null");
+
+ final InteractionHand hand = CraftEquipmentSlot.getHand(slot);
+ final ItemStack itemInHand = playerOwner.getItemInHand(hand);
+ Preconditions.checkState(itemInHand.is(Items.FISHING_ROD), "Item in slot is not a FISHING_ROD");
+
+ return fishingHook.retrieve(itemInHand, hand);
+ }
}
diff --git a/paper-server/src/main/resources/configurations/help.yml b/paper-server/src/main/resources/configurations/help.yml
index 2cd6b353e..2a41f56a8 100644
--- a/paper-server/src/main/resources/configurations/help.yml
+++ b/paper-server/src/main/resources/configurations/help.yml
@@ -57,6 +57,7 @@
# If you need help with the configuration or have any questions related to Paper,
# join us in our Discord or check the docs page.
#
+# File Reference: https://docs.papermc.io/paper/reference/bukkit-help-configuration/
# Docs: https://docs.papermc.io/
# Discord: https://discord.gg/papermc
# Website: https://papermc.io/