diff --git a/FEATURES.md b/FEATURES.md
index e1f2bc4c..31cd3387 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -6,6 +6,9 @@ New features
- New area commands
- Mohist compatibility (kind of, you need to use particle aiming)
- Folia support
+- Custom projectile definitions (Custom gravity, drag, water drag, constant acceleration, Custom model data and more)
+- Custom blocks support (Slimefun & ItemsAdder so far, requires custom format)
+- Command completions
Fixes:
---------------
@@ -41,6 +44,9 @@ FireTask API:
Exchange API:
- You can define your own exchanges for cannons creation requirement, example at [CannonsEXP](https://github.com/Intybyte/CannonsEXP)
+Schematic Processing API:
+- You can define your own custom blocks to add by defining a namespace and adding it to SchematicWorldProcessorImpl
+
Projectiles:
- Projectile type events now give the FlyingProjectile
- ProjectilePiercingEvent is now cancellable
diff --git a/api-internal/pom.xml b/api-internal/pom.xml
index f4df5edd..37b6cda6 100644
--- a/api-internal/pom.xml
+++ b/api-internal/pom.xml
@@ -15,12 +15,24 @@
UTF-8
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
com.google.code.gson
gson
2.11.0
+
+ com.github.Intybyte
+ SchematicLib
+ f420e46a34
+
org.jetbrains
annotations
diff --git a/cannons-bukkit/pom.xml b/cannons-bukkit/pom.xml
index 073173a6..b397f32f 100644
--- a/cannons-bukkit/pom.xml
+++ b/cannons-bukkit/pom.xml
@@ -65,6 +65,14 @@
true
+
+ matteodev
+ https://maven.devs.beer/
+
+
+ nexo
+ https://repo.nexomc.com/releases
+
@@ -149,6 +157,38 @@
provided
+
+
+ com.github.Slimefun
+ Slimefun4
+ RC-37
+ provided
+
+
+ dev.lone
+ api-itemsadder
+ 4.0.10
+ provided
+
+
+ com.nexomc
+ nexo
+ 1.12.1-dev
+ provided
+
+
+
+ dev.triumphteam
+ triumph-gui
+
+
+ net.byteflux
+ libby-bukkit
+
+
+
+
+
org.junit.jupiter
@@ -189,7 +229,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.0
+ 3.14.1
${java.source}
${java.target}
@@ -241,6 +281,14 @@
io.papermc.lib
at.pavlov.cannons.shaded.paperlib
+
+ me.vaan
+ at.pavlov.cannons.shaded.vaan
+
+
+ com.google.gson
+ at.pavlov.cannons.shaded.google
+
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/Cannons.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/Cannons.java
index 3d33b713..c7ee73b7 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/Cannons.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/Cannons.java
@@ -180,7 +180,7 @@ public void onEnable() {
ProjectileManager.initialize(this);
CannonSelector.initialize(this);
- DesignStorage.getInstance().loadCannonDesigns();
+ //DesignStorage.getInstance().loadCannonDesigns();
ProjectileStorage.getInstance().loadProjectiles();
CannonManager.getInstance().updateCannons();
UserMessages.getInstance().loadLanguage();
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/Cannon.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/Cannon.java
index 8077aeff..9bfcd38e 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/Cannon.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/Cannon.java
@@ -25,6 +25,7 @@
import at.pavlov.cannons.interfaces.functionalities.Rotational;
import at.pavlov.cannons.projectile.Projectile;
import at.pavlov.cannons.projectile.ProjectileStorage;
+import at.pavlov.cannons.schematic.world.SchematicWorldProcessorImpl;
import at.pavlov.cannons.utils.CannonsUtil;
import at.pavlov.cannons.utils.InventoryManagement;
import at.pavlov.cannons.utils.SoundUtils;
@@ -32,6 +33,13 @@
import com.google.common.base.Preconditions;
import lombok.Getter;
import lombok.Setter;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.schematic.OffsetSchematic;
+import me.vaan.schematiclib.base.schematic.OffsetSchematicImpl;
+import me.vaan.schematiclib.base.schematic.Schematic;
+import me.vaan.schematiclib.file.block.FileBlock;
+import me.vaan.schematiclib.file.block.FileCoord;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Effect;
@@ -45,7 +53,6 @@
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
-import org.bukkit.material.Attachable;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
@@ -85,11 +92,12 @@ public class Cannon implements ICannon, Rotational {
//TODO make a vector util class and add this there
private final static Vector noVelocity = new Vector(0,0,0);
-
public Cannon(CannonDesign design, UUID world, Vector cannonOffset, BlockFace cannonDirection, UUID owner) {
this.design = design;
this.cannonPosition = new CannonPosition(cannonDirection, cannonOffset, world, false, noVelocity.clone());
+
+ Schematic schematic = design.getSchematicMap().get(cannonPosition.getCannonDirection());
boolean noFee = design.getEconomyBuildingCost() instanceof EmptyExchanger;
this.mainData = new CannonMainData( UUID.randomUUID(),null, noFee, owner, true);
@@ -130,7 +138,7 @@ public Cannon(CannonDesign design, UUID world, Vector cannonOffset, BlockFace ca
* @return location of the cannon
*/
public Location getLocation() {
- return design.getAllCannonBlocks(this).get(0);
+ return getFirstCannonBlock();
}
/**
@@ -151,8 +159,10 @@ public Location getRandomBarrelBlock() {
List barrel = design.getBarrelBlocks(this);
if (!barrel.isEmpty())
return barrel.get(random.nextInt(barrel.size()));
- List all = design.getAllCannonBlocks(this);
- return all.get(random.nextInt(all.size()));
+
+ List positions = getOffsetSchematic().realBlocks().positions();
+ IBlock position = positions.get(random.nextInt(positions.size()));
+ return new Location(getWorldBukkit(), position.x(), position.y(), position.z());
}
@@ -737,39 +747,20 @@ public MessageEnum destroyCannon(boolean breakBlocks, boolean canExplode, BreakC
* this will force the cannon to show up at this location - all blocks will be overwritten
*/
public void show() {
- for (SimpleBlock cBlock : design.getAllCannonBlocks(this.getCannonDirection())) {
- Block wBlock = cBlock.toLocation(getWorldBukkit(), getOffset()).getBlock();
- //todo check show
- wBlock.setBlockData(cBlock.getBlockData());
- //wBlock.setBlockData(cBlock);
- }
+ SchematicWorldProcessorImpl.getProcessor().place(
+ getOffsetSchematic(),
+ cannonPosition.getWorld()
+ );
}
/**
* this will force the cannon blocks to become AIR
*/
public void hide() {
- //remove only attachable block
- for (SimpleBlock cBlock : design.getAllCannonBlocks(this.getCannonDirection())) {
- Block wBlock = cBlock.toLocation(getWorldBukkit(), getOffset()).getBlock();
- //if that block is not loaded
-
- if (wBlock.getState() instanceof Attachable) {
- //Cannons.logger().info("hide " + wBlock.getType());
- wBlock.setType(Material.AIR);
- //wBlock.setData((byte) 0, false);
- }
- }
-
- //remove all
- for (SimpleBlock cBlock : design.getAllCannonBlocks(this.getCannonDirection())) {
- Block wBlock = cBlock.toLocation(getWorldBukkit(), getOffset()).getBlock();
-
- if (wBlock.getType() != Material.AIR) {
- wBlock.setType(Material.AIR);
- // wBlock.setData((byte) 0, false);
- }
- }
+ SchematicWorldProcessorImpl.getProcessor().destroy(
+ getOffsetSchematic(),
+ cannonPosition.getWorld()
+ );
}
@@ -777,10 +768,22 @@ public void hide() {
* breaks all cannon blocks of the cannon
*/
private void breakAllCannonBlocks() {
- List locList = design.getAllCannonBlocks(this);
- for (Location loc : locList) {
- loc.getBlock().breakNaturally();
- }
+ SchematicWorldProcessorImpl.getProcessor().breakNaturally(
+ getOffsetSchematic(),
+ cannonPosition.getWorld()
+ );
+ }
+
+ public OffsetSchematic getOffsetSchematic() {
+ Schematic schematic = design.getSchematicMap().get(cannonPosition.getCannonDirection());
+ Vector off = cannonPosition.getOffset();
+
+ return new OffsetSchematicImpl(
+ off.getBlockX(),
+ off.getBlockY(),
+ off.getBlockZ(),
+ schematic
+ );
}
@@ -791,15 +794,45 @@ private void breakAllCannonBlocks() {
* @return - true if it is part of this cannon
*/
public boolean isCannonBlock(Block block) {
- if (!getWorld().equals(block.getWorld().getUID())) {
+ UUID world = cannonPosition.getWorld();
+ if (!world.equals(block.getWorld().getUID())) {
return false;
}
- for (SimpleBlock designBlock : design.getAllCannonBlocks(getCannonDirection())) {
- if (designBlock.compareMaterialAndLoc(block, getOffset())) {
+ SchematicWorldProcessorImpl processor = SchematicWorldProcessorImpl.getProcessor();
+ OffsetSchematic offsetSchematic = getOffsetSchematic();
+ IBlock obtain = processor.registry().getBlock(block.getX(), block.getY(), block.getZ(), world);
+ BlockKey schemBlock = offsetSchematic.blockMap().get(
+ new FileCoord(
+ block.getX() - offsetSchematic.x(),
+ block.getY() - offsetSchematic.y(),
+ block.getZ() - offsetSchematic.z()
+ )
+ );
+
+ return obtain.key().equals(schemBlock);
+ }
+
+ public boolean isCannonBlock(IBlock block, UUID blockWorld) {
+ UUID world = cannonPosition.getWorld();
+ if (!world.equals(blockWorld)) {
+ return false;
+ }
+
+ Vector offset = cannonPosition.getOffset();
+ FileBlock offsetLessBlock = new FileBlock(
+ block.x() - offset.getBlockX(),
+ block.y() - offset.getBlockY(),
+ block.z() - offset.getBlockZ(),
+ block.key()
+ );
+
+ for (IBlock schemBlock : getOffsetSchematic().positions()) {
+ if (offsetLessBlock.matches(schemBlock)) {
return true;
}
}
+
return false;
}
@@ -976,8 +1009,14 @@ public boolean isSentryAutomatic() {
* @return - first block of the cannon
*/
public Location getFirstCannonBlock() {
- return design.getAllCannonBlocks(getCannonDirection()).get(0).toLocation(getWorldBukkit(), getOffset());
-
+ Vector vec = cannonPosition.getOffset();
+ OffsetSchematic schematic = getOffsetSchematic();
+ IBlock first = schematic.positions().get(0);
+ return new Location(getWorldBukkit(),
+ first.x() + vec.getBlockX(),
+ first.y() + vec.getBlockY(),
+ first.z() + vec.getBlockZ()
+ );
}
/**
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonBlocks.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonBlocks.java
index fef85f12..1b8ff935 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonBlocks.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonBlocks.java
@@ -1,6 +1,8 @@
package at.pavlov.cannons.cannon;
import at.pavlov.cannons.container.SimpleBlock;
+import lombok.Getter;
+import lombok.Setter;
import org.bukkit.util.Vector;
import java.util.ArrayList;
@@ -10,11 +12,12 @@
* it is not suggested to create new instances of it but instead
* use it to get the positions of the various stuff
*/
+@Setter
+@Getter
public class CannonBlocks {
private Vector rotationCenter; //center off all rotation blocks
private Vector muzzle; //center off all muzzle blocks - spawing Vector for snowball
- private ArrayList allCannonBlocks = new ArrayList<>();
private ArrayList barrelBlocks = new ArrayList<>();
private ArrayList chestsAndSigns = new ArrayList<>();
private ArrayList redstoneTorches = new ArrayList<>();
@@ -50,109 +53,36 @@ public Vector getFiringTrigger() {
return redstoneTrigger.get(0);
return null;
}
-
- public Vector getRotationCenter() {
- return rotationCenter;
- }
- public void setRotationCenter(Vector rotationCenter) {
- this.rotationCenter = rotationCenter;
- }
-
- public Vector getMuzzle() {
- return muzzle;
- }
- public void setMuzzle(Vector muzzle) {
- this.muzzle = muzzle;
- }
-
- public ArrayList getAllCannonBlocks() {
- return allCannonBlocks;
- }
- public void setAllCannonBlocks(ArrayList allCannonBlocks) {
- this.allCannonBlocks = allCannonBlocks;
- }
- public void addAllCannonBlocks(SimpleBlock add) {
- this.allCannonBlocks.add(add);
- }
- public ArrayList getBarrelBlocks() {
- return barrelBlocks;
- }
- public void setBarrel(ArrayList barrelBlocks) {
- this.barrelBlocks = barrelBlocks;
- }
- public void addBarrel(Vector add) {
+ public void addBarrelBlocks(Vector add) {
this.barrelBlocks.add(add);
}
- public ArrayList getRedstoneTorches() {
- return redstoneTorches;
- }
- public void setRedstoneTorches(ArrayList redstoneTorches) {
- this.redstoneTorches = redstoneTorches;
- }
- public void addRedstoneTorch(Vector add) {
+ public void addRedstoneTorch(Vector add) {
this.redstoneTorches.add(add);
}
- public ArrayList getRedstoneTrigger() {
- return redstoneTrigger;
- }
- public void setRedstoneTrigger(ArrayList redstoneTrigger) {
- this.redstoneTrigger = redstoneTrigger;
- }
- public void addRedstoneTrigger(Vector add) {
+ public void addRedstoneTrigger(Vector add) {
this.redstoneTrigger.add(add);
}
- public ArrayList getRightClickTrigger() {
- return rightClickTrigger;
- }
- public void setRightClickTrigger(ArrayList rightClickTrigger) {
- this.rightClickTrigger = rightClickTrigger;
- }
- public void addRightClickTrigger(Vector add) {
+ public void addRightClickTrigger(Vector add) {
this.rightClickTrigger.add(add);
}
- public ArrayList getChestsAndSigns() {
- return chestsAndSigns;
- }
- public void setChestsAndSigns(ArrayList chestsAndSigns) {
- this.chestsAndSigns = chestsAndSigns;
- }
- public void addChestsAndSigns(SimpleBlock add) {
+ public void addChestsAndSigns(SimpleBlock add) {
this.chestsAndSigns.add(add);
}
- public ArrayList getRedstoneWiresAndRepeater() {
- return redstoneWiresAndRepeater;
- }
- public void setRedstoneWiresAndRepeater(ArrayList redstoneWiresAndRepeater) {
- this.redstoneWiresAndRepeater = redstoneWiresAndRepeater;
- }
- public void addRedstoneWiresAndRepeater(SimpleBlock add) {
+ public void addRedstoneWiresAndRepeater(SimpleBlock add) {
this.redstoneWiresAndRepeater.add(add);
}
- public ArrayList getFiringIndicator() {
- return firingIndicator;
- }
- public void setFiringIndicator(ArrayList firingIndicator) {
- this.firingIndicator = firingIndicator;
- }
- public void addFiringIndicator(Vector add) {
+ public void addFiringIndicator(Vector add) {
this.firingIndicator.add(add);
}
- public ArrayList getDestructibleBlocks() {
- return destructibleBlocks;
- }
- public void setDestructibleBlocks(ArrayList destructibleBlocks) {
- this.destructibleBlocks = destructibleBlocks;
- }
- public void addDestructibleBlocks(Vector add) {
+ public void addDestructibleBlocks(Vector add) {
this.destructibleBlocks.add(add);
}
-
}
\ No newline at end of file
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonDesign.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonDesign.java
index 65eb8260..9f155ce1 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonDesign.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonDesign.java
@@ -6,10 +6,15 @@
import at.pavlov.cannons.container.SoundHolder;
import at.pavlov.cannons.exchange.BExchanger;
import at.pavlov.cannons.projectile.Projectile;
+import at.pavlov.cannons.schematic.world.SchematicWorldProcessorImpl;
import at.pavlov.internal.Key;
import lombok.Data;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.schematic.Schematic;
import org.bukkit.Location;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
@@ -177,21 +182,22 @@
//constructionblocks:
- private BlockData schematicBlockTypeIgnore; //this block this is ignored in the schematic file
- private BlockData schematicBlockTypeMuzzle; //location of the muzzle
- private BlockData schematicBlockTypeRotationCenter; //location of the roatation
- private BlockData schematicBlockTypeChestAndSign; //locations of the chest and sign
- private BlockData schematicBlockTypeRedstoneTorch; //locations of the redstone torches
- private BlockData schematicBlockTypeRedstoneWireAndRepeater; //locations of the redstone wires and repeaters
- private BlockData schematicBlockTypeRedstoneTrigger; //locations of button or levers
- private BlockData ingameBlockTypeRedstoneTrigger; //block which is placed instead of the place holder
- private BlockData schematicBlockTypeRightClickTrigger; //locations of the right click trigger
- private BlockData ingameBlockTypeRightClickTrigger; //block type of the tigger in game
- private BlockData schematicBlockTypeFiringIndicator; //location of the firing indicator
- private List schematicBlockTypeProtected; //list of blocks that are protected from explosions (e.g. buttons)
+ private BlockKey schematicBlockTypeIgnore; //this block this is ignored in the schematic file
+ private BlockKey schematicBlockTypeMuzzle; //location of the muzzle
+ private BlockKey schematicBlockTypeRotationCenter; //location of the roatation
+ private BlockKey schematicBlockTypeChestAndSign; //locations of the chest and sign
+ private BlockKey schematicBlockTypeRedstoneTorch; //locations of the redstone torches
+ private BlockKey schematicBlockTypeRedstoneWireAndRepeater; //locations of the redstone wires and repeaters
+ private BlockKey schematicBlockTypeRedstoneTrigger; //locations of button or levers
+ private BlockKey ingameBlockTypeRedstoneTrigger; //block which is placed instead of the place holder
+ private BlockKey schematicBlockTypeRightClickTrigger; //locations of the right click trigger
+ private BlockKey ingameBlockTypeRightClickTrigger; //block type of the tigger in game
+ private BlockKey schematicBlockTypeFiringIndicator; //location of the firing indicator
+ private List schematicBlockTypeProtected; //list of blocks that are protected from explosions (e.g. buttons)
//cannon design block lists for every direction (NORTH, EAST, SOUTH, WEST)
private final HashMap cannonBlockMap = new HashMap<>();
+ private final HashMap schematicMap = new HashMap<>();
private final EnumSet allowedMaterials = EnumSet.noneOf(Material.class);
@@ -245,43 +251,6 @@ public Location getFiringTrigger(Cannon cannon)
}
return null;
}
-
- /**
- * returns a list of all cannonBlocks
- * @param cannonDirection - the direction the cannon is facing
- * @return List of cannon blocks
- */
- public List getAllCannonBlocks(BlockFace cannonDirection)
- {
- CannonBlocks cannonBlocks = cannonBlockMap.get(cannonDirection);
- if (cannonBlocks != null)
- {
- return cannonBlocks.getAllCannonBlocks();
- }
-
- return new ArrayList<>();
- }
-
-
- /**
- * returns a list of all cannonBlocks
- * @param cannon
- * @return
- */
- public List getAllCannonBlocks(Cannon cannon)
- {
- CannonBlocks cannonBlocks = cannonBlockMap.get(cannon.getCannonDirection());
- List locList = new ArrayList<>();
- if (cannonBlocks != null)
- {
- for (SimpleBlock block : cannonBlocks.getAllCannonBlocks())
- {
- Vector vect = block.toVector();
- locList.add(vect.clone().add(cannon.getOffset()).toLocation(cannon.getWorldBukkit()));
- }
- }
- return locList;
- }
/**
* returns a list of all destructible blocks
@@ -552,13 +521,22 @@ public void setRotatable(boolean isRotatable)
}
- public void putCannonBlockMap(BlockFace cannonDirection, CannonBlocks blocks) {
- for (var block : blocks.getAllCannonBlocks()) {
- allowedMaterials.add(block.getBlockData().getMaterial());
- }
+ public void putCannonBlockMap(BlockFace cannonDirection, CannonBlocks blocks) {
+ cannonBlockMap.put(cannonDirection, blocks);
+ }
- cannonBlockMap.put(cannonDirection, blocks);
- }
+ public void putSchematicMap(BlockFace cannonDirection, Schematic blocks) {
+ Schematic parsed = SchematicWorldProcessorImpl.getProcessor().parseToMaterial(blocks);
+ parsed.forEach(b -> {
+ Material m = Registry.MATERIAL.get(
+ NamespacedKey.minecraft(b.key().key())
+ );
+
+ allowedMaterials.add(m);
+ });
+
+ schematicMap.put(cannonDirection, blocks);
+ }
public boolean isAllowedMaterial(Material m) {
return allowedMaterials.contains(m);
@@ -567,7 +545,7 @@ public boolean isAllowedMaterial(Material m) {
@Override
public String toString()
{
- return "designID:" + designID + " name:" + designName + " blocks:" + getAllCannonBlocks(BlockFace.NORTH).size();
+ return "designID:" + designID + " name:" + designName + " blocks:" + schematicMap.get(BlockFace.NORTH).positions().size();
}
/**
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonManager.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonManager.java
index af44a88e..8b05fab2 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonManager.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/CannonManager.java
@@ -7,7 +7,6 @@
import at.pavlov.cannons.config.Config;
import at.pavlov.cannons.config.UserMessages;
import at.pavlov.cannons.container.ItemHolder;
-import at.pavlov.cannons.container.SimpleBlock;
import at.pavlov.cannons.dao.AsyncTaskManager;
import at.pavlov.cannons.dao.LoadWhitelistTask;
import at.pavlov.cannons.event.CannonAfterCreateEvent;
@@ -16,25 +15,34 @@
import at.pavlov.cannons.dao.wrappers.RemoveTaskWrapper;
import at.pavlov.cannons.exchange.BExchanger;
import at.pavlov.cannons.exchange.EmptyExchanger;
+import at.pavlov.cannons.schematic.world.SchematicWorldProcessorImpl;
import at.pavlov.cannons.utils.SoundUtils;
import com.google.common.base.Preconditions;
import lombok.Getter;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.namespace.NamespaceRegistry;
+import me.vaan.schematiclib.base.schematic.OffsetSchematic;
+import me.vaan.schematiclib.base.schematic.OffsetSchematicImpl;
+import me.vaan.schematiclib.base.schematic.Schematic;
import org.bukkit.Bukkit;
import org.bukkit.Location;
+import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
-import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -430,9 +438,21 @@ public void dismantleCannonsInBox(Player player, Location center, int size) {
*/
public static HashSet getCannonsByLocations(List locations) {
HashSet newCannonList = new HashSet<>();
+ Map blocks = new HashMap<>(locations.size());
+ NamespaceRegistry registry = SchematicWorldProcessorImpl.getProcessor().registry();
+ for (Location loc : locations) {
+ UUID world = loc.getWorld().getUID();
+ blocks.put(registry.getBlock(
+ loc.getBlockX(),
+ loc.getBlockY(),
+ loc.getBlockZ(),
+ world
+ ), world);
+ }
+
for (Cannon cannon : getCannonList().values()) {
- for (Location loc : locations) {
- if (cannon.isCannonBlock(loc.getBlock()))
+ for (var loc : blocks.entrySet()) {
+ if (cannon.isCannonBlock(loc.getKey(), loc.getValue()))
newCannonList.add(cannon);
}
}
@@ -521,6 +541,7 @@ public Cannon getCannon(Location cannonBlock, UUID owner, boolean silent) {
//check if there is a cannon at this location
Cannon cannon = checkCannon(cannonBlock, owner);
+ plugin.logDebug("Time to process Schematic: " + (System.nanoTime() - startTime) + "ns");
//if there is no cannon, exit
if (cannon == null)
@@ -583,15 +604,16 @@ private boolean fireCannonBeforeCreateEvent(Cannon cannon, MessageEnum message,
return event;
});
+ MessageEnum msg = cbceEvent.getMessage();
//add cannon to the list if everything was fine and return the cannon
- if (!cbceEvent.isCancelled() && cbceEvent.getMessage() != null && cbceEvent.getMessage() == MessageEnum.CannonCreated) {
+ if (!cbceEvent.isCancelled() && msg == MessageEnum.CannonCreated) {
return true;
}
//send messages
- if (!silent) {
+ if (!silent && msg != null) {
taskManager.scheduler.runTask(player, () -> {
- userMessages.sendMessage(message, player, cannon);
+ userMessages.sendMessage(msg, player, cannon);
SoundUtils.playErrorSound(cannon.getMuzzle());
});
}
@@ -638,54 +660,72 @@ public static Cannon getCannon(UUID uid) {
private Cannon checkCannon(Location cannonBlock, UUID owner) {
// is this block material used for a cannon design
Block block = cannonBlock.getBlock();
- BlockData blockData = cannonBlock.getBlock().getBlockData();
+ SchematicWorldProcessorImpl processor = SchematicWorldProcessorImpl.getProcessor();
+ IBlock blockStuff = processor.registry()
+ .getBlock(block.getX(), block.getY(), block.getZ(), block.getWorld().getUID());
+ Material material = block.getType();
+
if (!isValidCannonBlock(block))
return null;
World world = cannonBlock.getWorld();
- var designList = DesignStorage.getInstance().getCannonDesignList();
- designList = designList.stream().filter(it -> it.isAllowedMaterial(block.getType())).toList();
+
+ var allDesigns = DesignStorage.getInstance().getCannonDesignList();
+ List designList = new ArrayList<>();
+ for (CannonDesign it : allDesigns) {
+ if (it.isAllowedMaterial(material)) {
+ designList.add(it);
+ }
+ }
// check all cannon design if this block is part of the design
for (CannonDesign cannonDesign : designList) {
+ var schemMap = cannonDesign.getSchematicMap();
// check of all directions
for (BlockFace cannonDirection : blockFaces) {
// for all blocks for the design
- List designBlockList = cannonDesign.getAllCannonBlocks(cannonDirection);
+ Schematic schem = schemMap.get(cannonDirection);
//check for empty entries
- if (designBlockList.isEmpty()) {
+ if (schem.positions().isEmpty()) {
plugin.logSevere("There are empty cannon design schematics in your design folder. Please check it.");
return null;
}
- for (SimpleBlock designBlock : designBlockList) {
+
+ for (IBlock designBlock : schem) {
// compare blocks
- if (!designBlock.compareMaterial(blockData)) {
+ if (!designBlock.key().equals(blockStuff.key())) {
continue;
}
// this block is same as in the design, get the offset
- Vector offset = designBlock.subtractInverted(cannonBlock).toVector();
-
- // check all other blocks of the cannon
- boolean isCannon = true;
-
- for (SimpleBlock checkBlocks : designBlockList) {
- if (!checkBlocks.compareMaterial(world, offset)) {
- // if the block does not match this is not the
- // right one
- isCannon = false;
- break;
- }
- }
-
- // this is a cannon
- if (isCannon) {
- return new Cannon(cannonDesign, world.getUID(), offset, cannonDirection, owner);
+ Vector offset = new Vector(
+ cannonBlock.getBlockX() - designBlock.x(),
+ cannonBlock.getBlockY() - designBlock.y(),
+ cannonBlock.getBlockZ() - designBlock.z()
+ );
+
+ OffsetSchematic offsetSchematic = new OffsetSchematicImpl(
+ offset.getBlockX(),
+ offset.getBlockY(),
+ offset.getBlockZ(),
+ schem
+ );
+
+ boolean matches = processor.matches(offsetSchematic, world.getUID());
+ if (matches) {
+ return new Cannon(
+ cannonDesign,
+ world.getUID(),
+ offset,
+ cannonDirection,
+ owner
+ );
}
}
}
}
+
return null;
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/DesignStorage.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/DesignStorage.java
index a986f0f0..7800b608 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/DesignStorage.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/cannon/DesignStorage.java
@@ -2,53 +2,55 @@
import at.pavlov.cannons.Cannons;
import at.pavlov.cannons.config.Config;
-import at.pavlov.internal.container.DesignFileName;
import at.pavlov.cannons.container.ItemHolder;
import at.pavlov.cannons.container.SimpleBlock;
import at.pavlov.cannons.container.SoundHolder;
import at.pavlov.cannons.exchange.BExchanger;
import at.pavlov.cannons.exchange.ExchangeLoader;
+import at.pavlov.cannons.schematic.formats.WorldEditFormat;
+import at.pavlov.cannons.schematic.world.SchematicWorldProcessorImpl;
import at.pavlov.cannons.utils.CannonsUtil;
import at.pavlov.cannons.utils.DesignComparator;
import at.pavlov.cannons.utils.ParseUtils;
import at.pavlov.internal.Exchanger;
import at.pavlov.internal.Key;
-import com.sk89q.worldedit.extent.clipboard.Clipboard;
-import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
-import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
-import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
-import com.sk89q.worldedit.math.BlockVector3;
-import com.sk89q.worldedit.math.transform.AffineTransform;
-import com.sk89q.worldedit.session.ClipboardHolder;
-import com.sk89q.worldedit.util.io.Closer;
-import com.sk89q.worldedit.world.block.BlockState;
+import at.pavlov.internal.container.DesignFileName;
import lombok.Getter;
-import org.bukkit.Bukkit;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.block.ICoord;
+import me.vaan.schematiclib.base.formats.SchematicLoader;
+import me.vaan.schematiclib.base.schematic.Schematic;
+import me.vaan.schematiclib.file.block.FileBlock;
+import me.vaan.schematiclib.file.block.FileCoord;
+import me.vaan.schematiclib.file.formats.VaanFormat;
+import me.vaan.schematiclib.file.schematic.FileSchematic;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
import org.bukkit.block.BlockFace;
-import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.util.Vector;
-import java.io.BufferedInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.EnumSet;
import java.util.List;
+import java.util.Set;
-public class DesignStorage
-{
+public class DesignStorage {
@Getter
private static DesignStorage instance = null;
- private final List cannonDesignList;
+ @Getter
+ private final List cannonDesignList;
private final Cannons plugin;
- private final List cannonBlockMaterials;
+ @Getter
+ private final Set cannonBlockMaterials;
public static void initialize(Cannons cannons) {
if (instance != null)
@@ -61,7 +63,7 @@ public static void initialize(Cannons cannons) {
private DesignStorage(Cannons cannons) {
plugin = cannons;
cannonDesignList = new ArrayList<>();
- cannonBlockMaterials = new ArrayList<>();
+ cannonBlockMaterials = EnumSet.noneOf(Material.class);
}
/**
@@ -85,7 +87,7 @@ public void loadCannonDesigns()
//clear designList before loading
cannonDesignList.clear();
-
+
// check if design folder is empty or does not exist
if (CannonsUtil.isFolderEmpty(getPath()))
{
@@ -109,19 +111,22 @@ public void loadCannonDesigns()
if (loadDesignSchematic(cannonDesign, designFile.schematicString()))
cannonDesignList.add(cannonDesign);
}
-
+
//sort the list so the designs with more cannon blocks comes first
- //important if there is a design with one block less but else identically
+ //important if there is a design with one block less but else identically
Comparator comparator = new DesignComparator();
cannonDesignList.sort(comparator);
- for (CannonDesign cannonDesign : getCannonDesignList()) {
- for (SimpleBlock sBlock : cannonDesign.getAllCannonBlocks(BlockFace.NORTH)){
- Material material = sBlock.getBlockData().getMaterial();
- if (material != Material.AIR && !cannonBlockMaterials.contains(material)) {
- cannonBlockMaterials.add(sBlock.getBlockData().getMaterial());
- }
- }
+ SchematicWorldProcessorImpl processor = SchematicWorldProcessorImpl.getProcessor();
+ for (CannonDesign cannonDesign : cannonDesignList) {
+ Schematic schematic = cannonDesign.getSchematicMap().get(BlockFace.NORTH);
+ Schematic materials = processor.parseToMaterial(schematic);
+ materials.positions().stream()
+ .map(IBlock::key)
+ .map(key -> NamespacedKey.minecraft(key.key()))
+ .map(Registry.MATERIAL::get)
+ .filter(mat -> mat != Material.AIR)
+ .forEach(cannonBlockMaterials::add);
}
@@ -134,7 +139,7 @@ public void loadCannonDesigns()
/**
* returns a list with valid cannon designs (.yml + .schematic)
- *
+ *
* @return
*/
private ArrayList getDesignFiles() {
@@ -164,14 +169,21 @@ private ArrayList getDesignFiles() {
String schematicFile = CannonsUtil.changeExtension(ymlFile, ".schematic");
String schemFile = CannonsUtil.changeExtension(ymlFile, ".schem");
- if (new File(getPath() + schematicFile).isFile()) {
- // there is a shematic file and a .yml file
- designList.add(new DesignFileName(ymlFile, schematicFile));
- } else if (new File(getPath() + schemFile).isFile()) {
- // there is a shematic file and a .yml file
- designList.add(new DesignFileName(ymlFile, schemFile));
- } else {
- plugin.logSevere(schematicFile + " is missing");
+ String vschemFile = CannonsUtil.changeExtension(ymlFile, ".vschem");
+
+ String[] toCheck = new String[] {schemFile, schematicFile, vschemFile};
+ boolean success = false;
+ for (String entry: toCheck) {
+ File file = new File(getPath() + entry);
+ if (file.isFile()) {
+ designList.add(new DesignFileName(ymlFile, entry));
+ success = true;
+ break;
+ }
+ }
+
+ if (!success) {
+ plugin.logSevere("Schematic is missing for configuration: " + ymlFile);
}
}
} catch (Exception e) {
@@ -360,118 +372,104 @@ private void loadDesignYml(CannonDesign cannonDesign, String ymlFile)
cannonDesign.setSoundSelected(new SoundHolder(cannonDesignConfig.getString("sounds.selected","BLOCK_ANVIL_LAND:1:2")));
// constructionBlocks
- cannonDesign.setSchematicBlockTypeIgnore(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.ignore", "minecraft:sand")));
- cannonDesign.setSchematicBlockTypeMuzzle(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.muzzle", "minecraft:snow_block")));
- cannonDesign.setSchematicBlockTypeFiringIndicator(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.firingIndicator", "minecraft:torch")));
- cannonDesign.setSchematicBlockTypeRotationCenter(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.rotationCenter", "minecraft:redstone_ore")));
- cannonDesign.setSchematicBlockTypeChestAndSign(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.chestAndSign", "minecraft:oak_wall_sign")));
- cannonDesign.setSchematicBlockTypeRedstoneTorch(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.redstoneTorch", "minecraft:redstone_torch")));
- cannonDesign.setSchematicBlockTypeRedstoneWireAndRepeater(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.restoneWireAndRepeater", "minecraft:repeater")));
+ cannonDesign.setSchematicBlockTypeIgnore(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.ignore", "minecraft:sand")));
+ cannonDesign.setSchematicBlockTypeMuzzle(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.muzzle", "minecraft:snow_block")));
+ cannonDesign.setSchematicBlockTypeFiringIndicator(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.firingIndicator", "minecraft:torch")));
+ cannonDesign.setSchematicBlockTypeRotationCenter(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.rotationCenter", "minecraft:redstone_ore")));
+ cannonDesign.setSchematicBlockTypeChestAndSign(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.chestAndSign", "minecraft:oak_wall_sign")));
+ cannonDesign.setSchematicBlockTypeRedstoneTorch(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.redstoneTorch", "minecraft:redstone_torch")));
+ cannonDesign.setSchematicBlockTypeRedstoneWireAndRepeater(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.restoneWireAndRepeater", "minecraft:repeater")));
// RedstoneTrigger
- cannonDesign.setSchematicBlockTypeRedstoneTrigger(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.redstoneTrigger.schematic", "minecraft:lever")));
- cannonDesign.setIngameBlockTypeRedstoneTrigger(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.redstoneTrigger.ingame", "minecraft:stone_button")));
+ cannonDesign.setSchematicBlockTypeRedstoneTrigger(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.redstoneTrigger.schematic", "minecraft:lever")));
+ cannonDesign.setIngameBlockTypeRedstoneTrigger(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.redstoneTrigger.ingame", "minecraft:stone_button")));
// rightClickTrigger
- cannonDesign.setSchematicBlockTypeRightClickTrigger(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.rightClickTrigger.schematic", "minecraft:torch")));
- cannonDesign.setIngameBlockTypeRightClickTrigger(CannonsUtil.createBlockData(cannonDesignConfig.getString("constructionBlocks.rightClickTrigger.ingame", "minecraft:torch")));
+ cannonDesign.setSchematicBlockTypeRightClickTrigger(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.rightClickTrigger.schematic", "minecraft:torch")));
+ cannonDesign.setIngameBlockTypeRightClickTrigger(ParseUtils.toBlockKey(cannonDesignConfig.getString("constructionBlocks.rightClickTrigger.ingame", "minecraft:torch")));
// protected Blocks
- cannonDesign.setSchematicBlockTypeProtected(ParseUtils.toBlockDataList(cannonDesignConfig.getStringList("constructionBlocks.protectedBlocks")));
+ cannonDesign.setSchematicBlockTypeProtected(ParseUtils.toBlockKeyList(cannonDesignConfig.getStringList("constructionBlocks.protectedBlocks")));
}
- private Clipboard loadSchematic(String schematicFile) {
- String schematicPath = getPath() + schematicFile;
- File f = new File(schematicPath);
- ClipboardFormat format = ClipboardFormats.findByFile(f);
-
- if(format == null) {
- plugin.logSevere("Error while loading schematic " + schematicPath + " : Format not found");
- return null;
- }
-
- try (Closer closer = Closer.create()) {
- FileInputStream fis = closer.register(new FileInputStream(f));
- BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
- ClipboardReader reader = closer.register(format.getReader(bis));
-
- return reader.read();
- } catch (IOException e) {
- plugin.logSevere("Error while loading schematic " + schematicPath + " : IO Error");
- return null;
- }
- }
+ private static boolean isSchematicValid(Schematic schematic) {
+ try {
+ SchematicWorldProcessorImpl.getProcessor().parseToMaterial(schematic);
+ return true;
+ } catch (Throwable throwable) {
+ return false;
+ }
+ }
/**
* loads the schematic of the config file
* @param cannonDesign design of the cannon
* @param schematicFile path of the schematic file
*/
+ private static final BlockFace[] HORIZONTALS = new BlockFace[] {BlockFace.EAST, BlockFace.SOUTH, BlockFace.NORTH, BlockFace.WEST};
private boolean loadDesignSchematic(CannonDesign cannonDesign, String schematicFile) {
long startTime = System.nanoTime();
-
- // load schematic with worldedit
- Clipboard cc = loadSchematic(schematicFile);
- //failed to load schematic
- if (cc == null) {
- return false;
- }
+
+ String schematicPath = getPath() + schematicFile;
+ File schemFile = new File(schematicPath);
// convert all schematic blocks from the config to BaseBlocks so they
// can be rotated
- BlockData blockIgnore = cannonDesign.getSchematicBlockTypeIgnore();
- BlockData blockMuzzle = cannonDesign.getSchematicBlockTypeMuzzle();
- BlockData blockFiringIndicator = cannonDesign.getSchematicBlockTypeFiringIndicator();
- BlockData blockRotationCenter = cannonDesign.getSchematicBlockTypeRotationCenter();
- BlockData blockChestAndSign = cannonDesign.getSchematicBlockTypeChestAndSign();
- BlockData blockRedstoneTorch = cannonDesign.getSchematicBlockTypeRedstoneTorch();
- BlockData blockRedstoneWireAndRepeater = cannonDesign.getSchematicBlockTypeRedstoneWireAndRepeater();
- BlockData blockRedstoneTrigger = cannonDesign.getSchematicBlockTypeRedstoneTrigger();
- BlockData blockRightClickTrigger = cannonDesign.getSchematicBlockTypeRightClickTrigger();
- BlockData replaceRedstoneTrigger = cannonDesign.getIngameBlockTypeRedstoneTrigger();
- BlockData replaceRightClickTrigger = cannonDesign.getIngameBlockTypeRightClickTrigger();
- List blockProtectedList = new ArrayList<>(cannonDesign.getSchematicBlockTypeProtected());
-
- // get facing of the cannon
- BlockFace cannonDirection = cannonDesign.getDefaultHorizontalFacing();
-
- ClipboardHolder clipboardHolder = new ClipboardHolder(cc);
- clipboardHolder.setTransform(new AffineTransform().translate(cc.getMinimumPoint().multiply(-1)));
- cc = clipboardHolder.getClipboard();
-
- // read out blocks
- int width = cc.getDimensions().getBlockX();
- int height = cc.getDimensions().getBlockY();
- int length = cc.getDimensions().getBlockZ();
-
- cc.setOrigin(BlockVector3.ZERO);
+ BlockKey blockIgnore = cannonDesign.getSchematicBlockTypeIgnore();
+ BlockKey blockMuzzle = cannonDesign.getSchematicBlockTypeMuzzle();
+ BlockKey blockFiringIndicator = cannonDesign.getSchematicBlockTypeFiringIndicator();
+ BlockKey blockRotationCenter = cannonDesign.getSchematicBlockTypeRotationCenter();
+ BlockKey blockChestAndSign = cannonDesign.getSchematicBlockTypeChestAndSign();
+ BlockKey blockRedstoneTorch = cannonDesign.getSchematicBlockTypeRedstoneTorch();
+ BlockKey blockRedstoneWireAndRepeater = cannonDesign.getSchematicBlockTypeRedstoneWireAndRepeater();
+ BlockKey blockRedstoneTrigger = cannonDesign.getSchematicBlockTypeRedstoneTrigger();
+ BlockKey blockRightClickTrigger = cannonDesign.getSchematicBlockTypeRightClickTrigger();
+ BlockKey replaceRedstoneTrigger = cannonDesign.getIngameBlockTypeRedstoneTrigger();
+ BlockKey replaceRightClickTrigger = cannonDesign.getIngameBlockTypeRightClickTrigger();
+ List blockProtectedList = new ArrayList<>(cannonDesign.getSchematicBlockTypeProtected());
+
+ // get facing of the cannon
+ BlockFace cannonDirection = cannonDesign.getDefaultHorizontalFacing();
//plugin.logDebug("design: " + schematicFile);
- ArrayList schematicList = getSchematic(width, height, length, cc, blockIgnore);
+ Schematic blocks = getSchematic(schemFile, blockIgnore);
+ if (!isSchematicValid(blocks)) {
+ plugin.logSevere("Schematic " + schematicFile + " is invalid, maybe it has custom blocks whose plugin has been removed?");
+ return false;
+ }
+ if (blocks == null) return false;
- for (int i = 0; i < 4; i++) {
- // create CannonBlocks entry
- CannonBlocks cannonBlocks = new CannonBlocks();
+ ICoord max = blocks.getMax();
+ ICoord min = blocks.getMin();
+ int width = max.x() - min.x() + 1;
+ int height = max.y() - min.y() + 1;
+ int length = max.z() - min.z() + 1;
- // to set the muzzle location the maximum and mininum x, y, z values
- // of all muzzle blocks have to be found
- Vector minMuzzle = new Vector(0, 0, 0);
- Vector maxMuzzle = new Vector(0, 0, 0);
- boolean firstEntryMuzzle = true;
-
- // to set the rotation Center maximum and mininum x, y, z values
- // of all rotation blocks have to be found
- // setting max to the size of the marked area is a good approximation
- // if no rotationblock is given
- Vector minRotation = new Vector(0, 0, 0);
- Vector maxRotation = new Vector(width, height, length);
- boolean firstEntryRotation = true;
-
- for (SimpleBlock sblock : schematicList) {
- int x = sblock.getLocX();
- int y = sblock.getLocY();
- int z = sblock.getLocZ();
+ for (int i = 0; i < 4; i++) {
+ // create CannonBlocks entry
+ CannonBlocks cannonBlocks = new CannonBlocks();
+ List filteredSchematic = new ArrayList<>();
+
+ // to set the muzzle location the maximum and mininum x, y, z values
+ // of all muzzle blocks have to be found
+ Vector minMuzzle = new Vector(0, 0, 0);
+ Vector maxMuzzle = new Vector(0, 0, 0);
+ boolean firstEntryMuzzle = true;
+
+ // to set the rotation Center maximum and mininum x, y, z values
+ // of all rotation blocks have to be found
+ // setting max to the size of the marked area is a good approximation
+ // if no rotationblock is given
+ Vector minRotation = new Vector(0, 0, 0);
+ Vector maxRotation = new Vector(width, height, length);
+ boolean firstEntryRotation = true;
+
+ for (IBlock sblock : blocks) {
+ int x = sblock.x();
+ int y = sblock.y();
+ int z = sblock.z();
// ############# find the min and max for muzzle blocks so the
// cannonball is fired from the middle
- if (sblock.compareMaterial(blockMuzzle)) {
+ BlockKey key = sblock.key();
+ if (key.equals(blockMuzzle)) {
// reset for the first entry
if (firstEntryMuzzle) {
firstEntryMuzzle = false;
@@ -482,10 +480,11 @@ private boolean loadDesignSchematic(CannonDesign cannonDesign, String schematicF
setMaximum(x, y, z, maxMuzzle);
}
//muzzle blocks need to be air - else the projectile would spawn in a block
- cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, Material.AIR));
+ //cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, Material.AIR));
+ filteredSchematic.add(new FileBlock(x, y, z, BlockKey.mc("air")));
}
// ############# find the min and max for rotation blocks
- else if (sblock.compareMaterial(blockRotationCenter)) {
+ else if (key.equals(blockRotationCenter)) {
// reset for the first entry
if (firstEntryRotation) {
firstEntryRotation = false;
@@ -497,69 +496,87 @@ else if (sblock.compareMaterial(blockRotationCenter)) {
}
}
// ############# redstoneTorch
- else if (sblock.compareMaterial(blockRedstoneTorch))
+ else if (key.equals(blockRedstoneTorch)) {
cannonBlocks.addRedstoneTorch(new Vector(x, y, z));
+ }
// ############# redstoneWire and Repeater
- else if (sblock.compareMaterial(blockRedstoneWireAndRepeater))
+ else if (key.equals(blockRedstoneWireAndRepeater)) {
cannonBlocks.addRedstoneWiresAndRepeater(new SimpleBlock(x, y, z, Material.REPEATER));
- // ############# redstoneTrigger
- else if (sblock.compareMaterial(blockRedstoneTrigger)) {
- cannonBlocks.addRedstoneTrigger(new Vector(x, y, z));
- // buttons or levers are part of the cannon
- cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, replaceRedstoneTrigger));
- // this can be a destructible block
- if (!isInList(blockProtectedList, sblock.getBlockData()))
- cannonBlocks.addDestructibleBlocks(new Vector(x, y, z));
}
- // ############# rightClickTrigger
- else if (sblock.compareMaterial(blockRightClickTrigger)) {
- cannonBlocks.addRightClickTrigger(new Vector(x, y, z));
- //can be also a sign
- if (sblock.compareMaterial(blockChestAndSign))
+ // ############# redstoneTrigger
+ else if (!key.equals(blockRedstoneTrigger)) {
+ if (key.equals(blockRightClickTrigger)) {
+ cannonBlocks.addRightClickTrigger(new Vector(x, y, z));
+ //can be also a sign
+ if (key.equals(blockChestAndSign))
+ // the id does not matter, but the data is important for signs
+ cannonBlocks.addChestsAndSigns(new SimpleBlock(x, y, z, key)); //Material.WALL_SIGN
+ // firing blocks are also part of the cannon are
+ // part of the cannon
+ //cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, replaceRightClickTrigger));
+ filteredSchematic.add(new FileBlock(x, y, z, replaceRightClickTrigger));
+ // this can be a destructible block
+ if (!isInList(blockProtectedList, key))
+ cannonBlocks.addDestructibleBlocks(new Vector(x, y, z));
+ }
+ // ############# chests and signs
+ else if (key.equals(blockChestAndSign)) {
// the id does not matter, but the data is important for signs
- cannonBlocks.addChestsAndSigns(new SimpleBlock(x, y, z, sblock.getBlockData())); //Material.WALL_SIGN
- // firing blocks are also part of the cannon are
- // part of the cannon
- cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, replaceRightClickTrigger));
- // this can be a destructible block
- if (!isInList(blockProtectedList, sblock.getBlockData()))
- cannonBlocks.addDestructibleBlocks(new Vector(x, y, z));
- }
- // ############# chests and signs
- else if (sblock.compareMaterial(blockChestAndSign)) {
- // the id does not matter, but the data is important for signs
- cannonBlocks.addChestsAndSigns(new SimpleBlock(x, y, z, sblock.getBlockData())); //Material.WALL_SIGN
+ cannonBlocks.addChestsAndSigns(new SimpleBlock(x, y, z, key)); //Material.WALL_SIGN
+ }
+ // ############# loading Interface is a cannonblock that is non of
+ // the previous blocks
+ else {
+ // all remaining blocks are loading interface or cannonBlocks
+ cannonBlocks.addBarrelBlocks(new Vector(x, y, z));
+ //cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, key));
+ filteredSchematic.add(new FileBlock(x, y, z, key));
+ // this can be a destructible block
+ if (!isInList(blockProtectedList, key))
+ cannonBlocks.addDestructibleBlocks(new Vector(x, y, z));
+ }
}
- // ############# loading Interface is a cannonblock that is non of
- // the previous blocks
+ // ############# rightClickTrigger
else {
- // all remaining blocks are loading interface or cannonBlocks
- cannonBlocks.addBarrel(new Vector(x, y, z));
- cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, sblock.getBlockData()));
+ cannonBlocks.addRedstoneTrigger(new Vector(x, y, z));
+ // buttons or levers are part of the cannon
+ //cannonBlocks.addAllCannonBlocks(new SimpleBlock(x, y, z, replaceRedstoneTrigger));
+ filteredSchematic.add(new FileBlock(x, y, z, replaceRedstoneTrigger));
// this can be a destructible block
- if (!isInList(blockProtectedList, sblock.getBlockData()))
+ if (!isInList(blockProtectedList, key))
cannonBlocks.addDestructibleBlocks(new Vector(x, y, z));
}
+
// ############# firingIndicator
// can be everywhere on the cannon
- if (sblock.compareMaterial(blockFiringIndicator))
+ if (key.equals(blockFiringIndicator))
cannonBlocks.addFiringIndicator(new Vector(x, y, z));
}
- // calculate the muzzle location
- maxMuzzle.add(new Vector(1, 1, 1));
- cannonBlocks.setMuzzle(maxMuzzle.add(minMuzzle).multiply(0.5));
+ // calculate the muzzle location
+ maxMuzzle.add(new Vector(1, 1, 1));
+ cannonBlocks.setMuzzle(maxMuzzle.add(minMuzzle).multiply(0.5));
- // calculate the rotation Center
- maxRotation.add(new Vector(1, 1, 1));
- cannonBlocks.setRotationCenter(maxRotation.add(maxRotation).multiply(0.5));
+ // calculate the rotation Center
+ maxRotation.add(new Vector(1, 1, 1));
+ cannonBlocks.setRotationCenter(maxRotation.add(maxRotation).multiply(0.5));
//set the muzzle location
Vector compensation = new Vector(cannonBlocks.getMuzzle().getBlockX(), cannonBlocks.getMuzzle().getBlockY(), cannonBlocks.getMuzzle().getBlockZ());
- for (SimpleBlock block : cannonBlocks.getAllCannonBlocks())
- block.directSubtract(compensation);
+ List actualSchematic = new ArrayList<>();
+ for (IBlock iblock : filteredSchematic)
+ actualSchematic.add(
+ iblock.add(
+ new FileCoord(
+ -compensation.getBlockX(),
+ -compensation.getBlockY(),
+ -compensation.getBlockZ()
+ )
+ )
+ );
+
for (Vector block : cannonBlocks.getBarrelBlocks())
block.subtract(compensation);
for (SimpleBlock block : cannonBlocks.getChestsAndSigns())
@@ -579,33 +596,28 @@ else if (sblock.compareMaterial(blockChestAndSign)) {
cannonBlocks.getMuzzle().subtract(compensation);
cannonBlocks.getRotationCenter().subtract(compensation);
- // add blocks to the HashMap
- cannonDesign.putCannonBlockMap(cannonDirection, cannonBlocks);
-
- //rotate blocks for the next iteration
- blockIgnore = CannonsUtil.roateBlockFacingClockwise(blockIgnore);
- blockMuzzle = CannonsUtil.roateBlockFacingClockwise(blockMuzzle);
- blockFiringIndicator = CannonsUtil.roateBlockFacingClockwise(blockFiringIndicator);
- blockRotationCenter = CannonsUtil.roateBlockFacingClockwise(blockRotationCenter);
- blockChestAndSign = CannonsUtil.roateBlockFacingClockwise(blockChestAndSign);
- blockRedstoneTorch = CannonsUtil.roateBlockFacingClockwise(blockRedstoneTorch);
- blockRedstoneTrigger = CannonsUtil.roateBlockFacingClockwise(blockRedstoneTrigger);
- blockRightClickTrigger = CannonsUtil.roateBlockFacingClockwise(blockRightClickTrigger);
- replaceRedstoneTrigger = CannonsUtil.roateBlockFacingClockwise(replaceRedstoneTrigger);
- replaceRightClickTrigger = CannonsUtil.roateBlockFacingClockwise(replaceRightClickTrigger);
-
- blockProtectedList = blockProtectedList.stream().map(CannonsUtil::roateBlockFacingClockwise).toList();
-
- //rotate schematic blocks
- for (SimpleBlock simpleBlock : schematicList){
- simpleBlock.rotate90();
- }
+ // add blocks to the HashMap
+ cannonDesign.putCannonBlockMap(cannonDirection, cannonBlocks);
+ cannonDesign.putSchematicMap(cannonDirection, new FileSchematic(actualSchematic));
+
+ //rotate schematic blocks
+ ArrayList newList = new ArrayList<>();
+ for (IBlock simpleBlock : blocks){
+ newList.add(
+ new FileBlock(
+ -simpleBlock.z(),
+ simpleBlock.y(),
+ simpleBlock.x(),
+ simpleBlock.key()
+ )
+ );
+ }
- //rotate cannonDirection
- cannonDirection = CannonsUtil.roatateFace(cannonDirection);
+ blocks = new FileSchematic(newList);
+ cannonDirection = CannonsUtil.roatateFace(cannonDirection);
+ }
- }
plugin.logDebug("Time to load designs: " + new DecimalFormat("0.00").format((System.nanoTime() - startTime)/1000000.0) + "ms");
return true;
@@ -653,31 +665,26 @@ private void copyFile(String fileName)
CannonsUtil.copyFile(plugin.getResource("designs/" + fileName + ".schematic"), SchematicFile);
}
}
-
- private boolean isInList(List list, BlockData block)
+
+ private boolean isInList(List list, T block)
{
if (block == null) return true;
-
- for (BlockData listBlock : list)
+
+ for (T listBlock : list)
{
- if (listBlock != null && listBlock.getMaterial().equals(block.getMaterial()))
+ if (listBlock != null && listBlock.equals(block))
return true;
}
return false;
}
-
- private String getPath()
+
+ public static String getPath()
{
// Directory path here
return "plugins/Cannons/designs/";
}
-
- public List getCannonDesignList()
- {
- return cannonDesignList;
- }
-
- /**
+
+ /**
* returns the cannon design of the cannon
* @param cannon the cannon
* @return design of cannon
@@ -686,7 +693,7 @@ public CannonDesign getDesign(Cannon cannon)
{
return getDesign(cannon.getDesignID());
}
-
+
/**
* returns the cannon design by its id
* @param designId Name of the design
@@ -716,34 +723,27 @@ public boolean hasDesign(String name){
return false;
}
- public List getCannonBlockMaterials() {
- return cannonBlockMaterials;
- }
-
- public boolean isCannonBlockMaterial(Material material) {
+ public boolean isCannonBlockMaterial(Material material) {
return material != Material.AIR && cannonBlockMaterials.contains(material);
}
- private ArrayList getSchematic(int width, int height, int length, Clipboard cc, BlockData blockIgnore) {
- ArrayList schematiclist = new ArrayList<>();
- for (int x = 0; x < width; ++x) {
- for (int y = 0; y < height; ++y) {
- for (int z = 0; z < length; ++z) {
-
- BlockVector3 pt = BlockVector3.at(x, y, z);
- BlockState blockState = cc.getBlock(pt.add(cc.getMinimumPoint()));
- //plugin.logDebug("blockstate: " + blockState.getAsString());
- BlockData block = Bukkit.getServer().createBlockData(blockState.getAsString());
+ private static final SchematicLoader[] loaders = new SchematicLoader[] {
+ new WorldEditFormat(),
+ new VaanFormat()
+ };
+ private Schematic getSchematic(File file, BlockKey blockIgnore) {
+ for (SchematicLoader loader : loaders) {
+ try {
+ Schematic schm = loader.load(file);
+ if (schm == null) continue;
+ List list = schm.positions().stream().filter(it -> !it.key().equals(blockIgnore) && !it.key().key().equals("air")).toList();
+ return new FileSchematic(list);
+ } catch (Throwable ignored) {}
+ }
- // ignore if block is AIR or the IgnoreBlock type
- if (!block.getMaterial().equals(Material.AIR) && !block.matches(blockIgnore)) {
- schematiclist.add(new SimpleBlock(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ(), block));
- }
- }
- }
- }
- return schematiclist;
+ plugin.logSevere("Couldn't load " + file.getPath());
+ return null;
}
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/CannonsCommandManager.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/CannonsCommandManager.java
index a0c61fa7..90e14a40 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/CannonsCommandManager.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/CannonsCommandManager.java
@@ -1,12 +1,16 @@
package at.pavlov.cannons.commands;
import at.pavlov.cannons.Enum.SelectCannon;
+import at.pavlov.cannons.cannon.DesignStorage;
+import at.pavlov.cannons.projectile.ProjectileStorage;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.PaperCommandManager;
+import com.google.common.collect.ImmutableList;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
+import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
@@ -14,12 +18,13 @@
public class CannonsCommandManager extends PaperCommandManager {
public CannonsCommandManager(Plugin plugin) {
super(plugin);
- this.registerContexts();
+ this.registerCommandContexts();
+ this.registerCommandCompletions();
}
- private void registerContexts() {
- var context = this.getCommandContexts();
- context.registerContext(SelectCannon.class, c -> {
+ private void registerCommandContexts() {
+ var commandContexts = this.getCommandContexts();
+ commandContexts.registerContext(SelectCannon.class, c -> {
String select = c.popFirstArg();
switch (select.toLowerCase(Locale.ROOT)) {
case "mob" -> {
@@ -43,6 +48,12 @@ private void registerContexts() {
});
}
+ private void registerCommandCompletions() {
+ var commandCompletions = this.getCommandCompletions();
+ commandCompletions.registerCompletion("cannon_designs", c -> Collections.unmodifiableList(DesignStorage.getInstance().getDesignIds()));
+ commandCompletions.registerCompletion("cannon_projectiles" , c -> Collections.unmodifiableList(ProjectileStorage.getProjectileIds()));
+ }
+
private static final Pattern COMMA = Pattern.compile(",");
private static final Pattern PIPE = Pattern.compile("\\|");
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/Commands.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/Commands.java
index 6dafbdc7..45640363 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/Commands.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/commands/Commands.java
@@ -18,11 +18,15 @@
import at.pavlov.cannons.projectile.Projectile;
import at.pavlov.cannons.projectile.ProjectileStorage;
import at.pavlov.cannons.projectile.definitions.ProjectileDefinitionLoader;
+import at.pavlov.cannons.schematic.SchemUtil;
+import at.pavlov.cannons.schematic.world.SchematicWorldProcessorImpl;
import at.pavlov.cannons.utils.CannonSelector;
import at.pavlov.cannons.utils.CannonsUtil;
import at.pavlov.cannons.utils.StringUtils;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.*;
+import me.vaan.schematiclib.base.schematic.Schematic;
+import me.vaan.schematiclib.file.formats.VaanFormat;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
@@ -31,6 +35,7 @@
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
+import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -38,7 +43,6 @@
import java.util.concurrent.CompletableFuture;
@CommandAlias("cannons")
-@SuppressWarnings("deprecation")
public class Commands extends BaseCommand {
private static final String tag = "[Cannons] ";
@@ -165,6 +169,7 @@ public static void onList(CommandSender sender, @Optional String arg) {
@Subcommand("create")
@Syntax("[DESIGN]")
@CommandPermission("cannons.admin.create")
+ @CommandCompletion("@cannon_designs")
public static void onCreate(Player player, String arg) {
//check if the design name is valid
if (!designStorage.hasDesign(arg)) {
@@ -183,6 +188,7 @@ public static void onCreate(Player player, String arg) {
@Subcommand("give")
@Syntax("[PROJECTILE] ")
@CommandPermission("cannons.admin.give")
+ @CommandCompletion("@cannon_projectiles")
public static void onGive(Player player, String projectileString, @Default("1") int amount) {
//check if the projectile id is valid
Projectile projectile = ProjectileStorage.getProjectile(projectileString);
@@ -315,6 +321,50 @@ public static void onObserver(Player player, String[] args) {
//sendMessage(sender, ChatColor.RED + "Usage '/cannons observer' or '/cannons observer ' or '/cannons observer '");
}
+ @Subcommand("schematic")
+ @CommandPermission("cannons.admin.reload")
+ public class onSchematic extends BaseCommand {
+ private static final SchematicWorldProcessorImpl processor = SchematicWorldProcessorImpl.getProcessor();
+
+ @Default
+ public static void help(Player player) {
+ sendMessage(player, ChatColor.RED + "Usage '/cannons schematic '");
+ }
+
+ @Subcommand("tool")
+ public static void tool(Player player) {
+ player.getInventory().addItem(SchemUtil.SELECT_TOOL.clone());
+ }
+
+ @Subcommand("save")
+ @Syntax("")
+ public static void save(Player player, String name) {
+ String fullName = name + ".vschem";
+ if (!SchemUtil.coord1.getWorld().equals(SchemUtil.coord2.getWorld())) {
+ sendMessage(player, ChatColor.RED + "[Cannons] Coordinates must be in the same world");
+ return;
+ }
+
+ Schematic schematic = processor.schematicOf(
+ SchemUtil.coord1.getX(), SchemUtil.coord1.getY(), SchemUtil.coord1.getZ(),
+ SchemUtil.coord2.getX(), SchemUtil.coord2.getY(), SchemUtil.coord2.getZ(),
+ SchemUtil.coord1.getWorld().getUID()
+ );
+
+ String fullPath = DesignStorage.getPath() + fullName;
+ File file = new File(fullPath);
+
+ VaanFormat format = new VaanFormat();
+ try {
+ format.save(file, schematic);
+ } catch (Throwable e) {
+ sendMessage(player, ChatColor.RED + "[Cannons] Failed to save");
+ }
+
+ sendMessage(player, ChatColor.GREEN + "[Cannons] Successful save");
+ }
+ }
+
@Subcommand("whitelist")
@CommandPermission("cannons.player.whitelist")
public class onWhitelist extends BaseCommand {
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/container/SimpleBlock.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/container/SimpleBlock.java
index b8de6cc1..9bf4e211 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/container/SimpleBlock.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/container/SimpleBlock.java
@@ -4,8 +4,12 @@
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
import org.bukkit.Location;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -14,10 +18,11 @@
@Setter
@Getter
@ToString
-public class SimpleBlock {
+public class SimpleBlock implements IBlock {
private int locX;
private int locY;
private int locZ;
+ private Material material;
private BlockData blockData;
public SimpleBlock(int x, int y, int z, BlockData blockData) {
@@ -25,6 +30,7 @@ public SimpleBlock(int x, int y, int z, BlockData blockData) {
locY = y;
locZ = z;
+ this.material = blockData.getMaterial();
this.blockData = blockData;
}
@@ -41,11 +47,11 @@ private SimpleBlock(Vector vect, Material material) {
}
public SimpleBlock(Location loc, Material material) {
- locX = loc.getBlockX();
- locY = loc.getBlockY();
- locZ = loc.getBlockZ();
+ this(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), material);
+ }
- this.blockData = material.createBlockData();
+ public SimpleBlock(int x, int y, int z, BlockKey key) {
+ this(x, y, z, Registry.MATERIAL.get(new NamespacedKey(key.namespace(), key.key())));
}
@@ -90,7 +96,7 @@ public boolean compareMaterial(World world, Vector offset) {
* @return true if both block match
*/
public boolean compareMaterial(BlockData block) {
- return block.getMaterial().equals(this.blockData.getMaterial());
+ return block.getMaterial().equals(this.material);
}
/**
@@ -99,7 +105,7 @@ public boolean compareMaterial(BlockData block) {
* @return true if both block match
*/
public boolean compareBlockData(BlockData blockData) {
- return this.blockData.matches(blockData);
+ return this.material.equals(blockData.getMaterial());
}
/**
@@ -108,7 +114,7 @@ public boolean compareBlockData(BlockData blockData) {
* @return new Simpleblock
*/
public SimpleBlock add(Location loc) {
- return new SimpleBlock(locX + loc.getBlockX(), locY + loc.getBlockY(), locZ + loc.getBlockZ(), this.blockData);
+ return new SimpleBlock(locX + loc.getBlockX(), locY + loc.getBlockY(), locZ + loc.getBlockZ(), this.material);
}
/**
@@ -117,7 +123,7 @@ public SimpleBlock add(Location loc) {
* @return a new block with a shifted location
*/
public SimpleBlock add(Vector vect) {
- return new SimpleBlock(toVector().add(vect), this.blockData);
+ return new SimpleBlock(toVector().add(vect), this.material);
}
/**
@@ -126,7 +132,7 @@ public SimpleBlock add(Vector vect) {
* @return new block with new subtracted location
*/
public SimpleBlock subtract(Vector vect) {
- return new SimpleBlock(vect.getBlockX() - locX, vect.getBlockY() - locY, vect.getBlockZ() - locZ, this.blockData);
+ return new SimpleBlock(vect.getBlockX() - locX, vect.getBlockY() - locY, vect.getBlockZ() - locZ, this.material);
}
/**
@@ -143,7 +149,7 @@ public void directSubtract(Vector vect) {
* shifts the location of the block without comparing the id
*/
public SimpleBlock subtractInverted(Location loc) {
- return new SimpleBlock(loc.getBlockX() - locX, loc.getBlockY() - locY, loc.getBlockZ() - locZ, this.blockData);
+ return new SimpleBlock(loc.getBlockX() - locX, loc.getBlockY() - locY, loc.getBlockZ() - locZ, this.material);
}
@@ -151,7 +157,7 @@ public SimpleBlock subtractInverted(Location loc) {
* shifts the location of the block without comparing the id
*/
public SimpleBlock subtract(Location loc) {
- return new SimpleBlock(locX - loc.getBlockX(), locY - loc.getBlockY(), locZ - loc.getBlockZ(), this.blockData);
+ return new SimpleBlock(locX - loc.getBlockX(), locY - loc.getBlockY(), locZ - loc.getBlockZ(), this.material);
}
/**
@@ -169,4 +175,25 @@ public void rotate90() {
public Vector toVector() {
return new Vector(locX, locY, locZ);
}
+
+ @Override
+ public int x() {
+ return locX;
+ }
+
+ @Override
+ public int y() {
+ return locY;
+ }
+
+ @Override
+ public int z() {
+ return locZ;
+ }
+
+ @Override
+ public BlockKey key() {
+ NamespacedKey key = material.getKey();
+ return new BlockKey(key.getNamespace(), key.getKey());
+ }
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftCannonTracker.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftCannonTracker.java
new file mode 100644
index 00000000..79790152
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftCannonTracker.java
@@ -0,0 +1,24 @@
+package at.pavlov.cannons.hooks.movecraft;
+
+import at.pavlov.cannons.cannon.Cannon;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class MovecraftCannonTracker {
+ private static final Map> cannonMap = new HashMap<>();
+
+ public static Set getCannons(UUID uuid) {
+ return cannonMap.get(uuid);
+ }
+
+ public static void setCannons(UUID craftId, Set cannons) {
+ cannonMap.put(craftId, cannons);
+ }
+
+ public static void clearCannons(UUID craftId) {
+ cannonMap.remove(craftId);
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftHook.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftHook.java
index 226f7644..8fa0fdd0 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftHook.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/MovecraftHook.java
@@ -2,6 +2,8 @@
import at.pavlov.cannons.Cannons;
import at.pavlov.cannons.hooks.BukkitHook;
+import at.pavlov.cannons.hooks.movecraft.listener.CraftReleaseListener;
+import at.pavlov.cannons.hooks.movecraft.listener.PreventRecreateHandler;
import at.pavlov.cannons.hooks.movecraft.listener.CraftDetectListener;
import at.pavlov.cannons.hooks.movecraft.listener.RotationListener;
import at.pavlov.cannons.hooks.movecraft.listener.SinkListener;
@@ -9,7 +11,6 @@
import at.pavlov.internal.Hook;
import net.countercraft.movecraft.Movecraft;
import org.bukkit.ChatColor;
-import org.bukkit.event.HandlerList;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
@@ -42,6 +43,8 @@ public void onEnable() {
pluginManager.registerEvents(new TranslationListener(), plugin);
pluginManager.registerEvents(new RotationListener(), plugin);
pluginManager.registerEvents(new SinkListener(), plugin);
+ pluginManager.registerEvents(new PreventRecreateHandler(), plugin);
+ pluginManager.registerEvents(new CraftReleaseListener(), plugin);
plugin.logInfo(ChatColor.GREEN + enabledMessage());
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftDetectListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftDetectListener.java
index ed05c327..54dcaa4a 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftDetectListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftDetectListener.java
@@ -1,16 +1,13 @@
package at.pavlov.cannons.hooks.movecraft.listener;
import at.pavlov.cannons.Cannons;
-import at.pavlov.cannons.Enum.BreakCause;
import at.pavlov.cannons.cannon.Cannon;
-import at.pavlov.cannons.cannon.CannonManager;
+import at.pavlov.cannons.hooks.movecraft.MovecraftCannonTracker;
import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import at.pavlov.cannons.hooks.movecraft.type.MaxCannonsEntry;
import net.countercraft.movecraft.craft.Craft;
-import net.countercraft.movecraft.craft.PlayerCraft;
import net.countercraft.movecraft.craft.type.CraftType;
import net.countercraft.movecraft.events.CraftDetectEvent;
-import net.countercraft.movecraft.events.CraftSinkEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -23,7 +20,7 @@
public class CraftDetectListener implements Listener {
private static final Set notifyError = new HashSet<>();
- private static final Cannons cannon = Cannons.getPlugin();
+ private static final Cannons plugin = Cannons.getPlugin();
@EventHandler
public void onCraftDetect(CraftDetectEvent e) {
@@ -32,13 +29,7 @@ public void onCraftDetect(CraftDetectEvent e) {
if (notifyError.contains(type))
return;
- if (!(craft instanceof PlayerCraft))
- return;
-
Set> maxCannons = getCannonProperty(type);
- if (maxCannons.isEmpty())
- return; // Return if empty set to improve performance
-
// Sum up counts of each cannon design
Set cannons = MovecraftUtils.getCannons(craft);
Map cannonCount = new HashMap<>();
@@ -51,8 +42,10 @@ public void onCraftDetect(CraftDetectEvent e) {
// Check designs against maxCannons
int size = craft.getOrigBlockCount();
for (var entry : maxCannons) {
- if (!(entry instanceof MaxCannonsEntry max))
- throw new IllegalStateException("maxCannons must be a set of MaxCannonsEntry.");
+ if (!(entry instanceof MaxCannonsEntry max)) {
+ plugin.logSevere("maxCannons must be a set of MaxCannonsEntry.");
+ continue;
+ }
var cannonName = max.getName();
var count = cannonCount.get(cannonName.toLowerCase());
@@ -67,11 +60,13 @@ public void onCraftDetect(CraftDetectEvent e) {
+ cannonName + ": " + error);
});
}
+
+ MovecraftCannonTracker.setCannons(craft.getUUID(), cannons);
}
private void printCannonCount(Map cannonCount) {
for (var entry : cannonCount.entrySet()) {
- cannon.logDebug("Cannon found: " + entry.getKey() + " | " + entry.getValue());
+ plugin.logDebug("Cannon found: " + entry.getKey() + " | " + entry.getValue());
}
}
@@ -85,7 +80,7 @@ private Set getCannonProperty(CraftType type) {
}
} catch (Exception exception) {
notifyError.add(type);
- cannon.logSevere(
+ plugin.logSevere(
"Failed to get maxCannons property from craft " +
type.getStringProperty(CraftType.NAME) +
" maxCannons won't be applied. - Cause: " +
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftReleaseListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftReleaseListener.java
new file mode 100644
index 00000000..6e8dcf79
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/CraftReleaseListener.java
@@ -0,0 +1,14 @@
+package at.pavlov.cannons.hooks.movecraft.listener;
+
+import at.pavlov.cannons.hooks.movecraft.MovecraftCannonTracker;
+import net.countercraft.movecraft.events.CraftReleaseEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class CraftReleaseListener implements Listener {
+
+ @EventHandler
+ public void onCraftRelease(CraftReleaseEvent event) {
+ MovecraftCannonTracker.clearCannons(event.getCraft().getUUID());
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/PreventRecreateHandler.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/PreventRecreateHandler.java
new file mode 100644
index 00000000..d77f20db
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/PreventRecreateHandler.java
@@ -0,0 +1,44 @@
+package at.pavlov.cannons.hooks.movecraft.listener;
+
+import at.pavlov.cannons.event.CannonBeforeCreateEvent;
+import net.countercraft.movecraft.events.CraftTeleportEntityEvent;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+public class PreventRecreateHandler implements Listener {
+ private final HashMap playerCannonCooldown = new HashMap<>();
+
+ @EventHandler(ignoreCancelled = true)
+ private void handleTeleport(CraftTeleportEntityEvent event) {
+ Entity entity = event.getEntity();
+ if (!(entity instanceof Player player)) {
+ return;
+ }
+
+ long tickCooldown = event.getCraft().getTickCooldown() * 50L;
+ playerCannonCooldown.put(
+ player.getUniqueId(),
+ tickCooldown * 2 + System.currentTimeMillis()
+ );
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ private void handleCreation(CannonBeforeCreateEvent event) {
+ UUID player = event.getPlayer();
+ Long cooldown = playerCannonCooldown.get(player);
+ if (cooldown == null) return;
+
+ if (System.currentTimeMillis() < cooldown) {
+ event.setCancelled(true);
+ event.setMessage(null);
+ return;
+ }
+
+ playerCannonCooldown.remove(player);
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/RotationListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/RotationListener.java
index f338d2fa..2003541a 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/RotationListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/RotationListener.java
@@ -1,7 +1,7 @@
package at.pavlov.cannons.hooks.movecraft.listener;
import at.pavlov.cannons.cannon.Cannon;
-import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
+import at.pavlov.cannons.hooks.movecraft.MovecraftCannonTracker;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.events.CraftRotateEvent;
import org.bukkit.event.EventHandler;
@@ -17,7 +17,7 @@ public class RotationListener implements Listener {
public void rotateListener(CraftRotateEvent e) {
Craft craft = e.getCraft();
- Set cannons = MovecraftUtils.getCannons(craft);
+ Set cannons = MovecraftCannonTracker.getCannons(craft.getUUID());
if (cannons.isEmpty())
return;
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/SinkListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/SinkListener.java
index d57fd694..ef23a162 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/SinkListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/SinkListener.java
@@ -3,6 +3,7 @@
import at.pavlov.cannons.Enum.BreakCause;
import at.pavlov.cannons.cannon.Cannon;
import at.pavlov.cannons.cannon.CannonManager;
+import at.pavlov.cannons.hooks.movecraft.MovecraftCannonTracker;
import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import net.countercraft.movecraft.events.CraftSinkEvent;
import org.bukkit.event.EventHandler;
@@ -15,6 +16,7 @@ public class SinkListener implements Listener {
@EventHandler
public void onCraftSink(CraftSinkEvent event) {
Set cannons = MovecraftUtils.getCannons(event.getCraft());
- cannons.forEach(cannon -> CannonManager.getInstance().removeCannon(cannon.getUID(), false, true, BreakCause.Explosion));
+ MovecraftCannonTracker.clearCannons(event.getCraft().getUUID());
+ cannons.forEach(cannon -> CannonManager.getInstance().removeCannon(cannon.getUID(), true, true, BreakCause.Explosion));
}
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/TranslationListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/TranslationListener.java
index 35fa1ccf..4e619ae5 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/TranslationListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/hooks/movecraft/listener/TranslationListener.java
@@ -1,6 +1,7 @@
package at.pavlov.cannons.hooks.movecraft.listener;
import at.pavlov.cannons.cannon.Cannon;
+import at.pavlov.cannons.hooks.movecraft.MovecraftCannonTracker;
import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.events.CraftTranslateEvent;
@@ -21,7 +22,9 @@ public void translateListener(CraftTranslateEvent e) {
if (v == null)
return;
- Set cannons = MovecraftUtils.getCannons(e.getCraft());
+ Set cannons = MovecraftCannonTracker.getCannons(e.getCraft().getUUID());
+ if (cannons == null) return; //for sinking ships
+
if (cannons.isEmpty())
return;
for (Cannon c : cannons) {
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/BlockListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/BlockListener.java
index 8a47a94f..e1ea3e3d 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/BlockListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/BlockListener.java
@@ -58,6 +58,7 @@ public void blockExplodeEvent(BlockExplodeEvent event) {
}
//search for destroyed cannons
+ event.blockList().removeIf(it -> it.getType().isAir());
EventUtils.handleExplosion(event.blockList());
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/EntityListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/EntityListener.java
index f5bad471..06df3625 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/EntityListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/EntityListener.java
@@ -47,6 +47,7 @@ public void EntityExplode(EntityExplodeEvent event) {
return;
}
+ event.blockList().removeIf(it -> it.getType().isAir());
EventUtils.handleExplosion(event.blockList());
}
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/PlayerListener.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/PlayerListener.java
index 03a1b3b3..5a7479b2 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/PlayerListener.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/listener/PlayerListener.java
@@ -15,10 +15,12 @@
import at.pavlov.cannons.projectile.Projectile;
import at.pavlov.cannons.projectile.ProjectileManager;
import at.pavlov.cannons.projectile.ProjectileStorage;
+import at.pavlov.cannons.schematic.SchemUtil;
import at.pavlov.cannons.utils.CannonSelector;
import at.pavlov.cannons.utils.CannonsUtil;
import at.pavlov.cannons.utils.SoundUtils;
import com.cryptomorin.xseries.XPotion;
+import org.bukkit.ChatColor;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.Sound;
@@ -229,6 +231,25 @@ public void playerInteract(PlayerInteractEvent event) {
return;
}
+ if (event.getHand() != EquipmentSlot.HAND) {
+ return;
+ }
+
+ // handle schematic positioning
+ ItemStack mainHand = player.getInventory().getItemInMainHand();
+ if (mainHand.isSimilar(SchemUtil.SELECT_TOOL)) {
+ if (action == Action.RIGHT_CLICK_BLOCK) {
+ SchemUtil.coord1 = clickedBlock;
+ player.sendMessage(ChatColor.GREEN + "[Cannons] Saved position 1");
+ } else if (action == Action.LEFT_CLICK_BLOCK) {
+ SchemUtil.coord2 = clickedBlock;
+ player.sendMessage(ChatColor.GREEN + "[Cannons] Saved position 2");
+ }
+
+ event.setCancelled(true);
+ return;
+ }
+
final Location barrel = clickedBlock.getLocation();
//try if the player has really nothing in his hands, or minecraft is blocking it
@@ -240,10 +261,6 @@ public void playerInteract(PlayerInteractEvent event) {
// ############ select a cannon ####################
if (isCannonSelect(event, clickedBlock, cannon)) return;
- if (event.getHand() != EquipmentSlot.HAND) {
- return;
- }
-
boolean isRMB = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK;
if ((isRMB || event.getAction() == Action.PHYSICAL) && cannon != null) {
// prevent eggs and snowball from firing when loaded into the gun
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/RetryUntil.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/RetryUntil.java
new file mode 100644
index 00000000..d0a19afb
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/RetryUntil.java
@@ -0,0 +1,29 @@
+package at.pavlov.cannons.schematic;
+
+import at.pavlov.cannons.Cannons;
+import lombok.AllArgsConstructor;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.function.Supplier;
+
+@AllArgsConstructor
+public class RetryUntil extends BukkitRunnable {
+ private final Supplier booleanSupplier;
+ private final Runnable runnable;
+
+ public RetryUntil(Supplier booleanSupplier) {
+ this(booleanSupplier, () -> {});
+ }
+
+ @Override
+ public void run() {
+ if (booleanSupplier.get()) return;
+
+ runnable.run();
+ cancel();
+ }
+
+ public void start() {
+ runTaskTimer(Cannons.getPlugin(), 1L, 4L);
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/SchemUtil.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/SchemUtil.java
new file mode 100644
index 00000000..8db3bdfa
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/SchemUtil.java
@@ -0,0 +1,36 @@
+package at.pavlov.cannons.schematic;
+
+import at.pavlov.cannons.Cannons;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.block.Block;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemFlag;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataType;
+
+public class SchemUtil {
+ public static Block coord1 = null;
+ public static Block coord2 = null;
+
+ public static final NamespacedKey CANNON_KEY = new NamespacedKey(Cannons.getPlugin(), "selector");
+ public static final ItemStack SELECT_TOOL = getSelectTool();
+ private static ItemStack getSelectTool() {
+ ItemStack item = new ItemStack(Material.BLAZE_ROD);
+ ItemMeta meta = item.getItemMeta();
+ if (meta == null) {
+ meta = Bukkit.getItemFactory().getItemMeta(Material.BLAZE_ROD);
+ }
+
+ meta.setDisplayName(ChatColor.RESET + "Cannon Select Tool");
+ meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
+ meta.addEnchant(Enchantment.UNBREAKING, 1, true);
+ meta.getPersistentDataContainer().set(CANNON_KEY, PersistentDataType.BOOLEAN, true);
+
+ item.setItemMeta(meta);
+ return item;
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/BlockImpl.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/BlockImpl.java
new file mode 100644
index 00000000..4e7a159b
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/BlockImpl.java
@@ -0,0 +1,39 @@
+package at.pavlov.cannons.schematic.block;
+
+import lombok.Getter;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import org.bukkit.NamespacedKey;
+import org.bukkit.block.Block;
+
+@Getter
+public class BlockImpl implements IBlock {
+ private final Block block;
+ private final BlockKey key;
+
+ public BlockImpl(Block block) {
+ this.block = block;
+ NamespacedKey key = block.getType().getKey();
+ this.key = BlockKey.mc(key.getKey());
+ }
+
+ @Override
+ public int x() {
+ return block.getX();
+ }
+
+ @Override
+ public int y() {
+ return block.getY();
+ }
+
+ @Override
+ public int z() {
+ return block.getZ();
+ }
+
+ @Override
+ public BlockKey key() {
+ return this.key;
+ }
+}
\ No newline at end of file
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/WorldEditBlock.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/WorldEditBlock.java
new file mode 100644
index 00000000..abcb25b0
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/block/WorldEditBlock.java
@@ -0,0 +1,36 @@
+package at.pavlov.cannons.schematic.block;
+
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.world.block.BlockState;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import lombok.AllArgsConstructor;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+
+@AllArgsConstructor
+public class WorldEditBlock implements IBlock {
+ private final BlockState state;
+ private final BlockVector3 pos;
+
+ @Override
+ public int x() {
+ return pos.getBlockX();
+ }
+
+ @Override
+ public int y() {
+ return pos.getBlockY();
+ }
+
+ @Override
+ public int z() {
+ return pos.getBlockZ();
+ }
+
+ @Override
+ public BlockKey key() {
+ String id = state.getBlockType().getId();
+ String[] splitted = id.split(":");
+ return new BlockKey(splitted[0], splitted[1]);
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/formats/WorldEditFormat.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/formats/WorldEditFormat.java
new file mode 100644
index 00000000..d6be6d86
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/formats/WorldEditFormat.java
@@ -0,0 +1,74 @@
+package at.pavlov.cannons.schematic.formats;
+
+import at.pavlov.cannons.Cannons;
+import at.pavlov.cannons.schematic.block.WorldEditBlock;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import com.sk89q.worldedit.session.ClipboardHolder;
+import com.sk89q.worldedit.util.io.Closer;
+import com.sk89q.worldedit.world.block.BlockState;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.formats.FileExceptionUnknown;
+import me.vaan.schematiclib.base.formats.SchematicLoader;
+import me.vaan.schematiclib.base.schematic.Schematic;
+import me.vaan.schematiclib.file.schematic.FileSchematic;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class WorldEditFormat implements SchematicLoader {
+ @Override
+ public Schematic load(File file) throws Throwable {
+ Clipboard cc = loadSchematic(file);
+ if (cc == null) throw new FileExceptionUnknown("Schematic not found");
+
+ ClipboardHolder clipboardHolder = new ClipboardHolder(cc);
+ clipboardHolder.setTransform(new AffineTransform().translate(cc.getMinimumPoint().multiply(-1)));
+ Clipboard transformedCC = clipboardHolder.getClipboard();
+
+ cc.setOrigin(BlockVector3.ZERO);
+
+ List positions = new ArrayList<>();
+
+ int width = transformedCC.getDimensions().getBlockX();
+ int height = transformedCC.getDimensions().getBlockY();
+ int length = transformedCC.getDimensions().getBlockZ();
+
+ for (int x = 0; x < width; ++x) {
+ for (int y = 0; y < height; ++y) {
+ for (int z = 0; z < length; ++z) {
+ BlockVector3 pt = BlockVector3.at(x, y, z);
+ BlockState blockState = cc.getBlock(pt.add(cc.getMinimumPoint()));
+
+ positions.add(new WorldEditBlock(blockState, pt));
+ }
+ }
+ }
+
+ return new FileSchematic(positions);
+ }
+
+ private Clipboard loadSchematic(File schematicFile) throws IOException {
+ ClipboardFormat format = ClipboardFormats.findByFile(schematicFile);
+
+ if(format == null) {
+ return null;
+ }
+
+ try (Closer closer = Closer.create()) {
+ FileInputStream fis = closer.register(new FileInputStream(schematicFile));
+ BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
+ ClipboardReader reader = closer.register(format.getReader(bis));
+
+ return reader.read();
+ }
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/Initialize.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/Initialize.java
new file mode 100644
index 00000000..11f029b2
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/Initialize.java
@@ -0,0 +1,5 @@
+package at.pavlov.cannons.schematic.namespace;
+
+public interface Initialize {
+ void init();
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/ItemsAdderNamespaceHandler.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/ItemsAdderNamespaceHandler.java
new file mode 100644
index 00000000..c32c99b9
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/ItemsAdderNamespaceHandler.java
@@ -0,0 +1,81 @@
+package at.pavlov.cannons.schematic.namespace;
+
+import at.pavlov.cannons.schematic.RetryUntil;
+import at.pavlov.cannons.utils.SchemLibUtils;
+import dev.lone.itemsadder.api.CustomBlock;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.block.ICoord;
+import me.vaan.schematiclib.base.namespace.NamespaceHandler;
+import me.vaan.schematiclib.file.block.FileBlock;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+
+import java.util.UUID;
+
+public class ItemsAdderNamespaceHandler implements NamespaceHandler {
+
+ @Override
+ public void place(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+
+ String realKey = iBlock.key().key().replace('$', ':');
+ CustomBlock.place(realKey, location);
+ }
+
+ @Override
+ public IBlock get(int x, int y, int z, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, x, y, z);
+
+ Block bBlock = location.getBlock();
+ CustomBlock iaBlock = CustomBlock.byAlreadyPlaced(bBlock);
+ if (iaBlock == null) {
+ return null;
+ }
+
+ // war crimes go brrrrrrrrrr
+ String replaced = iaBlock.getNamespacedID().replace(':', '$');
+ return new FileBlock(x, y, z, new BlockKey("itemsadder", replaced));
+ }
+
+ @Override
+ public void destroy(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+
+ if (!CustomBlock.remove(location)) {
+ new RetryUntil(
+ () -> !CustomBlock.remove(location)
+ ).start();
+ }
+ }
+
+ @Override
+ public void breakNaturally(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+
+ Block bBlock = location.getBlock();
+ CustomBlock iaBlock = CustomBlock.byAlreadyPlaced(bBlock);
+ iaBlock.drop(location);
+
+ destroy(iBlock, uuid);
+ }
+
+ @Override
+ public void move(ICoord from, ICoord to, BlockKey blockKey) {
+
+ }
+
+ @Override
+ public BlockKey toMaterial(BlockKey blockKey) {
+ String realKey = blockKey.key().replace('$', ':');
+ Material material = CustomBlock.getBaseBlockData(realKey).getMaterial();
+ return SchemLibUtils.materialKey(material);
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/MinecraftNamespaceHandler.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/MinecraftNamespaceHandler.java
new file mode 100644
index 00000000..a63997fe
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/MinecraftNamespaceHandler.java
@@ -0,0 +1,62 @@
+package at.pavlov.cannons.schematic.namespace;
+
+
+import at.pavlov.cannons.schematic.block.BlockImpl;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.block.ICoord;
+import me.vaan.schematiclib.base.namespace.NamespaceHandler;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+
+import java.util.UUID;
+
+public class MinecraftNamespaceHandler implements NamespaceHandler {
+ @Override
+ public void place(IBlock iBlock, UUID uuid) {
+ NamespacedKey key = new NamespacedKey("minecraft", iBlock.key().key());
+ Material mat = Registry.MATERIAL.get(key);
+ Bukkit.getWorld(uuid).getBlockAt(
+ iBlock.x(),
+ iBlock.y(),
+ iBlock.z()
+ ).setType(mat);
+ }
+
+ @Override
+ public IBlock get(int x, int y, int z, UUID uuid) {
+ return new BlockImpl(
+ Bukkit.getWorld(uuid).getBlockAt(x, y, z)
+ );
+ }
+
+ @Override
+ public void destroy(IBlock iBlock, UUID uuid) {
+ Bukkit.getWorld(uuid).getBlockAt(
+ iBlock.x(),
+ iBlock.y(),
+ iBlock.z()
+ ).setType(Material.AIR);
+ }
+
+ @Override
+ public void breakNaturally(IBlock iBlock, UUID uuid) {
+ Bukkit.getWorld(uuid).getBlockAt(
+ iBlock.x(),
+ iBlock.y(),
+ iBlock.z()
+ ).breakNaturally();
+ }
+
+ // no internal state to move
+ @Override
+ public void move(ICoord iCoord, ICoord iCoord1, BlockKey blockKey) {}
+
+ // should be unused
+ @Override
+ public BlockKey toMaterial(BlockKey blockKey) {
+ return blockKey;
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/NexoNamespaceHandler.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/NexoNamespaceHandler.java
new file mode 100644
index 00000000..48ba95d5
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/NexoNamespaceHandler.java
@@ -0,0 +1,148 @@
+package at.pavlov.cannons.schematic.namespace;
+
+import at.pavlov.cannons.Aiming;
+import at.pavlov.cannons.Cannons;
+import at.pavlov.cannons.Enum.BreakCause;
+import at.pavlov.cannons.cannon.Cannon;
+import at.pavlov.cannons.cannon.CannonManager;
+import at.pavlov.cannons.cannon.DesignStorage;
+import at.pavlov.cannons.utils.CannonSelector;
+import com.nexomc.nexo.api.NexoBlocks;
+import com.nexomc.nexo.api.NexoFurniture;
+import com.nexomc.nexo.api.events.furniture.NexoFurnitureBreakEvent;
+import com.nexomc.nexo.mechanics.custom_block.CustomBlockMechanic;
+import com.nexomc.nexo.mechanics.furniture.FurnitureMechanic;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.block.ICoord;
+import me.vaan.schematiclib.base.namespace.NamespaceHandler;
+import me.vaan.schematiclib.file.block.FileBlock;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.NamespacedKey;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+import java.util.UUID;
+
+public class NexoNamespaceHandler implements NamespaceHandler, Listener, Initialize {
+ @Override
+ public void init() {
+ Cannons pl = Cannons.getPlugin();
+ // furnitures aren't handled by block break
+ Bukkit.getPluginManager().registerEvents(this, pl);
+ // nexo block resolution requires some time first, so rerun load
+ Bukkit.getScheduler().runTaskLater(pl, DesignStorage.getInstance()::loadCannonDesigns, 5L);
+ }
+
+ @Override
+ public void place(IBlock iBlock, UUID world) {
+ String item_id = iBlock.key().key().replace('$', ':');
+ Location loc = new Location(Bukkit.getWorld(world), iBlock.x(), iBlock.y(), iBlock.z());
+
+ if (NexoBlocks.isCustomBlock(item_id)) NexoBlocks.place(item_id, loc);
+
+ FurnitureMechanic fm = getOneBlockFurniture(item_id);
+ if (fm == null) return;
+ NexoFurniture.place(item_id, loc, 0f, BlockFace.NORTH);
+ }
+
+ @Override
+ public IBlock get(int x, int y, int z, UUID world) {
+ Block block = new Location(Bukkit.getWorld(world), x, y, z).getBlock();
+
+ CustomBlockMechanic cbm = NexoBlocks.customBlockMechanic(block);
+ if (cbm != null) {
+ String item_id = cbm.getItemID().replace(':', '$');
+ return new FileBlock(x, y, z, new BlockKey("nexo", item_id));
+ }
+
+ FurnitureMechanic fm = NexoFurniture.furnitureMechanic(block);
+ if (fm != null && fm.getHitbox().getBarriers().size() == 1) {
+ String item_id = fm.getItemID().replace(':', '$');
+ return new FileBlock(x, y, z, new BlockKey("nexo", item_id));
+ }
+
+ return null;
+
+ }
+
+ @Override
+ public void destroy(IBlock iBlock, UUID world) {
+
+ // 0 idea how to use this, but maybe this is what I am looking for
+ //CustomBlockRegistry.INSTANCE.getMechanic("ee").getType().removeWorldEdit();
+ Location loc = new Location(Bukkit.getWorld(world), iBlock.x(), iBlock.y(), iBlock.z());
+ Block block = loc.getBlock();
+ if (NexoBlocks.isCustomBlock(block)) NexoBlocks.remove(loc);
+ if (NexoFurniture.isFurniture(loc)) NexoFurniture.remove(loc);
+ }
+
+ @Override
+ public void breakNaturally(IBlock iBlock, UUID world) {
+ Location loc = new Location(Bukkit.getWorld(world), iBlock.x(), iBlock.y(), iBlock.z());
+ Block block = loc.getBlock();
+ if (NexoBlocks.isCustomBlock(block)) NexoBlocks.remove(loc, null, true);
+ if (NexoFurniture.isFurniture(loc)) NexoFurniture.remove(loc);
+ }
+
+ @Override
+ public void move(ICoord from, ICoord to, BlockKey blockKey) {
+
+ }
+
+ private static final BlockKey BARRIER = BlockKey.mc("air");
+ @Override
+ public BlockKey toMaterial(BlockKey blockKey) {
+ String item_id = blockKey.key().replace('$', ':');
+ BlockData data = NexoBlocks.blockData(item_id);
+ if (data != null) {
+ NamespacedKey key = data.getMaterial().getKey();
+ return BlockKey.mc(key.getKey());
+ }
+
+ if (NexoFurniture.isFurniture(item_id)) return BARRIER;
+
+ throw new UnsupportedOperationException();
+ }
+
+ private FurnitureMechanic getOneBlockFurniture(String id) {
+ FurnitureMechanic fm = NexoFurniture.furnitureMechanic(id);
+ if (fm == null) return null;
+ if (fm.getHitbox().getBarriers().size() != 1) return null;
+
+ return fm;
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ private void onFurnitureBreak(NexoFurnitureBreakEvent event) {
+ if (event.getMechanic().getHitbox().getBarriers().size() != 1) return;
+
+ CannonManager cannonManager = CannonManager.getInstance();
+ Cannons plugin = Cannons.getPlugin();
+
+ Location location = event.getBaseEntity().getLocation().getBlock().getLocation();
+ Cannon cannon = cannonManager.getCannonFromStorage(location);
+
+ if (cannon == null) {
+ return;
+ }
+
+ Cannon aimingCannon = null;
+ Player player = event.getPlayer();
+ if (Aiming.getInstance().isInAimingMode(player.getUniqueId()))
+ aimingCannon = Aiming.getInstance().getCannonInAimingMode(player);
+
+ if (!cannon.equals(aimingCannon) && !CannonSelector.getInstance().isSelectingMode(player)) {
+ cannonManager.removeCannon(cannon, false, true, BreakCause.PlayerBreak);
+ plugin.logDebug("cannon broken: " + location);
+ } else {
+ event.setCancelled(true);
+ plugin.logDebug("cancelled cannon destruction: " + location);
+ }
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/SlimefunNamespaceHandler.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/SlimefunNamespaceHandler.java
new file mode 100644
index 00000000..184ade85
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/namespace/SlimefunNamespaceHandler.java
@@ -0,0 +1,137 @@
+package at.pavlov.cannons.schematic.namespace;
+
+import at.pavlov.cannons.schematic.RetryUntil;
+import at.pavlov.cannons.utils.SchemLibUtils;
+import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
+import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
+import me.mrCookieSlime.Slimefun.api.BlockStorage;
+import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
+import me.vaan.schematiclib.base.block.BlockKey;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.block.ICoord;
+import me.vaan.schematiclib.base.namespace.NamespaceHandler;
+import me.vaan.schematiclib.file.block.FileBlock;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+// doesn't support handlers as we have no acting player
+public class SlimefunNamespaceHandler implements NamespaceHandler {
+ @Override
+ public void place(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ String id = iBlock.key().key();
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+ Block block = location.getBlock();
+
+ SlimefunItem sfItem = SlimefunItem.getById(id.toUpperCase());
+ if (sfItem == null) {
+ return;
+ }
+
+ Material material = sfItem.getItem().getType();
+ if (!material.isBlock()) {
+ return;
+ }
+
+ if (Slimefun.getTickerTask().isDeletedSoon(location)) {
+ new RetryUntil(
+ () -> Slimefun.getTickerTask().isDeletedSoon(location),
+ () -> place(iBlock, uuid)
+ ).start();
+ return;
+ }
+
+ block.setType(material);
+ if (Slimefun.getBlockDataService().isTileEntity(block.getType())) {
+ Slimefun.getBlockDataService().setBlockData(block, sfItem.getId());
+ }
+
+ BlockStorage.addBlockInfo(block, "id", sfItem.getId(), true);
+ }
+
+ @Override
+ public IBlock get(int x, int y, int z, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ SlimefunItem item = BlockStorage.check(new Location(world, x, y, z));
+ if (item == null) {
+ return null;
+ }
+
+ String id = item.getId().toLowerCase();
+ return new FileBlock(x, y, z, new BlockKey("slimefun", id));
+ }
+
+ @Override
+ public void destroy(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+
+ SlimefunItem sfItem = BlockStorage.check(location);
+ if (sfItem == null) {
+ return; // probably old data from a removed addon?
+ }
+
+ BlockMenu inventory = BlockStorage.getInventory(location);
+ if (inventory != null) {
+ for (HumanEntity human : new ArrayList<>(inventory.toInventory().getViewers())) {
+ human.closeInventory();
+ }
+ }
+
+ BlockStorage.clearBlockInfo(location);
+ location.getBlock().setType(Material.AIR);
+ }
+
+ @Override
+ public void breakNaturally(IBlock iBlock, UUID uuid) {
+ World world = Bukkit.getWorld(uuid);
+ Location location = new Location(world, iBlock.x(), iBlock.y(), iBlock.z());
+
+ SlimefunItem sfItem = BlockStorage.check(location);
+ if (sfItem == null) {
+ return; // probably old data from a removed addon?
+ }
+
+ List drops = new ArrayList<>(sfItem.getDrops());
+ BlockMenu inventory = BlockStorage.getInventory(location);
+ if (inventory != null) {
+ for (HumanEntity human : new ArrayList<>(inventory.toInventory().getViewers())) {
+ human.closeInventory();
+ }
+ }
+
+ BlockStorage.clearBlockInfo(location);
+
+ for (ItemStack drop : drops) {
+ if (drop != null && drop.getType() != Material.AIR) {
+ world.dropItemNaturally(location, drop);
+ }
+ }
+
+ location.getBlock().setType(Material.AIR);
+ }
+
+ @Override
+ public void move(ICoord from, ICoord to, BlockKey blockKey) {
+
+ }
+
+ @Override
+ public BlockKey toMaterial(BlockKey blockKey) {
+ SlimefunItem sfItem = SlimefunItem.getById(blockKey.key().toUpperCase());
+ if (sfItem == null) {
+ return BlockKey.mc("air");
+ }
+
+ return SchemLibUtils.materialKey(sfItem.getItem().getType());
+ }
+}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/world/SchematicWorldProcessorImpl.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/world/SchematicWorldProcessorImpl.java
new file mode 100644
index 00000000..ad97d6bb
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/schematic/world/SchematicWorldProcessorImpl.java
@@ -0,0 +1,73 @@
+package at.pavlov.cannons.schematic.world;
+
+import at.pavlov.cannons.schematic.block.BlockImpl;
+import at.pavlov.cannons.schematic.namespace.Initialize;
+import at.pavlov.cannons.schematic.namespace.ItemsAdderNamespaceHandler;
+import at.pavlov.cannons.schematic.namespace.MinecraftNamespaceHandler;
+import at.pavlov.cannons.schematic.namespace.NexoNamespaceHandler;
+import at.pavlov.cannons.schematic.namespace.SlimefunNamespaceHandler;
+import lombok.Getter;
+import me.vaan.schematiclib.base.block.IBlock;
+import me.vaan.schematiclib.base.namespace.NamespaceHandler;
+import me.vaan.schematiclib.base.namespace.NamespaceRegistry;
+import me.vaan.schematiclib.base.world.SchematicWorldProcessor;
+import org.bukkit.block.Block;
+
+import java.util.UUID;
+
+public class SchematicWorldProcessorImpl implements SchematicWorldProcessor {
+ @Getter
+ private static final SchematicWorldProcessorImpl processor = new SchematicWorldProcessorImpl().registerAll();
+ private final NamespaceRegistry registry;
+ private final MinecraftNamespaceHandler mcHandler = new MinecraftNamespaceHandler();
+
+ private SchematicWorldProcessorImpl() {
+ this.registry = new NamespaceRegistry("minecraft", mcHandler);
+ }
+
+ @Override
+ public NamespaceRegistry registry() {
+ return registry;
+ }
+
+ public void registerReflectionNamespace(String namespace, String classToFind, NamespaceHandler handler) {
+ try {
+ Class.forName(classToFind);
+ } catch (Exception e) {
+ return;
+ }
+
+ if (handler instanceof Initialize initialize) {
+ initialize.init();
+ }
+
+ registry.registerNamespaceHandler(namespace, handler);
+ }
+
+ public Block getRaw(IBlock block, UUID world) {
+ return ((BlockImpl) mcHandler.get(block.x(), block.y(), block.z(), world)).getBlock();
+ }
+
+ private SchematicWorldProcessorImpl registerAll() {
+ registerReflectionNamespace(
+ "slimefun",
+ "io.github.thebusybiscuit.slimefun4.implementation.Slimefun",
+ new SlimefunNamespaceHandler()
+ );
+
+ registerReflectionNamespace(
+ "itemsadder",
+ "dev.lone.itemsadder.api.CustomBlock",
+ new ItemsAdderNamespaceHandler()
+ );
+
+ registerReflectionNamespace(
+ "nexo",
+ "com.nexomc.nexo.api.NexoBlocks",
+ new NexoNamespaceHandler()
+ );
+
+ return this;
+ }
+}
+
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/DesignComparator.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/DesignComparator.java
index 28aaeab3..04223f3f 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/DesignComparator.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/DesignComparator.java
@@ -2,6 +2,7 @@
import at.pavlov.cannons.Cannons;
import at.pavlov.cannons.cannon.CannonDesign;
+import me.vaan.schematiclib.base.schematic.Schematic;
import org.bukkit.block.BlockFace;
import java.util.Comparator;
@@ -23,13 +24,14 @@ private Integer getCannonBlockAmount(CannonDesign design)
{
if (design == null) return 0;
//if the design is invalid something goes wrong, message the user
- if (design.getAllCannonBlocks(BlockFace.NORTH) == null)
+ Schematic schematic = design.getSchematicMap().get(BlockFace.NORTH);
+ if (schematic == null)
{
Cannons.logger().log(Level.SEVERE, "invalid cannon design for " + design.getDesignName());
return 0;
}
- return design.getAllCannonBlocks(BlockFace.NORTH).size();
+ return schematic.positions().size();
}
}
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/EventUtils.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/EventUtils.java
index a1ae674d..38414571 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/EventUtils.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/EventUtils.java
@@ -3,8 +3,10 @@
import at.pavlov.cannons.Enum.BreakCause;
import at.pavlov.cannons.cannon.Cannon;
import at.pavlov.cannons.cannon.CannonManager;
+import org.bukkit.Location;
import org.bukkit.block.Block;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
@@ -19,17 +21,23 @@ private EventUtils() {}
public static void handleExplosion(List blocklist) {
CannonManager cannonManager = CannonManager.getInstance();
HashSet remove = new HashSet<>();
+ HashMap cannonHashMap = new HashMap<>();
// first search if a barrel block was destroyed.
for (Block block : blocklist) {
- Cannon cannon = cannonManager.getCannon(block.getLocation(), null);
+ if (block == null) continue;
+ Location location = block.getLocation();
+ if (block.getType().isAir()) continue;
+
+ Cannon cannon = cannonManager.getCannonFromStorage(location);
// if it is a cannon block
if (cannon == null) {
continue;
}
- if (cannon.isDestructibleBlock(block.getLocation())) {
+ cannonHashMap.put(block, cannon);
+ if (cannon.isDestructibleBlock(location)) {
//this cannon is destroyed
remove.add(cannon.getUID());
}
@@ -38,7 +46,7 @@ public static void handleExplosion(List blocklist) {
//iterate again and remove all block of intact cannons
for (int i = 0; i < blocklist.size(); i++) {
Block block = blocklist.get(i);
- Cannon cannon = cannonManager.getCannon(block.getLocation(), null);
+ Cannon cannon = cannonHashMap.get(block);
// if it is a cannon block and the cannon is not destroyed (see above)
if (cannon == null || remove.contains(cannon.getUID())) {
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/ParseUtils.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/ParseUtils.java
index 425e78a1..18e12c04 100644
--- a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/ParseUtils.java
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/ParseUtils.java
@@ -4,6 +4,7 @@
import at.pavlov.cannons.container.ItemHolder;
import at.pavlov.cannons.container.SpawnEntityHolder;
import at.pavlov.internal.container.SpawnMaterialHolder;
+import me.vaan.schematiclib.base.block.BlockKey;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Material;
@@ -139,6 +140,20 @@ public static List toBlockDataList(List stringList) {
return blockDataList;
}
+ public static BlockKey toBlockKey(String entry) {
+ return BlockKey.fromString(entry.split("\\[")[0]);
+ }
+
+ public static List toBlockKeyList(List stringList) {
+ List blockKeyList = new ArrayList<>();
+
+ for (String str : stringList) {
+ blockKeyList.add(toBlockKey(str));
+ }
+
+ return blockKeyList;
+ }
+
/**
* returns a list of ItemHolder
* @param stringList list of Materials as strings
diff --git a/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/SchemLibUtils.java b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/SchemLibUtils.java
new file mode 100644
index 00000000..a081b15a
--- /dev/null
+++ b/cannons-bukkit/src/main/java/at/pavlov/cannons/utils/SchemLibUtils.java
@@ -0,0 +1,13 @@
+package at.pavlov.cannons.utils;
+
+import me.vaan.schematiclib.base.block.BlockKey;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.jetbrains.annotations.NotNull;
+
+public class SchemLibUtils {
+ public static BlockKey materialKey(@NotNull Material mat) {
+ NamespacedKey key = mat.getKey();
+ return BlockKey.mc(key.getKey());
+ }
+}
diff --git a/cannons-bukkit/src/main/resources/plugin.yml b/cannons-bukkit/src/main/resources/plugin.yml
index 14c3a02e..cd1cb85f 100644
--- a/cannons-bukkit/src/main/resources/plugin.yml
+++ b/cannons-bukkit/src/main/resources/plugin.yml
@@ -3,7 +3,14 @@ main: at.pavlov.cannons.Cannons
api-version: 1.13
version: ${version}
depend: [WorldEdit]
-softdepend: [Vault, Movecraft, Movecraft-Combat, PlaceholderAPI]
+softdepend:
+ - Vault
+ - Movecraft
+ - Movecraft-Combat
+ - PlaceholderAPI
+ - Slimefun
+ - ItemsAdder
+ - Nexo
authors: [DerPavlov, Vaan1310]
description: Fire block build cannons and smash your enemies
folia-supported: true