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