fix: fixed history rollback (#2367)

- Also go ahead and clean up other parts of this, with more appropriate operators used
 - Fixes #2366
This commit is contained in:
Jordan 2023-11-25 20:32:29 +00:00 committed by GitHub
parent c0a2eef648
commit 6722d73c68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 51 deletions

View File

@ -159,15 +159,20 @@ public class RollbackDatabase extends AsyncNotifyQueue {
Future<Integer> future = call(() -> { Future<Integer> future = call(() -> {
try { try {
int count = 0; int count = 0;
String stmtStr = ascending ? uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND" + String stmtStr;
" `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` , `id`" : if (ascending) {
"SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND" + if (uuid == null) {
" `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC" : stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " +
uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? " + "`y2`>=? AND `y1`<=? ORDER BY `time` , `id`";
"AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` DESC, `id` DESC" : } else {
"SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND" + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " +
" `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC";
try (PreparedStatement stmt = connection.prepareStatement(stmtStr)) { }
} 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(1, (int) (minTime / 1000));
stmt.setInt(2, pos1.getBlockX()); stmt.setInt(2, pos1.getBlockX());
stmt.setInt(3, pos2.getBlockX()); stmt.setInt(3, pos2.getBlockX());
@ -193,20 +198,20 @@ public class RollbackDatabase extends AsyncNotifyQueue {
if (delete && uuid != null) { if (delete && uuid != null) {
try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM`" + this.prefix + 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`<=?")) { "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 byte[] uuidBytes = ByteBuffer
.allocate(16) .allocate(16)
.putLong(uuid.getMostSignificantBits()) .putLong(uuid.getMostSignificantBits())
.putLong(uuid.getLeastSignificantBits()) .putLong(uuid.getLeastSignificantBits())
.array(); .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; return count;

View File

@ -15,8 +15,10 @@ import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -35,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class DiskStorageHistory extends FaweStreamChangeSet { public class DiskStorageHistory extends FaweStreamChangeSet {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final Map<String, Map<UUID, Integer>> NEXT_INDEX = new ConcurrentHashMap<>(); private static final Map<String, Map<UUID, Integer>> NEXT_INDEX = new ConcurrentHashMap<>();
private UUID uuid; private UUID uuid;
@ -141,9 +144,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
e.printStackTrace(); e.printStackTrace();
return; return;
} }
EditSession session = toEditSession(actor, regions); try (EditSession session = toEditSession(actor, regions)) {
session.setBlocks(this, ChangeSetExecutor.Type.UNDO); session.setBlocks(this, ChangeSetExecutor.Type.UNDO);
deleteFiles(); }
} }
public void undo(Actor actor) { public void undo(Actor actor) {
@ -371,9 +374,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!bdFile.exists()) { if (!bdFile.exists()) {
return null; return null;
} }
FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); try {
readHeader(is); FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile));
return is; readHeader(is);
return is;
} catch (IOException e) {
LOGGER.error("Could not load block history file {}", bdFile);
throw e;
}
} }
@Override @Override
@ -381,7 +389,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!bioFile.exists()) { if (!bioFile.exists()) {
return null; 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 @Override
@ -389,7 +402,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!enttFile.exists()) { if (!enttFile.exists()) {
return null; 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 @Override
@ -397,7 +415,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!entfFile.exists()) { if (!entfFile.exists()) {
return null; 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 @Override
@ -405,7 +428,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbttFile.exists()) { if (!nbttFile.exists()) {
return null; 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 @Override
@ -413,7 +441,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbtfFile.exists()) { if (!nbtfFile.exists()) {
return null; 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 @Override

View File

@ -7,6 +7,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.IOException; 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 @Override
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException { public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
minX = x; minX = x;

View File

@ -257,12 +257,14 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
} }
public EditSession toEditSession(Actor actor, Region[] regions) { public EditSession toEditSession(Actor actor, Region[] regions) {
EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(getWorld()).actor(actor). EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world)
fastMode(false).checkMemory(false).changeSet(this).limitUnlimited(); .checkMemory(false)
if (regions != null) { .changeSetNull()
builder.allowedRegions(regions); .fastMode(false)
} else { .limitUnprocessed(actor)
builder.allowedRegionsEverywhere(); .actor(actor);
if (!actor.getLimit().RESTRICT_HISTORY_TO_REGIONS) {
builder = builder.allowedRegionsEverywhere();
} }
EditSession editSession = builder.build(); EditSession editSession = builder.build();
editSession.setSize(1); editSession.setSize(1);

View File

@ -167,7 +167,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readX(FaweInputStream in) throws IOException { public int readX(FaweInputStream in) throws IOException {
in.readFully(buffer); 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 @Override
@ -177,7 +177,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readZ(FaweInputStream in) throws IOException { 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 { } else {
@ -203,17 +203,17 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override @Override
public int readX(FaweInputStream is) throws IOException { public int readX(FaweInputStream is) throws IOException {
is.readFully(buffer); is.readFully(buffer);
return lx = (lx + (buffer[0] & 0xFF) + (buffer[1] << 8)); return lx = lx + ((buffer[0] & 0xFF) | (buffer[1] << 8));
} }
@Override @Override
public int readY(FaweInputStream is) throws IOException { 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 @Override
public int readZ(FaweInputStream is) throws IOException { 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)); 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) // 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. // 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(from.getInternalId());
os.writeVarInt(to.getInternalId()); os.writeVarInt(to.getInternalId());
} catch (IOException e) { } catch (IOException e) {

View File

@ -13,20 +13,22 @@ import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.StringMan;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Lists; 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.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.argument.Arguments;
import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissions;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; 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.Confirm;
import com.sk89q.worldedit.command.util.annotation.Time; import com.sk89q.worldedit.command.util.annotation.Time;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor; 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.history.changeset.ChangeSet;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Identifiable; import com.sk89q.worldedit.util.Identifiable;
@ -80,7 +82,6 @@ public class HistorySubCommands {
@Confirm @Confirm
public synchronized void rerun( public synchronized void rerun(
Player player, World world, RollbackDatabase database, Player player, World world, RollbackDatabase database,
@AllowedRegion Region[] allowedRegions,
@ArgFlag(name = 'u', desc = "String user", def = "me") @ArgFlag(name = 'u', desc = "String user", def = "me")
UUID other, UUID other,
@ArgFlag(name = 'r', def = "0", desc = "radius") @ArgFlag(name = 'r', def = "0", desc = "radius")
@ -90,7 +91,7 @@ public class HistorySubCommands {
@Time @Time
long timeDiff long timeDiff
) throws WorldEditException { ) throws WorldEditException {
rollback(player, world, database, allowedRegions, other, radius, timeDiff, true); rollback(player, world, database, other, radius, timeDiff, true);
} }
@Command( @Command(
@ -102,7 +103,6 @@ public class HistorySubCommands {
@Confirm @Confirm
public synchronized void rollback( public synchronized void rollback(
Player player, World world, RollbackDatabase database, Player player, World world, RollbackDatabase database,
@AllowedRegion Region[] allowedRegions,
@ArgFlag(name = 'u', desc = "String user", def = "") @ArgFlag(name = 'u', desc = "String user", def = "")
UUID other, UUID other,
@ArgFlag(name = 'r', def = "0", desc = "radius") @ArgFlag(name = 'r', def = "0", desc = "radius")
@ -149,9 +149,9 @@ public class HistorySubCommands {
count++; count++;
RollbackOptimizedHistory edit = supplier.get(); RollbackOptimizedHistory edit = supplier.get();
if (restore) { if (restore) {
edit.redo(player, allowedRegions); edit.redo(player);
} else { } else {
edit.undo(player, allowedRegions); edit.undo(player);
} }
String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex(); String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex();
player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path)); player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path));
@ -201,8 +201,7 @@ public class HistorySubCommands {
.at(summary.maxX, world.getMaxY(), summary.maxZ) .at(summary.maxX, world.getMaxY(), summary.maxZ)
); );
rollback.setTime(historyFile.lastModified()); rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.dbHandler() RollbackDatabase db = DBHandler.dbHandler().getDatabase(world);
.getDatabase(world);
db.logEdit(rollback); db.logEdit(rollback);
actor.print(TextComponent.of("Logging: " + historyFile)); actor.print(TextComponent.of("Logging: " + historyFile));
} }

View File

@ -54,7 +54,8 @@
"fawe.worldedit.brush.brush.source.mask": "Brush source mask set", "fawe.worldedit.brush.brush.source.mask": "Brush source mask set",
"fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled", "fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled",
"fawe.worldedit.brush.brush.transform": "Brush transform set", "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": "Inspect tool bound to {0}.",
"fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago", "fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago",
"fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes", "fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes",