diff --git a/BauSystem_12/src/de/steamwar/bausystem/world/Region_12.java b/BauSystem_12/src/de/steamwar/bausystem/world/Region_12.java index 302e8dc..d95b106 100644 --- a/BauSystem_12/src/de/steamwar/bausystem/world/Region_12.java +++ b/BauSystem_12/src/de/steamwar/bausystem/world/Region_12.java @@ -40,7 +40,7 @@ class Region_12 { private Region_12() { } - static void paste(File file, int x, int y, int z, boolean rotate) { + static EditSession paste(File file, int x, int y, int z, boolean rotate) { World w = new BukkitWorld(Bukkit.getWorlds().get(0)); Clipboard clipboard; try { @@ -49,10 +49,10 @@ class Region_12 { throw new SecurityException("Bausystem schematic not found", e); } - paste(clipboard, x, y, z, rotate); + return paste(clipboard, x, y, z, rotate); } - static void paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { + static EditSession paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { World w = new BukkitWorld(Bukkit.getWorlds().get(0)); Vector dimensions = clipboard.getDimensions(); @@ -70,6 +70,6 @@ class Region_12 { ClipboardHolder ch = new ClipboardHolder(clipboard, w.getWorldData()); ch.setTransform(aT); Operations.completeBlindly(ch.createPaste(e, w.getWorldData()).to(v).build()); - e.flushQueue(); + return e; } } diff --git a/BauSystem_15/src/de/steamwar/bausystem/world/Region_15.java b/BauSystem_15/src/de/steamwar/bausystem/world/Region_15.java index 3aea08c..2eb5a52 100644 --- a/BauSystem_15/src/de/steamwar/bausystem/world/Region_15.java +++ b/BauSystem_15/src/de/steamwar/bausystem/world/Region_15.java @@ -21,7 +21,6 @@ package de.steamwar.bausystem.world; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; @@ -29,14 +28,7 @@ import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.world.block.BaseBlock; -import net.minecraft.server.v1_15_R1.BlockPosition; -import net.minecraft.server.v1_15_R1.WorldServer; import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData; import java.io.File; import java.io.FileInputStream; @@ -47,7 +39,7 @@ class Region_15 { private Region_15() { } - static void paste(File file, int x, int y, int z, boolean rotate) { + static EditSession paste(File file, int x, int y, int z, boolean rotate) { Clipboard clipboard; try { clipboard = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file)).read(); @@ -55,10 +47,10 @@ class Region_15 { throw new SecurityException("Bausystem schematic not found", e); } - paste(clipboard, x, y, z, rotate); + return paste(clipboard, x, y, z, rotate); } - static void paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { + static EditSession paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { BlockVector3 dimensions = clipboard.getDimensions(); BlockVector3 v = BlockVector3.at(x, y, z); BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); @@ -70,48 +62,11 @@ class Region_15 { v = v.subtract(dimensions.getX() / 2 - dimensions.getX() % 2, 0, dimensions.getZ() / 2 - dimensions.getZ() % 2).subtract(offset); } - EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1); - ClipboardHolder ch = new ClipboardHolder(clipboard); - ch.setTransform(aT); - Operations.completeBlindly(ch.createPaste(e).to(v).build()); - e.flushSession(); - } - - static void fastpaste(File file, int x, int y, int z, boolean rotate) { - Clipboard clipboard; - try { - clipboard = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file)).read(); - } catch (NullPointerException | IOException e) { - throw new SecurityException("Bausystem schematic not found", e); - } - - BlockVector3 dimensions = clipboard.getDimensions(); - BlockVector3 v = BlockVector3.at(x, y, z); - BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); - if (rotate) { - v = v.add(dimensions.getX() / 2 + dimensions.getX() % 2, 0, dimensions.getZ() / 2 + dimensions.getZ() % 2).subtract(offset.multiply(-1, 1, -1)).subtract(1, 0, 1); - } else { - v = v.subtract(dimensions.getX() / 2 - dimensions.getX() % 2, 0, dimensions.getZ() / 2 - dimensions.getZ() % 2).subtract(offset); - } - - WorldServer world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle(); - v = v.add(offset); - - for (int X = v.getBlockX(); X < v.getBlockX() + dimensions.getBlockX(); X++) { - for (int Y = v.getBlockY(); Y < v.getBlockY() + dimensions.getBlockY(); Y++) { - for (int Z = v.getBlockZ(); Z < v.getBlockZ() + dimensions.getBlockZ(); Z++) { - BlockPosition blockPos = new BlockPosition(X, Y, Z); - BaseBlock fullBlock = clipboard.getFullBlock(BlockVector3.at(X, Y, Z).subtract(v).add(clipboard.getRegion().getMinimumPoint())); - BlockData replaceTo = BukkitAdapter.adapt(fullBlock); - world.removeTileEntity(blockPos); - if (replaceTo == null) - world.setTypeAndData(blockPos, ((CraftBlockData) Material.AIR.createBlockData()).getState(), 1042); - else - world.setTypeAndData(new BlockPosition(X, Y, Z), ((CraftBlockData) replaceTo).getState(), 1042); - - world.getChunkProvider().flagDirty(blockPos); - } - } + try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) { + ClipboardHolder ch = new ClipboardHolder(clipboard); + ch.setTransform(aT); + Operations.completeBlindly(ch.createPaste(e).to(v).build()); + return e; } } } diff --git a/BauSystem_API/src/de/steamwar/bausystem/world/SizedStack.java b/BauSystem_API/src/de/steamwar/bausystem/world/SizedStack.java new file mode 100644 index 0000000..92a4dd3 --- /dev/null +++ b/BauSystem_API/src/de/steamwar/bausystem/world/SizedStack.java @@ -0,0 +1,124 @@ +package de.steamwar.bausystem.world; + +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public class SizedStack { + + private int maxSize; + private T[] data; + private int size; + private int head; + + public SizedStack(int size) { + this.maxSize = size; + //noinspection unchecked + this.data = (T[]) new Object[this.maxSize]; + this.head = 0; + this.size = 0; + } + + public T push(final T element) { + this.data[this.head] = element; + this.increaseHead(); + this.increaseSize(); + return element; + } + + public T pop() { + this.decreaseHead(); + this.decreaseSize(); + final T result = this.data[this.head]; + this.data[this.head] = null; + return result; + } + + public T peek() { + return this.data[this.head]; + } + + public boolean empty() { + return this.size == 0; + } + + protected boolean canEqual(final Object other) { + return other instanceof SizedStack; + } + + private void increaseHead() { + this.head++; + while (this.head > this.maxSize - 1) { + this.head -= this.maxSize; + } + } + + private void decreaseHead() { + this.head--; + while (this.head < 0) { + this.head += this.maxSize; + } + } + + private void increaseSize() { + if (this.size < this.maxSize) { + this.size++; + } + } + + private void decreaseSize() { + if (this.size > 0) { + this.size--; + } + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = 1; + result = result * PRIME + this.maxSize; + result = result * PRIME + this.toString().hashCode(); + result = result * PRIME + this.size; + return result; + } + + @Override + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof SizedStack)) { + return false; + } + final SizedStack other = (SizedStack) o; + if (!other.canEqual(this)) { + return false; + } + if (this.maxSize != other.maxSize) { + return false; + } + if (this.size != other.size) { + return false; + } + if (!this.data.getClass().equals(other.data.getClass())) { + return false; + } + return this.toString().equals(other.toString()); + } + + public int getMaxSize() { + return maxSize; + } + + public int getSize() { + return size; + } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder("["); + for (int i = 0; i < this.size - 1; i++) { + result.append(this.data[(this.head - i - 1 < 0) ? (this.head - i - 1 + this.maxSize) : (this.head - i - 1)]).append(","); + } + result.append(this.data[(this.head - this.size < 0) ? (this.head - this.size + this.maxSize) : (this.head - this.size)]); + result.append("]"); + return result.toString(); + } +} \ No newline at end of file diff --git a/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java b/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java index 945c246..44af4f7 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java @@ -95,6 +95,7 @@ public class BauSystem extends JavaPlugin implements Listener { new CommandRedstoneTester(); new CommandGUI(); new CommandWorldSpawn(); + new CommandRegion(); Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(new RegionListener(), this); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandRegion.java b/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandRegion.java new file mode 100644 index 0000000..7ed72ff --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandRegion.java @@ -0,0 +1,73 @@ +package de.steamwar.bausystem.commands; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.world.Region; +import de.steamwar.bausystem.world.Welt; +import de.steamwar.command.SWCommand; +import org.bukkit.entity.Player; + +public class CommandRegion extends SWCommand { + + public CommandRegion() { + super("region", "rg"); + } + + @Register + public void genericCommand(Player player) { + genericHelp(player); + } + + @Register(help = true) + public void genericHelp(Player player, String... args) { + player.sendMessage(BauSystem.PREFIX + "§8/§7region undo §8- §7Mache die letzten 10 /testblock oder /reset rückgängig"); + player.sendMessage(BauSystem.PREFIX + "§8/§7region redo §8- §7Wiederhohle die letzten 10 §8/§7rg undo"); + } + + @Register + public void undoCommand(Player p, Action action) { + if(!permissionCheck(p)) return; + Region region = Region.getRegion(p.getLocation()); + if(checkGlobalRegion(region, p)) return; + + switch (action) { + case UNDO: + if(region.undo()) { + p.sendMessage(BauSystem.PREFIX + "Letzte Aktion rückgangig gemacht"); + } else { + p.sendMessage(BauSystem.PREFIX + "§cNichts zum rückgängig machen"); + } + break; + case REDO: + if(region.redo()) { + p.sendMessage(BauSystem.PREFIX + "Letzte Aktion wiederhohlt"); + } else { + p.sendMessage(BauSystem.PREFIX + "§cNichts zum wiederhohlen"); + } + break; + default: + genericHelp(p); + } + } + + static boolean checkGlobalRegion(Region region, Player p) { + if(Region.GlobalRegion.isGlobalRegion(region)) { + p.sendMessage(BauSystem.PREFIX + "§cDu bist in keiner Region"); + return true; + } + return false; + } + + private boolean permissionCheck(Player player) { + if (Welt.noPermission(player, Permission.worldedit)) { + player.sendMessage(BauSystem.PREFIX + "§cDu darfst hier nicht die Region verändern"); + return false; + } + return true; + } + + enum Action { + UNDO, + REDO + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandReset.java b/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandReset.java index 5c7b31b..05d3988 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandReset.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/commands/CommandReset.java @@ -68,7 +68,7 @@ public class CommandReset extends SWCommand { return; } try { - region.reset(null); + region.reset(schem); p.sendMessage(BauSystem.PREFIX + "§7Region zurückgesetzt"); } catch (IOException e) { p.sendMessage(BauSystem.PREFIX + "§cFehler beim Zurücksetzen der Region"); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/world/Region.java b/BauSystem_Main/src/de/steamwar/bausystem/world/Region.java index b41339f..5d83467 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/world/Region.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/world/Region.java @@ -19,9 +19,10 @@ package de.steamwar.bausystem.world; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extent.clipboard.Clipboard; import de.steamwar.bausystem.commands.CommandTNT.TNTMode; -import de.steamwar.core.VersionedRunnable; +import de.steamwar.core.VersionedCallable; import de.steamwar.sql.NoClipboardException; import de.steamwar.sql.Schematic; import org.bukkit.Bukkit; @@ -85,6 +86,8 @@ public class Region { private final Prototype prototype; private final String optionsLinkedWith; // nullable private Region linkedRegion = null; // nullable + private SizedStack undosessions; + private SizedStack redosessions; private TNTMode tntMode = Region.buildAreaEnabled() ? TNTMode.ONLY_TB : TNTMode.OFF; private boolean freeze = false; @@ -171,12 +174,14 @@ public class Region { return prototype.buildArea.inRegionExtension(this, l); } - public void fastreset() { - prototype.fastreset(this); + public void reset() throws IOException { + initSessions(); + undosessions.push(prototype.reset(this, null)); } public void reset(Schematic schem) throws IOException, NoClipboardException { - prototype.reset(this, schem); + initSessions(); + undosessions.push(prototype.reset(this, schem)); } public boolean hasTestblock() { @@ -184,7 +189,8 @@ public class Region { } public void resetTestblock(Schematic schem) throws IOException, NoClipboardException { - prototype.resetTestblock(this, schem); + initSessions(); + undosessions.push(prototype.resetTestblock(this, schem)); } public boolean hasProtection() { @@ -192,7 +198,8 @@ public class Region { } public void protect(Schematic schem) throws IOException, NoClipboardException { - prototype.protect(this, schem); + initSessions(); + undosessions.push(prototype.protect(this, schem)); } public boolean hasExtensionAreaRegistered() { @@ -203,6 +210,47 @@ public class Region { return prototype.testblock.extensionPrototypeArea; } + private void initSessions() { + if(undosessions == null) { + undosessions = new SizedStack<>(20); + redosessions = new SizedStack<>(20); + } + } + + public boolean undo() { + initSessions(); + EditSession session = null; + try { + session = undosessions.pop(); + if(session == null) + return false; + session.undo(session); + redosessions.push(session); + return true; + } finally { + if (session != null) { + session.flushSession(); + } + } + } + + public boolean redo() { + initSessions(); + EditSession session = null; + try { + session = redosessions.pop(); + if(session == null) + return false; + session.redo(session); + undosessions.push(session); + return true; + } finally { + if (session != null) { + session.flushSession(); + } + } + } + public static class GlobalRegion extends Region { private static final GlobalRegion GLOBAL_REGION = new GlobalRegion(); @@ -320,59 +368,50 @@ public class Region { inRange(l.getZ(), region.minZ + offsetZ - extensionNegativeZ + 1, sizeZ + extensionNegativeZ - 1 + extensionPositiveZ); } - public void fastreset(Region region) { - File file = new File(schematic); - int x = region.minX + offsetX + sizeX / 2; - int y = region.minY + offsetY; - int z = region.minZ + offsetZ + sizeZ / 2; - VersionedRunnable.call(new VersionedRunnable(() -> Region_12.paste(file, x, y, z, rotate), 8), - new VersionedRunnable(() -> Region_15.fastpaste(file, x, y, z, rotate), 15)); - } - - public void reset(Region region, Schematic schem) throws IOException, NoClipboardException { + public EditSession reset(Region region, Schematic schem) throws IOException, NoClipboardException { int x = region.minX + offsetX + sizeX / 2; int y = region.minY + offsetY; int z = region.minZ + offsetZ + sizeZ / 2; if (schem == null) - paste(new File(schematic), x, y, z, rotate); + return paste(new File(schematic), x, y, z, rotate); else - paste(schem.load(), x, y, z, rotate); + return paste(schem.load(), x, y, z, rotate); } public boolean hasProtection() { return protectSchematic != null; } - public void protect(Region region, Schematic schem) throws IOException, NoClipboardException { + public EditSession protect(Region region, Schematic schem) throws IOException, NoClipboardException { int x = region.minX + offsetX + sizeX / 2; int y = region.minY + testblock.offsetY - 1; int z = region.minZ + offsetZ + sizeZ / 2; if (schem == null) - paste(new File(protectSchematic), x, y, z, rotate); + return paste(new File(protectSchematic), x, y, z, rotate); else - paste(schem.load(), x, y, z, rotate); + return paste(schem.load(), x, y, z, rotate); } public boolean hasTestblock() { return testblock != null; } - public void resetTestblock(Region region, Schematic schem) throws IOException, NoClipboardException { - testblock.reset(region, schem); + public EditSession resetTestblock(Region region, Schematic schem) throws IOException, NoClipboardException { + return testblock.reset(region, schem); } private static boolean inRange(double l, int min, int size) { return min <= l && l <= min + size; } - private static void paste(File file, int x, int y, int z, boolean rotate) { //Type of protect - VersionedRunnable.call(new VersionedRunnable(() -> Region_12.paste(file, x, y, z, rotate), 8), - new VersionedRunnable(() -> Region_15.paste(file, x, y, z, rotate), 15)); + private static EditSession paste(File file, int x, int y, int z, boolean rotate) { //Type of protect + return (EditSession) VersionedCallable.call(new VersionedCallable(() -> Region_12.paste(file, x, y, z, rotate), 8), + new VersionedCallable(() -> Region_15.paste(file, x, y, z, rotate), 15)); } - private static void paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { - VersionedRunnable.call(new VersionedRunnable(() -> Region_12.paste(clipboard, x, y, z, rotate), 8), - new VersionedRunnable(() -> Region_15.paste(clipboard, x, y, z, rotate), 15)); + private static EditSession paste(Clipboard clipboard, int x, int y, int z, boolean rotate) { + return (EditSession) VersionedCallable.call(new VersionedCallable(() -> Region_12.paste(clipboard, x, y, z, rotate), 8), + new VersionedCallable(() -> Region_15.paste(clipboard, x, y, z, rotate), 15)); } } }