diff --git a/paper-api/src/main/java/org/bukkit/block/BlockState.java b/paper-api/src/main/java/org/bukkit/block/BlockState.java index 631cbf2be5..7c5438fa92 100644 --- a/paper-api/src/main/java/org/bukkit/block/BlockState.java +++ b/paper-api/src/main/java/org/bukkit/block/BlockState.java @@ -7,6 +7,7 @@ import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.material.MaterialData; import org.bukkit.metadata.Metadatable; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,6 +48,15 @@ public interface BlockState extends Metadatable { @NotNull BlockData getBlockData(); + /** + * Returns a copy of this BlockState as an unplaced BlockState. + * + * @return a copy of the block state + */ + @NotNull + @ApiStatus.Experimental + BlockState copy(); + /** * Gets the type of this block state. * diff --git a/paper-api/src/main/java/org/bukkit/event/world/AsyncStructureGenerateEvent.java b/paper-api/src/main/java/org/bukkit/event/world/AsyncStructureGenerateEvent.java new file mode 100644 index 0000000000..91a137379f --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/event/world/AsyncStructureGenerateEvent.java @@ -0,0 +1,229 @@ +package org.bukkit.event.world; + +import com.google.common.base.Preconditions; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.event.HandlerList; +import org.bukkit.generator.structure.Structure; +import org.bukkit.util.BlockTransformer; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.EntityTransformer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This event will sometimes fire synchronously, depending on how it was + * triggered. + * <p> + * The constructor provides a boolean to indicate if the event was fired + * synchronously or asynchronously. When asynchronous, this event can be called + * from any thread, sans the main thread, and has limited access to the API. + * <p> + * If a {@link Structure} is naturally placed in a chunk of the world, this + * event will be asynchronous. If a player executes the '/place structure' + * command, this event will be synchronous. + * + * Allows to register transformers that can modify the blocks placed and + * entities spawned by the structure. + * <p> + * Care should be taken to check {@link #isAsynchronous()} and treat the event + * appropriately. + * <p> + */ +@ApiStatus.Experimental +public class AsyncStructureGenerateEvent extends WorldEvent { + + public static enum Cause { + COMMAND, + WORLD_GENERATION, + CUSTOM; + } + + private static final HandlerList handlers = new HandlerList(); + + private final Cause cause; + + private final Structure structure; + private final BoundingBox boundingBox; + + private final int chunkX, chunkZ; + + private final Map<NamespacedKey, BlockTransformer> blockTransformers = new LinkedHashMap<>(); + private final Map<NamespacedKey, EntityTransformer> entityTransformers = new LinkedHashMap<>(); + + public AsyncStructureGenerateEvent(@NotNull World world, boolean async, @NotNull Cause cause, @NotNull Structure structure, @NotNull BoundingBox boundingBox, int chunkX, int chunkZ) { + super(world, async); + this.structure = structure; + this.boundingBox = boundingBox; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.cause = cause; + } + + /** + * Gets the event cause. + * + * @return the event cause + */ + @NotNull + public Cause getCause() { + return cause; + } + + /** + * Gets a block transformer by key. + * + * @param key the key of the block transformer + * + * @return the block transformer or null + */ + @Nullable + public BlockTransformer getBlockTransformer(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + return blockTransformers.get(key); + } + + /** + * Sets a block transformer to a key. + * + * @param key the key + * @param transformer the block transformer + */ + public void setBlockTransformer(@NotNull NamespacedKey key, @NotNull BlockTransformer transformer) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + Preconditions.checkNotNull(transformer, "BlockTransformer cannot be null"); + blockTransformers.put(key, transformer); + } + + /** + * Removes a block transformer. + * + * @param key the key of the block transformer + */ + public void removeBlockTransformer(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + blockTransformers.remove(key); + } + + /** + * Removes all block transformers. + */ + public void clearBlockTransformers() { + blockTransformers.clear(); + } + + /** + * Gets all block transformers in a unmodifiable map. + * + * @return the block transformers in a map + */ + @NotNull + public Map<NamespacedKey, BlockTransformer> getBlockTransformers() { + return Collections.unmodifiableMap(blockTransformers); + } + + /** + * Gets a entity transformer by key. + * + * @param key the key of the entity transformer + * + * @return the entity transformer or null + */ + @Nullable + public EntityTransformer getEntityTransformer(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + return entityTransformers.get(key); + } + + /** + * Sets a entity transformer to a key. + * + * @param key the key + * @param transformer the entity transformer + */ + public void setEntityTransformer(@NotNull NamespacedKey key, @NotNull EntityTransformer transformer) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + Preconditions.checkNotNull(transformer, "EntityTransformer cannot be null"); + entityTransformers.put(key, transformer); + } + + /** + * Removes a entity transformer. + * + * @param key the key of the entity transformer + */ + public void removeEntityTransformer(@NotNull NamespacedKey key) { + Preconditions.checkNotNull(key, "NamespacedKey cannot be null"); + entityTransformers.remove(key); + } + + /** + * Removes all entity transformers. + */ + public void clearEntityTransformers() { + entityTransformers.clear(); + } + + /** + * Gets all entity transformers in a unmodifiable map. + * + * @return the entity transformers in a map + */ + @NotNull + public Map<NamespacedKey, EntityTransformer> getEntityTransformers() { + return Collections.unmodifiableMap(entityTransformers); + } + + /** + * Get the structure reference that is generated. + * + * @return the structure + */ + @NotNull + public Structure getStructure() { + return structure; + } + + /** + * Get the bounding box of the structure. + * + * @return the bounding box + */ + @NotNull + public BoundingBox getBoundingBox() { + return boundingBox.clone(); + } + + /** + * Get the x coordinate of the origin chunk of the structure. + * + * @return the chunk x coordinate + */ + public int getChunkX() { + return chunkX; + } + + /** + * Get the z coordinate of the origin chunk of the structure. + * + * @return the chunk z coordinate + */ + public int getChunkZ() { + return chunkZ; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/paper-api/src/main/java/org/bukkit/util/BlockTransformer.java b/paper-api/src/main/java/org/bukkit/util/BlockTransformer.java new file mode 100644 index 0000000000..7f430519c0 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/util/BlockTransformer.java @@ -0,0 +1,62 @@ +package org.bukkit.util; + +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.generator.LimitedRegion; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * A BlockTransformer is used to modify blocks that are placed by structure. + */ +@FunctionalInterface +@ApiStatus.Experimental +public interface BlockTransformer { + + /** + * The TransformationState allows access to the original block state and the + * block state of the block that was at the location of the transformation + * in the world before the transformation started. + */ + public static interface TransformationState { + + /** + * Creates a clone of the original block state that a structure wanted + * to place and caches it for the current transformer. + * + * @return a clone of the original block state. + */ + @NotNull + BlockState getOriginal(); + + /** + * Creates a clone of the block state that was at the location of the + * currently modified block at the start of the transformation process + * and caches it for the current transformer. + * + * @return a clone of the world block state. + */ + @NotNull + BlockState getWorld(); + + } + + /** + * Transforms a block in a structure. + * + * NOTE: The usage of {@link BlockData#createBlockState()} can provide even + * more flexibility to return the exact block state you might want to + * return. + * + * @param region the accessible region + * @param x the x position of the block + * @param y the y position of the block + * @param z the z position of the block + * @param current the state of the block that should be placed + * @param state the state of this transformation. + * + * @return the new block state + */ + @NotNull + BlockState transform(@NotNull LimitedRegion region, int x, int y, int z, @NotNull BlockState current, @NotNull TransformationState state); +} diff --git a/paper-api/src/main/java/org/bukkit/util/EntityTransformer.java b/paper-api/src/main/java/org/bukkit/util/EntityTransformer.java new file mode 100644 index 0000000000..d1f44fd90d --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/util/EntityTransformer.java @@ -0,0 +1,29 @@ +package org.bukkit.util; + +import org.bukkit.entity.Entity; +import org.bukkit.generator.LimitedRegion; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * A EntityTransformer is used to modify entities that are spawned by structure. + */ +@FunctionalInterface +@ApiStatus.Experimental +public interface EntityTransformer { + + /** + * Transforms a entity in a structure. + * + * @param region the accessible region + * @param x the x position of the entity + * @param y the y position of the entity + * @param z the z position of the entity + * @param entity the entity + * @param allowedToSpawn if the entity is allowed to spawn + * + * @return {@code true} if the entity should be spawned otherwise + * {@code false} + */ + boolean transform(@NotNull LimitedRegion region, int x, int y, int z, @NotNull Entity entity, boolean allowedToSpawn); +}