From 6722d73c686c5009d23997f8d06bc8c2b3ad5b3b Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 25 Nov 2023 20:32:29 +0000 Subject: [PATCH] fix: fixed history rollback (#2367) - Also go ahead and clean up other parts of this, with more appropriate operators used - Fixes #2366 --- .../core/database/RollbackDatabase.java | 41 ++++++++------ .../core/history/DiskStorageHistory.java | 55 +++++++++++++++---- .../history/RollbackOptimizedHistory.java | 21 +++++++ .../history/changeset/AbstractChangeSet.java | 14 +++-- .../changeset/FaweStreamChangeSet.java | 12 ++-- .../worldedit/command/HistorySubCommands.java | 17 +++--- .../src/main/resources/lang/strings.json | 3 +- 7 files changed, 112 insertions(+), 51 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java index 3624e34c0..603e8061a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java @@ -159,15 +159,20 @@ public class RollbackDatabase extends AsyncNotifyQueue { Future future = call(() -> { try { int count = 0; - String stmtStr = ascending ? uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND" + - " `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` , `id`" : - "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND" + - " `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC" : - uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? " + - "AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` DESC, `id` DESC" : - "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND" + - " `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; - try (PreparedStatement stmt = connection.prepareStatement(stmtStr)) { + String stmtStr; + if (ascending) { + if (uuid == null) { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? ORDER BY `time` , `id`"; + } else { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC"; + } + } else { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; + } + try (PreparedStatement stmt = connection.prepareStatement(stmtStr.formatted(this.prefix))) { stmt.setInt(1, (int) (minTime / 1000)); stmt.setInt(2, pos1.getBlockX()); stmt.setInt(3, pos2.getBlockX()); @@ -193,20 +198,20 @@ public class RollbackDatabase extends AsyncNotifyQueue { if (delete && uuid != null) { try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM`" + this.prefix + "edits` WHERE `player`=? AND `time`>? AND `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?")) { - stmt.setInt(1, (int) (minTime / 1000)); - stmt.setInt(2, pos1.getBlockX()); - stmt.setInt(3, pos2.getBlockX()); - stmt.setInt(4, pos1.getBlockZ()); - stmt.setInt(5, pos2.getBlockZ()); - // Keep 128 offset for backwards-compatibility - stmt.setInt(6, pos1.getBlockY() - 128); - stmt.setInt(7, pos2.getBlockY() - 128); byte[] uuidBytes = ByteBuffer .allocate(16) .putLong(uuid.getMostSignificantBits()) .putLong(uuid.getLeastSignificantBits()) .array(); - stmt.setBytes(8, uuidBytes); + stmt.setBytes(1, uuidBytes); + stmt.setInt(2, (int) (minTime / 1000)); + stmt.setInt(3, pos1.getBlockX()); + stmt.setInt(4, pos2.getBlockX()); + stmt.setInt(5, pos1.getBlockZ()); + stmt.setInt(6, pos2.getBlockZ()); + // Keep 128 offset for backwards-compatibility + stmt.setInt(7, pos1.getBlockY() - 128); + stmt.setInt(8, pos2.getBlockY() - 128); } } return count; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java index 7d0bfe76e..eb2e3da59 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java @@ -15,8 +15,10 @@ import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; +import org.apache.logging.log4j.Logger; import java.io.File; import java.io.FileInputStream; @@ -35,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class DiskStorageHistory extends FaweStreamChangeSet { + private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Map> NEXT_INDEX = new ConcurrentHashMap<>(); private UUID uuid; @@ -141,9 +144,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet { e.printStackTrace(); return; } - EditSession session = toEditSession(actor, regions); - session.setBlocks(this, ChangeSetExecutor.Type.UNDO); - deleteFiles(); + try (EditSession session = toEditSession(actor, regions)) { + session.setBlocks(this, ChangeSetExecutor.Type.UNDO); + } } public void undo(Actor actor) { @@ -371,9 +374,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bdFile.exists()) { return null; } - FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); - readHeader(is); - return is; + try { + FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); + readHeader(is); + return is; + } catch (IOException e) { + LOGGER.error("Could not load block history file {}", bdFile); + throw e; + } } @Override @@ -381,7 +389,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bioFile.exists()) { return null; } - return MainUtil.getCompressedIS(new FileInputStream(bioFile)); + try { + return MainUtil.getCompressedIS(new FileInputStream(bioFile)); + } catch (IOException e) { + LOGGER.error("Could not load biome history file {}", bdFile); + throw e; + } } @Override @@ -389,7 +402,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!enttFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); + } catch (IOException e) { + LOGGER.error("Could not load entity create history file {}", bdFile); + throw e; + } } @Override @@ -397,7 +415,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!entfFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); + } catch (IOException e) { + LOGGER.error("Could not load entity remove history file {}", bdFile); + throw e; + } } @Override @@ -405,7 +428,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbttFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); + } catch (IOException e) { + LOGGER.error("Could not load tile create history file {}", bdFile); + throw e; + } } @Override @@ -413,7 +441,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbtfFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); + } catch (IOException e) { + LOGGER.error("Could not load tile remove history file {}", bdFile); + throw e; + } } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java index 6e7e4068b..1f01f99de 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java @@ -7,6 +7,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BiomeType; import org.apache.logging.log4j.Logger; import java.io.IOException; @@ -125,6 +126,26 @@ public class RollbackOptimizedHistory extends DiskStorageHistory { } } + @Override + public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) { + super.addBiomeChange(x, y, z, from, to); + if (x < minX) { + minX = x; + } else if (x > maxX) { + maxX = x; + } + if (y < minY) { + minY = y; + } else if (y > maxY) { + maxY = y; + } + if (z < minZ) { + minZ = z; + } else if (z > maxZ) { + maxZ = z; + } + } + @Override public void writeHeader(OutputStream os, int x, int y, int z) throws IOException { minX = x; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index c6a2d9bb0..4bbe1a109 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -257,12 +257,14 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } public EditSession toEditSession(Actor actor, Region[] regions) { - EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(getWorld()).actor(actor). - fastMode(false).checkMemory(false).changeSet(this).limitUnlimited(); - if (regions != null) { - builder.allowedRegions(regions); - } else { - builder.allowedRegionsEverywhere(); + EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world) + .checkMemory(false) + .changeSetNull() + .fastMode(false) + .limitUnprocessed(actor) + .actor(actor); + if (!actor.getLimit().RESTRICT_HISTORY_TO_REGIONS) { + builder = builder.allowedRegionsEverywhere(); } EditSession editSession = builder.build(); editSession.setSize(1); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java index c2ae362ae..71232a31c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java @@ -167,7 +167,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readX(FaweInputStream in) throws IOException { in.readFully(buffer); - return lx = lx + ((((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); + return lx = lx + ((((buffer[1] & 0xFF) | ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); } @Override @@ -177,7 +177,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readZ(FaweInputStream in) throws IOException { - return lz = lz + ((((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); + return lz = lz + ((((buffer[2] & 0xFF) | ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); } }; } else { @@ -203,17 +203,17 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readX(FaweInputStream is) throws IOException { is.readFully(buffer); - return lx = (lx + (buffer[0] & 0xFF) + (buffer[1] << 8)); + return lx = lx + ((buffer[0] & 0xFF) | (buffer[1] << 8)); } @Override public int readY(FaweInputStream is) throws IOException { - return ly = (ly + (buffer[4] & 0xFF) + (buffer[5] << 8)); + return ly = ly + ((buffer[4] & 0xFF) | (buffer[5]) << 8); } @Override public int readZ(FaweInputStream is) throws IOException { - return lz = (lz + (buffer[2] & 0xFF) + (buffer[3] << 8)); + return lz = lz + ((buffer[2] & 0xFF) | (buffer[3]) << 8); } }; } @@ -353,7 +353,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { os.write((byte) (z)); // only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127) // means -512 -> 508. Add 128 to avoid negative value casting. - os.write((byte) (y + 128)); + os.write((byte) (y + 32)); os.writeVarInt(from.getInternalId()); os.writeVarInt(to.getInternalId()); } catch (IOException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java index 6b30e9e7e..08f48b265 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java @@ -13,20 +13,22 @@ import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.StringMan; import com.google.common.base.Function; import com.google.common.collect.Lists; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.EditSessionBuilder; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -import com.sk89q.worldedit.command.util.annotation.AllowedRegion; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Time; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Identifiable; @@ -80,7 +82,6 @@ public class HistorySubCommands { @Confirm public synchronized void rerun( Player player, World world, RollbackDatabase database, - @AllowedRegion Region[] allowedRegions, @ArgFlag(name = 'u', desc = "String user", def = "me") UUID other, @ArgFlag(name = 'r', def = "0", desc = "radius") @@ -90,7 +91,7 @@ public class HistorySubCommands { @Time long timeDiff ) throws WorldEditException { - rollback(player, world, database, allowedRegions, other, radius, timeDiff, true); + rollback(player, world, database, other, radius, timeDiff, true); } @Command( @@ -102,7 +103,6 @@ public class HistorySubCommands { @Confirm public synchronized void rollback( Player player, World world, RollbackDatabase database, - @AllowedRegion Region[] allowedRegions, @ArgFlag(name = 'u', desc = "String user", def = "") UUID other, @ArgFlag(name = 'r', def = "0", desc = "radius") @@ -149,9 +149,9 @@ public class HistorySubCommands { count++; RollbackOptimizedHistory edit = supplier.get(); if (restore) { - edit.redo(player, allowedRegions); + edit.redo(player); } else { - edit.undo(player, allowedRegions); + edit.undo(player); } String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex(); player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path)); @@ -201,8 +201,7 @@ public class HistorySubCommands { .at(summary.maxX, world.getMaxY(), summary.maxZ) ); rollback.setTime(historyFile.lastModified()); - RollbackDatabase db = DBHandler.dbHandler() - .getDatabase(world); + RollbackDatabase db = DBHandler.dbHandler().getDatabase(world); db.logEdit(rollback); actor.print(TextComponent.of("Logging: " + historyFile)); } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 5e922cdae..d702ead53 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -54,7 +54,8 @@ "fawe.worldedit.brush.brush.source.mask": "Brush source mask set", "fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled", "fawe.worldedit.brush.brush.transform": "Brush transform set", - "fawe.worldedit.rollback.rollback.element": "Undoing {0}", + "fawe.worldedit.rollback.rollingback.index": "Undoing {0} ...", + "fawe.worldedit.rollback.rollback.element": "{0} undone.", "fawe.worldedit.tool.tool.inspect": "Inspect tool bound to {0}.", "fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago", "fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes",