Various major

Add regen
Add //history [find|restore|rollback|summary|clear]
 - history commands are interactable
 - inspect brush info is interactable
Commands are now logged to a searchable database
Fix some cases of id/ordinal mismatch
This commit is contained in:
Jesse Boyd 2019-11-23 04:31:48 +00:00
parent edcaeb6cfe
commit 1844d4dba7
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
56 changed files with 1236 additions and 924 deletions

View File

@ -1,56 +0,0 @@
package com.boydti.fawe.bukkit;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.object.FaweCommand;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitBlockCommandSender;
import com.sk89q.worldedit.bukkit.BukkitCommandSender;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extension.platform.Actor;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class BukkitCommand implements CommandExecutor {
private final FaweCommand cmd;
public BukkitCommand(FaweCommand cmd) {
this.cmd = cmd;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, Command cmd, String label, String[] args) {
final Actor plr = wrapCommandSender(sender);
if (!sender.hasPermission(this.cmd.getPerm()) && !sender.isOp()) {
plr.printError(TranslatableComponent.of("fawe.error.no.perm", TextComponent.of(this.cmd.getPerm())));
return true;
}
this.cmd.executeSafe(plr, args);
return true;
}
/**
* Used to wrap a Bukkit Player as a WorldEdit Player.
*
* @param player a player
* @return a wrapped player
*/
public com.sk89q.worldedit.bukkit.BukkitPlayer wrapPlayer(Player player) {
return BukkitAdapter.adapt(player);
}
public Actor wrapCommandSender(CommandSender sender) {
if (sender instanceof Player) {
return wrapPlayer((Player) sender);
} else if (sender instanceof BlockCommandSender) {
return new BukkitBlockCommandSender(WorldEditPlugin.getInstance(), (BlockCommandSender) sender);
}
return new BukkitCommandSender(WorldEditPlugin.getInstance(), sender);
}
}

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_14;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger; import static org.slf4j.LoggerFactory.getLogger;
import com.bekvon.bukkit.residence.commands.set;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.IChunkSet;
@ -67,26 +68,18 @@ import org.jetbrains.annotations.NotNull;
public class BukkitGetBlocks_1_14 extends CharGetBlocks { public class BukkitGetBlocks_1_14 extends CharGetBlocks {
public ChunkSection[] sections; public ChunkSection[] sections;
public Chunk nmsChunk; public Chunk nmsChunk;
public CraftWorld world; public WorldServer world;
public int X, Z; public int X, Z;
// private boolean forceLoad;
public BukkitGetBlocks_1_14(World world, int X, int Z, boolean forceLoad) { public BukkitGetBlocks_1_14(World world, int X, int Z) {
this.world = (CraftWorld) world; this(((CraftWorld) world).getHandle(), X, Z);
this.X = X;
this.Z = Z;
// if (forceLoad) {
// this.world.getHandle().setForceLoaded(X, Z, this.forceLoad = true);
// }
} }
// @Override public BukkitGetBlocks_1_14(WorldServer world, int X, int Z) {
// protected void finalize() { this.world = world;
// if (forceLoad) { this.X = X;
// this.world.getHandle().setForceLoaded(X, Z, forceLoad = false); this.Z = Z;
// } }
// }
public int getX() { public int getX() {
return X; return X;
@ -133,14 +126,16 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
@Override @Override
public CompoundTag getEntity(UUID uuid) { public CompoundTag getEntity(UUID uuid) {
org.bukkit.entity.Entity bukkitEnt = world.getEntity(uuid); Entity entity = world.getEntity(uuid);
if (bukkitEnt != null) { if (entity != null) {
org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
} }
for (List<Entity> entry : getChunk().getEntitySlices()) { for (List<Entity> entry : getChunk().getEntitySlices()) {
if (entry != null) { if (entry != null) {
for (Entity entity : entry) { for (Entity ent : entry) {
if (uuid.equals(entity.getUniqueID())) { if (uuid.equals(ent.getUniqueID())) {
org.bukkit.entity.Entity bukkitEnt = ent.getBukkitEntity();
return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData();
} }
} }
@ -235,7 +230,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
@Override @Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) { public <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
try { try {
WorldServer nmsWorld = world.getHandle(); WorldServer nmsWorld = world;
Chunk nmsChunk = BukkitAdapter_1_14.ensureLoaded(nmsWorld, X, Z); Chunk nmsChunk = BukkitAdapter_1_14.ensureLoaded(nmsWorld, X, Z);
// Remove existing tiles // Remove existing tiles
@ -633,7 +628,7 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
synchronized (this) { synchronized (this) {
tmp = nmsChunk; tmp = nmsChunk;
if (tmp == null) { if (tmp == null) {
nmsChunk = tmp = BukkitAdapter_1_14.ensureLoaded(this.world.getHandle(), X, Z); nmsChunk = tmp = BukkitAdapter_1_14.ensureLoaded(this.world, X, Z);
} }
} }
} }

View File

@ -10,6 +10,7 @@ import org.bukkit.World;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collection; import java.util.Collection;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -19,10 +20,10 @@ public class GriefPreventionFilter extends CuboidRegionFilter {
public GriefPreventionFilter(World world) { public GriefPreventionFilter(World world) {
checkNotNull(world); checkNotNull(world);
this.claims = TaskManager.IMP.sync(new RunnableVal<Collection<Claim>>() { this.claims = TaskManager.IMP.sync(new Supplier<Collection<Claim>>() {
@Override @Override
public void run(Collection<Claim> claims) { public Collection<Claim> get() {
this.value = new ArrayDeque<>(GriefPrevention.instance.dataStore.getClaims()); return new ArrayDeque<>(GriefPrevention.instance.dataStore.getClaims());
} }
}); });
this.world = world; this.world = world;

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.filter;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger; import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.regions.general.CuboidRegionFilter; import com.boydti.fawe.regions.general.CuboidRegionFilter;
@ -26,9 +27,7 @@ public class WorldGuardFilter extends CuboidRegionFilter {
} }
@Override @Override
public void calculateRegions() { public void calculateRegions() {
TaskManager.IMP.sync(new RunnableVal<Object>() { Fawe.get().getQueueHandler().sync(() -> {
@Override
public void run(Object value) {
WorldGuardFilter.this.manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(FaweAPI.getWorld(world.getName())); WorldGuardFilter.this.manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(FaweAPI.getWorld(world.getName()));
for (ProtectedRegion region : manager.getRegions().values()) { for (ProtectedRegion region : manager.getRegions().values()) {
BlockVector3 min = region.getMinimumPoint(); BlockVector3 min = region.getMinimumPoint();
@ -40,7 +39,6 @@ public class WorldGuardFilter extends CuboidRegionFilter {
} }
add(min.toBlockVector2(), max.toBlockVector2()); add(min.toBlockVector2(), max.toBlockVector2());
} }
}
}); });
} }

View File

@ -74,27 +74,6 @@ public class BukkitBlockCommandSender extends AbstractNonPlayerActor implements
} }
} }
@Override
public void print(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.GRAY));
}
}
@Override
public void printDebug(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.GRAY));
}
}
@Override
public void printError(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.RED));
}
}
@Override @Override
public void print(Component component) { public void print(Component component) {
TextAdapter.sendComponent(sender, WorldEditText.format(component, getLocale())); TextAdapter.sendComponent(sender, WorldEditText.format(component, getLocale()));

View File

@ -74,27 +74,6 @@ public class BukkitCommandSender extends AbstractNonPlayerActor {
} }
} }
@Override
public void print(String msg) {
for (String part : msg.split("\n")) {
sender.sendMessage("\u00A7d" + part);
}
}
@Override
public void printDebug(String msg) {
for (String part : msg.split("\n")) {
sender.sendMessage("\u00A77" + part);
}
}
@Override
public void printError(String msg) {
for (String part : msg.split("\n")) {
sender.sendMessage("\u00A7c" + part);
}
}
@Override @Override
public void print(Component component) { public void print(Component component) {
TextAdapter.sendComponent(sender, WorldEditText.format(component, getLocale())); TextAdapter.sendComponent(sender, WorldEditText.format(component, getLocale()));

View File

@ -190,6 +190,10 @@ public class BukkitWorld extends AbstractWorld {
@Override @Override
public boolean regenerate(Region region, EditSession editSession) { public boolean regenerate(Region region, EditSession editSession) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
return adapter.regenerate(this, region, editSession);
}
/* /*
BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)]; BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];
@ -560,7 +564,7 @@ public class BukkitWorld extends AbstractWorld {
@Override @Override
public IChunkGet get(int chunkX, int chunkZ) { public IChunkGet get(int chunkX, int chunkZ) {
return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ, Settings.IMP.QUEUE.POOL); return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ);
} }
@Override @Override

View File

@ -24,11 +24,13 @@ import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.FaweBukkit;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.DataFixer;
@ -232,4 +234,8 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
default void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) { default void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket packet) {
throw new UnsupportedOperationException("Cannot send fake chunks"); throw new UnsupportedOperationException("Cannot send fake chunks");
} }
default boolean regenerate(com.sk89q.worldedit.world.World world, Region region, EditSession editSession) {
return editSession.regenerate(region);
}
} }

View File

@ -20,23 +20,35 @@
package com.sk89q.worldedit.bukkit.adapter.impl; package com.sk89q.worldedit.bukkit.adapter.impl;
import com.bekvon.bukkit.residence.commands.material; import com.bekvon.bukkit.residence.commands.material;
import com.bekvon.bukkit.residence.commands.server;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.packet.ChunkPacket;
import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent;
import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler;
import com.boydti.fawe.bukkit.adapter.mc1_14.BlockMaterial_1_14; import com.boydti.fawe.bukkit.adapter.mc1_14.BlockMaterial_1_14;
import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitAdapter_1_14; import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitAdapter_1_14;
import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14;
import com.boydti.fawe.bukkit.adapter.mc1_14.MapChunkUtil_1_14; import com.boydti.fawe.bukkit.adapter.mc1_14.MapChunkUtil_1_14;
import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14;
import com.google.common.io.Files;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.blocks.TileEntityBlock;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter;
import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.Spigot_v1_14_R4; import com.sk89q.worldedit.bukkit.adapter.impl.Spigot_v1_14_R4;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.LazyBaseEntity; import com.sk89q.worldedit.entity.LazyBaseEntity;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
@ -50,6 +62,7 @@ import net.minecraft.server.v1_14_R1.Block;
import net.minecraft.server.v1_14_R1.BlockPosition; import net.minecraft.server.v1_14_R1.BlockPosition;
import net.minecraft.server.v1_14_R1.Chunk; import net.minecraft.server.v1_14_R1.Chunk;
import net.minecraft.server.v1_14_R1.ChunkCoordIntPair; import net.minecraft.server.v1_14_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_14_R1.ChunkProviderServer;
import net.minecraft.server.v1_14_R1.ChunkSection; import net.minecraft.server.v1_14_R1.ChunkSection;
import net.minecraft.server.v1_14_R1.Entity; import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityPlayer; import net.minecraft.server.v1_14_R1.EntityPlayer;
@ -58,6 +71,7 @@ import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.IRegistry; import net.minecraft.server.v1_14_R1.IRegistry;
import net.minecraft.server.v1_14_R1.ItemStack; import net.minecraft.server.v1_14_R1.ItemStack;
import net.minecraft.server.v1_14_R1.MinecraftKey; import net.minecraft.server.v1_14_R1.MinecraftKey;
import net.minecraft.server.v1_14_R1.MinecraftServer;
import net.minecraft.server.v1_14_R1.NBTBase; import net.minecraft.server.v1_14_R1.NBTBase;
import net.minecraft.server.v1_14_R1.NBTTagCompound; import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.NBTTagInt; import net.minecraft.server.v1_14_R1.NBTTagInt;
@ -65,11 +79,13 @@ import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_14_R1.PlayerChunk; import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.TileEntity; import net.minecraft.server.v1_14_R1.TileEntity;
import net.minecraft.server.v1_14_R1.World; import net.minecraft.server.v1_14_R1.World;
import net.minecraft.server.v1_14_R1.WorldNBTStorage;
import net.minecraft.server.v1_14_R1.WorldServer; import net.minecraft.server.v1_14_R1.WorldServer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; import org.bukkit.craftbukkit.v1_14_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_14_R1.CraftServer;
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity;
@ -80,6 +96,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.OptionalInt; import java.util.OptionalInt;
@ -358,4 +376,53 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I
} }
return parent.fromNative(foreign); return parent.fromNative(foreign);
} }
@Override
public boolean regenerate(com.sk89q.worldedit.world.World world, Region region, EditSession editSession) {
WorldServer originalWorld = ((CraftWorld) world).getHandle();
ChunkProviderServer provider = originalWorld.getChunkProvider();
if (!(provider instanceof ChunkProviderServer)) {
return false;
}
File saveFolder = Files.createTempDir();
// register this just in case something goes wrong
// normally it should be deleted at the end of this method
saveFolder.deleteOnExit();
try {
CraftServer server = originalWorld.getServer();
WorldNBTStorage originalDataManager = originalWorld.getDataManager();
WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server.getServer(), originalDataManager.getDataFixer());
try (WorldServer freshWorld = new WorldServer(server.getServer(),
server.getServer().executorService, saveHandler,
originalWorld.worldData,
originalWorld.worldProvider.getDimensionManager(),
originalWorld.getMethodProfiler(),
server.getServer().worldLoadListenerFactory.create(11),
((CraftWorld) world).getEnvironment(),
server.getGenerator(world.getName()))) {
// Pre-gen all the chunks
// We need to also pull one more chunk in every direction
CuboidRegion expandedPreGen = new CuboidRegion(region.getMinimumPoint().subtract(16, 0, 16), region.getMaximumPoint().add(16, 0, 16));
for (BlockVector2 chunk : expandedPreGen.getChunks()) {
freshWorld.getChunkAt(chunk.getBlockX(), chunk.getBlockZ());
}
// TODO optimize
SingleThreadQueueExtent extent = new SingleThreadQueueExtent();
extent.init(null, (x, z) -> new BukkitGetBlocks_1_14(freshWorld, x, z), null);
for (BlockVector3 vec : region) {
editSession.setBlock(vec, extent.getFullBlock(vec));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (MaxChangedBlocksException e) {
throw new RuntimeException(e);
} finally {
saveFolder.delete();
}
return true;
}
} }

View File

@ -1,184 +0,0 @@
package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class Rollback extends FaweCommand {
public Rollback() {
super("fawe.rollback");
}
@Override
public boolean execute(Actor actor, String... args) {
if (!(actor.isPlayer() && actor instanceof Player)) {
return false;
}
Player player = (Player) actor;
if (!Settings.IMP.HISTORY.USE_DATABASE) {
player.print(TranslatableComponent.of("fawe.error.setting.disable" , "history.use-database (Import with /frb #import )"));
return false;
}
if (args.length != 3) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb u:<uuid> r:<radius> t:<time>"));
return false;
}
switch (args[0]) {
case "i":
case "info":
case "undo":
case "revert":
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb u:<uuid> r:<radius> t:<time>"));
return false;
}
if (args.length < 1) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb <info|undo> u:<uuid> r:<radius> t:<time>"));
return false;
}
World world = player.getWorld();
switch (args[0]) {
case "i":
case "info":
if (args.length < 2) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb <info|undo> u:<uuid> r:<radius> t:<time>"));
return false;
}
player.deleteMeta(Player.METADATA_KEYS.ROLLBACK);
Location origin = player.getLocation();
rollback(player, !player.hasPermission("fawe.rollback.deep"), Arrays.copyOfRange(args, 1, args.length), new RunnableVal<List<DiskStorageHistory>>() {
@Override
public void run(List<DiskStorageHistory> edits) {
long total = 0;
player.print("&d=| Username | Bounds | Distance | Changes | Age |=");
for (DiskStorageHistory edit : edits) {
DiskStorageHistory.DiskStorageSummary summary = edit.summarize(new RegionWrapper(origin.getBlockX(), origin.getBlockX(), origin.getBlockZ(), origin.getBlockZ()), !player.hasPermission("fawe.rollback.deep"));
RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
int distance = region.distance(origin.getBlockX(), origin.getBlockZ());
String name = Fawe.imp().getName(edit.getUUID());
long seconds = (System.currentTimeMillis() - edit.getBDFile().lastModified()) / 1000;
total += edit.getBDFile().length();
int size = summary.getSize();
Map<BlockState, Double> percents = summary.getPercents();
StringBuilder percentString = new StringBuilder();
String prefix = "";
for (Map.Entry<BlockState, Double> entry : percents.entrySet()) {
BlockState state = entry.getKey();
String itemName = "#" + state;
percentString.append(prefix).append(entry.getValue()).append("% ").append(itemName);
prefix = ", ";
}
player.print("&c" + name + " | " + region + " | " + distance + "m | " + size + " | " + MainUtil.secToTime(seconds));
player.print("&8 - &7(" + percentString + ")");
}
player.print("&d==================================================");
player.print("&dSize: " + (double) (total / 1024) / 1000 + "MB");
player.print("&dTo rollback: /frb undo");
player.print("&d==================================================");
player.setMeta(Player.METADATA_KEYS.ROLLBACK, edits);
}
});
break;
case "undo":
case "revert":
if (!player.hasPermission("fawe.rollback.perform")) {
player.print(TranslatableComponent.of("fawe.error.no.perm", "fawe.rollback.perform"));
return false;
}
final List<DiskStorageHistory> edits = player.getMeta(Player.METADATA_KEYS.ROLLBACK);
player.deleteMeta(Player.METADATA_KEYS.ROLLBACK);
if (edits == null) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb info u:<uuid> r:<radius> t:<time>"));
return false;
}
for (DiskStorageHistory edit : edits) {
player.print("&d" + edit.getBDFile());
EditSession session = edit.toEditSession(null);
session.undo(session);
edit.deleteFiles();
session.flushQueue();
}
player.print("Rollback complete!");
default:
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb info u:<uuid> r:<radius> t:<time>"));
return false;
}
return true;
}
public void rollback(Player player, boolean shallow, String[] args, RunnableVal<List<DiskStorageHistory>> result) {
UUID user = null;
int radius = Integer.MAX_VALUE;
long time = Long.MAX_VALUE;
for (String arg : args) {
String[] split = arg.split(":");
if (split.length != 2) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb <info|undo> u:<uuid> r:<radius> t:<time>"));
return;
}
switch (split[0].toLowerCase()) {
case "username":
case "user":
case "u":
try {
if (split[1].length() > 16) {
user = UUID.fromString(split[1]);
} else {
user = Fawe.imp().getUUID(split[1]);
}
} catch (IllegalArgumentException ignored) {
}
if (user == null) {
player.print("&dInvalid user: " + split[1]);
return;
}
break;
case "r":
case "radius":
if (!MathMan.isInteger(split[1])) {
player.print("&dInvalid radius: " + split[1]);
return;
}
radius = Integer.parseInt(split[1]);
break;
case "t":
case "time":
time = MainUtil.timeToSec(split[1]) * 1000;
break;
default:
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb <info|undo> u:<uuid> r:<radius> t:<time>"));
return;
}
}
Location origin = player.getLocation();
List<DiskStorageHistory> edits = FaweAPI.getBDFiles(origin, user, radius, time, shallow);
if (edits == null) {
player.print("&cToo broad, try refining your search!");
return;
}
if (edits.size() == 0) {
player.print("&cNo edits found!");
return;
}
result.run(edits);
}
}

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.config; package com.boydti.fawe.config;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.util.formatting.WorldEditText; import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
@ -11,6 +12,14 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
public class Caption { public class Caption {
public static String toString(Component component) {
return toString(component, WorldEdit.getInstance().getTranslationManager().getDefaultLocale());
}
public static String toString(Component component, Locale locale) {
return WorldEditText.reduceToText(color(component), locale);
}
/** /**
* Colorize a component with legacy color codes * Colorize a component with legacy color codes
* @param parent * @param parent

View File

@ -6,10 +6,13 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.collection.YieldIterable;
import com.boydti.fawe.object.task.AsyncNotifyQueue; import com.boydti.fawe.object.task.AsyncNotifyQueue;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -19,10 +22,21 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -36,217 +50,240 @@ public class RollbackDatabase extends AsyncNotifyQueue {
private final World world; private final World world;
private Connection connection; private Connection connection;
private String INSERT_EDIT; private @Language("sql") String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `{0}edits` (`player` BLOB(16) NOT NULL,`id` INT NOT NULL, `time` INT NOT NULL,`x1` INT NOT NULL,`x2` INT NOT NULL,`z1` INT NOT NULL,`z2` INT NOT NULL,`y1` INT NOT NULL, `y2` INT NOT NULL, `size` INT NOT NULL, `command` VARCHAR, PRIMARY KEY (player, id))";
private String CREATE_TABLE; private @Language("sql") String UPDATE_TABLE = "ALTER TABLE `{0}edits` ADD `command` VARCHAR, ADD `size` INT NOT NULL";
// private String GET_EDITS_POINT; private @Language("sql") String INSERT_EDIT = "INSERT OR REPLACE INTO `{0}edits` (`player`,`id`,`time`,`x1`,`x2`,`z1`,`z2`,`y1`,`y2`,`command`,`size`) VALUES(?,?,?,?,?,?,?,?,?,?,?)";
private String GET_EDITS; private @Language("sql") String PURGE = "DELETE FROM `{0}edits` WHERE `time`<?";
private String GET_EDITS_USER; private @Language("sql") String GET_EDITS_USER = "SELECT * FROM `{0}edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC";
private String GET_EDITS_ASC; private @Language("sql") String GET_EDITS_USER_ASC = "SELECT * FROM `{0}edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC";
private String GET_EDITS_USER_ASC; private @Language("sql") String GET_EDITS = "SELECT * FROM `{0}edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` DESC, `id` DESC";
private String DELETE_EDITS_USER; private @Language("sql") String GET_EDITS_ASC = "SELECT * FROM `{0}edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` ASC, `id` ASC";
private String DELETE_EDIT_USER; private @Language("sql") String GET_EDIT_USER = "SELECT * FROM `{0}edits` WHERE `player`=? AND `id`=?";
private String PURGE;
private @Language("sql") String DELETE_EDITS_USER = "DELETE FROM `{0}edits` WHERE `player`=? AND `time`>? AND `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?";
private @Language("sql") String DELETE_EDIT_USER = "DELETE FROM `{0}edits` WHERE `player`=? AND `id`=?";
private ConcurrentLinkedQueue<RollbackOptimizedHistory> historyChanges = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue<RollbackOptimizedHistory> historyChanges = new ConcurrentLinkedQueue<>();
private final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<>();
public RollbackDatabase(String world) throws SQLException, ClassNotFoundException { public RollbackDatabase(String world) throws SQLException, ClassNotFoundException {
this(FaweAPI.getWorld(world)); this(FaweAPI.getWorld(world));
} }
public RollbackDatabase(World world) throws SQLException, ClassNotFoundException { public RollbackDatabase(World world) throws SQLException, ClassNotFoundException {
super((t, e) -> e.printStackTrace());
this.prefix = ""; this.prefix = "";
this.worldName = world.getName(); this.worldName = world.getName();
this.world = world; this.world = world;
this.dbLocation = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + world.getName() + File.separator + "summary.db"); this.dbLocation = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + world.getName() + File.separator + "summary.db");
connection = openConnection(); connection = openConnection();
CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `" + prefix + "edits` (`player` BLOB(16) NOT NULL,`id` INT NOT NULL,`x1` INT NOT NULL,`y1` INT NOT NULL,`z1` INT NOT NULL,`x2` INT NOT NULL,`y2` INT NOT NULL,`z2` INT NOT NULL,`time` INT NOT NULL, PRIMARY KEY (player, id))";
INSERT_EDIT = "INSERT OR REPLACE INTO `" + prefix + "edits` (`player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`) VALUES(?,?,?,?,?,?,?,?,?)"; // update vars
PURGE = "DELETE FROM `" + prefix + "edits` WHERE `time`<?"; CREATE_TABLE = CREATE_TABLE.replace("{0}", prefix);
// GET_EDITS_POINT = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?"; UPDATE_TABLE = UPDATE_TABLE.replace("{0}", prefix);
GET_EDITS = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? ORDER BY `time` DESC, `id` DESC"; INSERT_EDIT = INSERT_EDIT.replace("{0}", prefix);
GET_EDITS_USER = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=? ORDER BY `time` DESC, `id` DESC"; PURGE = PURGE.replace("{0}", prefix);
GET_EDITS_ASC = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? ORDER BY `time` ASC, `id` ASC"; GET_EDITS_USER = GET_EDITS_USER.replace("{0}", prefix);
GET_EDITS_USER_ASC = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=? ORDER BY `time` ASC, `id` ASC"; GET_EDITS_USER_ASC = GET_EDITS_USER_ASC.replace("{0}", prefix);
DELETE_EDITS_USER = "DELETE FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=?"; GET_EDITS = GET_EDITS.replace("{0}", prefix);
DELETE_EDIT_USER = "DELETE FROM `" + prefix + "edits` WHERE `player`=? AND `id`=?"; GET_EDITS_ASC = GET_EDITS_ASC.replace("{0}", prefix);
init(); GET_EDIT_USER = GET_EDIT_USER.replace("{0}", prefix);
DELETE_EDITS_USER = DELETE_EDITS_USER.replace("{0}", prefix);
DELETE_EDIT_USER = DELETE_EDIT_USER.replace("{0}", prefix);
try {
init().get();
purge((int) TimeUnit.DAYS.toMillis(Settings.IMP.HISTORY.DELETE_AFTER_DAYS)); purge((int) TimeUnit.DAYS.toMillis(Settings.IMP.HISTORY.DELETE_AFTER_DAYS));
} } catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
@Override
public boolean hasQueued() {
return connection != null && (!historyChanges.isEmpty() || !tasks.isEmpty());
}
@Override
public void operate() {
synchronized (this) {
if (connection == null) {
return;
}
while (sendBatch()) {
// Still processing
}
} }
} }
public void init() { private byte[] toBytes(UUID uuid) {
return ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array();
}
public Future<Boolean> init() {
return call(() -> {
try (PreparedStatement stmt = connection.prepareStatement(CREATE_TABLE)) { try (PreparedStatement stmt = connection.prepareStatement(CREATE_TABLE)) {
stmt.executeUpdate(); stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} }
try (PreparedStatement stmt = connection.prepareStatement(UPDATE_TABLE)) {
stmt.executeUpdate();
} catch (SQLException ignore) {} // Already updated
return true;
});
} }
public void delete(UUID uuid, int id) { public Future<Integer> delete(UUID uuid, int id) {
addTask(new Runnable() { return call(() -> {
@Override
public void run() {
try (PreparedStatement stmt = connection.prepareStatement(DELETE_EDIT_USER)) { try (PreparedStatement stmt = connection.prepareStatement(DELETE_EDIT_USER)) {
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array(); stmt.setBytes(1, toBytes(uuid));
stmt.setBytes(1, uuidBytes);
stmt.setInt(2, id); stmt.setInt(2, id);
} catch (SQLException e) { return stmt.executeUpdate();
e.printStackTrace();
}
} }
}); });
} }
public void purge(int diff) { public Future<RollbackOptimizedHistory> getEdit(@NotNull UUID uuid, int id) {
return call(() -> {
try (PreparedStatement stmt = connection.prepareStatement(GET_EDIT_USER)) {
stmt.setBytes(1, toBytes(uuid));
stmt.setInt(2, id);
ResultSet result = stmt.executeQuery();
if (!result.next()) {
return null;
}
return create(result).get();
}
});
}
private Supplier<RollbackOptimizedHistory> create(ResultSet result) throws SQLException {
byte[] uuidBytes = result.getBytes("player");
int index = result.getInt("id");
int x1 = result.getInt("x1");
int x2 = result.getInt("x2");
int y1 = result.getInt("y1");
int y2 = result.getInt("y2");
int z1 = result.getInt("z1");
int z2 = result.getInt("z2");
CuboidRegion region = new CuboidRegion(BlockVector3.at(x1, y1, z1), BlockVector3.at(x2, y2, z2));
long time = result.getInt("time") * 1000L;
long size = result.getInt("size");
String command = result.getString("command");
ByteBuffer bb = ByteBuffer.wrap(uuidBytes);
long high = bb.getLong();
long low = bb.getLong();
UUID uuid = new UUID(high, low);
return () -> new RollbackOptimizedHistory(world, uuid, index, time, size, region, command);
}
public Future<Integer> purge(int diff) {
long now = System.currentTimeMillis() / 1000; long now = System.currentTimeMillis() / 1000;
final int then = (int) (now - diff); final int then = (int) (now - diff);
addTask(() -> { return call(() -> {
try (PreparedStatement stmt = connection.prepareStatement(PURGE)) { try (PreparedStatement stmt = connection.prepareStatement(PURGE)) {
stmt.setInt(1, then); stmt.setInt(1, then);
stmt.executeUpdate(); return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} }
}); });
} }
public void getPotentialEdits(UUID uuid, long minTime, BlockVector3 pos1, BlockVector3 pos2, RunnableVal<DiskStorageHistory> onEach, Runnable whenDone, boolean delete, boolean ascending) { public Iterable<Supplier<RollbackOptimizedHistory>> getEdits(BlockVector3 pos, boolean ascending) {
final World world = FaweAPI.getWorld(this.worldName); return getEdits(null, 0, pos, pos, false, ascending);
addTask(() -> { }
public Iterable<Supplier<RollbackOptimizedHistory>> getEdits(UUID uuid, long minTime, BlockVector3 pos1, BlockVector3 pos2, boolean delete, boolean ascending) {
YieldIterable<Supplier<RollbackOptimizedHistory>> yieldIterable = new YieldIterable<>();
Future<Integer> future = call(() -> {
try {
int count = 0;
String stmtStr = ascending ? uuid == null ? GET_EDITS_ASC : GET_EDITS_USER_ASC : String stmtStr = ascending ? uuid == null ? GET_EDITS_ASC : GET_EDITS_USER_ASC :
uuid == null ? GET_EDITS : GET_EDITS_USER; uuid == null ? GET_EDITS : GET_EDITS_USER;
// `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)) { try (PreparedStatement stmt = connection.prepareStatement(stmtStr)) {
stmt.setInt(1, pos1.getBlockX()); stmt.setInt(1, (int) (minTime / 1000));
stmt.setInt(2, pos2.getBlockX()); stmt.setInt(2, pos1.getBlockX());
stmt.setByte(3, (byte) (pos1.getBlockY() - 128)); stmt.setInt(3, pos2.getBlockX());
stmt.setByte(4, (byte) (pos2.getBlockY() - 128)); stmt.setInt(4, pos1.getBlockZ());
stmt.setInt(5, pos1.getBlockZ()); stmt.setInt(5, pos2.getBlockZ());
stmt.setInt(6, pos2.getBlockZ()); stmt.setByte(6, (byte) (pos1.getBlockY() - 128));
stmt.setInt(7, (int) (minTime / 1000)); stmt.setByte(7, (byte) (pos2.getBlockY() - 128));
if (uuid != null) { if (uuid != null) {
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array(); byte[] uuidBytes = toBytes(uuid);
stmt.setBytes(8, uuidBytes); stmt.setBytes(8, uuidBytes);
} }
ResultSet result = stmt.executeQuery(); ResultSet result = stmt.executeQuery();
if (!result.next()) { if (!result.next()) {
TaskManager.IMP.taskNow(whenDone, false); return 0;
return;
} }
do { do {
byte[] uuidBytes = result.getBytes(1); count++;
int index = result.getInt(2); Supplier<RollbackOptimizedHistory> history = create(result);
ByteBuffer bb = ByteBuffer.wrap(uuidBytes); // if (history.getBDFile().exists())
long high = bb.getLong(); {
long low = bb.getLong(); yieldIterable.accept(history);
DiskStorageHistory history = new DiskStorageHistory(world, new UUID(high, low), index);
if (history.getBDFile().exists()) {
onEach.run(history);
} }
} while (result.next()); } while (result.next());
TaskManager.IMP.taskNow(whenDone, false);
} catch (SQLException e) {
e.printStackTrace();
} }
if (delete && uuid != null) { if (delete && uuid != null) {
try (PreparedStatement stmt = connection.prepareStatement(DELETE_EDITS_USER)) { try (PreparedStatement stmt = connection.prepareStatement(DELETE_EDITS_USER)) {
stmt.setInt(1, pos1.getBlockX()); stmt.setInt(1, (int) (minTime / 1000));
stmt.setInt(2, pos2.getBlockX()); stmt.setInt(2, pos1.getBlockX());
stmt.setByte(3, (byte) (pos1.getBlockY() - 128)); stmt.setInt(3, pos2.getBlockX());
stmt.setByte(4, (byte) (pos2.getBlockY() - 128)); stmt.setInt(4, pos1.getBlockZ());
stmt.setInt(5, pos1.getBlockZ()); stmt.setInt(5, pos2.getBlockZ());
stmt.setInt(6, pos2.getBlockZ()); stmt.setByte(6, (byte) (pos1.getBlockY() - 128));
stmt.setInt(7, (int) (minTime / 1000)); stmt.setByte(7, (byte) (pos2.getBlockY() - 128));
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array(); byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array();
stmt.setBytes(8, uuidBytes); stmt.setBytes(8, uuidBytes);
} catch (SQLException e) {
e.printStackTrace();
} }
} }
return count;
} finally {
yieldIterable.close();
}
}); });
yieldIterable.setFuture(future);
return yieldIterable;
} }
public void logEdit(RollbackOptimizedHistory history) { public Future<?> logEdit(RollbackOptimizedHistory history) {
queue(() -> historyChanges.add(history)); historyChanges.add(history);
return call(this::sendBatch);
} }
public void addTask(Runnable run) { private boolean sendBatch() throws SQLException {
queue(() -> tasks.add(run));
}
private void runTasks() {
Runnable task;
while ((task = tasks.poll()) != null) {
try {
task.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private boolean sendBatch() {
try {
runTasks();
commit();
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
int size = Math.min(1048572, historyChanges.size()); int size = Math.min(1048572, historyChanges.size());
if (size == 0) { if (size == 0) {
return false; return false;
} }
commit();
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
RollbackOptimizedHistory[] copy = IntStream.range(0, size) RollbackOptimizedHistory[] copy = IntStream.range(0, size)
.mapToObj(i -> historyChanges.poll()).toArray(RollbackOptimizedHistory[]::new); .mapToObj(i -> historyChanges.poll()).toArray(RollbackOptimizedHistory[]::new);
try (PreparedStatement stmt = connection.prepareStatement(INSERT_EDIT)) { try (PreparedStatement stmt = connection.prepareStatement(INSERT_EDIT)) {
// `player`,`id`,`time`,`x1`,`x2`,`z1`,`z2`,`y1`,`y2`,`command`,`size`) VALUES(?,?,?,?,?,?,?,?,?,?,?)"
for (RollbackOptimizedHistory change : copy) { for (RollbackOptimizedHistory change : copy) {
// `player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`
UUID uuid = change.getUUID(); UUID uuid = change.getUUID();
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array(); byte[] uuidBytes = toBytes(uuid);
stmt.setBytes(1, uuidBytes); stmt.setBytes(1, uuidBytes);
stmt.setInt(2, change.getIndex()); stmt.setInt(2, change.getIndex());
stmt.setInt(3, change.getMinX()); stmt.setInt(3, (int) (change.getTime() / 1000));
stmt.setByte(4, (byte) (change.getMinY() - 128));
stmt.setInt(5, change.getMinZ()); BlockVector3 pos1 = change.getMinimumPoint();
stmt.setInt(6, change.getMaxX()); BlockVector3 pos2 = change.getMaximumPoint();
stmt.setByte(7, (byte) (change.getMaxY() - 128));
stmt.setInt(8, change.getMaxZ()); stmt.setInt(4, pos1.getX());
stmt.setInt(9, (int) (change.getTime() / 1000)); stmt.setInt(5, pos2.getX());
stmt.setInt(6, pos1.getZ());
stmt.setInt(7, pos2.getZ());
stmt.setByte(8, (byte) (pos1.getY() - 128));
stmt.setByte(9, (byte) (pos2.getY() - 128));
stmt.setString(10, change.getCommand());
stmt.setInt(11, change.size());
stmt.executeUpdate(); stmt.executeUpdate();
stmt.clearParameters(); stmt.clearParameters();
} }
} catch (Exception e) { } finally {
e.printStackTrace();
}
commit(); commit();
return true;
} catch (Exception e) {
e.printStackTrace();
} }
return false; return true;
} }
public void commit() { private void commit() {
try { try {
if (connection == null) { if (connection == null) {
return; return;
@ -260,7 +297,7 @@ public class RollbackDatabase extends AsyncNotifyQueue {
} }
} }
public Connection openConnection() throws SQLException, ClassNotFoundException { private Connection openConnection() throws SQLException, ClassNotFoundException {
if (checkConnection()) { if (checkConnection()) {
return connection; return connection;
} }
@ -281,7 +318,7 @@ public class RollbackDatabase extends AsyncNotifyQueue {
return connection; return connection;
} }
public Connection forceConnection() throws SQLException, ClassNotFoundException { private Connection forceConnection() throws SQLException, ClassNotFoundException {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + dbLocation); connection = DriverManager.getConnection("jdbc:sqlite:" + dbLocation);
return connection; return connection;
@ -335,4 +372,14 @@ public class RollbackDatabase extends AsyncNotifyQueue {
return false; return false;
} }
} }
@Override
public void close() {
try {
closeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
super.close();
}
} }

View File

@ -4,7 +4,9 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase; import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -19,6 +21,7 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
private int maxY; private int maxY;
private int minZ; private int minZ;
private int maxZ; private int maxZ;
private String command;
public RollbackOptimizedHistory(World world, UUID uuid, int index) { public RollbackOptimizedHistory(World world, UUID uuid, int index) {
super(world, uuid, index); super(world, uuid, index);
@ -40,32 +43,40 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
this.time = System.currentTimeMillis(); this.time = System.currentTimeMillis();
} }
public RollbackOptimizedHistory(World world, UUID uuid, int index, long time, long size, CuboidRegion region, String command) {
super(world, uuid, index);
this.time = time;
this.minX = region.getMinimumX();
this.minY = region.getMinimumY();
this.minZ = region.getMinimumZ();
this.maxX = region.getMaximumX();
this.maxY = region.getMaximumY();
this.maxZ = region.getMaximumZ();
this.blockSize = (int) size;
this.command = command;
this.closed = true;
}
public long getTime() { public long getTime() {
return time; return time;
} }
public int getMinX() { @Override
return minX; protected DiskStorageSummary summarizeShallow() {
DiskStorageSummary summary = super.summarizeShallow();
summary.minX = this.minX;
summary.minZ = this.minZ;
summary.maxX = this.maxX;
summary.maxZ = this.maxZ;
return summary;
} }
public int getMaxX() { public void setCommand(String command) {
return maxX; this.command = command;
} }
public int getMinY() { public String getCommand() {
return minY; return command;
}
public int getMaxY() {
return maxY;
}
public int getMinZ() {
return minZ;
}
public int getMaxZ() {
return maxZ;
} }
public void setDimensions(BlockVector3 pos1, BlockVector3 pos2) { public void setDimensions(BlockVector3 pos1, BlockVector3 pos2) {
@ -121,4 +132,12 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
maxZ = z; maxZ = z;
super.writeHeader(os, x, y, z); super.writeHeader(os, x, y, z);
} }
public BlockVector3 getMinimumPoint() {
return BlockVector3.at(minX, minY, minZ);
}
public BlockVector3 getMaximumPoint() {
return BlockVector3.at(maxX, maxY, maxZ);
}
} }

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object.brush; package com.boydti.fawe.object.brush;
import static com.boydti.fawe.object.brush.BrushSettings.SettingType.BRUSH;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
@ -273,4 +274,13 @@ public class BrushSettings {
return perms.isEmpty(); return perms.isEmpty();
} }
@Override
public String toString() {
String name = (String) getSettings().get(BRUSH);
if (name != null) {
return name;
}
name = brush.getClass().getName();
return name.substring(name.lastIndexOf('.') + 1);
}
} }

View File

@ -1,11 +1,12 @@
package com.boydti.fawe.object.brush; package com.boydti.fawe.object.brush;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase; import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.change.MutableFullBlockChange; import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
@ -19,12 +20,16 @@ import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier;
public class InspectBrush extends BrushTool implements DoubleActionTraceTool { public class InspectBrush extends BrushTool implements DoubleActionTraceTool {
@ -61,21 +66,21 @@ public class InspectBrush extends BrushTool implements DoubleActionTraceTool {
return false; return false;
} }
if (!Settings.IMP.HISTORY.USE_DATABASE) { if (!Settings.IMP.HISTORY.USE_DATABASE) {
player.print(TranslatableComponent.of("fawe.error.setting.disable", ("history.use-database (Import with /frb #import )"))); player.print(TranslatableComponent.of("fawe.error.setting.disable", ("history.use-database (Import with /history import )")));
return false; return false;
} }
try {
BlockVector3 target = getTarget(player, rightClick).toBlockPoint(); BlockVector3 target = getTarget(player, rightClick).toBlockPoint();
final int x = target.getBlockX(); final int x = target.getBlockX();
final int y = target.getBlockY(); final int y = target.getBlockY();
final int z = target.getBlockZ(); final int z = target.getBlockZ();
World world = player.getWorld(); World world = player.getWorld();
RollbackDatabase db = DBHandler.IMP.getDatabase(world); RollbackDatabase db = DBHandler.IMP.getDatabase(world);
final AtomicInteger count = new AtomicInteger(); int count = 0;
db.getPotentialEdits(null, 0, target, target, new RunnableVal<DiskStorageHistory>() { for (Supplier<RollbackOptimizedHistory> supplier : db.getEdits(target, false)) {
@Override count++;
public void run(DiskStorageHistory value) { RollbackOptimizedHistory edit = supplier.get();
try { Iterator<MutableFullBlockChange> iter = edit.getFullBlockIterator(null, 0, false);
Iterator<MutableFullBlockChange> iter = value.getFullBlockIterator(null, 0, false);
while (iter.hasNext()) { while (iter.hasNext()) {
MutableFullBlockChange change = iter.next(); MutableFullBlockChange change = iter.next();
if (change.x != x || change.y != y || change.z != z) { if (change.x != x || change.y != y || change.z != z) {
@ -83,25 +88,27 @@ public class InspectBrush extends BrushTool implements DoubleActionTraceTool {
} }
int from = change.from; int from = change.from;
int to = change.to; int to = change.to;
UUID uuid = value.getUUID(); UUID uuid = edit.getUUID();
String name = Fawe.imp().getName(uuid); String name = Fawe.imp().getName(uuid);
int index = value.getIndex(); int index = edit.getIndex();
long age = System.currentTimeMillis() - value.getBDFile().lastModified(); long age = System.currentTimeMillis() - edit.getBDFile().lastModified();
String ageFormatted = MainUtil.secToTime(age / 1000); String ageFormatted = MainUtil.secToTime(age / 1000);
player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info" , name, BlockState.getFromOrdinal(from).getAsString(), BlockState.getFromOrdinal(to).getAsString(), ageFormatted)); BlockState blockFrom = BlockState.getFromOrdinal(from);
count.incrementAndGet(); BlockState blockTo = BlockState.getFromOrdinal(to);
return; TranslatableComponent msg = TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info", name, blockFrom, blockTo, ageFormatted);
}
} catch (IOException e) { String cmd = edit.getCommand();
e.printStackTrace(); TextComponent hover = TextComponent.of(cmd, TextColor.GOLD);
String infoCmd = "//history summary " + uuid + " " + index;
msg = msg.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, hover));
msg = msg.clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, infoCmd));
player.print(msg);
} }
} }
}, new Runnable() {
@Override
public void run() {
player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info.footer" , count)); player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info.footer" , count));
} catch (IOException e) {
throw new RuntimeException(e);
} }
}, false, false);
return true; return true;
} }

View File

@ -48,6 +48,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -847,7 +848,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr
try { try {
update(); update();
Player esPlayer = curES.getPlayer(); Player esPlayer = curES.getPlayer();
UUID uuid = esPlayer != null ? esPlayer.getUniqueId() : EditSession.CONSOLE; UUID uuid = esPlayer != null ? esPlayer.getUniqueId() : Identifiable.CONSOLE;
try { try {
curES.setRawChangeSet(new CFIChangeSet(this, uuid)); curES.setRawChangeSet(new CFIChangeSet(this, uuid));
} catch (IOException e) { } catch (IOException e) {

View File

@ -10,14 +10,14 @@ public class MutableBlockChange implements Change {
public int z; public int z;
public int y; public int y;
public int x; public int x;
public int combinedId; public int ordinal;
public MutableBlockChange(int x, int y, int z, int combinedId) { public MutableBlockChange(int x, int y, int z, int ordinal) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
this.combinedId = combinedId; this.ordinal = ordinal;
} }
@Override @Override
@ -31,6 +31,6 @@ public class MutableBlockChange implements Change {
} }
public void create(UndoContext context) { public void create(UndoContext context) {
context.getExtent().setBlock(x, y, z, BlockState.getFromOrdinal(combinedId)); context.getExtent().setBlock(x, y, z, BlockState.getFromOrdinal(ordinal));
} }
} }

View File

@ -22,10 +22,6 @@ import java.util.concurrent.Future;
public class AbstractDelegateChangeSet extends FaweChangeSet { public class AbstractDelegateChangeSet extends FaweChangeSet {
public final FaweChangeSet parent; public final FaweChangeSet parent;
public static FaweChangeSet getDefaultChangeSet(World world, UUID uuid) {
return FaweChangeSet.getDefaultChangeSet(world, uuid);
}
public AbstractDelegateChangeSet(FaweChangeSet parent) { public AbstractDelegateChangeSet(FaweChangeSet parent) {
super(parent.getWorld()); super(parent.getWorld());
this.parent = parent; this.parent = parent;

View File

@ -8,22 +8,28 @@ import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -168,6 +174,26 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return bdFile; return bdFile;
} }
public File getNbtfFile() {
return nbtfFile;
}
public File getNbttFile() {
return nbttFile;
}
public File getEntfFile() {
return entfFile;
}
public File getEnttFile() {
return enttFile;
}
public File getBioFile() {
return bioFile;
}
public int getIndex() { public int getIndex() {
return index; return index;
} }
@ -379,46 +405,35 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile)));
} }
public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) { protected DiskStorageSummary summarizeShallow() {
return new DiskStorageSummary(getOriginX(), getOriginZ());
}
public DiskStorageSummary summarize(Region region, boolean shallow) {
if (bdFile.exists()) { if (bdFile.exists()) {
int ox = getOriginX(); int ox = getOriginX();
int oz = getOriginZ(); int oz = getOriginZ();
if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) { DiskStorageSummary summary = summarizeShallow();
return new DiskStorageSummary(ox, oz); if (region != null && !region.contains(ox, oz)) {
}
try (FileInputStream fis = new FileInputStream(bdFile)) {
FaweInputStream gis = MainUtil.getCompressedIS(fis);
// skip mode
gis.skipFully(1);
// origin
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());
setOrigin(ox, oz);
DiskStorageSummary summary = new DiskStorageSummary(ox, oz);
if (!requiredRegion.isIn(ox, oz)) {
fis.close();
gis.close();
return summary; return summary;
} }
byte[] buffer = new byte[4]; try (FaweInputStream fis = getBlockIS()) {
int i = 0; if (!shallow) {
int amount = (Settings.IMP.HISTORY.BUFFER_SIZE - HEADER_SIZE) / 9; int amount = (Settings.IMP.HISTORY.BUFFER_SIZE - HEADER_SIZE) / 9;
while (!shallow && ++i < amount) { MutableFullBlockChange change = new MutableFullBlockChange(null, 0, false);
if (gis.read(buffer) == -1) { for (int i = 0; i < amount; i++) {
fis.close(); int x = posDel.readX(fis) + ox;
gis.close(); int y = posDel.readY(fis);
return summary; int z = posDel.readZ(fis) + ox;
idDel.readCombined(fis, change);
summary.add(x, z, change.to);
} }
int x = (buffer[0] & 0xFF) + (buffer[1] << 8) + ox;
int z = (buffer[2] & 0xFF) + (buffer[3] << 8) + oz;
int from = gis.readVarInt();
int to = gis.readVarInt();
summary.add(x, z, to);
} }
return summary; } catch (EOFException ignored) {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return summary;
} }
return null; return null;
} }
@ -463,7 +478,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
public void add(int x, int z, int id) { public void add(int x, int z, int id) {
blocks[BlockState.getFromInternalId(id).getOrdinal()]++; blocks[id]++;
if (x < minX) { if (x < minX) {
minX = x; minX = x;
} else if (x > maxX) { } else if (x > maxX) {
@ -487,6 +502,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return map; return map;
} }
public List<Countable<BlockState>> getBlockDistributionWithData() {
ArrayList<Countable<BlockState>> list = new ArrayList<>();
for (Map.Entry<BlockState, Integer> entry : getBlocks().entrySet()) {
list.add(new Countable<>(entry.getKey(), entry.getValue()));
}
return list;
}
public Map<BlockState, Double> getPercents() { public Map<BlockState, Double> getPercents() {
Map<BlockState, Integer> map = getBlocks(); Map<BlockState, Integer> map = getBlocks();
int count = getSize(); int count = getSize();

View File

@ -49,19 +49,7 @@ public abstract class FaweChangeSet implements ChangeSet, IBatchProcessor, Close
private final String worldName; private final String worldName;
protected AtomicInteger waitingCombined = new AtomicInteger(0); protected AtomicInteger waitingCombined = new AtomicInteger(0);
protected AtomicInteger waitingAsync = new AtomicInteger(0); protected AtomicInteger waitingAsync = new AtomicInteger(0);
private boolean closed; protected boolean closed;
public static FaweChangeSet getDefaultChangeSet(World world, UUID uuid) {
if (Settings.IMP.HISTORY.USE_DISK) {
if (Settings.IMP.HISTORY.USE_DATABASE) {
return new RollbackOptimizedHistory(world, uuid);
} else {
return new DiskStorageHistory(world, uuid);
}
} else {
return new MemoryOptimizedHistory(world);
}
}
public FaweChangeSet(String world) { public FaweChangeSet(String world) {
this.worldName = world; this.worldName = world;

View File

@ -33,8 +33,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
private int mode; private int mode;
private final int compression; private final int compression;
private FaweStreamIdDelegate idDel; protected FaweStreamIdDelegate idDel;
private FaweStreamPositionDelegate posDel; protected FaweStreamPositionDelegate posDel;
public FaweStreamChangeSet(World world) { public FaweStreamChangeSet(World world) {
this(world, Settings.IMP.HISTORY.COMPRESSION_LEVEL, Settings.IMP.HISTORY.STORE_REDO, Settings.IMP.HISTORY.SMALL_EDITS); this(world, Settings.IMP.HISTORY.COMPRESSION_LEVEL, Settings.IMP.HISTORY.STORE_REDO, Settings.IMP.HISTORY.SMALL_EDITS);
@ -85,10 +85,10 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException; void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException;
void readCombined(FaweInputStream in, MutableFullBlockChange change, boolean dir) throws IOException; void readCombined(FaweInputStream in, MutableFullBlockChange change) throws IOException;
} }
private void setupStreamDelegates(int mode) { protected void setupStreamDelegates(int mode) {
this.mode = mode; this.mode = mode;
if (mode == 3 || mode == 4) { if (mode == 3 || mode == 4) {
idDel = new FaweStreamIdDelegate() { idDel = new FaweStreamIdDelegate() {
@ -102,15 +102,15 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
public void readCombined(FaweInputStream is, MutableBlockChange change, boolean dir) throws IOException { public void readCombined(FaweInputStream is, MutableBlockChange change, boolean dir) throws IOException {
if (dir) { if (dir) {
is.readVarInt(); is.readVarInt();
change.combinedId = is.readVarInt(); change.ordinal = is.readVarInt();
} else { } else {
change.combinedId = is.readVarInt(); change.ordinal = is.readVarInt();
is.readVarInt(); is.readVarInt();
} }
} }
@Override @Override
public void readCombined(FaweInputStream is, MutableFullBlockChange change, boolean dir) throws IOException { public void readCombined(FaweInputStream is, MutableFullBlockChange change) throws IOException {
change.from = is.readVarInt(); change.from = is.readVarInt();
change.to = is.readVarInt(); change.to = is.readVarInt();
} }
@ -126,11 +126,11 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
public void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException { public void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException {
int from1 = in.read(); int from1 = in.read();
int from2 = in.read(); int from2 = in.read();
change.combinedId = in.readVarInt(); change.ordinal = in.readVarInt();
} }
@Override @Override
public void readCombined(FaweInputStream is, MutableFullBlockChange change, boolean dir) throws IOException { public void readCombined(FaweInputStream is, MutableFullBlockChange change) throws IOException {
change.from = is.readVarInt(); change.from = is.readVarInt();
change.to = BlockTypes.AIR.getInternalId(); change.to = BlockTypes.AIR.getInternalId();
} }
@ -290,7 +290,7 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
public abstract NBTInputStream getTileRemoveIS() throws IOException; public abstract NBTInputStream getTileRemoveIS() throws IOException;
private int blockSize; protected int blockSize;
public int entityCreateSize; public int entityCreateSize;
public int entityRemoveSize; public int entityRemoveSize;
public int tileCreateSize; public int tileCreateSize;
@ -530,7 +530,7 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
change.x = posDel.readX(is) + originX; change.x = posDel.readX(is) + originX;
change.y = posDel.readY(is); change.y = posDel.readY(is);
change.z = posDel.readZ(is) + originZ; change.z = posDel.readZ(is) + originZ;
idDel.readCombined(is, change, dir); idDel.readCombined(is, change);
return change; return change;
} catch (EOFException ignored) { } catch (EOFException ignored) {
} catch (Exception e) { } catch (Exception e) {

View File

@ -67,7 +67,7 @@ public class ResizableClipboardBuilder extends MemoryOptimizedHistory {
Change change = iterator.next(); Change change = iterator.next();
if (change instanceof MutableBlockChange) { if (change instanceof MutableBlockChange) {
MutableBlockChange blockChange = (MutableBlockChange) change; MutableBlockChange blockChange = (MutableBlockChange) change;
BlockState block = BlockState.getFromInternalId(blockChange.combinedId); BlockState block = BlockState.getFromOrdinal(blockChange.ordinal);
clipboard.setBlock(blockChange.x, blockChange.y, blockChange.z, block); clipboard.setBlock(blockChange.x, blockChange.y, blockChange.z, block);
} else if (change instanceof MutableTileChange) { } else if (change instanceof MutableTileChange) {
MutableTileChange tileChange = (MutableTileChange) change; MutableTileChange tileChange = (MutableTileChange) change;

View File

@ -4,8 +4,10 @@ import com.boydti.fawe.util.MathMan;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
/**
* Efficient blur / average color over an image
*/
public class SummedColorTable { public class SummedColorTable {
private static float inv256 = 1/256f;
private final long[] reds, greens, blues, alpha; private final long[] reds, greens, blues, alpha;
private final int[] hasAlpha; private final int[] hasAlpha;
private final int length; private final int length;
@ -84,11 +86,6 @@ public class SummedColorTable {
} }
} }
private long getSum(int index, long[] summed) {
if (index < 0) return 0;
return summed[index];
}
public int averageRGB(int x1, int z1, int x2, int z2) { public int averageRGB(int x1, int z1, int x2, int z2) {
int minX = Math.max(0, x1); int minX = Math.max(0, x1);
int minZ = Math.max(0, z1); int minZ = Math.max(0, z1);

View File

@ -0,0 +1,71 @@
package com.boydti.fawe.object.collection;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
import java.util.Iterator;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class YieldIterable<T> implements Iterable<T>, Consumer<T>, Closeable {
private static final Object END_MARKER = new Object();
private final LinkedBlockingQueue<T> queue;
private Future future;
public YieldIterable(@Nullable Future task) {
this.queue = new LinkedBlockingQueue<>();
this.future = task;
}
public YieldIterable() {
this(null);
}
public void setFuture(Future future) {
this.future = future;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private boolean interrupted;
private T buffer;
@Override
public boolean hasNext() {
try {
while (buffer == null && !interrupted && (future == null || !future.isCancelled())) {
buffer = queue.poll(50, TimeUnit.MILLISECONDS);
if (buffer == END_MARKER) {
interrupted = true;
return false;
}
}
} catch (InterruptedException e) {
interrupted = true;
}
return buffer != null;
}
@Override
public T next() {
hasNext();
T result = buffer;
buffer = null;
return result;
}
};
}
@Override
public void accept(T t) {
queue.add(t);
}
@Override
public void close() {
queue.add((T) END_MARKER);
}
}

View File

@ -1,37 +1,72 @@
package com.boydti.fawe.object.task; package com.boydti.fawe.object.task;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
public abstract class AsyncNotifyQueue { public class AsyncNotifyQueue implements Closeable {
protected Object lock = new Object(); private final Lock lock = new ReentrantLock(true);
protected final Runnable task; private final Thread.UncaughtExceptionHandler handler;
protected final AtomicBoolean running = new AtomicBoolean(); private boolean closed;
public AsyncNotifyQueue() { public AsyncNotifyQueue(Thread.UncaughtExceptionHandler handler) {
this.task = new Runnable() { this.handler = handler;
@Override }
public void run() {
operate(); public Thread.UncaughtExceptionHandler getHandler() {
synchronized (lock) { return handler;
if (hasQueued()) TaskManager.IMP.async(this); }
else running.set(false);
public <T> Future<T> run(Runnable task) {
return call(() -> {
task.run();
return null;
});
}
public <T> Future<T> supply(Supplier<T> task) {
return call(task::get);
}
public <T> Future<T> call(Callable<T> task) {
Future[] self = new Future[1];
Callable<T> wrapped = () -> {
if (!closed) {
try {
lock.lock();
if (!closed) {
try {
return task.call();
} catch (Throwable e) {
handler.uncaughtException(Thread.currentThread(), e);
if (self[0] != null) self[0].cancel(true);
} }
} }
} finally {
lock.unlock();
}
}
if (self[0] != null) self[0].cancel(true);
return null;
}; };
self[0] = Fawe.get().getQueueHandler().async(wrapped);
return self[0];
} }
public abstract boolean hasQueued(); @Override
public void close() {
public void queue(Runnable queueTask) { closed = true;
synchronized (lock) {
if (queueTask != null) queueTask.run();
if (!running.get()) {
running.set(true);
TaskManager.IMP.async(task);
}
}
} }
public abstract void operate(); public boolean isClosed() {
return closed;
}
} }

View File

@ -1,49 +0,0 @@
package com.boydti.fawe.object.task;
import com.boydti.fawe.util.TaskManager;
import java.util.concurrent.ConcurrentLinkedQueue;
public class SimpleAsyncNotifyQueue extends AsyncNotifyQueue {
private ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<>();
private Thread.UncaughtExceptionHandler handler;
public SimpleAsyncNotifyQueue(Thread.UncaughtExceptionHandler handler) {
this.handler = handler;
}
@Override
public boolean hasQueued() {
return !tasks.isEmpty();
}
@Override
public void operate() {
while (!tasks.isEmpty()) {
Runnable task = tasks.poll();
try {
if (task != null) task.run();
} catch (Throwable e) {
if (handler != null) handler.uncaughtException(Thread.currentThread(), e);
}
}
}
public void queue(Runnable queueTask) {
synchronized (lock) {
if (queueTask != null) tasks.add(queueTask);
if (!running.get()) {
running.set(true);
TaskManager.IMP.async(task);
}
}
}
public int getSize() {
return tasks.size();
}
public void clear() {
tasks.clear();
}
}

View File

@ -9,6 +9,7 @@ import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.processors.LimitProcessor; import com.boydti.fawe.beta.implementation.processors.LimitProcessor;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent; import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.LoggingChangeSet; import com.boydti.fawe.logging.LoggingChangeSet;
@ -58,6 +59,7 @@ public class EditSessionBuilder {
private BlockBag blockBag; private BlockBag blockBag;
private boolean threaded = true; private boolean threaded = true;
private EditSessionEvent event; private EditSessionEvent event;
private String command;
/** /**
* An EditSession builder<br> * An EditSession builder<br>
@ -130,6 +132,11 @@ public class EditSessionBuilder {
return setDirty(); return setDirty();
} }
public EditSessionBuilder command(String command) {
this.command = command;
return this;
}
/** /**
* @param disk If it should be stored on disk * @param disk If it should be stored on disk
* @param uuid The uuid to store it under (if on disk) * @param uuid The uuid to store it under (if on disk)
@ -360,7 +367,7 @@ public class EditSessionBuilder {
if (!this.fastmode || changeSet != null) { if (!this.fastmode || changeSet != null) {
if (changeSet == null) { if (changeSet == null) {
if (Settings.IMP.HISTORY.USE_DISK) { if (Settings.IMP.HISTORY.USE_DISK) {
UUID uuid = player == null ? EditSession.CONSOLE : player.getUniqueId(); UUID uuid = player == null ? Identifiable.CONSOLE : player.getUniqueId();
if (Settings.IMP.HISTORY.USE_DATABASE) { if (Settings.IMP.HISTORY.USE_DATABASE) {
changeSet = new RollbackOptimizedHistory(world, uuid); changeSet = new RollbackOptimizedHistory(world, uuid);
} else { } else {
@ -378,6 +385,9 @@ public class EditSessionBuilder {
if (this.limit.SPEED_REDUCTION > 0) { if (this.limit.SPEED_REDUCTION > 0) {
this.extent = this.bypassHistory = new SlowExtent(this.bypassHistory, this.limit.SPEED_REDUCTION); this.extent = this.bypassHistory = new SlowExtent(this.bypassHistory, this.limit.SPEED_REDUCTION);
} }
if (command != null && changeSet instanceof RollbackOptimizedHistory) {
((RollbackOptimizedHistory) changeSet).setCommand(this.command);
}
if (changeSet instanceof NullChangeSet && Fawe.imp().getBlocksHubApi() != null && player != null) { if (changeSet instanceof NullChangeSet && Fawe.imp().getBlocksHubApi() != null && player != null) {
changeSet = LoggingChangeSet.wrap(player, changeSet); changeSet = LoggingChangeSet.wrap(player, changeSet);
} }

View File

@ -80,6 +80,8 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream; import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Compressor;
@ -336,7 +338,7 @@ public class MainUtil {
public static FaweInputStream getCompressedIS(InputStream is, int buffer) throws IOException { public static FaweInputStream getCompressedIS(InputStream is, int buffer) throws IOException {
int mode = (byte) is.read(); int mode = (byte) is.read();
is = new BufferedInputStream(is, buffer); is = new FastBufferedInputStream(is, buffer);
if (mode == 0) { if (mode == 0) {
return new FaweInputStream(is); return new FaweInputStream(is);
} }
@ -353,7 +355,7 @@ public class MainUtil {
int amountAbs = Math.abs(mode); int amountAbs = Math.abs(mode);
if (amountAbs > 6) { if (amountAbs > 6) {
if (mode > 0) { if (mode > 0) {
is = new BufferedInputStream(new GZIPInputStream(is, buffer)); is = new FastBufferedInputStream(new GZIPInputStream(is, buffer));
} else { } else {
is = new ZstdInputStream(is); is = new ZstdInputStream(is);
} }
@ -366,7 +368,7 @@ public class MainUtil {
is = new LZ4BlockInputStream(is); is = new LZ4BlockInputStream(is);
} }
} }
return new FaweInputStream(is); return new FaweInputStream(new FastBufferedInputStream(is));
} }
public static URL upload(UUID uuid, String file, String extension, final RunnableVal<OutputStream> writeTask) { public static URL upload(UUID uuid, String file, String extension, final RunnableVal<OutputStream> writeTask) {
@ -833,7 +835,7 @@ public class MainUtil {
long age = now - file.lastModified(); long age = now - file.lastModified();
if (age > timeDiff) { if (age > timeDiff) {
pool.submit(file::delete); pool.submit(file::delete);
Component msg = WorldEditText.format(TranslatableComponent.of("fawe.info.file.deleted"), Locale.ROOT); Component msg = WorldEditText.format(TranslatableComponent.of("worldedit.schematic.delete.deleted"), Locale.ROOT);
if (printDebug) Fawe.debug(msg); if (printDebug) Fawe.debug(msg);
} }
}); });

View File

@ -210,8 +210,6 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
private final int maxY; private final int maxY;
public static final UUID CONSOLE = UUID.fromString("1-1-3-3-7");
@Deprecated @Deprecated
public EditSession(@NotNull World world, @Nullable Player player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) { public EditSession(@NotNull World world, @Nullable Player player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) {
this(null, world, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, bus, event); this(null, world, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, bus, event);

View File

@ -1432,6 +1432,10 @@ public class LocalSession implements TextureHolder {
* @return an edit session * @return an edit session
*/ */
public EditSession createEditSession(Actor actor) { public EditSession createEditSession(Actor actor) {
return createEditSession(actor, null);
}
public EditSession createEditSession(Actor actor, String command) {
checkNotNull(actor); checkNotNull(actor);
World world = null; World world = null;
@ -1449,6 +1453,7 @@ public class LocalSession implements TextureHolder {
builder.player((Player) actor); builder.player((Player) actor);
builder.blockBag(blockBag); builder.blockBag(blockBag);
} }
builder.command(command);
builder.fastmode(fastMode); builder.fastmode(fastMode);
editSession = builder.build(); editSession = builder.build();

View File

@ -19,19 +19,6 @@
package com.sk89q.worldedit.command; package com.sk89q.worldedit.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
@ -42,20 +29,10 @@ import com.sk89q.worldedit.command.util.annotation.Confirm;
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.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.Command;
import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.CommandContainer;
import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.Arg;
import org.enginehub.piston.annotation.param.Switch;
import org.jetbrains.annotations.Range;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -77,149 +54,6 @@ public class HistoryCommands {
this.worldEdit = worldEdit; this.worldEdit = worldEdit;
} }
@Command(
name = "fawerollback",
aliases = {"frb", "/fawerollback", "/rollback"},
desc = "Undo a specific edit. " +
" - The time uses s, m, h, d, y.\n" +
" - Import from disk: /frb #import"
)
@CommandPermissions("worldedit.history.rollback")
public void faweRollback(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") @Range(from = 0, to=Integer.MAX_VALUE) int radius, @Arg(name = "time", desc = "String", def = "0") String time, @Switch(name = 'r', desc = "TODO") boolean restore) throws WorldEditException {
if (!Settings.IMP.HISTORY.USE_DATABASE) {
player.print(TranslatableComponent.of("fawe.error.setting.disable" , "history.use-database (Import with /frb #import )"));
return;
}
if (user.charAt(0) == '#') {
if (user.equals("#import")) {
if (!player.hasPermission("fawe.rollback.import")) {
player.print(TranslatableComponent.of("fawe.error.no.perm", "fawe.rollback.import"));
return;
}
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY);
if (folder.exists()) {
for (File worldFolder : Objects.requireNonNull(folder.listFiles())) {
if (worldFolder != null && worldFolder.isDirectory()) {
String worldName = worldFolder.getName();
World world = FaweAPI.getWorld(worldName);
if (world != null) {
for (File userFolder : worldFolder.listFiles()) {
if (!userFolder.isDirectory()) {
continue;
}
String userUUID = userFolder.getName();
try {
UUID uuid = UUID.fromString(userUUID);
for (File historyFile : userFolder.listFiles()) {
String name = historyFile.getName();
if (!name.endsWith(".bd")) {
continue;
}
RollbackOptimizedHistory rollback = new RollbackOptimizedHistory(
world, uuid,
Integer.parseInt(
name.substring(0, name.length() - 3)));
DiskStorageHistory.DiskStorageSummary summary = rollback
.summarize(RegionWrapper.GLOBAL(), false);
if (summary != null) {
rollback.setDimensions(
BlockVector3.at(summary.minX, 0, summary.minZ),
BlockVector3
.at(summary.maxX, 255, summary.maxZ));
rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.IMP
.getDatabase(world);
db.logEdit(rollback);
player.print("Logging: " + historyFile);
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
}
player.print("Done import!");
}
return;
}
String toParse = user.substring(1);
if (!MathMan.isInteger(toParse)) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb #<index>"));
return;
}
int index = Integer.parseInt(toParse);
final World world = player.getWorld();
UUID uuid = player.getUniqueId();
DiskStorageHistory file = new DiskStorageHistory(world, uuid, index);
if (file.getBDFile().exists()) {
if (restore) file.redo(player);
else file.undo(player);
player.print(TranslatableComponent.of("fawe.worldedit.rollback.rollback.element" , world.getName() + "/" + user + "-" + index));
} else {
player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info.footer" , 0));
}
return;
}
UUID other = Fawe.imp().getUUID(user);
if (other == null) {
player.print(TranslatableComponent.of("fawe.error.player.not.found" , user));
return;
}
if (radius == 0) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb " + user + " <radius> <time>"));
return;
}
long timeDiff = MainUtil.timeToSec(time) * 1000;
if (timeDiff == 0) {
player.print(TranslatableComponent.of("fawe.error.command.syntax" , "/frb " + user + " " + radius + " <time>"));
return;
}
radius = Math.max(Math.min(500, radius), 0);
final World world = player.getWorld();
Location origin = player.getLocation();
BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius);
bot = bot.withY(Math.max(0, bot.getY()));
BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius);
bot = bot.withY(Math.min(255, top.getY()));
RollbackDatabase database = DBHandler.IMP.getDatabase(world);
final AtomicInteger count = new AtomicInteger();
Region[] allowedRegions = player.getCurrentRegions(FaweMaskManager.MaskType.OWNER);
if (allowedRegions == null) {
player.printError(TranslatableComponent.of("fawe.error.no.region"));
return;
}
// TODO mask the regions bot / top to the bottom and top coord in the allowedRegions
// TODO: then mask the edit to the bot / top
// if (allowedRegions.length != 1 || !allowedRegions[0].isGlobal()) {
// finalQueue = new MaskedIQueueExtent(SetQueue.IMP.getNewQueue(fp.getWorld(), true, false), allowedRegions);
// } else {
// finalQueue = SetQueue.IMP.getNewQueue(fp.getWorld(), true, false);
// }
database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal<DiskStorageHistory>() {
@Override
public void run(DiskStorageHistory edit) {
edit.undo(player, allowedRegions);
player.print(TranslatableComponent.of("fawe.worldedit.rollback.rollback.element" , edit.getWorld().getName() + "/" + user + "-" + edit.getIndex()));
count.incrementAndGet();
}
}, () -> player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info.footer" , count)), true, restore);
}
@Command(
name = "fawerestore",
aliases = {"/fawerestore", "/frestore"},
desc = "Redo a specific edit. " +
" - The time uses s, m, h, d, y.\n" +
" - Import from disk: /frb #import"
)
@CommandPermissions("worldedit.history.rollback")
public void restore(Player player, LocalSession session, @Arg(desc = "String user") String user, @Arg(def = "0", desc = "radius") int radius, @Arg(name = "time", desc = "String", def = "0") String time) throws WorldEditException {
faweRollback(player, session, user, radius, time, true);
}
@Command( @Command(
name = "/undo", name = "/undo",
aliases = { "/un", "/ud", "undo" }, aliases = { "/un", "/ud", "undo" },
@ -310,5 +144,4 @@ public class HistoryCommands {
session.clearHistory(); session.clearHistory();
actor.printInfo(TranslatableComponent.of("worldedit.clearhistory.cleared")); actor.printInfo(TranslatableComponent.of("worldedit.clearhistory.cleared"));
} }
} }

View File

@ -0,0 +1,376 @@
package com.sk89q.worldedit.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.google.common.base.Function;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.sk89q.worldedit.LocalSession;
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.Confirm;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.annotation.AllowedRegion;
import com.sk89q.worldedit.internal.annotation.Time;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import org.enginehub.piston.annotation.Command;
import org.enginehub.piston.annotation.CommandContainer;
import org.enginehub.piston.annotation.param.Arg;
import org.enginehub.piston.annotation.param.ArgFlag;
import org.enginehub.piston.annotation.param.Switch;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable;
import java.io.File;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Supplier;
import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument;
@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class)
public class HistorySubCommands {
private final HistoryCommands parent;
public HistorySubCommands(HistoryCommands parent) {
this.parent = parent;
}
@Command(
name = "restore",
aliases = {"rerun"},
desc = "Rerun edits" +
" - The time uses s, m, h, d, y.\n" +
" - Import from disk: /history import"
)
@CommandPermissions("worldedit.history.redo")
@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")
@Range(from = 0, to=Integer.MAX_VALUE) int radius,
@ArgFlag(name = 't', desc = "Time e.g. 20s", def = "0")
@Time long timeDiff) throws WorldEditException {
rollback(player, world, database, allowedRegions, other, radius, timeDiff, true);
}
@Command(
name = "rollback",
desc = "Undo a specific edit. " +
" - The time uses s, m, h, d, y.\n" +
" - Import from disk: /history import"
)
@CommandPermissions("worldedit.history.undo")
@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")
@Range(from = 0, to=Integer.MAX_VALUE) int radius,
@ArgFlag(name = 't', desc = "Time e.g. 20s", def = "0") @Time long timeDiff,
@Switch(name = 'f', desc = "Restore instead of rollback") boolean restore) throws WorldEditException {
if (!Settings.IMP.HISTORY.USE_DATABASE) {
player.print(TranslatableComponent.of("fawe.error.setting.disable" , "history.use-database (Import with /history import )"));
return;
}
checkCommandArgument(radius > 0, "Radius must be >= 0");
checkCommandArgument(timeDiff > 0, "Time must be >= 0");
if (other == null) other = player.getUniqueId();
if (!other.equals(player.getUniqueId())) {
player.checkPermission("worldedit.history.undo.other");
}
if (other == Identifiable.EVERYONE) other = null;
Location origin = player.getLocation();
BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius);
BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius);
bot = bot.clampY(0, world.getMaxY());
top = top.clampY(0, world.getMaxY());
// TODO mask the regions bot / top to the bottom and top coord in the allowedRegions
// TODO: then mask the edit to the bot / top
// if (allowedRegions.length != 1 || !allowedRegions[0].isGlobal()) {
// finalQueue = new MaskedIQueueExtent(SetQueue.IMP.getNewQueue(fp.getWorld(), true, false), allowedRegions);
// } else {
// finalQueue = SetQueue.IMP.getNewQueue(fp.getWorld(), true, false);
// }
int count = 0;
UUID finalOther = other;
long minTime = System.currentTimeMillis() - timeDiff;
for (Supplier<RollbackOptimizedHistory> supplier : database.getEdits(other, minTime, bot, top, !restore, restore)) {
count++;
RollbackOptimizedHistory edit = supplier.get();
edit.undo(player, allowedRegions);
String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex();
player.print(TranslatableComponent.of("fawe.worldedit.rollback.rollback.element", path));
}
player.print(TranslatableComponent.of("fawe.worldedit.tool.tool.inspect.info.footer" , count));
}
@Command(
name = "import",
desc = "Import history into the database" +
" - The time uses s, m, h, d, y.\n" +
" - Import from disk: /history import"
)
@CommandPermissions("fawe.rollback.import")
@Confirm
public synchronized void importdb(Actor actor) throws WorldEditException {
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY);
if (folder.exists()) {
for (File worldFolder : Objects.requireNonNull(folder.listFiles())) {
if (worldFolder != null && worldFolder.isDirectory()) {
String worldName = worldFolder.getName();
World world = FaweAPI.getWorld(worldName);
if (world != null) {
for (File userFolder : worldFolder.listFiles()) {
if (!userFolder.isDirectory()) {
continue;
}
String userUUID = userFolder.getName();
try {
UUID uuid = UUID.fromString(userUUID);
for (File historyFile : userFolder.listFiles()) {
String name = historyFile.getName();
if (!name.endsWith(".bd")) {
continue;
}
RollbackOptimizedHistory rollback = new RollbackOptimizedHistory(
world, uuid,
Integer.parseInt(
name.substring(0, name.length() - 3)));
DiskStorageHistory.DiskStorageSummary summary = rollback
.summarize(RegionWrapper.GLOBAL(), false);
if (summary != null) {
rollback.setDimensions(
BlockVector3.at(summary.minX, 0, summary.minZ),
BlockVector3
.at(summary.maxX, 255, summary.maxZ));
rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.IMP
.getDatabase(world);
db.logEdit(rollback);
actor.print("Logging: " + historyFile);
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
}
actor.print("Done import!");
}
}
@Command(
name = "summary",
aliases = {"info", "summarize"},
desc = "Summarize an edit"
)
@CommandPermissions("worldedit.history.find")
public synchronized void summary(Player player, RollbackDatabase database, Arguments arguments,
@Arg(desc = "Player uuid/name") UUID other,
@Arg(desc = "edit index") Integer index,
@ArgFlag(name = 'p', desc = "Page to view.", def = "-1") int page) throws WorldEditException, ExecutionException, InterruptedException {
RollbackOptimizedHistory edit = database.getEdit(other, index).get();
if (edit == null) {
player.print(TranslatableComponent.of("fawe.worldedit.schematic.schematic.none"));
return;
}
Location origin = player.getLocation();
String name = Fawe.imp().getName(edit.getUUID());
String cmd = edit.getCommand();
BlockVector3 pos1 = edit.getMinimumPoint();
BlockVector3 pos2 = edit.getMaximumPoint();
double distanceX = Math.min( Math.abs(pos1.getX() - origin.getX()), Math.abs(pos2.getX() - origin.getX()));
double distanceZ = Math.min( Math.abs(pos1.getZ() - origin.getZ()), Math.abs(pos2.getZ() - origin.getZ()));
int distance = (int) Math.sqrt(distanceX * distanceX + distanceZ * distanceZ);
BlockVector2 dirVec = BlockVector2.at(edit.getOriginX() - origin.getX(), edit.getOriginZ() - origin.getZ());
Direction direction = Direction.findClosest(dirVec.toVector3(), Direction.Flag.ALL);
long seconds = (System.currentTimeMillis() - edit.getBDFile().lastModified()) / 1000;
String timeStr = MainUtil.secToTime(seconds);
int size = edit.size();
String pageCommand = arguments.get().replaceAll("-p [0-9]+", "").trim();
List<Countable<BlockState>> list = null;
Reference<List<Countable<BlockState>>> cached = player.getMeta(pageCommand);
if (cached != null) {
list = cached.get();
}
if (list == null) {
DiskStorageHistory.DiskStorageSummary summary = edit.summarize(null, false);
if (summary != null) {
list = summary.getBlockDistributionWithData();
player.setMeta(pageCommand, new SoftReference<>(list));
}
}
boolean biomes = edit.getBioFile().exists();
boolean createdEnts = edit.getEnttFile().exists();
boolean removedEnts = edit.getEntfFile().exists();
boolean createdTiles = edit.getNbttFile().exists();
boolean removedTiles = edit.getNbtfFile().exists();
System.out.println("TODO FIXME move to translations");
if (page == -1) {
player.print("name: " + name
+ ", cmd: " + edit.getCommand()
+ ", dist: " + distance + "m " + direction.name()
+ ", time: " + timeStr + " ago"
+ ", size: " + size + " blocks"
+ ", biomes: " + biomes
+ ", +entity: " + createdEnts
+ ", -entity: " + removedEnts
+ ", +tile: " + createdTiles
+ ", -tile: " + removedTiles
+ ", disk: " + (edit.getSizeOnDisk() / 1000) + "mb"
+ ", min " + edit.getMinimumPoint()
+ ", max " + edit.getMaximumPoint()
);
}
page = 1;
if (list != null) {
SelectionCommands.BlockDistributionResult pages = new SelectionCommands.BlockDistributionResult((List) list, true, pageCommand);
player.print(pages.create(page));
}
}
@Command(
name = "find",
aliases = {"inspect", "search", "near"},
desc = "Find nearby edits"
)
@CommandPermissions("worldedit.history.find")
public synchronized void find(Player player, World world, RollbackDatabase database, Arguments arguments,
@ArgFlag(name = 'u', desc = "String user") UUID other,
@ArgFlag(name = 'r', def = "0", desc = "radius")
@Range(from = 0, to=Integer.MAX_VALUE) int radius,
@ArgFlag(name = 't', desc = "Time e.g. 20s", def = "0")
@Time long timeDiff,
@ArgFlag(name = 'p', desc = "Page to view.", def = "1") int page) throws WorldEditException {
if (!Settings.IMP.HISTORY.USE_DATABASE) {
player.print(TranslatableComponent.of("fawe.error.setting.disable" , "history.use-database (Import with //history import )"));
return;
}
checkCommandArgument(radius > 0, "Radius must be >= 0");
checkCommandArgument(timeDiff > 0, "Time must be >= 0");
Location origin = player.getLocation();
String pageCommand = arguments.get().replaceAll("-p [0-9]+", "").trim();
List<Supplier<RollbackOptimizedHistory>> list = null;
Reference<List<Supplier<RollbackOptimizedHistory>>> cached = player.getMeta(pageCommand);
if (cached != null) {
list = cached.get();
}
if (list == null) {
if (other == null) other = player.getUniqueId();
if (!other.equals(player.getUniqueId())) {
player.checkPermission("worldedit.history.undo.other");
}
if (other == Identifiable.EVERYONE) other = null;
BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius);
BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius);
bot = bot.clampY(0, world.getMaxY());
top = top.clampY(0, world.getMaxY());
LongAdder total = new LongAdder();
long minTime = System.currentTimeMillis() - timeDiff;
Iterable<Supplier<RollbackOptimizedHistory>> edits = database.getEdits(other, minTime, bot, top, false, false);
list = Lists.newArrayList(edits);
player.setMeta(pageCommand, new SoftReference<>(list));
}
PaginationBox pages = PaginationBox.fromStrings("Edits:", pageCommand, list, new Function<Supplier<RollbackOptimizedHistory>, Component>() {
@Nullable
@Override
public Component apply(@Nullable Supplier<RollbackOptimizedHistory> input) {
RollbackOptimizedHistory edit = input.get();
UUID uuid = edit.getUUID();
int index = edit.getIndex();
if (!edit.getBDFile().exists()) {
database.delete(uuid, index);
return TextComponent.empty();
}
String name = Fawe.imp().getName(edit.getUUID());
String cmd = edit.getCommand();
BlockVector3 pos1 = edit.getMinimumPoint();
BlockVector3 pos2 = edit.getMaximumPoint();
double distanceX = Math.min(Math.abs(pos1.getX() - origin.getX()), Math.abs(pos2.getX() - origin.getX()));
double distanceZ = Math.min(Math.abs(pos1.getZ() - origin.getZ()), Math.abs(pos2.getZ() - origin.getZ()));
int distance = (int) Math.sqrt(distanceX * distanceX + distanceZ * distanceZ);
BlockVector2 dirVec = BlockVector2.at(edit.getOriginX() - origin.getX(), edit.getOriginZ() - origin.getZ());
Direction direction = Direction.findClosest(dirVec.toVector3(), Direction.Flag.ALL);
long seconds = (System.currentTimeMillis() - edit.getBDFile().lastModified()) / 1000;
String timeStr = MainUtil.secToTime(seconds);
int size = edit.size();
TranslatableComponent elem = TranslatableComponent.of("fawe.worldedit.history.find.element", name, timeStr, distance, direction.name(), cmd);
String infoCmd = "//history summary " + uuid + " " + index;
TranslatableComponent hover = TranslatableComponent.of("fawe.worldedit.history.find.hover", size);
elem = elem.hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, hover));
elem = elem.clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, infoCmd));
return elem;
}
});
player.print(pages.create(page));
}
@Command(
name = "clear",
desc = "Clear your history"
)
@CommandPermissions("worldedit.history.clear")
public void clearHistory(Actor actor, LocalSession session) {
parent.clearHistory(actor, session);
}
}

View File

@ -552,11 +552,6 @@ public class SchematicCommands {
throw new StopExecutionException(TextComponent.of("Cannot sort by oldest and newest.")); throw new StopExecutionException(TextComponent.of("Cannot sort by oldest and newest."));
} }
String pageCommand = arguments.get(); String pageCommand = arguments.get();
if (pageCommand.contains("-p ")) {
pageCommand = pageCommand.replaceAll("-p [0-9]+", "-p %page%");
} else{
pageCommand = pageCommand + " -p %page%";
}
LocalConfiguration config = worldEdit.getConfiguration(); LocalConfiguration config = worldEdit.getConfiguration();
File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
@ -840,7 +835,7 @@ public class SchematicCommands {
actor.printError(TranslatableComponent.of("worldedit.schematic.delete.failed", TextComponent.of(filename))); actor.printError(TranslatableComponent.of("worldedit.schematic.delete.failed", TextComponent.of(filename)));
continue; continue;
} }
actor.print(TranslatableComponent.of("fawe.info.file.deleted" , filename)); actor.print(TranslatableComponent.of("worldedit.schematic.delete.deleted" , filename));
} }
} }

View File

@ -558,14 +558,18 @@ public class SelectionCommands {
actor.print(res.create(page)); actor.print(res.create(page));
} }
private static class BlockDistributionResult extends PaginationBox { public static class BlockDistributionResult extends PaginationBox {
private final List<Countable> distribution; private final List<Countable> distribution;
private final int totalBlocks; private final int totalBlocks;
private final boolean separateStates; private final boolean separateStates;
BlockDistributionResult(List<Countable> distribution, boolean separateStates) { public BlockDistributionResult(List<Countable> distribution, boolean separateStates) {
super("Block Distribution", "//distr -p %page%" + (separateStates ? " -d" : "")); this(distribution, separateStates, "//distr -p %page%" + (separateStates ? " -d" : ""));
}
public BlockDistributionResult(List<Countable> distribution, boolean separateStates, String pageCommand) {
super("Block Distribution", pageCommand);
this.distribution = distribution; this.distribution = distribution;
// note: doing things like region.getArea is inaccurate for non-cuboids. // note: doing things like region.getArea is inaccurate for non-cuboids.
this.totalBlocks = distribution.stream().mapToInt(Countable::getAmount).sum(); this.totalBlocks = distribution.stream().mapToInt(Countable::getAmount).sum();

View File

@ -69,7 +69,6 @@ public class SuperPickaxeCommands {
player.printError(TranslatableComponent.of("worldedit.superpickaxe.max-range", TextComponent.of(config.maxSuperPickaxeSize))); player.printError(TranslatableComponent.of("worldedit.superpickaxe.max-range", TextComponent.of(config.maxSuperPickaxeSize)));
return; return;
} }
session.setSuperPickaxe(new AreaPickaxe(range)); session.setSuperPickaxe(new AreaPickaxe(range));
session.enableSuperPickAxe(); session.enableSuperPickAxe();
player.printInfo(TranslatableComponent.of("worldedit.tool.superpickaxe.mode.area")); player.printInfo(TranslatableComponent.of("worldedit.tool.superpickaxe.mode.area"));

View File

@ -62,7 +62,7 @@ public class AreaPickaxe implements BlockTool {
return false; return false;
} }
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "AreaPickaxe")) {
editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
try { try {

View File

@ -85,7 +85,7 @@ public class BlockDataCyler implements DoubleActionBlockTool {
Property<Object> objProp = (Property<Object>) currentProperty; Property<Object> objProp = (Property<Object>) currentProperty;
BlockState newBlock = block.with(objProp, currentProperty.getValues().get(index)); BlockState newBlock = block.with(objProp, currentProperty.getValues().get(index));
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "BlockDataCyler")) {
editSession.disableBuffering(); editSession.disableBuffering();
try { try {

View File

@ -54,7 +54,7 @@ public class BlockReplacer implements DoubleActionBlockTool {
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) {
BlockBag bag = session.getBlockBag(player); BlockBag bag = session.getBlockBag(player);
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "BlockReplacer")) {
try { try {
BlockVector3 position = clicked.toVector().toBlockPoint(); BlockVector3 position = clicked.toVector().toBlockPoint();
editSession.setBlock(position, pattern); editSession.setBlock(position, pattern);

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.command.tool; package com.sk89q.worldedit.command.tool;
import static com.boydti.fawe.object.brush.BrushSettings.SettingType.BRUSH;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger; import static org.slf4j.LoggerFactory.getLogger;
@ -110,7 +111,6 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
private transient BrushSettings context = primary; private transient BrushSettings context = primary;
private transient PersistentChunkSendProcessor visualExtent; private transient PersistentChunkSendProcessor visualExtent;
private transient Lock lock = new ReentrantLock();
private transient BaseItem holder; private transient BaseItem holder;
@ -210,7 +210,6 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
} }
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
lock = new ReentrantLock();
boolean multi = stream.readBoolean(); boolean multi = stream.readBoolean();
primary = (BrushSettings) stream.readObject(); primary = (BrushSettings) stream.readObject();
if (multi) { if (multi) {
@ -497,7 +496,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
player.print(TranslatableComponent.of("fawe.error.no.perm" , StringMan.join(current.getPermissions(), ","))); player.print(TranslatableComponent.of("fawe.error.no.perm" , StringMan.join(current.getPermissions(), ",")));
return false; return false;
} }
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, current.toString())) {
Location target = player.getBlockTrace(getRange(), true, traceMask); Location target = player.getBlockTrace(getRange(), true, traceMask);
if (target == null) { if (target == null) {
@ -639,7 +638,9 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool
BrushSettings current = getContext(); BrushSettings current = getContext();
Brush brush = current.getBrush(); Brush brush = current.getBrush();
if (brush == null) return; if (brush == null) return;
EditSessionBuilder builder = new EditSessionBuilder(player.getWorld()) EditSessionBuilder builder = new EditSessionBuilder(player.getWorld())
.command(current.toString())
.player(player) .player(player)
.allowedRegionsEverywhere() .allowedRegionsEverywhere()
.autoQueue(false) .autoQueue(false)

View File

@ -78,7 +78,7 @@ public class FloatingTreeRemover implements BlockTool {
return true; return true;
} }
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "FloatingTreeRemover")) {
try { try {
final Set<BlockVector3> blockSet = bfs(world, clicked.toVector().toBlockPoint()); final Set<BlockVector3> blockSet = bfs(world, clicked.toVector().toBlockPoint());
if (blockSet == null) { if (blockSet == null) {

View File

@ -73,7 +73,7 @@ public class FloodFillTool implements BlockTool {
return true; return true;
} }
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "FloodFillTool")) {
try { try {
Mask mask = initialType.toMask(editSession); Mask mask = initialType.toMask(editSession);
BlockReplace function = new BlockReplace(editSession, pattern); BlockReplace function = new BlockReplace(editSession, pattern);

View File

@ -59,7 +59,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo
if (pos == null) return false; if (pos == null) return false;
BlockBag bag = session.getBlockBag(player); BlockBag bag = session.getBlockBag(player);
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "LongRangeBuildTool")) {
try { try {
editSession.disableBuffering(); editSession.disableBuffering();
BlockVector3 blockPoint = pos.toVector().toBlockPoint(); BlockVector3 blockPoint = pos.toVector().toBlockPoint();
@ -87,7 +87,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo
if (pos == null) return false; if (pos == null) return false;
BlockBag bag = session.getBlockBag(player); BlockBag bag = session.getBlockBag(player);
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "LongRangeBuildTool")) {
try { try {
editSession.disableBuffering(); editSession.disableBuffering();
BlockVector3 blockPoint = pos.toVector().toBlockPoint(); BlockVector3 blockPoint = pos.toVector().toBlockPoint();

View File

@ -72,7 +72,7 @@ public class RecursivePickaxe implements BlockTool {
return false; return false;
} }
try (EditSession editSession = session.createEditSession(player)) { try (EditSession editSession = session.createEditSession(player, "RecursivePickaxe")) {
editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
final int radius = (int) range; final int radius = (int) range;

View File

@ -19,8 +19,10 @@
package com.sk89q.worldedit.extension.factory.parser; package com.sk89q.worldedit.extension.factory.parser;
import com.boydti.fawe.config.Caption;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.jnbt.JSON2NBT; import com.boydti.fawe.jnbt.JSON2NBT;
import com.boydti.fawe.jnbt.NBTException; import com.boydti.fawe.jnbt.NBTException;
@ -421,12 +423,12 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
if (context.isRestricted()) { if (context.isRestricted()) {
Actor actor = context.requireActor(); Actor actor = context.requireActor();
if (!actor.hasPermission("worldedit.anyblock") && worldEdit.getConfiguration().checkDisallowedBlocks(holder)) { if (!actor.hasPermission("worldedit.anyblock") && worldEdit.getConfiguration().checkDisallowedBlocks(holder)) {
throw new DisallowedUsageException(TranslatableComponent.of("fawe.error.block.not.allowed") + " '" + holder + "'"); throw new DisallowedUsageException(Caption.toString(TranslatableComponent.of("fawe.error.block.not.allowed", holder)));
} }
CompoundTag nbt = holder.getNbtData(); CompoundTag nbt = holder.getNbtData();
if (nbt != null) { if (nbt != null) {
if (!actor.hasPermission("worldedit.anyblock")) { if (!actor.hasPermission("worldedit.anyblock")) {
throw new DisallowedUsageException("You are not allowed to nbt'"); throw new DisallowedUsageException("You are not allowed to use nbt'");
} }
} }
} }

View File

@ -20,7 +20,7 @@
package com.sk89q.worldedit.extension.platform; package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue; import com.boydti.fawe.object.task.AsyncNotifyQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.internal.cui.CUIEvent;
@ -65,7 +65,7 @@ public abstract class AbstractNonPlayerActor implements Actor {
// Queue for async tasks // Queue for async tasks
private AtomicInteger runningCount = new AtomicInteger(); private AtomicInteger runningCount = new AtomicInteger();
private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue( private AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue(
(thread, throwable) -> { (thread, throwable) -> {
while (throwable.getCause() != null) { while (throwable.getCause() != null) {
throwable = throwable.getCause(); throwable = throwable.getCause();
@ -106,7 +106,7 @@ public abstract class AbstractNonPlayerActor implements Actor {
} }
}; };
if (async) { if (async) {
asyncNotifyQueue.queue(wrapped); asyncNotifyQueue.run(wrapped);
} else { } else {
TaskManager.IMP.taskNow(wrapped, false); TaskManager.IMP.taskNow(wrapped, false);
} }

View File

@ -19,11 +19,12 @@
package com.sk89q.worldedit.extension.platform; package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.config.Caption;
import com.boydti.fawe.object.task.AsyncNotifyQueue;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.task.SimpleAsyncNotifyQueue;
import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.WEManager;
@ -94,7 +95,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
// Queue for async tasks // Queue for async tasks
private AtomicInteger runningCount = new AtomicInteger(); private AtomicInteger runningCount = new AtomicInteger();
private SimpleAsyncNotifyQueue asyncNotifyQueue = new SimpleAsyncNotifyQueue( private AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue(
(thread, throwable) -> { (thread, throwable) -> {
while (throwable.getCause() != null) { while (throwable.getCause() != null) {
throwable = throwable.getCause(); throwable = throwable.getCause();
@ -618,7 +619,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
@Override @Override
public void checkPermission(String permission) throws AuthorizationException { public void checkPermission(String permission) throws AuthorizationException {
if (!hasPermission(permission)) { if (!hasPermission(permission)) {
throw new AuthorizationException(); throw new AuthorizationException(Caption.toString(TranslatableComponent.of("fawe.error.no.perm", permission)));
} }
} }
@ -675,7 +676,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
} }
}; };
if (async) { if (async) {
asyncNotifyQueue.queue(wrapped); asyncNotifyQueue.run(wrapped);
} else { } else {
TaskManager.IMP.taskNow(wrapped, false); TaskManager.IMP.taskNow(wrapped, false);
} }

View File

@ -110,7 +110,7 @@ public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatab
* @param component The component to print * @param component The component to print
*/ */
default void printError(Component component) { default void printError(Component component) {
print(TranslatableComponent.of("error", component)); print(TranslatableComponent.of("fawe.error", component));
} }
/** /**
@ -119,7 +119,7 @@ public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatab
* @param component The component to print * @param component The component to print
*/ */
default void printInfo(Component component) { default void printInfo(Component component) {
print(TranslatableComponent.of("info", component)); print(TranslatableComponent.of("fawe.info", component));
} }
/** /**
@ -142,7 +142,7 @@ public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatab
* @param component The component to print * @param component The component to print
*/ */
default void printDebug(Component component) { default void printDebug(Component component) {
print(TranslatableComponent.of("debug", component)); print(TranslatableComponent.of("fawe.debug", component));
} }
/** /**

View File

@ -25,6 +25,8 @@ import com.boydti.fawe.command.AnvilCommandsRegistration;
import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.command.CFICommandsRegistration; import com.boydti.fawe.command.CFICommandsRegistration;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.command.HistorySubCommands;
import com.sk89q.worldedit.command.HistorySubCommandsRegistration;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator; import com.boydti.fawe.object.brush.visualization.cfi.HeightMapMCAGenerator;
@ -294,6 +296,7 @@ public final class PlatformCommandManager {
} }
}); });
}); });
/*
globalInjectedValues.injectValue(Key.of(EditSession.class), globalInjectedValues.injectValue(Key.of(EditSession.class),
context -> { context -> {
LocalSession localSession = context.injectedValue(Key.of(LocalSession.class)) LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
@ -306,6 +309,7 @@ public final class PlatformCommandManager {
return editSession; return editSession;
}); });
}); });
*/
globalInjectedValues.injectValue(Key.of(CFICommands.CFISettings.class), globalInjectedValues.injectValue(Key.of(CFICommands.CFISettings.class),
context -> context.injectedValue(Key.of(Actor.class)) context -> context.injectedValue(Key.of(Actor.class))
.orElseThrow(() -> new IllegalStateException("No CFI Settings")).getMeta("CFISettings")); .orElseThrow(() -> new IllegalStateException("No CFI Settings")).getMeta("CFISettings"));
@ -518,10 +522,18 @@ public final class PlatformCommandManager {
GenerationCommandsRegistration.builder(), GenerationCommandsRegistration.builder(),
new GenerationCommands(worldEdit) new GenerationCommands(worldEdit)
); );
HistoryCommands history = new HistoryCommands(worldEdit);
this.registration.register( this.registration.register(
commandManager, commandManager,
HistoryCommandsRegistration.builder(), HistoryCommandsRegistration.builder(),
new HistoryCommands(worldEdit) history
);
registerSubCommands(
"/history",
ImmutableList.of(),
"Manage your history",
HistorySubCommandsRegistration.builder(),
new HistorySubCommands(history)
); );
this.registration.register( this.registration.register(
commandManager, commandManager,
@ -739,7 +751,7 @@ public final class PlatformCommandManager {
} catch (UsageException e) { } catch (UsageException e) {
ImmutableList<Command> cmd = e.getCommands(); ImmutableList<Command> cmd = e.getCommands();
if (!cmd.isEmpty()) { if (!cmd.isEmpty()) {
actor.printError(TranslatableComponent.of("fawe.error.command.syntax", HelpGenerator.create(e.getCommandParseResult()).getUsage())); actor.printError(TranslatableComponent.of("fawe.error.command.syntax", HelpGenerator.create(e.getCommandParseResult()).getFullHelp()));
} }
actor.printError(e.getRichMessage()); actor.printError(e.getRichMessage());
} catch (CommandExecutionException e) { } catch (CommandExecutionException e) {

View File

@ -1,5 +1,8 @@
package com.sk89q.worldedit.extension.platform.binding; package com.sk89q.worldedit.extension.platform.binding;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Caption;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.image.ImageUtil; import com.boydti.fawe.util.image.ImageUtil;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
@ -14,12 +17,15 @@ import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.extension.platform.PlatformCommandManager;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.annotation.Time;
import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.Expression;
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.regions.Region;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
@ -36,6 +42,7 @@ import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.inject.Key; import org.enginehub.piston.inject.Key;
import java.util.Collection; import java.util.Collection;
import java.util.UUID;
public class ConsumeBindings extends Bindings { public class ConsumeBindings extends Bindings {
private final PlatformCommandManager manager; private final PlatformCommandManager manager;
@ -45,6 +52,12 @@ public class ConsumeBindings extends Bindings {
this.manager = manager; this.manager = manager;
} }
@Time
@Binding
public Long time(Actor actor, String argument) {
return MainUtil.timeToSec(argument) * 1000;
}
@Binding @Binding
@Confirm @Confirm
@Selection @Selection
@ -99,6 +112,30 @@ public class ConsumeBindings extends Bindings {
return radius; return radius;
} }
@Binding
public UUID playerUUID(Actor actor, String argument) {
if (argument.equals("me")) {
return actor.getUniqueId();
}
if (argument.equals("*")) {
return Identifiable.EVERYONE;
}
if (argument.equalsIgnoreCase("console") || argument.equalsIgnoreCase("server")) {
return Identifiable.CONSOLE;
}
UUID uuid;
if (argument.length() > 16) {
uuid = UUID.fromString(argument);
} else {
uuid = Fawe.imp().getUUID(argument);
}
if (uuid == null) {
throw new InputParseException(Caption.toString(TranslatableComponent.of("fawe.error.player.not.found" , argument)));
}
return uuid;
}
@Binding @Binding
public ProvideBindings.ImageUri getImage(String argument) { public ProvideBindings.ImageUri getImage(String argument) {
return new ProvideBindings.ImageUri(ImageUtil.getImageURI(argument)); return new ProvideBindings.ImageUri(ImageUtil.getImageURI(argument));

View File

@ -1,19 +1,28 @@
package com.sk89q.worldedit.extension.platform.binding; package com.sk89q.worldedit.extension.platform.binding;
import com.boydti.fawe.command.CFICommands; import com.boydti.fawe.command.CFICommands;
import com.boydti.fawe.config.Caption;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.LoggingChangeSet;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.image.ImageUtil; import com.boydti.fawe.util.image.ImageUtil;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.argument.Arguments;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.internal.annotation.AllowedRegion;
import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
@ -57,8 +66,10 @@ public class ProvideBindings extends Bindings {
} }
@Binding @Binding
public EditSession editSession(LocalSession localSession, Player player) { public EditSession editSession(LocalSession localSession, Player player, InjectedValueAccess context) {
EditSession editSession = localSession.createEditSession(player); Arguments arguments = context.injectedValue(Key.of(Arguments.class)).orElse(null);
String command = arguments == null ? null : arguments.get();
EditSession editSession = localSession.createEditSession(player, command);
editSession.enableStandardMode(); editSession.enableStandardMode();
Request.request().setEditSession(editSession); Request.request().setEditSession(editSession);
return editSession; return editSession;
@ -70,6 +81,31 @@ public class ProvideBindings extends Bindings {
return localSession.getSelection(player.getWorld()); return localSession.getSelection(player.getWorld());
} }
@Binding
public RollbackDatabase database(World world) {
return DBHandler.IMP.getDatabase(world);
}
@AllowedRegion(FaweMaskManager.MaskType.OWNER)
@Binding
public Region[] regionsOwner(Player player) {
return regions(player, FaweMaskManager.MaskType.OWNER);
}
@AllowedRegion(FaweMaskManager.MaskType.MEMBER)
@Binding
public Region[] regionsMember(Player player) {
return regions(player, FaweMaskManager.MaskType.MEMBER);
}
public Region[] regions(Player player, FaweMaskManager.MaskType type) {
Region[] regions = player.getCurrentRegions(type);
if (regions == null) {
throw new IllegalArgumentException(Caption.toString(TranslatableComponent.of("fawe.error.no.region")));
}
return regions;
}
@Binding @Binding
public TextureUtil getTexture(LocalSession session) { public TextureUtil getTexture(LocalSession session) {
return session.getTextureUtil(); return session.getTextureUtil();
@ -102,7 +138,7 @@ public class ProvideBindings extends Bindings {
return extent; return extent;
} }
Player plr = getPlayer(actor); Player plr = getPlayer(actor);
EditSession editSession = editSession(getLocalSession(plr), plr); EditSession editSession = editSession(getLocalSession(plr), plr, access);
if (access instanceof InjectedValueStore) { if (access instanceof InjectedValueStore) {
InjectedValueStore store = (InjectedValueStore) access; InjectedValueStore store = (InjectedValueStore) access;
store.injectValue(Key.of(EditSession.class), ValueProvider.constant(editSession)); store.injectValue(Key.of(EditSession.class), ValueProvider.constant(editSession));

View File

@ -0,0 +1,16 @@
package com.sk89q.worldedit.internal.annotation;
import com.boydti.fawe.regions.FaweMaskManager;
import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@InjectAnnotation
public @interface AllowedRegion {
FaweMaskManager.MaskType value() default FaweMaskManager.MaskType.OWNER;
}

View File

@ -0,0 +1,14 @@
package com.sk89q.worldedit.internal.annotation;
import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@InjectAnnotation
public @interface Time {
}

View File

@ -33,4 +33,6 @@ public interface Identifiable {
*/ */
UUID getUniqueId(); UUID getUniqueId();
UUID CONSOLE = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be");
UUID EVERYONE = UUID.fromString("1-1-3-3-7");
} }

View File

@ -19,6 +19,10 @@
package com.sk89q.worldedit.util.formatting.component; package com.sk89q.worldedit.util.formatting.component;
import com.boydti.fawe.object.collection.AdaptedSetCollection;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
@ -27,9 +31,11 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
public abstract class PaginationBox extends MessageBox { public abstract class PaginationBox extends MessageBox {
@ -75,7 +81,11 @@ public abstract class PaginationBox extends MessageBox {
super(title, new TextComponentProducer()); super(title, new TextComponentProducer());
if (pageCommand != null && !pageCommand.contains("%page%")) { if (pageCommand != null && !pageCommand.contains("%page%")) {
throw new IllegalArgumentException("pageCommand must contain %page% if provided."); if (pageCommand.contains("-p ")) {
pageCommand = pageCommand.replaceAll("-p [0-9]+", "-p %page%");
} else{
pageCommand = pageCommand + " -p %page%";
}
} }
this.pageCommand = pageCommand; this.pageCommand = pageCommand;
} }
@ -131,12 +141,16 @@ public abstract class PaginationBox extends MessageBox {
throw new IllegalStateException("Pagination components must be created with a page"); throw new IllegalStateException("Pagination components must be created with a page");
} }
public static <T> PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection<T> lines, Function<T, Component> adapt) {
return fromStrings(header, pageCommand, Collections2.transform(lines, adapt));
}
public static PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection lines) { public static PaginationBox fromStrings(String header, @Nullable String pageCommand, Collection lines) {
return new ListPaginationBox(header, pageCommand, lines); return new ListPaginationBox(header, pageCommand, lines);
} }
public static PaginationBox fromStrings(String header, @Nullable String pageCommand, List<String> lines) { public static PaginationBox fromStrings(String header, @Nullable String pageCommand, List<String> lines) {
return new ListPaginationBox(header, pageCommand, lines); return fromStrings(header, pageCommand, (Collection) lines);
} }
public static class ListPaginationBox extends PaginationBox { public static class ListPaginationBox extends PaginationBox {
@ -168,6 +182,9 @@ public abstract class PaginationBox extends MessageBox {
iterIndex++; iterIndex++;
} while (iterIndex < number); } while (iterIndex < number);
} }
if (obj instanceof Supplier) {
obj = ((Supplier) obj).get();
}
if (obj instanceof Component) { if (obj instanceof Component) {
return (Component) obj; return (Component) obj;
} }

View File

@ -156,4 +156,8 @@ public class TranslationManager {
public Component convertText(Component component, Locale locale) { public Component convertText(Component component, Locale locale) {
return friendlyComponentRenderer.render(component, locale); return friendlyComponentRenderer.render(component, locale);
} }
public Locale getDefaultLocale() {
return defaultLocale;
}
} }

View File

@ -1,19 +1,23 @@
{ {
"fawe.prefix": "&8(&4&lFAWE&8)&7 ", "fawe.prefix": "&8(&4&lHELLO&8)&7 ",
"error": "&c{0}", "fawe.error": "&c{0}",
"info": "&7{0}", "fawe.info": "&7{0}",
"debug": "&3{0}", "fawe.debug": "&3{0}",
"piston.style.help.text": "Help: {0}", "piston.style.help.text": "Help: {0}",
"piston.style.text.modifier": "TODO modifier: {0}, {1}, {2}", "piston.style.text.modifier": "TODO modifier: {0}, {1}, {2}",
"piston.style.main.text": "{0}", "piston.style.main.text": "{0}",
"piston.style.part.wrapping": "&8{0}&e{1}&8{2}", "piston.style.part.wrapping": "&8{0}&e{1}&8{2}",
"piston.text.command.prefix": "/", "piston.text.command.prefix": "TODO prefix",
"fawe.worldedit.history.find.element": "&8 - &2{0}: {1} &7ago &3{2}m &6{3} &c/{4}",
"fawe.worldedit.history.find.hover": "{0} blocks changed, click for more info",
"fawe.info.file.deleted": "{0} has been deleted.",
"fawe.info.schematic.pasting": "&7The schematic is pasting. This cannot be undone.",
"fawe.info.lighting.propagate.selection": "Lighting has been propogated in {0} chunks. (Note: To remove light use //removelight)", "fawe.info.lighting.propagate.selection": "Lighting has been propogated in {0} chunks. (Note: To remove light use //removelight)",
"fawe.info.updated.lighting.selection": "Lighting has been updated in {0} chunks. (It may take a second for the packets to send)", "fawe.info.updated.lighting.selection": "Lighting has been updated in {0} chunks. (It may take a second for the packets to send)",
"fawe.info.set.region": "Selection set to your current allowed region", "fawe.info.set.region": "Selection set to your current allowed region",
"fawe.info.worldedit.command.limit": "Please wait until your current action completes", "fawe.info.worldedit.command.limit": "Please wait until your current action completes",
"fawe.info.worldedit.delayed": "Please wait while we process your FAWE action...", "fawe.info.worldedit.delayed": "Please wait while we process your FAWE action...",
"fawe.info.worldedit.run": "Apologies for the delay. Now executing: {0}", "fawe.info.worldedit.run": "Apologies for the delay. Now executing: {0}",
@ -505,6 +509,7 @@
"worldedit.paste.pasted": "The clipboard has been pasted at {0}", "worldedit.paste.pasted": "The clipboard has been pasted at {0}",
"worldedit.paste.selected": "Selected clipboard paste region.", "worldedit.paste.selected": "Selected clipboard paste region.",
"worldedit.rotate.no-interpolation": "Note: Interpolation is not yet supported, so angles that are multiples of 90 is recommended.", "worldedit.rotate.no-interpolation": "Note: Interpolation is not yet supported, so angles that are multiples of 90 is recommended.",
"worldedit.rotate.rotated": "The clipboard copy has been rotated.", "worldedit.rotate.rotated": "The clipboard copy has been rotated.",
"worldedit.flip.flipped": "The clipboard copy has been flipped.", "worldedit.flip.flipped": "The clipboard copy has been flipped.",
@ -568,7 +573,7 @@
"worldedit.command.permissions": "You are not permitted to do that. Are you in the right mode?", "worldedit.command.permissions": "You are not permitted to do that. Are you in the right mode?",
"worldedit.command.player-only": "This command must be used with a player.", "worldedit.command.player-only": "This command must be used with a player.",
"worldedit.command.error.report": "Please report this error: [See console]", "worldedit.command.error.report": "Please report this error: [See console]",
"worldedit.pastebin.uploading": "(Please wait... sending output to pastebin...)", "worldedit.pastebin.uploading": "(Please wait... uploading paste...)",
"worldedit.session.cant-find-session": "Unable to find session for {0}", "worldedit.session.cant-find-session": "Unable to find session for {0}",
"worldedit.platform.no-file-dialog": "File dialogs are not supported in your environment.", "worldedit.platform.no-file-dialog": "File dialogs are not supported in your environment.",