SPIGOT-2903: Add Structure API

By: Sander Knauff <sanderknauff@hotmail.com>
This commit is contained in:
Bukkit/Spigot 2021-10-05 11:55:20 +11:00
parent 8b6e67635a
commit 6882afca7b
6 changed files with 410 additions and 0 deletions

View File

@ -46,6 +46,7 @@ import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.messaging.Messenger;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.structure.StructureManager;
import org.bukkit.util.CachedServerIcon;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -1683,6 +1684,16 @@ public final class Bukkit {
return server.selectEntities(sender, selector);
}
/**
* Gets the structure manager for loading and saving structures.
*
* @return the structure manager
*/
@NotNull
public static StructureManager getStructureManager() {
return server.getStructureManager();
}
/**
* @return the unsafe values instance
* @see UnsafeValues

View File

@ -47,6 +47,7 @@ import org.bukkit.plugin.messaging.Messenger;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.structure.StructureManager;
import org.bukkit.util.CachedServerIcon;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -1426,6 +1427,14 @@ public interface Server extends PluginMessageRecipient {
@NotNull
List<Entity> selectEntities(@NotNull CommandSender sender, @NotNull String selector) throws IllegalArgumentException;
/**
* Gets the structure manager for loading and saving structures.
*
* @return the structure manager
*/
@NotNull
StructureManager getStructureManager();
/**
* @return the unsafe values instance
* @see UnsafeValues

View File

@ -0,0 +1,33 @@
package org.bukkit.structure;
import java.util.List;
import org.bukkit.block.BlockState;
import org.jetbrains.annotations.NotNull;
/**
* Represent a variation of a structure.
*
* Most structures, like the ones generated with structure blocks, only have a
* single variant.
*/
public interface Palette {
/**
* Gets a copy of the blocks this Palette is made of.
*
* The {@link BlockState#getLocation() positions} of the returned block
* states are offsets relative to the structure's position that is provided
* once the structure is placed into the world.
*
* @return The blocks in this palette
*/
@NotNull
List<BlockState> getBlocks();
/**
* Gets the number of blocks stored in this palette.
*
* @return The number of blocks in this palette
*/
int getBlockCount();
}

View File

@ -0,0 +1,146 @@
package org.bukkit.structure;
import java.util.List;
import java.util.Random;
import org.bukkit.Location;
import org.bukkit.RegionAccessor;
import org.bukkit.block.structure.Mirror;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.entity.Entity;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
/**
* Represents a structure.
* <p>
* A structure is a mutable template of captured blocks and entities that can be
* copied back into the world. The {@link StructureManager}, retrieved via
* {@link org.bukkit.Server#getStructureManager()}, allows you to create new
* structures, load existing structures, and save structures.
* <p>
* In order for a structure to be usable by structure blocks, it needs to be
* null {@link StructureManager#registerStructure(org.bukkit.NamespacedKey, Structure)
* registered} with the {@link StructureManager}, or located in the primary
* world folder, a DataPack, or the server's own default resources, so that the
* StructureManager can find it.
*/
public interface Structure {
/**
* Gets the current size of the structure.
* <p>
* The size of the structure may not be fixed.
*
* @return A new vector that represents the size of the structure along each
* axis.
*/
@NotNull
BlockVector getSize();
/**
* Gets a list of available block palettes.
*
* @return a list of available variants of this structure.
*/
@NotNull
List<Palette> getPalettes();
/**
* Gets the number of palettes in this structure.
*
* @return The number of palettes in this structure
*/
int getPaletteCount();
/**
* Gets a list of entities that have been included in the Structure.
*
* The entity positions are offsets relative to the structure's position
* that is provided once the structure is placed into the world.
*
* @return a list of Entities included in the Structure.
*/
@NotNull
List<Entity> getEntities();
/**
* Gets the number of entities in this structure.
*
* @return The number of entities in this structure
*/
int getEntityCount();
/**
* Place a structure in the world.
*
* @param location The location to place the structure at.
* @param includeEntities If the entities present in the structure should be
* spawned.
* @param structureRotation The rotation of the structure.
* @param mirror The mirror settings of the structure.
* @param palette The palette index of the structure to use, starting at
* {@code 0}, or {@code -1} to pick a random palette.
* @param integrity Determines how damaged the building should look by
* randomly skipping blocks to place. This value can range from 0 to 1. With
* 0 removing all blocks and 1 spawning the structure in pristine condition.
* @param random The randomizer used for setting the structure's
* {@link org.bukkit.loot.LootTable}s and integrity.
*/
void place(@NotNull Location location, boolean includeEntities, @NotNull StructureRotation structureRotation, @NotNull Mirror mirror, int palette, float integrity, @NotNull Random random);
/**
* Place a structure in the world.
*
* @param regionAccessor The world to place the structure in.
* @param location The location to place the structure at.
* @param includeEntities If the entities present in the structure should be
* spawned.
* @param structureRotation The rotation of the structure.
* @param mirror The mirror settings of the structure.
* @param palette The palette index of the structure to use, starting at
* {@code 0}, or {@code -1} to pick a random palette.
* @param integrity Determines how damaged the building should look by
* randomly skipping blocks to place. This value can range from 0 to 1. With
* 0 removing all blocks and 1 spawning the structure in pristine condition.
* @param random The randomizer used for setting the structure's
* {@link org.bukkit.loot.LootTable}s and integrity.
*/
void place(@NotNull RegionAccessor regionAccessor, @NotNull BlockVector location, boolean includeEntities, @NotNull StructureRotation structureRotation, @NotNull Mirror mirror, int palette, float integrity, @NotNull Random random);
/**
* Fills the structure from an area in a world. The origin and size will be
* calculated automatically from the two corners provided.
* <p>
* Be careful as this will override the current data of the structure.
* <p>
* Be aware that this method allows for creating structures larger than the
* 48x48x48 size that Minecraft's Structure blocks support. Any structures
* saved this way can not be loaded by using a structure block. Using the
* API however will still work.
*
* @param corner1 A corner of the structure.
* @param corner2 The corner opposite from corner1.
* @param includeEntities true if entities should be included in the saved
* structure.
*/
void fill(@NotNull Location corner1, @NotNull Location corner2, boolean includeEntities);
/**
* Fills the Structure from an area in a world, starting at the specified
* origin and extending in each axis according to the specified size vector.
* <p>
* Be careful as this will override the current data of the structure.
* <p>
* Be aware that this method allows for saving structures larger than the
* 48x48x48 size that Minecraft's Structure blocks support. Any structures
* saved this way can not be loaded by using a structure block. Using the
* API however will still work.
*
* @param origin The origin of the structure.
* @param size The size of the structure, must be at least 1x1x1.
* @param includeEntities true if entities should be included in the saved
* structure.
* @throws IllegalArgumentException Thrown if size is smaller than 1x1x1
*/
void fill(@NotNull Location origin, @NotNull BlockVector size, boolean includeEntities);
}

View File

@ -0,0 +1,205 @@
package org.bukkit.structure;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface StructureManager {
/**
* Gets the currently registered structures.
* <p>
* These are the currently loaded structures that the StructureManager is
* aware of. When a structure block refers to a structure, these structures
* are checked first. If the specified structure is not found among the
* currently registered structures, the StructureManager may dynamically
* read the structure from the primary world folder, DataPacks, or the
* server's own resources. Structures can be registered via {@link
* #registerStructure(NamespacedKey, Structure)}
*
* @return an unmodifiable shallow copy of the currently registered
* structures
*/
@NotNull
Map<NamespacedKey, Structure> getStructures();
/**
* Gets a registered Structure.
*
* @param structureKey The key for which to get the structure
* @return The structure that belongs to the structureKey or
* <code>null</code> if there is none registered for that key.
*/
@Nullable
Structure getStructure(@NotNull NamespacedKey structureKey);
/**
* Registers the given structure. See {@link #getStructures()}.
*
* @param structureKey The key for which to register the structure
* @param structure The structure to register
* @return The structure for the specified key, or <code>null</code> if the
* structure could not be found.
*/
@Nullable
Structure registerStructure(@NotNull NamespacedKey structureKey, @NotNull Structure structure);
/**
* Unregisters a structure. Unregisters the specified structure. If the
* structure still exists in the primary world folder, a DataPack, or is
* part of the server's own resources, it may be loaded and registered again
* when it is requested by a plugin or the server itself.
*
* @param structureKey The key for which to save the structure for
* @return The structure that was registered for that key or
* <code>null</code> if there was none
*/
@Nullable
Structure unregisterStructure(@NotNull NamespacedKey structureKey);
/**
* Loads a structure for the specified key and optionally {@link
* #registerStructure(NamespacedKey, Structure) registers} it.
* <p>
* This will first check the already loaded {@link #getStructures()
* registered structures}, and otherwise load the structure from the primary
* world folder, DataPacks, and the server's own resources (in this order).
* <p>
* When loading the structure from the primary world folder, the given key
* is translated to a file as specified by
* {@link #getStructureFile(NamespacedKey)}.
*
* @param structureKey The key for which to load the structure
* @param register <code>true</code> to register the loaded structure.
* @return The structure, or <code>null</code> if no structure was found for
* the specified key
*/
@Nullable
Structure loadStructure(@NotNull NamespacedKey structureKey, boolean register);
/**
* Loads the structure for the specified key and automatically registers it.
* See {@link #loadStructure(NamespacedKey, boolean)}.
*
* @param structureKey The key for which to load the structure
* @return The structure for the specified key, or <code>null</code> if the
* structure could not be found.
*/
@Nullable
Structure loadStructure(@NotNull NamespacedKey structureKey);
/**
* Saves the currently {@link #getStructures() registered structure} for the
* specified {@link NamespacedKey key} to the primary world folder as
* specified by {#getStructureFile(NamespacedKey}.
*
* @param structureKey The key for which to save the structure for
*/
void saveStructure(@NotNull NamespacedKey structureKey);
/**
* Saves a structure with a given key to the primary world folder.
*
* @param structureKey The key for which to save the structure for
* @param structure The structure to save for this structureKey
*/
void saveStructure(@NotNull NamespacedKey structureKey, @NotNull Structure structure) throws IOException;
/**
* Unregisters the specified structure and deletes its {@link
* #getStructureFile(NamespacedKey) structure file} from the primary world
* folder. Note that this method cannot be used to delete vanilla Minecraft
* structures, or structures from DataPacks. Unregistering these structures
* will however work fine.
*
* @param structureKey The key of the structure to remove
* @throws IOException If the file could not be removed for some reason.
*/
void deleteStructure(@NotNull NamespacedKey structureKey) throws IOException;
/**
* Deletes the {@link #getStructureFile(NamespacedKey) structure file} for
* the specified structure from the primary world folder. Note that this
* method cannot be used to delete vanilla Minecraft structures, or
* structures from DataPacks. Unregistering these structures will however
* work fine.
*
* @param structureKey The key of the structure to remove
* @param unregister Whether to also unregister the specified structure if
* it is currently loaded.
* @throws IOException If the file could not be removed for some reason.
*/
void deleteStructure(@NotNull NamespacedKey structureKey, boolean unregister) throws IOException;
/**
* Gets the location where a structure file would exist in the primary world
* directory based on the NamespacedKey using the format
* world/generated/{NAMESPACE}/structures/{KEY}.nbt. This method will always
* return a file, even if none exists at the moment.
*
* @param structureKey The key to build the filepath from.
* @return The location where a file with this key would be.
*/
@NotNull
File getStructureFile(@NotNull NamespacedKey structureKey);
/**
* Reads a Structure from disk.
*
* @param file The file of the structure
* @return The read structure
* @throws IOException when the given file can not be read from
*/
@NotNull
Structure loadStructure(@NotNull File file) throws IOException;
/**
* Reads a Structure from a stream.
*
* @param inputStream The file of the structure
* @return The read Structure
*/
@NotNull
Structure loadStructure(@NotNull InputStream inputStream) throws IOException;
/**
* Save a structure to a file. This will overwrite a file if it already
* exists.
*
* @param file the target to save to.
* @param structure the Structure to save.
* @throws IOException when the given file can not be written to.
*/
void saveStructure(@NotNull File file, @NotNull Structure structure) throws IOException;
/**
* Save a structure to a stream.
*
* @param outputStream the stream to write to.
* @param structure the Structure to save.
* @throws IOException when the given file can not be written to.
*/
void saveStructure(@NotNull OutputStream outputStream, @NotNull Structure structure) throws IOException;
/**
* Creates a new empty structure.
*
* @return an empty structure.
*/
@NotNull
Structure createStructure();
/**
* Creates a copy of this structure.
*
* @param structure The structure to copy
* @return a copy of the structure
*/
@NotNull
Structure copy(@NotNull Structure structure);
}

View File

@ -0,0 +1,6 @@
/**
* Classes related to creating or using {@link org.bukkit.structure.Structure
* structures} without creating {@link org.bukkit.block.Structure Structure
* blocks} in the world.
*/
package org.bukkit.structure;