Add new Lidded API

This commit is contained in:
Isaac - The456 2024-12-25 01:03:12 +00:00
parent d568e175ee
commit 0450bda4fe
No known key found for this signature in database
GPG key ID: 57A848D2F87C14EF
15 changed files with 641 additions and 163 deletions

View file

@ -0,0 +1,50 @@
package io.papermc.paper.block;
import org.jspecify.annotations.NullMarked;
/**
* Represents how the lid of a block behaves.
*/
@NullMarked
public enum LidMode {
/**
* The default lid mode, the lid will open and close based on player interaction.
* <p>
* the state used for this is provided with {@link Lidded#getTrueLidState()}
*/
DEFAULT,
/**
* The lid will be forced open, regardless of player interaction.
* <p>
* This needs to be manually unset with another call to {@link Lidded#setLidMode(LidMode)}.
*/
FORCED_OPEN,
/**
* The lid will be forced closed, regardless of player interaction.
* <p>
* This needs to be manually unset with another call to {@link Lidded#setLidMode(LidMode)}.
*/
FORCED_CLOSED,
/**
* The lid will be forced open until at least one player has opened it.
* <p>
* It will then revert to {@link #DEFAULT}.
* <p>
* If at least one player is viewing it when this is set, it will immediately revert to
* {@link #DEFAULT}.
*/
OPEN_UNTIL_VIEWED,
/**
* The lid will be forced closed until all players currently viewing it have closed it.
* <p>
* It will then revert to {@link #DEFAULT}.
* <p>
* If no players are viewing it when this is set, it will immediately revert to
* {@link #DEFAULT}.
*/
CLOSED_UNTIL_NOT_VIEWED
}

View file

@ -0,0 +1,9 @@
package io.papermc.paper.block;
import org.jspecify.annotations.NullMarked;
@NullMarked
public enum LidState {
OPEN,
CLOSED
}

View file

@ -0,0 +1,37 @@
package io.papermc.paper.block;
import org.bukkit.block.TileState;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface Lidded extends TileState {
/**
* Gets the current state of the block, respecting the lidded mode.
*
* @return the effective lid state
*/
LidState getEffectiveLidState();
/**
* Gets how the lid would be without any lidded mode, based on players interacting with the block.
* @return the true lid state
*/
LidState getTrueLidState();
/**
* Gets the current lid mode of the block.
*
* @return the lid mode
*/
LidMode getLidMode();
/**
* Sets the lid mode of the block.
*
* @param mode the new lid mode
* @return the actually set lid mode
*/
LidMode setLidMode(LidMode mode);
}

View file

@ -5,4 +5,4 @@ import org.bukkit.loot.Lootable;
/** /**
* Represents a captured state of a Barrel. * Represents a captured state of a Barrel.
*/ */
public interface Barrel extends Container, com.destroystokyo.paper.loottable.LootableBlockInventory, Lidded { } // Paper public interface Barrel extends Container, com.destroystokyo.paper.loottable.LootableBlockInventory, Lidded, io.papermc.paper.block.Lidded { } // Paper

View file

@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* Represents a captured state of a chest. * Represents a captured state of a chest.
*/ */
public interface Chest extends Container, LootableBlockInventory, Lidded { // Paper public interface Chest extends Container, LootableBlockInventory, Lidded, io.papermc.paper.block.Lidded { // Paper
/** /**
* Gets the inventory of the chest block represented by this block state. * Gets the inventory of the chest block represented by this block state.

View file

@ -3,7 +3,7 @@ package org.bukkit.block;
/** /**
* Represents a captured state of an ender chest. * Represents a captured state of an ender chest.
*/ */
public interface EnderChest extends Lidded, TileState { public interface EnderChest extends Lidded, TileState, io.papermc.paper.block.Lidded {
// Paper start - More Chest Block API // Paper start - More Chest Block API
/** /**
* Checks whether this ender chest is blocked by a block above * Checks whether this ender chest is blocked by a block above

View file

@ -1,25 +1,34 @@
package org.bukkit.block; package org.bukkit.block;
/**
* @deprecated Incomplete api. Use {@link io.papermc.paper.block.Lidded} instead.
*/
@Deprecated // Paper - Deprecate Bukkit's Lidded API
public interface Lidded { public interface Lidded {
/** /**
* Sets the block's animated state to open and prevents it from being closed * Sets the block's animated state to open and prevents it from being closed
* until {@link #close()} is called. * until {@link #close()} is called.
* @deprecated Use {@link io.papermc.paper.block.Lidded#setLidMode(io.papermc.paper.block.LidMode)}
*/ */
@Deprecated
void open(); void open();
/** /**
* Sets the block's animated state to closed even if a player is currently * Unsets a corresponding call to {@link #open()}.
* viewing this block. * @deprecated Misleading name. Use {@link io.papermc.paper.block.Lidded#setLidMode(io.papermc.paper.block.LidMode)}
*/ */
@Deprecated
void close(); void close();
// Paper start - More Lidded Block API // Paper start - More Lidded Block API
/** /**
* Checks if the block's animation state. * Checks is the Lid is currently forced open.
* *
* @return true if the block's animation state is set to open. * @return true if the block's animation state is force open.
* @deprecated Misleading name. Use {@link io.papermc.paper.block.Lidded#getLidMode()} for the direct replacement, or {@link io.papermc.paper.block.Lidded#getEffectiveLidState()} to tell if the lid is visibly open to the player instead.
*/ */
@Deprecated
boolean isOpen(); boolean isOpen();
// Paper end - More Lidded Block API // Paper end - More Lidded Block API
} }

View file

@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
/** /**
* Represents a captured state of a ShulkerBox. * Represents a captured state of a ShulkerBox.
*/ */
public interface ShulkerBox extends Container, LootableBlockInventory, Lidded { // Paper public interface ShulkerBox extends Container, LootableBlockInventory, Lidded, io.papermc.paper.block.Lidded { // Paper
/** /**
* Get the {@link DyeColor} corresponding to this ShulkerBox * Get the {@link DyeColor} corresponding to this ShulkerBox

View file

@ -4,33 +4,92 @@
private static final int CHECK_TICK_DELAY = 5; private static final int CHECK_TICK_DELAY = 5;
private int openCount; private int openCount;
private double maxInteractionRange; private double maxInteractionRange;
+ public boolean opened; // CraftBukkit + // public boolean opened; // CraftBukkit // Paper - Replace with new Lidded API
protected abstract void onOpen(Level level, BlockPos pos, BlockState state); protected abstract void onOpen(Level level, BlockPos pos, BlockState state);
@@ -20,10 +_,36 @@ @@ -20,10 +_,94 @@
protected abstract void openerCountChanged(Level level, BlockPos pos, BlockState state, int count, int openCount); protected abstract void openerCountChanged(Level level, BlockPos pos, BlockState state, int count, int openCount);
+ // CraftBukkit start + // CraftBukkit start
+ public void onAPIOpen(Level level, BlockPos blockPos, BlockState blockState) { + // Paper start - Replace with new Lidded API
+ this.onOpen(level, blockPos, blockState); + // public void onAPIOpen(Level level, BlockPos blockPos, BlockState blockState) {
+ } + // this.onOpen(level, blockPos, blockState);
+ + // }
+ public void onAPIClose(Level level, BlockPos blockPos, BlockState blockState) { + //
+ this.onClose(level, blockPos, blockState); + // public void onAPIClose(Level level, BlockPos blockPos, BlockState blockState) {
+ } + // this.onClose(level, blockPos, blockState);
+ + // }
+ public void openerAPICountChanged(Level level, BlockPos blockPos, BlockState blockState, int count, int openCount) { + //
+ this.openerCountChanged(level, blockPos, blockState, count, openCount); + // public void openerAPICountChanged(Level level, BlockPos blockPos, BlockState blockState, int count, int openCount) {
+ } + // this.openerCountChanged(level, blockPos, blockState, count, openCount);
+ // }
+ // Paper end - Replace with new Lidded API
+ // CraftBukkit end + // CraftBukkit end
+ +
protected abstract boolean isOwnContainer(Player player); protected abstract boolean isOwnContainer(Player player);
public void incrementOpeners(Player player, Level level, BlockPos pos, BlockState state) { - public void incrementOpeners(Player player, Level level, BlockPos pos, BlockState state) {
+ // Paper start - add Improved Lidded API
+ private io.papermc.paper.block.LidMode apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ public void startForceLiddedLidOpen(Level level, BlockPos pos, BlockState state) {
+ incrementOpeners(null, level, pos, state);
+ }
+ public void stopForceLiddedLidOpen(Level level, BlockPos pos, BlockState state) {
+ decrementOpeners(null, level, pos, state);
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ }
+ public void startForceLiddedLidClose(Level level, BlockPos pos, BlockState state) {
+ if (this.getTrueLidState() == io.papermc.paper.block.LidState.OPEN) {
+ this.onClose(level, pos, state);
+ level.gameEvent(null, GameEvent.CONTAINER_CLOSE, pos);
+ }
+ this.openerCountChanged(level, pos, state, this.openCount, 0);
+ }
+ public void stopForceLiddedLidClose(Level level, BlockPos pos, BlockState state) {
+ if (this.getTrueLidState() == io.papermc.paper.block.LidState.OPEN) {
+ this.onOpen(level, pos, state);
+ level.gameEvent(null, GameEvent.CONTAINER_OPEN, pos);
+ scheduleRecheck(level, pos, state);
+ }
+ this.openerCountChanged(level, pos, state, 0, this.openCount);
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ }
+ public io.papermc.paper.block.LidMode getLidMode() {
+ return apiLidMode;
+ }
+ public void setLidMode(final io.papermc.paper.block.LidMode targetLidMode) {
+ apiLidMode = targetLidMode;
+ }
+ public io.papermc.paper.block.LidState getEffectiveLidState() {
+ return switch (apiLidMode) {
+ case OPEN_UNTIL_VIEWED, FORCED_OPEN -> io.papermc.paper.block.LidState.OPEN;
+ case CLOSED_UNTIL_NOT_VIEWED, FORCED_CLOSED -> io.papermc.paper.block.LidState.CLOSED;
+ default -> getTrueLidState();
+ };
+ }
+ public io.papermc.paper.block.LidState getTrueLidState() {
+ boolean virtualViewerPresent = (apiLidMode == io.papermc.paper.block.LidMode.FORCED_OPEN || apiLidMode == io.papermc.paper.block.LidMode.OPEN_UNTIL_VIEWED);
+ int trueOpenCount = this.openCount - (virtualViewerPresent ? 1 : 0);
+ if (trueOpenCount < 0) {
+ throw new IllegalStateException("trueOpenCount is negative: " + trueOpenCount + " openCount: " + openCount + " virtualViewerPresent: " + virtualViewerPresent);
+ }
+ return trueOpenCount > 0 ? io.papermc.paper.block.LidState.OPEN : io.papermc.paper.block.LidState.CLOSED;
+ }
+ // Paper end - add Improved Lidded API
+
+ public void incrementOpeners(@javax.annotation.Nullable Player player, Level level, BlockPos pos, BlockState state) { // Paper - make player nullable for New Lidded API
+ // Paper start - add Improved Lidded API
+ if (this.openCount == 0 && apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ stopForceLiddedLidClose(level, pos, state);
+ }
+ // Paper end - add Improved Lidded API
+
+ int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added
int i = this.openCount++; int i = this.openCount++;
+ if (apiLidMode == io.papermc.paper.block.LidMode.FORCED_CLOSED || apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) return; // Paper - add improved Lidded API
+ +
+ // CraftBukkit start - Call redstone event + // CraftBukkit start - Call redstone event
+ if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { + if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) {
@ -45,14 +104,38 @@
if (i == 0) { if (i == 0) {
this.onOpen(level, pos, state); this.onOpen(level, pos, state);
level.gameEvent(player, GameEvent.CONTAINER_OPEN, pos); level.gameEvent(player, GameEvent.CONTAINER_OPEN, pos);
@@ -35,7 +_,20 @@ @@ -31,11 +_,43 @@
}
this.openerCountChanged(level, pos, state, i, this.openCount);
+ if (player != null) // Paper - make player nullable for improved Lidded API
this.maxInteractionRange = Math.max(player.blockInteractionRange(), this.maxInteractionRange);
+
+ // Paper start - add Improved Lidded API
+ if (player != null && apiLidMode == io.papermc.paper.block.LidMode.OPEN_UNTIL_VIEWED) {
+ // reset to default
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ stopForceLiddedLidOpen(level, pos, state);
+ }
+ // Paper end - add Improved Lidded API
} }
public void decrementOpeners(Player player, Level level, BlockPos pos, BlockState state) { - public void decrementOpeners(Player player, Level level, BlockPos pos, BlockState state) {
+ public void decrementOpeners(@javax.annotation.Nullable Player player, Level level, BlockPos pos, BlockState state) { // Paper - make player nullable for New Lidded API
+ int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added
+ if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative + if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative
int i = this.openCount--; int i = this.openCount--;
+ +
+ // Paper start - add Improved Lidded API
+ if (apiLidMode == io.papermc.paper.block.LidMode.FORCED_CLOSED || apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ if (this.openCount == 0 && apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ stopForceLiddedLidClose(level, pos, state);
+ }
+ return;
+ }
+ // Paper end - add Improved Lidded API
+
+ // CraftBukkit start - Call redstone event + // CraftBukkit start - Call redstone event
+ if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { + if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) {
+ int newPower = Math.max(0, Math.min(15, this.openCount)); + int newPower = Math.max(0, Math.min(15, this.openCount));
@ -66,11 +149,20 @@
if (this.openCount == 0) { if (this.openCount == 0) {
this.onClose(level, pos, state); this.onClose(level, pos, state);
level.gameEvent(player, GameEvent.CONTAINER_CLOSE, pos); level.gameEvent(player, GameEvent.CONTAINER_CLOSE, pos);
@@ -60,6 +_,7 @@ @@ -59,8 +_,14 @@
this.maxInteractionRange = Math.max(player.blockInteractionRange(), this.maxInteractionRange);
} }
int size = playersWithContainerOpen.size(); - int size = playersWithContainerOpen.size();
+ if (this.opened) size++; // CraftBukkit - add dummy count from API - int i = this.openCount;
int i = this.openCount; + // Paper Start - Replace with add Improved Lidded API
+ boolean forceClosed = apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED || apiLidMode == io.papermc.paper.block.LidMode.FORCED_CLOSED;
+ boolean forceOpened = apiLidMode == io.papermc.paper.block.LidMode.OPEN_UNTIL_VIEWED || apiLidMode == io.papermc.paper.block.LidMode.FORCED_OPEN;
+ int size = forceClosed ? 0 : playersWithContainerOpen.size() + (forceOpened ? 1 : 0);
+ // if (this.opened) size++; // CraftBukkit - add dummy count from API
+ int i = forceClosed ? 0 : this.openCount;
+ // Paper End - Replace with add Improved Lidded API
+
if (i != size) { if (i != size) {
boolean flag = size != 0; boolean flag = size != 0;
boolean flag1 = i != 0;

View file

@ -7,7 +7,7 @@
+ // CraftBukkit start - add fields and methods + // CraftBukkit start - add fields and methods
+ public List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>(); + public List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK; + private int maxStack = MAX_STACK;
+ public boolean opened; + // public boolean opened; // Paper - replace with new Lidded API
+ +
+ public List<ItemStack> getContents() { + public List<ItemStack> getContents() {
+ return this.itemStacks; + return this.itemStacks;
@ -38,19 +38,140 @@
public ShulkerBoxBlockEntity(@Nullable DyeColor color, BlockPos pos, BlockState blockState) { public ShulkerBoxBlockEntity(@Nullable DyeColor color, BlockPos pos, BlockState blockState) {
super(BlockEntityType.SHULKER_BOX, pos, blockState); super(BlockEntityType.SHULKER_BOX, pos, blockState);
this.color = color; this.color = color;
@@ -167,6 +_,7 @@ @@ -139,6 +_,7 @@
@Override
public boolean triggerEvent(int id, int type) {
if (id == 1) {
+ if (apiLidMode != io.papermc.paper.block.LidMode.FORCED_CLOSED && apiLidMode != io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) this.openCount = type; // Paper - Skip mutate when forced closed by lidded api
this.openCount = type;
if (type == 0) {
this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSING;
@@ -159,6 +_,71 @@
level.updateNeighborsAt(pos, state.getBlock());
}
+ // Paper start - add Improved Lidded API
+ private io.papermc.paper.block.LidMode apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+
+ public void startForceLiddedLidOpen() {
+ this.openCount++;
+ this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
+ if (this.openCount == 1) {
+ this.level.gameEvent(null, GameEvent.CONTAINER_OPEN, this.worldPosition);
+ this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F);
+ }
+ }
+
+ public void stopForceLiddedLidOpen() {
+ this.openCount--;
+ this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
+ if (this.openCount <= 0) {
+ this.level.gameEvent(null, GameEvent.CONTAINER_CLOSE, this.worldPosition);
+ this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F);
+ }
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ }
+
+ public void startForceLiddedLidClose() {
+ if (this.getTrueLidState() == io.papermc.paper.block.LidState.OPEN) {
+ this.level.gameEvent(null, GameEvent.CONTAINER_CLOSE, this.worldPosition);
+ this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F);
+ }
+ this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, 0);
+ }
+
+ public void stopForceLiddedLidClose() {
+ if (this.getTrueLidState() == io.papermc.paper.block.LidState.OPEN) {
+ this.level.gameEvent(null, GameEvent.CONTAINER_OPEN, this.worldPosition);
+ this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F);
+ }
+ this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
+ }
+
+ public io.papermc.paper.block.LidMode getLidMode() {
+ return apiLidMode;
+ }
+
+ public void setLidMode(final io.papermc.paper.block.LidMode lidMode) {
+ this.apiLidMode = lidMode;
+ }
+
+ public io.papermc.paper.block.LidState getEffectiveLidState() {
+ return switch (apiLidMode) {
+ case OPEN_UNTIL_VIEWED, FORCED_OPEN -> io.papermc.paper.block.LidState.OPEN;
+ case CLOSED_UNTIL_NOT_VIEWED, FORCED_CLOSED -> io.papermc.paper.block.LidState.CLOSED;
+ default -> getTrueLidState();
+ };
+ }
+
+ public io.papermc.paper.block.LidState getTrueLidState() {
+ boolean virtualViewerPresent = (apiLidMode == io.papermc.paper.block.LidMode.FORCED_OPEN || apiLidMode == io.papermc.paper.block.LidMode.OPEN_UNTIL_VIEWED);
+ int trueOpenCount = this.openCount - (virtualViewerPresent ? 1 : 0);
+ // ensure trueOpenCount is never negative, throw
+ if (trueOpenCount < 0) {
+ throw new IllegalStateException("trueOpenCount is negative: " + trueOpenCount + " openCount: " + openCount + " virtualViewerPresent: " + virtualViewerPresent);
+ }
+ return trueOpenCount > 0 ? io.papermc.paper.block.LidState.OPEN : io.papermc.paper.block.LidState.CLOSED;
+ }
+ // Paper end - add Improved Lidded API
+
@Override
public void startOpen(Player player) {
if (!this.remove && !player.isSpectator()) {
@@ -166,13 +_,36 @@
this.openCount = 0;
} }
+ // Paper start - add Improved Lidded API
+ if (this.openCount == 0) {
+ if (apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ stopForceLiddedLidClose();
+ }
+ }
+ // Paper end - add Improved Lidded API
+
this.openCount++; this.openCount++;
+ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call +
+ // Paper start - replace with Improved Lidded API
+ // if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call
+ if (this.apiLidMode == io.papermc.paper.block.LidMode.FORCED_CLOSED || this.apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) return;
+ // Paper end - replace with Improved Lidded API
+
this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
if (this.openCount == 1) { if (this.openCount == 1) {
this.level.gameEvent(player, GameEvent.CONTAINER_OPEN, this.worldPosition); this.level.gameEvent(player, GameEvent.CONTAINER_OPEN, this.worldPosition);
@@ -180,6 +_,7 @@ this.level
.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F);
}
+
+ // Paper start - add Improved Lidded API
+ if (apiLidMode == io.papermc.paper.block.LidMode.OPEN_UNTIL_VIEWED) {
+ // reset to default
+ apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ stopForceLiddedLidOpen();
+ }
+ // Paper end - add Improved Lidded API
}
}
@@ -180,6 +_,19 @@
public void stopOpen(Player player) { public void stopOpen(Player player) {
if (!this.remove && !player.isSpectator()) { if (!this.remove && !player.isSpectator()) {
this.openCount--; this.openCount--;
+ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. +
+ // Paper start - add Improved Lidded API
+ // if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call.
+ if (this.apiLidMode == io.papermc.paper.block.LidMode.FORCED_CLOSED || this.apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ if (this.openCount <= 0 && this.apiLidMode == io.papermc.paper.block.LidMode.CLOSED_UNTIL_NOT_VIEWED) {
+ this.openCount = 0;
+ this.apiLidMode = io.papermc.paper.block.LidMode.DEFAULT;
+ this.stopForceLiddedLidClose();
+ }
+ return;
+ }
+ // Paper end - add Improved Lidded API
+
this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
if (this.openCount <= 0) { if (this.openCount <= 0) {
this.level.gameEvent(player, GameEvent.CONTAINER_CLOSE, this.worldPosition); this.level.gameEvent(player, GameEvent.CONTAINER_CLOSE, this.worldPosition);

View file

@ -0,0 +1,87 @@
package io.papermc.paper.block;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface PaperLidded extends Lidded, org.bukkit.block.Lidded {
@Override
default LidMode setLidMode(final LidMode targetLidMode) {
final LidMode oldLidMode = getLidMode();
final LidMode newLidMode = getResultantLidMode(targetLidMode);
if (oldLidMode == newLidMode) {
// already in correct state
return newLidMode;
}
boolean wasForcedOpen =
oldLidMode == LidMode.FORCED_OPEN || oldLidMode == LidMode.OPEN_UNTIL_VIEWED;
boolean wasForcedClosed =
oldLidMode == LidMode.FORCED_CLOSED || oldLidMode == LidMode.CLOSED_UNTIL_NOT_VIEWED;
boolean isForcedOpen =
newLidMode == LidMode.FORCED_OPEN || newLidMode == LidMode.OPEN_UNTIL_VIEWED;
boolean isForcedClosed =
newLidMode == LidMode.FORCED_CLOSED || newLidMode == LidMode.CLOSED_UNTIL_NOT_VIEWED;
// stop any existing force open/close, if next state doesn't need it.
if (wasForcedOpen && !isForcedOpen) {
stopForceLiddedLidOpen();
} else if (wasForcedClosed && !isForcedClosed) {
stopForceLiddedLidClose();
}
// start new force open/close, if it wasn't previously.
if (isForcedOpen && !wasForcedOpen) {
startForceLiddedLidOpen();
} else if (isForcedClosed && !wasForcedClosed) {
startForceLiddedLidClose();
}
// return the new lid mode, so it can be stored by the implementation.
return newLidMode;
}
private LidMode getResultantLidMode(LidMode targetLidMode) {
final LidState trueLidState = getTrueLidState();
// check that target lid mode is valid for true lid state.
LidMode newLidMode;
if (targetLidMode == LidMode.CLOSED_UNTIL_NOT_VIEWED
&& trueLidState == LidState.CLOSED) {
// insta-revert to default, as the lid is already closed.
newLidMode = LidMode.DEFAULT;
} else if (targetLidMode == LidMode.OPEN_UNTIL_VIEWED
&& trueLidState == LidState.OPEN) {
// insta-revert to default, as the lid is already open.
newLidMode = LidMode.DEFAULT;
} else {
newLidMode = targetLidMode;
}
return newLidMode;
}
// these should be similar to the vanilla open/close behavior.
void startForceLiddedLidOpen();
void stopForceLiddedLidOpen();
void startForceLiddedLidClose();
void stopForceLiddedLidClose();
// bukkit lidded impl using the paper lidded api.
@Override
default boolean isOpen() {
return getLidMode() == LidMode.FORCED_OPEN || getLidMode() == LidMode.OPEN_UNTIL_VIEWED;
}
@Override
default void close() {
setLidMode(LidMode.DEFAULT);
}
@Override
default void open() {
setLidMode(LidMode.FORCED_OPEN);
}
}

View file

@ -9,8 +9,9 @@ import org.bukkit.World;
import org.bukkit.block.Barrel; import org.bukkit.block.Barrel;
import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class CraftBarrel extends CraftLootable<BarrelBlockEntity> implements Barrel { public class CraftBarrel extends CraftLootable<BarrelBlockEntity> implements Barrel, io.papermc.paper.block.PaperLidded {
public CraftBarrel(World world, BarrelBlockEntity tileEntity) { public CraftBarrel(World world, BarrelBlockEntity tileEntity) {
super(world, tileEntity); super(world, tileEntity);
@ -34,36 +35,6 @@ public class CraftBarrel extends CraftLootable<BarrelBlockEntity> implements Bar
return new CraftInventory(this.getTileEntity()); return new CraftInventory(this.getTileEntity());
} }
@Override
public void open() {
this.requirePlaced();
if (!this.getTileEntity().openersCounter.opened) {
BlockState blockData = this.getTileEntity().getBlockState();
boolean open = blockData.getValue(BarrelBlock.OPEN);
if (!open) {
this.getTileEntity().updateBlockState(blockData, true);
if (this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
this.getTileEntity().playSound(blockData, SoundEvents.BARREL_OPEN);
}
}
}
this.getTileEntity().openersCounter.opened = true;
}
@Override
public void close() {
this.requirePlaced();
if (this.getTileEntity().openersCounter.opened) {
BlockState blockData = this.getTileEntity().getBlockState();
this.getTileEntity().updateBlockState(blockData, false);
if (this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
this.getTileEntity().playSound(blockData, SoundEvents.BARREL_CLOSE);
}
}
this.getTileEntity().openersCounter.opened = false;
}
@Override @Override
public CraftBarrel copy() { public CraftBarrel copy() {
return new CraftBarrel(this, null); return new CraftBarrel(this, null);
@ -74,10 +45,54 @@ public class CraftBarrel extends CraftLootable<BarrelBlockEntity> implements Bar
return new CraftBarrel(this, location); return new CraftBarrel(this, location);
} }
// Paper start - More Lidded Block API
@Override @Override
public boolean isOpen() { public void startForceLiddedLidOpen() {
return getTileEntity().openersCounter.opened; this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
} }
// Paper end - More Lidded Block API
@Override
public void stopForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void startForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void stopForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public io.papermc.paper.block.@NotNull LidState getEffectiveLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getEffectiveLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidState getTrueLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getTrueLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidMode getLidMode() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getLidMode();
}
@Override
public io.papermc.paper.block.@NotNull LidMode setLidMode(final io.papermc.paper.block.@NotNull LidMode targetLidMode) {
this.requirePlaced();
io.papermc.paper.block.LidMode newEffectiveMode = io.papermc.paper.block.PaperLidded.super.setLidMode(targetLidMode);
this.getTileEntity().openersCounter.setLidMode(newEffectiveMode);
return newEffectiveMode;
}
} }

View file

@ -13,8 +13,9 @@ import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest; import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest { public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest, io.papermc.paper.block.PaperLidded {
public CraftChest(World world, ChestBlockEntity tileEntity) { public CraftChest(World world, ChestBlockEntity tileEntity) {
super(world, tileEntity); super(world, tileEntity);
@ -57,32 +58,6 @@ public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest
return inventory; return inventory;
} }
@Override
public void open() {
this.requirePlaced();
if (!this.getTileEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
BlockState block = this.getTileEntity().getBlockState();
int openCount = this.getTileEntity().openersCounter.getOpenerCount();
this.getTileEntity().openersCounter.onAPIOpen((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block);
this.getTileEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, openCount + 1);
}
this.getTileEntity().openersCounter.opened = true;
}
@Override
public void close() {
this.requirePlaced();
if (this.getTileEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
BlockState block = this.getTileEntity().getBlockState();
int openCount = this.getTileEntity().openersCounter.getOpenerCount();
this.getTileEntity().openersCounter.onAPIClose((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block);
this.getTileEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, 0);
}
this.getTileEntity().openersCounter.opened = false;
}
@Override @Override
public CraftChest copy() { public CraftChest copy() {
return new CraftChest(this, null); return new CraftChest(this, null);
@ -93,13 +68,6 @@ public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest
return new CraftChest(this, location); return new CraftChest(this, location);
} }
// Paper start - More Lidded Block API
@Override
public boolean isOpen() {
return getTileEntity().openersCounter.opened;
}
// Paper end - More Lidded Block API
// Paper start - More Chest Block API // Paper start - More Chest Block API
@Override @Override
public boolean isBlocked() { public boolean isBlocked() {
@ -124,4 +92,54 @@ public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest
&& ChestBlock.isChestBlockedAt(world, neighbourBlockPos); && ChestBlock.isChestBlockedAt(world, neighbourBlockPos);
} }
// Paper end - More Chest Block API // Paper end - More Chest Block API
@Override
public void startForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void stopForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void startForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void stopForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public io.papermc.paper.block.@NotNull LidState getEffectiveLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getEffectiveLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidState getTrueLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getTrueLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidMode getLidMode() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getLidMode();
}
@Override
public io.papermc.paper.block.@NotNull LidMode setLidMode(final io.papermc.paper.block.@NotNull LidMode targetLidMode) {
this.requirePlaced();
io.papermc.paper.block.LidMode newEffectiveMode = io.papermc.paper.block.PaperLidded.super.setLidMode(targetLidMode);
this.getTileEntity().openersCounter.setLidMode(newEffectiveMode);
return newEffectiveMode;
}
} }

View file

@ -5,8 +5,9 @@ import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.EnderChest; import org.bukkit.block.EnderChest;
import org.jetbrains.annotations.NotNull;
public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity> implements EnderChest { public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity> implements EnderChest, io.papermc.paper.block.PaperLidded {
public CraftEnderChest(World world, EnderChestBlockEntity tileEntity) { public CraftEnderChest(World world, EnderChestBlockEntity tileEntity) {
super(world, tileEntity); super(world, tileEntity);
@ -16,32 +17,6 @@ public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity
super(state, location); super(state, location);
} }
@Override
public void open() {
this.requirePlaced();
if (!this.getTileEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
BlockState block = this.getTileEntity().getBlockState();
int openCount = this.getTileEntity().openersCounter.getOpenerCount();
this.getTileEntity().openersCounter.onAPIOpen((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block);
this.getTileEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, openCount + 1);
}
this.getTileEntity().openersCounter.opened = true;
}
@Override
public void close() {
this.requirePlaced();
if (this.getTileEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
BlockState block = this.getTileEntity().getBlockState();
int openCount = this.getTileEntity().openersCounter.getOpenerCount();
this.getTileEntity().openersCounter.onAPIClose((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block);
this.getTileEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, 0);
}
this.getTileEntity().openersCounter.opened = false;
}
@Override @Override
public CraftEnderChest copy() { public CraftEnderChest copy() {
return new CraftEnderChest(this, null); return new CraftEnderChest(this, null);
@ -52,13 +27,6 @@ public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity
return new CraftEnderChest(this, location); return new CraftEnderChest(this, location);
} }
// Paper start - More Lidded Block API
@Override
public boolean isOpen() {
return getTileEntity().openersCounter.opened;
}
// Paper end - More Lidded Block API
// Paper start - More Chest Block API // Paper start - More Chest Block API
@Override @Override
public boolean isBlocked() { public boolean isBlocked() {
@ -67,4 +35,54 @@ public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity
return this.isPlaced() && this.getWorldHandle().getBlockState(abovePos).isRedstoneConductor(this.getWorldHandle(), abovePos); return this.isPlaced() && this.getWorldHandle().getBlockState(abovePos).isRedstoneConductor(this.getWorldHandle(), abovePos);
} }
// Paper end - More Chest Block API // Paper end - More Chest Block API
@Override
public void startForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void stopForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidOpen(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void startForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.startForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public void stopForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().openersCounter.stopForceLiddedLidClose(this.getTileEntity().getLevel(), this.getTileEntity().getBlockPos(), this.getTileEntity().getBlockState());
}
@Override
public io.papermc.paper.block.@NotNull LidState getEffectiveLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getEffectiveLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidState getTrueLidState() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getTrueLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidMode getLidMode() {
this.requirePlaced();
return this.getTileEntity().openersCounter.getLidMode();
}
@Override
public io.papermc.paper.block.@NotNull LidMode setLidMode(final io.papermc.paper.block.@NotNull LidMode targetLidMode) {
this.requirePlaced();
io.papermc.paper.block.LidMode newEffectiveMode = io.papermc.paper.block.PaperLidded.super.setLidMode(targetLidMode);
this.getTileEntity().openersCounter.setLidMode(newEffectiveMode);
return newEffectiveMode;
}
} }

View file

@ -10,8 +10,9 @@ import org.bukkit.World;
import org.bukkit.block.ShulkerBox; import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.inventory.CraftInventory; import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implements ShulkerBox { public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implements ShulkerBox, io.papermc.paper.block.PaperLidded {
public CraftShulkerBox(World world, ShulkerBoxBlockEntity tileEntity) { public CraftShulkerBox(World world, ShulkerBoxBlockEntity tileEntity) {
super(world, tileEntity); super(world, tileEntity);
@ -42,28 +43,6 @@ public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implem
return (color == null) ? null : DyeColor.getByWoolData((byte) color.getId()); return (color == null) ? null : DyeColor.getByWoolData((byte) color.getId());
} }
@Override
public void open() {
this.requirePlaced();
if (!this.getTileEntity().opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
net.minecraft.world.level.Level world = this.getTileEntity().getLevel();
world.blockEvent(this.getPosition(), this.getTileEntity().getBlockState().getBlock(), 1, 1);
world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F);
}
this.getTileEntity().opened = true;
}
@Override
public void close() {
this.requirePlaced();
if (this.getTileEntity().opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) {
net.minecraft.world.level.Level world = this.getTileEntity().getLevel();
world.blockEvent(this.getPosition(), this.getTileEntity().getBlockState().getBlock(), 1, 0);
world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); // Paper - More Lidded Block API (Wrong sound)
}
this.getTileEntity().opened = false;
}
@Override @Override
public CraftShulkerBox copy() { public CraftShulkerBox copy() {
return new CraftShulkerBox(this, null); return new CraftShulkerBox(this, null);
@ -74,10 +53,53 @@ public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implem
return new CraftShulkerBox(this, location); return new CraftShulkerBox(this, location);
} }
// Paper start - More Lidded Block API
@Override @Override
public boolean isOpen() { public void startForceLiddedLidOpen() {
return getTileEntity().opened; this.requirePlaced();
this.getTileEntity().startForceLiddedLidOpen();
}
@Override
public void stopForceLiddedLidOpen() {
this.requirePlaced();
this.getTileEntity().stopForceLiddedLidOpen();
}
@Override
public void startForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().startForceLiddedLidClose();
}
@Override
public void stopForceLiddedLidClose() {
this.requirePlaced();
this.getTileEntity().stopForceLiddedLidClose();
}
@Override
public io.papermc.paper.block.@NotNull LidState getEffectiveLidState() {
this.requirePlaced();
return this.getTileEntity().getEffectiveLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidState getTrueLidState() {
this.requirePlaced();
return this.getTileEntity().getTrueLidState();
}
@Override
public io.papermc.paper.block.@NotNull LidMode getLidMode() {
this.requirePlaced();
return this.getTileEntity().getLidMode();
}
@Override
public io.papermc.paper.block.@NotNull LidMode setLidMode(final io.papermc.paper.block.@NotNull LidMode targetLidMode) {
this.requirePlaced();
io.papermc.paper.block.LidMode newEffectiveMode = io.papermc.paper.block.PaperLidded.super.setLidMode(targetLidMode);
this.getTileEntity().setLidMode(newEffectiveMode);
return newEffectiveMode;
} }
// Paper end - More Lidded Block API
} }