mirror of
https://github.com/YouHaveTrouble/BlockEdit.git
synced 2026-06-29 13:36:19 +00:00
schematic provider api
This commit is contained in:
@@ -3,6 +3,7 @@ package me.youhavetrouble.blockedit;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import me.youhavetrouble.blockedit.schematic.Schematic;
|
||||||
import me.youhavetrouble.blockedit.wands.SelectionWand;
|
import me.youhavetrouble.blockedit.wands.SelectionWand;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
@@ -15,7 +16,7 @@ import java.util.regex.Pattern;
|
|||||||
public final class BlockEdit extends JavaPlugin {
|
public final class BlockEdit extends JavaPlugin {
|
||||||
|
|
||||||
private static BlockEdit plugin;
|
private static BlockEdit plugin;
|
||||||
private static SchematicHandler schematicHandler;
|
private static SchematicHandler<? extends Schematic> schematicHandler;
|
||||||
private static WandsHandler wandsHandler;
|
private static WandsHandler wandsHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -30,7 +31,7 @@ public final class BlockEdit extends JavaPlugin {
|
|||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new JoinLeaveListener(), this);
|
getServer().getPluginManager().registerEvents(new JoinLeaveListener(), this);
|
||||||
|
|
||||||
schematicHandler = new SchematicHandler(this);
|
schematicHandler = new SchematicHandler<>(this);
|
||||||
wandsHandler = new WandsHandler(this);
|
wandsHandler = new WandsHandler(this);
|
||||||
|
|
||||||
SelectionWand selectionWand = new SelectionWand();
|
SelectionWand selectionWand = new SelectionWand();
|
||||||
@@ -44,7 +45,7 @@ public final class BlockEdit extends JavaPlugin {
|
|||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchematicHandler getSchematicHandler() {
|
public static SchematicHandler<? extends Schematic> getSchematicHandler() {
|
||||||
return schematicHandler;
|
return schematicHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,51 +1,79 @@
|
|||||||
package me.youhavetrouble.blockedit;
|
package me.youhavetrouble.blockedit;
|
||||||
|
|
||||||
|
import me.youhavetrouble.blockedit.schematic.Schematic;
|
||||||
|
import me.youhavetrouble.blockedit.schematic.SchematicProvider;
|
||||||
import me.youhavetrouble.blockedit.util.Clipboard;
|
import me.youhavetrouble.blockedit.util.Clipboard;
|
||||||
import me.youhavetrouble.blockedit.util.Schematic;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class SchematicHandler {
|
public class SchematicHandler<S extends Schematic> {
|
||||||
|
|
||||||
private final BlockEdit plugin;
|
private final BlockEdit plugin;
|
||||||
|
|
||||||
|
private final Map<String, SchematicProvider<S>> schematicProvidersByExtension = new HashMap<>();
|
||||||
|
private final Map<String, SchematicProvider<S>> schematicProvidersByName = new HashMap<>();
|
||||||
|
|
||||||
|
private final File schematicsDirectory;
|
||||||
|
|
||||||
protected SchematicHandler(BlockEdit plugin) {
|
protected SchematicHandler(BlockEdit plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.schematicsDirectory = new File(plugin.getDataFolder(), "schematics");
|
||||||
createSchematicsDirectory();
|
createSchematicsDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSchematicsDirectory() {
|
private void createSchematicsDirectory() {
|
||||||
try {
|
try {
|
||||||
if (!plugin.getDataFolder().exists()) {
|
this.schematicsDirectory.mkdirs();
|
||||||
plugin.getDataFolder().mkdir();
|
|
||||||
}
|
|
||||||
File schematicsDir = new File(plugin.getDataFolder(), "schematics");
|
|
||||||
if (!schematicsDir.exists()) {
|
|
||||||
schematicsDir.mkdir();
|
|
||||||
}
|
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
plugin.getLogger().warning("Could not create schematics directory. Make sure server has read/write access to the plugin folder.");
|
plugin.getLogger().warning("Could not create schematics directory. Make sure server has read/write access to the plugin folder.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerSchematicProvider(
|
||||||
|
@NotNull SchematicProvider<S> schematicProvider
|
||||||
|
) throws IllegalArgumentException {
|
||||||
|
if (!schematicProvider.name().matches("^[a-z0-9]+$")) {
|
||||||
|
throw new IllegalArgumentException("Schematic provider name can only contain lowercase letters and numbers");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schematicProvidersByName.containsKey(schematicProvider.name())) {
|
||||||
|
throw new IllegalArgumentException("Schematic provider " + schematicProvider.name() + " is already registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop extensions to verify if they're valid and not already registered
|
||||||
|
for (String extension : schematicProvider.fileExtensions()) {
|
||||||
|
String trimmedExtension = extension.trim();
|
||||||
|
if (trimmedExtension.isEmpty()) throw new IllegalArgumentException("File extension cannot be empty");
|
||||||
|
if (!trimmedExtension.matches("^[a-z0-9]+$")) throw new IllegalArgumentException("File extension can only contain lowercase letters and numbers");
|
||||||
|
if (schematicProvidersByExtension.containsKey(trimmedExtension)) {
|
||||||
|
throw new IllegalArgumentException("File extension " + trimmedExtension + " is already registered to " + schematicProvidersByExtension.get(trimmedExtension).name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop again to actually register the extensions
|
||||||
|
for (String extension : schematicProvider.fileExtensions()) {
|
||||||
|
String trimmedExtension = extension.trim();
|
||||||
|
schematicProvidersByExtension.put(trimmedExtension, schematicProvider);
|
||||||
|
}
|
||||||
|
schematicProvidersByName.put(schematicProvider.name(), schematicProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a schematic from the schematics directory
|
* Loads a schematic from the schematics directory
|
||||||
|
* @param providerName Schematic provider to use
|
||||||
* @param schematicName Name of the schematic
|
* @param schematicName Name of the schematic
|
||||||
* @return Clipboard object containing the schematic. Null if schematic does not exist
|
* @return Clipboard object containing the schematic. Null if schematic does not exist or could not be loaded.
|
||||||
*/
|
*/
|
||||||
public Clipboard loadSchematic(String schematicName) {
|
public @Nullable Schematic loadSchematic(String providerName, @NotNull String schematicName) {
|
||||||
File schematicFile = new File(plugin.getDataFolder(), "schematics/" + schematicName + ".schematic");
|
SchematicProvider<? extends Schematic> schematicProvider = schematicProvidersByName.get(providerName);
|
||||||
if (!schematicFile.exists()) {
|
if (schematicProvider == null) {
|
||||||
schematicFile = new File(plugin.getDataFolder(), "schematics/" + schematicName + ".schem");
|
throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered");
|
||||||
}
|
}
|
||||||
if (!schematicFile.exists()) {
|
return schematicProvider.load(schematicName);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Schematic schematic = new Schematic(schematicFile);
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,10 +81,19 @@ public class SchematicHandler {
|
|||||||
* @param schematicName Name of the schematic
|
* @param schematicName Name of the schematic
|
||||||
* @param clipboard Clipboard object containing the schematic
|
* @param clipboard Clipboard object containing the schematic
|
||||||
*/
|
*/
|
||||||
public void saveSchematic(String schematicName, Clipboard clipboard) {
|
public void saveSchematic(
|
||||||
|
@NotNull String schematicName,
|
||||||
|
@NotNull String providerName,
|
||||||
|
@NotNull Clipboard clipboard
|
||||||
|
) {
|
||||||
|
SchematicProvider<S> schematicProvider = schematicProvidersByName.get(providerName);
|
||||||
|
if (schematicProvider == null) {
|
||||||
|
throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered");
|
||||||
|
}
|
||||||
|
S schematic = schematicProvider.fromClipboard(schematicName, clipboard);
|
||||||
|
|
||||||
|
schematicProvider.save(schematic);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package me.youhavetrouble.blockedit.api;
|
|||||||
import me.youhavetrouble.blockedit.BlockEdit;
|
import me.youhavetrouble.blockedit.BlockEdit;
|
||||||
import me.youhavetrouble.blockedit.SchematicHandler;
|
import me.youhavetrouble.blockedit.SchematicHandler;
|
||||||
import me.youhavetrouble.blockedit.WandsHandler;
|
import me.youhavetrouble.blockedit.WandsHandler;
|
||||||
|
import me.youhavetrouble.blockedit.schematic.Schematic;
|
||||||
import me.youhavetrouble.blockedit.util.ChunkWork;
|
import me.youhavetrouble.blockedit.util.ChunkWork;
|
||||||
import me.youhavetrouble.blockedit.util.Selection;
|
import me.youhavetrouble.blockedit.util.Selection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -38,7 +39,8 @@ public class BlockEditAPI {
|
|||||||
* Gets schematic handler object that can be used to work with schematics
|
* Gets schematic handler object that can be used to work with schematics
|
||||||
* @return Schematic handler
|
* @return Schematic handler
|
||||||
*/
|
*/
|
||||||
public static SchematicHandler getSchematicHandler() {
|
public static SchematicHandler<? extends Schematic> getSchematicHandler() {
|
||||||
return BlockEdit.getSchematicHandler();
|
return BlockEdit.getSchematicHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package me.youhavetrouble.blockedit.schematic;
|
||||||
|
|
||||||
|
import me.youhavetrouble.blockedit.util.Clipboard;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public abstract class Schematic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schematic as a Clipboard object
|
||||||
|
* @param originLocation The location to use as the origin for the clipboard
|
||||||
|
* @return The clipboard object containing the schematic
|
||||||
|
*/
|
||||||
|
public abstract @NotNull Clipboard asClipboard(@NotNull Location originLocation);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package me.youhavetrouble.blockedit.schematic;
|
||||||
|
|
||||||
|
import me.youhavetrouble.blockedit.util.Clipboard;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for schematic providers. Schematic providers are used to load and save schematics in different formats.
|
||||||
|
*/
|
||||||
|
public interface SchematicProvider<S extends Schematic> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the schematic provider. Can only contain lowercase letters and numbers.
|
||||||
|
* @return The name of the schematic provider
|
||||||
|
*/
|
||||||
|
@NotNull String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file extensions of the schematic provider.
|
||||||
|
*/
|
||||||
|
@NotNull String[] fileExtensions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the schematic
|
||||||
|
* @param schematic The schematic to save
|
||||||
|
*/
|
||||||
|
void save(@NotNull S schematic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a schematic
|
||||||
|
* @param name Name of the schematic to load
|
||||||
|
* @return The loaded schematic. Returns null if the schematic could not be loaded.
|
||||||
|
*/
|
||||||
|
@Nullable S load(@NotNull String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new schematic from the given clipboard
|
||||||
|
* @param name Name of the schematic
|
||||||
|
* @param clipboard Clipboard object containing the schematic
|
||||||
|
* @return The created schematic
|
||||||
|
*/
|
||||||
|
@NotNull S fromClipboard(@NotNull String name, @NotNull Clipboard clipboard);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package me.youhavetrouble.blockedit.util;
|
|
||||||
|
|
||||||
import me.youhavetrouble.blockedit.commands.arguments.BlockDataArgument;
|
|
||||||
import me.youhavetrouble.blockedit.exception.SchematicLoadException;
|
|
||||||
import net.querz.nbt.io.NBTInputStream;
|
|
||||||
import net.querz.nbt.io.NamedTag;
|
|
||||||
import net.querz.nbt.tag.CompoundTag;
|
|
||||||
import net.querz.nbt.tag.Tag;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
public class Schematic {
|
|
||||||
|
|
||||||
private final short width, height, length;
|
|
||||||
private final int bitsPerBlock;
|
|
||||||
private final byte[] blocks;
|
|
||||||
|
|
||||||
private BlockData[] blockPalette = new BlockData[0];
|
|
||||||
|
|
||||||
public Schematic(File file) throws SchematicLoadException {
|
|
||||||
try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new GZIPInputStream(new FileInputStream(file))))) {
|
|
||||||
NamedTag tag = new NBTInputStream(dis).readTag(Tag.DEFAULT_MAX_DEPTH);
|
|
||||||
|
|
||||||
if (tag == null) throw new SchematicLoadException("Could not load schematic. Invalid file format");
|
|
||||||
if (!(tag.getTag() instanceof CompoundTag compoundTag)) throw new SchematicLoadException("Could not load schematic. Invalid file format");
|
|
||||||
|
|
||||||
this.width = compoundTag.getShort("Width");
|
|
||||||
this.height = compoundTag.getShort("Height");
|
|
||||||
this.length = compoundTag.getShort("Length");
|
|
||||||
|
|
||||||
CompoundTag paletteMap = compoundTag.getCompoundTag("Palette");
|
|
||||||
this.blocks = compoundTag.getByteArray("BlockData");
|
|
||||||
this.bitsPerBlock = blocks.length >> 6;
|
|
||||||
|
|
||||||
if (paletteMap.size() > 0) {
|
|
||||||
this.blockPalette = new BlockData[paletteMap.size()];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String key : paletteMap.keySet()) {
|
|
||||||
int index = paletteMap.getInt(key);
|
|
||||||
String[] split = key.split(":");
|
|
||||||
if (split.length == 2) {
|
|
||||||
key = split[1];
|
|
||||||
}
|
|
||||||
this.blockPalette[index] = BlockDataArgument.getBlockData(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SchematicLoadException("Could not load schematic due to I/O error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BlockData> getBlockPalette() {
|
|
||||||
return List.of(blockPalette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user