Per world session history index when using disk
Configurable clipboard/history save locations
Fixed and optimized packet sending
History caching optimizations (instant now)
This commit is contained in:
Jesse Boyd 2016-08-09 14:32:51 +10:00
parent 97ca92eedf
commit 1ed87eabbc
31 changed files with 628 additions and 738 deletions

View File

@ -314,10 +314,6 @@ public class FaweBukkit implements IFawe, Listener {
public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
Player player = event.getPlayer();
FawePlayer fp = FawePlayer.wrap(player);
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
}
@Override

View File

@ -134,9 +134,7 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
}
@Override
public void refreshChunk(World world, CHUNK chunk) {
return;
}
public void refreshChunk(FaweChunk fs) {}
@Override
public boolean regenerateChunk(World world, int x, int z) {

View File

@ -34,15 +34,12 @@ import java.util.UUID;
import net.minecraft.server.v1_10_R1.Block;
import net.minecraft.server.v1_10_R1.BlockPosition;
import net.minecraft.server.v1_10_R1.Blocks;
import net.minecraft.server.v1_10_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_10_R1.ChunkSection;
import net.minecraft.server.v1_10_R1.DataBits;
import net.minecraft.server.v1_10_R1.DataPaletteBlock;
import net.minecraft.server.v1_10_R1.Entity;
import net.minecraft.server.v1_10_R1.EntityHuman;
import net.minecraft.server.v1_10_R1.EntityPlayer;
import net.minecraft.server.v1_10_R1.EntityTracker;
import net.minecraft.server.v1_10_R1.EntityTrackerEntry;
import net.minecraft.server.v1_10_R1.EntityTypes;
import net.minecraft.server.v1_10_R1.EnumDifficulty;
import net.minecraft.server.v1_10_R1.EnumGamemode;
@ -52,7 +49,6 @@ import net.minecraft.server.v1_10_R1.IDataManager;
import net.minecraft.server.v1_10_R1.MinecraftServer;
import net.minecraft.server.v1_10_R1.NBTTagCompound;
import net.minecraft.server.v1_10_R1.NibbleArray;
import net.minecraft.server.v1_10_R1.PacketPlayOutEntityDestroy;
import net.minecraft.server.v1_10_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_10_R1.PlayerChunk;
import net.minecraft.server.v1_10_R1.PlayerChunkMap;
@ -246,86 +242,54 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<Chunk, ChunkSection[], Chunk
}
@Override
public void refreshChunk(World world, Chunk chunk) {
public void refreshChunk(FaweChunk fc) {
BukkitChunk_1_10 fs = (BukkitChunk_1_10) fc;
Chunk chunk = fs.getChunk();
if (!chunk.isLoaded()) {
return;
}
try {
net.minecraft.server.v1_10_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
ChunkCoordIntPair pos = nmsChunk.k(); // getPosition()
WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(pos.x, pos.z);
PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
if (playerChunk == null) {
return;
}
HashSet<EntityPlayer> set = new HashSet<EntityPlayer>(playerChunk.c);
EntityTracker tracker = w.getTracker();
// Get players
final HashSet<EntityPlayer> players = new HashSet<>();
for (EntityHuman human : w.players) {
if (set.contains(human)) {
players.add((EntityPlayer) human);
}
}
if (players.size() == 0) {
if (playerChunk.c.isEmpty()) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
Collection<Entity>[] entitieSlices = (Collection<Entity>[]) getEntitySlices.invoke(nmsChunk);
for (Collection<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
}
entities.add(entry);
PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId());
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
}
}
// Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535);
for (EntityPlayer player : players) {
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
// send ents
for (Collection<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (final Entity ent : slice) {
final EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
}
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayer player : players) {
boolean result = entry.trackedPlayers.remove(player);
if (result && ent != player) {
entry.updatePlayer(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
mask = 255;
}
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, mask);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public boolean hasEntities(net.minecraft.server.v1_10_R1.Chunk nmsChunk) {
try {
final Collection<Entity>[] entities = (Collection<Entity>[]) getEntitySlices.invoke(nmsChunk);
for (int i = 0; i < entities.length; i++) {
Collection<Entity> slice = entities[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
} catch (Throwable ignore) {}
return false;
}
@Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode != RelightMode.NONE) {
@ -548,8 +512,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<Chunk, ChunkSection[], Chunk
net.minecraft.server.v1_10_R1.World nmsWorld = nmsChunk.world;
ChunkSection[] sections = nmsChunk.getSections();
Class<? extends net.minecraft.server.v1_10_R1.Chunk> clazzChunk = nmsChunk.getClass();
final Field ef = clazzChunk.getDeclaredField("entitySlices");
final Collection<Entity>[] entities = (Collection<Entity>[]) ef.get(nmsChunk);
final Collection<Entity>[] entities = (Collection<Entity>[]) getEntitySlices.invoke(nmsChunk);
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
// Remove entities
for (int i = 0; i < entities.length; i++) {

View File

@ -35,7 +35,6 @@ import net.minecraft.server.v1_7_R4.ChunkSection;
import net.minecraft.server.v1_7_R4.Entity;
import net.minecraft.server.v1_7_R4.EntityPlayer;
import net.minecraft.server.v1_7_R4.EntityTracker;
import net.minecraft.server.v1_7_R4.EntityTrackerEntry;
import net.minecraft.server.v1_7_R4.EntityTypes;
import net.minecraft.server.v1_7_R4.EnumDifficulty;
import net.minecraft.server.v1_7_R4.EnumGamemode;
@ -44,7 +43,6 @@ import net.minecraft.server.v1_7_R4.LongHashMap;
import net.minecraft.server.v1_7_R4.MinecraftServer;
import net.minecraft.server.v1_7_R4.NBTTagCompound;
import net.minecraft.server.v1_7_R4.NibbleArray;
import net.minecraft.server.v1_7_R4.PacketPlayOutEntityDestroy;
import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk;
import net.minecraft.server.v1_7_R4.PlayerChunkMap;
import net.minecraft.server.v1_7_R4.ServerNBTManager;
@ -453,8 +451,12 @@ public class BukkitQueue17 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkSec
fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount);
}
@Override
public void refreshChunk(World world, Chunk chunk) {
public void refreshChunk(FaweChunk fc) {
BukkitChunk_1_7 fs = (BukkitChunk_1_7) fc;
Chunk chunk = fs.getChunk();
if (!chunk.isLoaded()) {
return;
}
@ -483,52 +485,34 @@ public class BukkitQueue17 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkSec
if (players.size() == 0) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
List<Entity>[] entitieSlices = nmsChunk.entitySlices;
for (List<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
}
entities.add(entry);
PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId());
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
}
}
// Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65535, 25);
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65280, 25);
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
// send ents
for (final EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
mask = 255;
}
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, mask, 25);
for (EntityPlayer player : players) {
boolean result = entry.trackedPlayers.remove(player);
if (result && entry.tracker != player) {
entry.updatePlayer(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
player.playerConnection.sendPacket(packet);
}
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
public boolean hasEntities(net.minecraft.server.v1_7_R4.Chunk nmsChunk) {
for (int i = 0; i < nmsChunk.entitySlices.length; i++) {
List<Entity> slice = nmsChunk.entitySlices[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode == RelightMode.ALL) {

View File

@ -30,12 +30,10 @@ import java.util.Set;
import java.util.UUID;
import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_8_R3.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EntityTracker;
import net.minecraft.server.v1_8_R3.EntityTrackerEntry;
import net.minecraft.server.v1_8_R3.EntityTypes;
import net.minecraft.server.v1_8_R3.EnumDifficulty;
import net.minecraft.server.v1_8_R3.EnumSkyBlock;
@ -43,7 +41,6 @@ import net.minecraft.server.v1_8_R3.LongHashMap;
import net.minecraft.server.v1_8_R3.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.NibbleArray;
import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerChunkMap;
import net.minecraft.server.v1_8_R3.ServerNBTManager;
@ -429,24 +426,21 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkS
}
@Override
public void refreshChunk(World world, Chunk chunk) {
public void refreshChunk(FaweChunk fc) {
BukkitChunk_1_8 fs = (BukkitChunk_1_8) fc;
Chunk chunk = fs.getChunk();
if (!chunk.isLoaded()) {
return;
}
try {
net.minecraft.server.v1_8_R3.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
ChunkCoordIntPair pos = nmsChunk.j(); // getPosition()
WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.x;
int z = pos.z;
int x = nmsChunk.locX;
int z = nmsChunk.locZ;
if (!chunkMap.isChunkInUse(x, z)) {
return;
}
HashSet<EntityPlayer> set = new HashSet<EntityPlayer>();
EntityTracker tracker = w.getTracker();
// Get players
Field fieldChunkMap = chunkMap.getClass().getDeclaredField("d");
fieldChunkMap.setAccessible(true);
LongHashMap<Object> map = (LongHashMap<Object>) fieldChunkMap.get(chunkMap);
@ -454,56 +448,38 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkS
Object playerChunk = map.getEntry(pair);
Field fieldPlayers = playerChunk.getClass().getDeclaredField("b");
fieldPlayers.setAccessible(true);
final HashSet<EntityPlayer> players = new HashSet<>((Collection<EntityPlayer>)fieldPlayers.get(playerChunk));
if (players.size() == 0) {
Collection<EntityPlayer> players = (Collection<EntityPlayer>) fieldPlayers.get(playerChunk);
if (players.isEmpty()) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
List<Entity>[] entitieSlices = nmsChunk.getEntitySlices();
for (List<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
}
entities.add(entry);
PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId());
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
}
}
// Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65535);
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65280);
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
// send ents
for (final EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
mask = 255;
}
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, mask);
for (EntityPlayer player : players) {
boolean result = entry.trackedPlayers.remove(player);
if (result && entry.tracker != player) {
entry.updatePlayer(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
player.playerConnection.sendPacket(packet);
}
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
public boolean hasEntities(net.minecraft.server.v1_8_R3.Chunk nmsChunk) {
for (int i = 0; i < nmsChunk.entitySlices.length; i++) {
List<Entity> slice = nmsChunk.entitySlices[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode == RelightMode.ALL) {

View File

@ -34,16 +34,13 @@ import java.util.UUID;
import net.minecraft.server.v1_9_R2.Block;
import net.minecraft.server.v1_9_R2.BlockPosition;
import net.minecraft.server.v1_9_R2.Blocks;
import net.minecraft.server.v1_9_R2.ChunkCoordIntPair;
import net.minecraft.server.v1_9_R2.ChunkSection;
import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R2.DataPalette;
import net.minecraft.server.v1_9_R2.DataPaletteBlock;
import net.minecraft.server.v1_9_R2.Entity;
import net.minecraft.server.v1_9_R2.EntityHuman;
import net.minecraft.server.v1_9_R2.EntityPlayer;
import net.minecraft.server.v1_9_R2.EntityTracker;
import net.minecraft.server.v1_9_R2.EntityTrackerEntry;
import net.minecraft.server.v1_9_R2.EntityTypes;
import net.minecraft.server.v1_9_R2.EnumDifficulty;
import net.minecraft.server.v1_9_R2.EnumSkyBlock;
@ -52,7 +49,6 @@ import net.minecraft.server.v1_9_R2.IDataManager;
import net.minecraft.server.v1_9_R2.MinecraftServer;
import net.minecraft.server.v1_9_R2.NBTTagCompound;
import net.minecraft.server.v1_9_R2.NibbleArray;
import net.minecraft.server.v1_9_R2.PacketPlayOutEntityDestroy;
import net.minecraft.server.v1_9_R2.PacketPlayOutMapChunk;
import net.minecraft.server.v1_9_R2.PlayerChunk;
import net.minecraft.server.v1_9_R2.PlayerChunkMap;
@ -128,81 +124,50 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Chu
}
@Override
public void refreshChunk(World world, Chunk chunk) {
public void refreshChunk(FaweChunk fc) {
BukkitChunk_1_9 fs = (BukkitChunk_1_9) fc;
Chunk chunk = fs.getChunk();
if (!chunk.isLoaded()) {
return;
}
try {
net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
ChunkCoordIntPair pos = nmsChunk.k(); // getPosition()
WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(pos.x, pos.z);
PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
if (playerChunk == null) {
return;
}
HashSet<EntityPlayer> set = new HashSet<EntityPlayer>(playerChunk.c);
EntityTracker tracker = w.getTracker();
// Get players
final HashSet<EntityPlayer> players = new HashSet<>();
for (EntityHuman human : w.players) {
if (set.contains(human)) {
players.add((EntityPlayer) human);
}
}
if (players.size() == 0) {
if (playerChunk.c.isEmpty()) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
List<Entity>[] entitieSlices = playerChunk.chunk.getEntitySlices();
for (List<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
}
entities.add(entry);
PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId());
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
}
}
// Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(playerChunk.chunk, 65535);
for (EntityPlayer player : players) {
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
// send ents
for (List<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
mask = 255;
}
for (final Entity ent : slice) {
final EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId());
if (entry == null) {
continue;
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, mask);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayer player : players) {
boolean result = entry.trackedPlayers.remove(player);
if (result && ent != player) {
entry.updatePlayer(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
e.printStackTrace();
}
}
public boolean hasEntities(net.minecraft.server.v1_9_R2.Chunk nmsChunk) {
for (int i = 0; i < nmsChunk.entitySlices.length; i++) {
List<Entity> slice = nmsChunk.entitySlices[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public World createWorld(final WorldCreator creator) {

View File

@ -55,6 +55,7 @@ import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.io.File;
@ -179,8 +180,8 @@ public class Fawe {
* Implementation dependent stuff
*/
this.setupConfigs();
MainUtil.deleteOlder(new File(IMP.getDirectory(), "history"), TimeUnit.DAYS.toMillis(Settings.HISTORY.DELETE_AFTER_DAYS));
MainUtil.deleteOlder(new File(IMP.getDirectory(), "clipboard"), TimeUnit.DAYS.toMillis(Settings.CLIPBOARD.DELETE_AFTER_DAYS));
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.PATHS.HISTORY), TimeUnit.DAYS.toMillis(Settings.HISTORY.DELETE_AFTER_DAYS));
MainUtil.deleteOlder(MainUtil.getFile(IMP.getDirectory(), Settings.PATHS.CLIPBOARD), TimeUnit.DAYS.toMillis(Settings.CLIPBOARD.DELETE_AFTER_DAYS));
TaskManager.IMP = this.IMP.getTaskManager();
TaskManager.IMP.repeat(timer = new FaweTimer(), 1);
@ -276,6 +277,7 @@ public class Fawe {
EditSession.inject(); // Custom block placer + optimizations
EditSessionEvent.inject(); // Add EditSession to event
LocalSession.inject(); // Add remember order / queue flushing
SessionManager.inject(); // Custom session saving
// Commands
BrushCommands.inject(); // Translations + heightmap
ToolCommands.inject(); // Translations + inspect

View File

@ -257,7 +257,7 @@ public class FaweAPI {
* @return
*/
public static List<DiskStorageHistory> getBDFiles(FaweLocation origin, UUID user, int radius, long timediff, boolean shallow) {
File history = new File(Fawe.imp().getDirectory(), "history" + File.separator + origin.world);
File history = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + origin.world);
if (!history.exists()) {
return new ArrayList<>();
}

View File

@ -37,6 +37,13 @@ public class Settings extends Config {
})
public static int MAX_MEMORY_PERCENT = 95;
@Comment("Paths for various directories")
public static final class PATHS {
public static String HISTORY = "history";
public static String CLIPBOARD = "clipboard";
}
@Create // This value will be generated automatically
public static ConfigBlock<LIMITS> LIMITS = null;
@ -129,7 +136,8 @@ public class Settings extends Config {
public static int CHUNK_WAIT_MS = 100;
@Comment("Delete history on disk after a number of days")
public static int DELETE_AFTER_DAYS = 7;
@Comment("Delete history in memory on logout (does not effect disk)")
@Comment("Delete history in memory on logout (does not effect disk) (BROKEN, USE DISK INSTEAD)")
@Final // Deprecated
public static boolean DELETE_ON_LOGOUT = true;
@Comment({
"If history should be enabled by default for plugins using WorldEdit:",
@ -227,7 +235,7 @@ public class Settings extends Config {
" - 1 = Optimal (Relight changed light sources and changed blocks)",
" - 2 = All (Slowly relight every blocks)"
})
public static int MODE = 2;
public static int MODE = 1;
}
public static void save(File file) {

View File

@ -6,6 +6,7 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.World;
@ -42,7 +43,7 @@ public class RollbackDatabase {
public RollbackDatabase(final String world) throws SQLException, ClassNotFoundException {
this.prefix = "";
this.world = world;
this.dbLocation = new File(Fawe.imp().getDirectory(), "history" + File.separator + world + File.separator + "summary.db");
this.dbLocation = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + world + File.separator + "summary.db");
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 INTO `" + prefix + "edits` (`player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`) VALUES(?,?,?,?,?,?,?,?,?)";

View File

@ -22,6 +22,7 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
public short[] air;
public short[] relight;
public int[][] biomes;
private int bitMask = -1;
public T chunk;
@ -103,6 +104,23 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return total;
}
@Override
public int getBitMask() {
if (bitMask == -1) {
this.bitMask = 0;
for (int section = 0; section < ids.length; section++) {
if (ids[section] != null) {
bitMask += 1 << section;
}
}
}
return bitMask;
}
public void setBitMask(int value) {
this.bitMask = value;
}
/**
* Get the raw data for a section
* @param i

View File

@ -66,7 +66,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
@Override
public void sendChunk(final FaweChunk fc) {
refreshChunk(getWorld(), (CHUNK) fc.getChunk());
refreshChunk(fc);
}
public abstract void setFullbright(CHUNKSECTION sections);
@ -159,7 +159,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
public abstract void setBlockLight(SECTION section, int x, int y, int z, int value);
public abstract void refreshChunk(WORLD world, CHUNK chunk);
public abstract void refreshChunk(FaweChunk fs);
public abstract CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTION sections, Map<?, ?> tiles, Collection<?>[] entities, Set<UUID> createdEntities, boolean all) throws Exception;

View File

@ -100,7 +100,15 @@ public class NMSRelighter {
public void sendChunks() {
for (Map.Entry<Long, RelightSkyEntry> entry : skyToRelight.entrySet()) {
RelightSkyEntry chunk = entry.getValue();
queue.sendChunk(queue.getFaweChunk(chunk.x, chunk.z));
CharFaweChunk fc = (CharFaweChunk) queue.getFaweChunk(chunk.x, chunk.z);
int mask = 0;
for (int y = 0; y < chunk.fix.length; y++) {
if (chunk.fix[y]) {
mask += 1 << y;
}
}
fc.setBitMask(mask);
queue.sendChunk(fc);
}
}

View File

@ -92,6 +92,12 @@ public abstract class FaweChunk<T> {
*/
public abstract char[][] getCombinedIdArrays();
/**
* The modified sections
* @return
*/
public abstract int getBitMask();
/**
* Get the combined block id at a location<br>
* combined = (id <<<< 4) + data

View File

@ -4,8 +4,6 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweStreamChangeSet;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.MainUtil;
@ -30,9 +28,6 @@ import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -111,23 +106,6 @@ public abstract class FawePlayer<T> {
if (Settings.CLIPBOARD.USE_DISK) {
loadClipboardFromDisk();
}
if (getSession() == null || getPlayer() == null || session.getSize() != 0 || !Settings.HISTORY.USE_DISK) {
return;
}
try {
UUID uuid = getUUID();
String currentWorldName = getLocation().world;
World world = getWorld();
if (world != null) {
if (Fawe.imp().getWorldName(world).equals(currentWorldName)) {
getSession().clearHistory();
loadSessionsFromDisk(world);
}
}
} catch (Exception e) {
MainUtil.handleError(e);
Fawe.debug("Failed to load history for: " + getName());
}
}
/**
@ -135,7 +113,7 @@ public abstract class FawePlayer<T> {
* - Should already be called if history on disk is enabled
*/
public void loadClipboardFromDisk() {
File file = new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + getUUID() + ".bd");
File file = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.CLIPBOARD + File.separator + getUUID() + ".bd");
try {
if (file.exists() && file.length() > 5) {
DiskOptimizedClipboard doc = new DiskOptimizedClipboard(file);
@ -174,45 +152,14 @@ public abstract class FawePlayer<T> {
/**
* Load all the undo EditSession's from disk for a world <br>
* - Usually already called when a player joins or changes world
* - Usually already called when necessary
* @param world
*/
public void loadSessionsFromDisk(final World world) {
if (world == null) {
return;
}
final long start = System.currentTimeMillis();
final UUID uuid = getUUID();
final List<Integer> editIds = new ArrayList<>();
final File folder = new File(Fawe.imp().getDirectory(), "history" + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid);
if (folder.isDirectory()) {
for (File file : folder.listFiles()) {
if (file.getName().endsWith(".bd")) {
int index = Integer.parseInt(file.getName().split("\\.")[0]);
editIds.add(index);
}
}
}
if (editIds.size() > 0) {
BBC.INDEXING_HISTORY.send(this, editIds.size());
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
Collections.sort(editIds);
for (int i = editIds.size() - 1; i >= 0; i--) {
int index = editIds.get(i);
FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index);
EditSession edit = set.toEditSession(FawePlayer.this);
if (world.equals(getWorld())) {
session.remember(edit, false, false, Integer.MAX_VALUE);
} else {
return;
}
}
BBC.INDEXING_COMPLETE.send(FawePlayer.this, (System.currentTimeMillis() - start) / 1000d);
}
});
}
getSession().loadSessionHistoryFromDisk(getUUID(), world);
}
/**
@ -417,9 +364,10 @@ public abstract class FawePlayer<T> {
*/
public void unregister() {
if (Settings.HISTORY.DELETE_ON_LOGOUT) {
getSession().setClipboard(null);
getSession().clearHistory();
session = getSession();
WorldEdit.getInstance().removeSession(getPlayer());
session.setClipboard(null);
session.clearHistory();
}
Fawe.get().unregister(getName());
}

View File

@ -35,11 +35,6 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
private File entfFile;
private File enttFile;
/**
* Summary of this change (not accurate for larger edits)
*/
private DiskStorageSummary summary;
/*
* Block data
*
@ -50,33 +45,20 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
* { short rel x, short rel z, unsigned byte y, short combinedFrom, short combinedTo }
*/
private OutputStream osBD;
// NBT From
private NBTOutputStream osNBTF;
// NBT To
private NBTOutputStream osNBTT;
// Entity Create From
private NBTOutputStream osENTCF;
// Entity Create To
private NBTOutputStream osENTCT;
private int index;
public void deleteFiles() {
bdFile.delete();
nbtfFile.delete();
nbttFile.delete();
entfFile.delete();
enttFile.delete();
}
public DiskStorageHistory(World world, UUID uuid) {
super(world);
String base = "history" + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid;
File folder = new File(Fawe.imp().getDirectory(), base);
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid);
int max = 0;
if (folder.exists()) {
for (File file : folder.listFiles()) {
@ -97,16 +79,34 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
init(uuid, index);
}
public DiskStorageHistory(File folder, World world, UUID uuid, int i) {
super(world);
this.uuid = uuid;
this.index = i;
initFiles(folder);
}
private void initFiles(File folder) {
nbtfFile = new File(folder, index + ".nbtf");
nbttFile = new File(folder, index + ".nbtt");
entfFile = new File(folder, index + ".entf");
enttFile = new File(folder, index + ".entt");
bdFile = new File(folder, index + ".bd");
}
private void init(UUID uuid, int i) {
this.uuid = uuid;
this.index = i;
String base = "history" + File.separator + Fawe.imp().getWorldName(getWorld()) + File.separator + uuid;
base += File.separator + i;
nbtfFile = new File(Fawe.imp().getDirectory(), base + ".nbtf");
nbttFile = new File(Fawe.imp().getDirectory(), base + ".nbtt");
entfFile = new File(Fawe.imp().getDirectory(), base + ".entf");
enttFile = new File(Fawe.imp().getDirectory(), base + ".entt");
bdFile = new File(Fawe.imp().getDirectory(), base + ".bd");
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(getWorld()) + File.separator + uuid);
initFiles(folder);
}
public void deleteFiles() {
bdFile.delete();
nbtfFile.delete();
nbttFile.delete();
entfFile.delete();
enttFile.delete();
}
public UUID getUUID() {
@ -162,6 +162,27 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return 80;
}
@Override
public long getSizeOnDisk() {
int total = 0;
if (bdFile.exists()) {
total += bdFile.getTotalSpace();
}
if (nbtfFile.exists()) {
total += entfFile.getTotalSpace();
}
if (nbttFile.exists()) {
total += entfFile.getTotalSpace();
}
if (entfFile.exists()) {
total += entfFile.getTotalSpace();
}
if (enttFile.exists()) {
total += entfFile.getTotalSpace();
}
return total;
}
@Override
public OutputStream getBlockOS(int x, int y, int z) throws IOException {
if (osBD != null) {
@ -281,14 +302,11 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) {
if (summary != null) {
return summary;
}
if (bdFile.exists()) {
int ox = getOriginX();
int oz = getOriginZ();
if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) {
return summary = new DiskStorageSummary(ox, oz);
return new DiskStorageSummary(ox, oz);
}
try (FileInputStream fis = new FileInputStream(bdFile)) {
FaweInputStream gis = MainUtil.getCompressedIS(fis);
@ -298,7 +316,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
setOrigin(ox, oz);
summary = new DiskStorageSummary(ox, oz);
DiskStorageSummary summary = new DiskStorageSummary(ox, oz);
if (!requiredRegion.isIn(ox, oz)) {
fis.close();
gis.close();
@ -323,7 +341,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
MainUtil.handleError(e);
}
}
return summary;
return null;
}
public IntegerPair readHeader() {

View File

@ -50,6 +50,10 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
public abstract long getSizeInMemory();
public long getSizeOnDisk() {
return 0;
}
public abstract OutputStream getBlockOS(int x, int y, int z) throws IOException;
public abstract NBTOutputStream getEntityCreateOS() throws IOException;
public abstract NBTOutputStream getEntityRemoveOS() throws IOException;

View File

@ -56,7 +56,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
private int last;
public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) {
this(width, height, length, new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + uuid + ".bd"));
this(width, height, length, MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.CLIPBOARD + File.separator + uuid + ".bd"));
}
public DiskOptimizedClipboard(File file) throws IOException {
@ -176,7 +176,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
}
public DiskOptimizedClipboard(int width, int height, int length) {
this(width, height, length, new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + UUID.randomUUID()));
this(width, height, length, MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd"));
}
public void close() {

View File

@ -1028,8 +1028,9 @@ public class EditSession implements Extent {
public void undo(final EditSession editSession) {
final UndoContext context = new UndoContext();
context.setExtent(editSession.bypassAll);
ChangeSet changeSet = getChangeSet();
editSession.getQueue().setChangeTask(null);
Operations.completeSmart(ChangeSetExecutor.createUndo(getChangeSet(), context), new Runnable() {
Operations.completeSmart(ChangeSetExecutor.createUndo(changeSet, context), new Runnable() {
@Override
public void run() {
editSession.flushQueue();
@ -1046,8 +1047,9 @@ public class EditSession implements Extent {
public void redo(final EditSession editSession) {
final UndoContext context = new UndoContext();
context.setExtent(editSession.bypassAll);
ChangeSet changeSet = getChangeSet();
editSession.getQueue().setChangeTask(null);
Operations.completeSmart(ChangeSetExecutor.createRedo(getChangeSet(), context), new Runnable() {
Operations.completeSmart(ChangeSetExecutor.createRedo(changeSet, context), new Runnable() {
@Override
public void run() {
editSession.flushQueue();

View File

@ -19,7 +19,13 @@
package com.sk89q.worldedit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
@ -50,15 +56,24 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.snapshot.Snapshot;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import javax.swing.filechooser.FileNameExtensionFilter;
import static com.google.common.base.Preconditions.checkNotNull;
@ -78,14 +93,37 @@ public class LocalSession {
// Session related
private transient RegionSelector selector = new CuboidRegionSelector();
private transient boolean placeAtPos1 = false;
private transient List<EditSession> history = Collections.synchronizedList(new LinkedList<EditSession>());
private transient volatile int historyPointer = 0;
private transient List<Object> history = Collections.synchronizedList(new LinkedList<Object>() {
@Override
public void add(int index, Object element) { // Integer = Lazy evaluated FaweChangeSet
if (element instanceof Integer || element instanceof FaweChangeSet) {
super.add(index, element);
} else {
throw new ClassCastException("Must add either Integer (index) or FaweChangeSet");
}
}
@Override
public Object get(int index) {
Object value = super.get(index);
if (value instanceof Integer) {
value = getChangeSet(value);
set(index, value);
}
return value;
}
@Override
public Object remove(int index) {
return getChangeSet(super.remove(index));
}
});
private transient volatile Integer historyNegativeIndex;
private transient volatile long historySize = 0;
private transient ClipboardHolder clipboard;
private transient boolean toolControl = true;
private transient boolean superPickaxe = false;
private transient BlockTool pickaxeMode = new SinglePickaxe();
private transient Map<Integer, Tool> tools = new HashMap<Integer, Tool>();
private transient Map<Integer, Tool> tools = new HashMap<>();
private transient int maxBlocksChanged = -1;
private transient boolean useInventory;
private transient Snapshot snapshot;
@ -95,6 +133,10 @@ public class LocalSession {
private transient Mask mask;
private transient TimeZone timezone = TimeZone.getDefault();
// May be null
private transient World currentWorld;
private transient UUID uuid;
// Saved properties
private String lastScript;
private RegionSelectorType defaultSelector;
@ -136,6 +178,97 @@ public class LocalSession {
}
}
/**
*
* @param uuid
* @param world
* @return If any loading occured
*/
public boolean loadSessionHistoryFromDisk(UUID uuid, World world) {
if (world == null || uuid == null) {
return false;
}
if (!world.equals(currentWorld)) {
this.uuid = uuid;
// Save history
saveHistoryNegativeIndex(uuid, currentWorld);
history.clear();
currentWorld = world;
// Load history
if (loadHistoryChangeSets(uuid, currentWorld)) {
loadHistoryNegativeIndex(uuid, currentWorld);
return true;
}
historyNegativeIndex = 0;
}
return false;
}
private boolean loadHistoryChangeSets(UUID uuid, World world) {
final List<Integer> editIds = new ArrayList<>();
final File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid);
if (folder.isDirectory()) {
final FileNameExtensionFilter filter = new FileNameExtensionFilter("BlockData files","bd");
folder.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName();
int i = name.lastIndexOf('.');
if ((name.length() == i + 3) && (name.charAt(i + 1) == 'b' && name.charAt(i + 2) == 'd')) {
int index = Integer.parseInt(name.substring(0, i));
editIds.add(index);
}
return false;
}
});
}
historySize = 0;
if (editIds.size() > 0) {
Collections.sort(editIds);
for (int index : editIds) {
history.add(index);
}
}
return editIds.size() > 0;
}
private void loadHistoryNegativeIndex(UUID uuid, World world) {
File file = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid + File.separator + "index");
if (file.exists()) {
try {
FaweInputStream is = new FaweInputStream(new FileInputStream(file));
historyNegativeIndex = Math.min(Math.max(0, is.readInt()), history.size());
is.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
historyNegativeIndex = 0;
}
}
private void saveHistoryNegativeIndex(UUID uuid, World world) {
if (world == null) {
return;
}
File file = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.HISTORY + File.separator + Fawe.imp().getWorldName(world) + File.separator + uuid + File.separator + "index");
if (getHistoryNegativeIndex() != 0) {
try {
if (file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
FaweOutputStream os = new FaweOutputStream(new FileOutputStream(file));
os.writeInt(getHistoryNegativeIndex());
os.close();
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.exists()) {
file.delete();
}
}
/**
* Get whether this session is "dirty" and has changes that needs to
* be committed.
@ -153,6 +286,29 @@ public class LocalSession {
dirty.set(true);
}
public int getHistoryIndex() {
return history.size() - 1 - (historyNegativeIndex == null ? 0 : historyNegativeIndex);
}
public int getHistoryNegativeIndex() {
return (historyNegativeIndex == null ? historyNegativeIndex = 0 : historyNegativeIndex);
}
public void setHistoryIndex(int value) {
historyNegativeIndex = history.size() - value - 1;
}
public boolean save() {
saveHistoryNegativeIndex(uuid, currentWorld);
if (defaultSelector == RegionSelectorType.CUBOID) {
defaultSelector = null;
}
if (lastScript != null || defaultSelector != null) {
return true;
}
return false;
}
/**
* Get whether this session is "dirty" and has changes that needs to
* be committed, and reset it to {@code false}.
@ -187,7 +343,7 @@ public class LocalSession {
*/
public void clearHistory() {
history.clear();
historyPointer = 0;
historyNegativeIndex = 0;
historySize = 0;
}
@ -203,6 +359,16 @@ public class LocalSession {
remember(editSession, true, false, limit);
}
private FaweChangeSet getChangeSet(Object o) {
if (o instanceof FaweChangeSet) {
return (FaweChangeSet) o;
}
if (o instanceof Integer) {
return new DiskStorageHistory(currentWorld, this.uuid, (Integer) o);
}
return null;
}
public void remember(final EditSession editSession, final boolean append, final boolean sendMessage, int limitMb) {
if (editSession == null || editSession.getChangeSet() == null || limitMb == 0 || ((historySize >> 20) > limitMb && !append)) {
return;
@ -213,27 +379,41 @@ public class LocalSession {
if (editSession.size() == 0 || editSession.hasFastMode()) {
return;
}
FawePlayer fp = editSession.getPlayer();
if (fp != null) {
loadSessionHistoryFromDisk(fp.getUUID(), editSession.getWorld());
}
// Destroy any sessions after this undo point
if (append) {
while (historyPointer < history.size()) {
EditSession item = history.get(historyPointer);
historySize -= MainUtil.getSizeInMemory(item.getChangeSet());
history.remove(historyPointer);
int size = getHistoryNegativeIndex();
ListIterator<Object> iter = history.listIterator();
int i = 0;
int cutoffIndex = history.size() - getHistoryNegativeIndex();
while (iter.hasNext()) {
Object item = iter.next();
if (++i > cutoffIndex) {
if (item instanceof FaweChangeSet) {
FaweChangeSet changeSet = (FaweChangeSet) item;
historySize -= MainUtil.getSizeInMemory(changeSet);
}
iter.remove();
}
}
historySize += MainUtil.getSizeInMemory(editSession.getChangeSet());
}
FaweChangeSet changeSet = (FaweChangeSet) editSession.getChangeSet();
historySize += MainUtil.getSizeInMemory(changeSet);
if (append) {
history.add(editSession);
historyPointer = history.size();
history.add(changeSet);
if (getHistoryNegativeIndex() != 0) {
setDirty();
historyNegativeIndex = 0;
}
} else {
history.add(0, editSession);
historyPointer++;
history.add(0, changeSet);
}
while ((history.size() > MAX_HISTORY_SIZE || (historySize >> 20) > limitMb) && history.size() > 1) {
EditSession item = history.get(0);
historySize -= MainUtil.getSizeInMemory(item.getChangeSet());
history.remove(0);
historyPointer--;
FaweChangeSet item = (FaweChangeSet) history.remove(0);
historySize -= MainUtil.getSizeInMemory(item);
}
}
@ -257,20 +437,26 @@ public class LocalSession {
*/
public EditSession undo(@Nullable BlockBag newBlockBag, Player player) {
checkNotNull(player);
--historyPointer;
if (historyPointer >= 0) {
EditSession editSession = history.get(historyPointer);
EditSession newEditSession = new EditSessionBuilder(editSession.getWorld())
loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld());
if (getHistoryNegativeIndex() < history.size()) {
FaweChangeSet changeSet = (FaweChangeSet) history.get(getHistoryIndex());
EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
.allowedRegionsEverywhere()
.checkMemory(false)
.changeSetNull()
.fastmode(true)
.changeSet(changeSet)
.fastmode(false)
.limitUnlimited()
.build();
editSession.undo(newEditSession);
return editSession;
newEditSession.undo(newEditSession);
setDirty();
historyNegativeIndex++;
return newEditSession;
} else {
historyPointer = 0;
int size = history.size();
if (getHistoryNegativeIndex() != size) {
historyNegativeIndex = history.size();
setDirty();
}
return null;
}
}
@ -295,20 +481,21 @@ public class LocalSession {
*/
public EditSession redo(@Nullable BlockBag newBlockBag, Player player) {
checkNotNull(player);
if (historyPointer < history.size()) {
EditSession editSession = history.get(historyPointer);
EditSession newEditSession = new EditSessionBuilder(editSession.getWorld())
loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld());
if (getHistoryNegativeIndex() > 0) {
setDirty();
historyNegativeIndex--;
FaweChangeSet changeSet = (FaweChangeSet) history.get(getHistoryIndex());
EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
.allowedRegionsEverywhere()
.checkMemory(false)
.changeSetNull()
.fastmode(true)
.changeSet(changeSet)
.fastmode(false)
.limitUnlimited()
.build();
editSession.redo(newEditSession);
++historyPointer;
return editSession;
newEditSession.redo(newEditSession);
return newEditSession;
}
return null;
}

View File

@ -91,7 +91,6 @@ public class HistoryCommands {
Vector top = origin.add(radius, radius, radius);
RollbackDatabase database = DBHandler.IMP.getDatabase(Fawe.imp().getWorldName(world));
final AtomicInteger count = new AtomicInteger();
System.out.println("ROLLING BACK");
database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal<DiskStorageHistory>() {
@Override
public void run(DiskStorageHistory edit) {

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.session;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.sk89q.worldedit.LocalConfiguration;
@ -33,19 +31,16 @@ import com.sk89q.worldedit.session.storage.SessionStore;
import com.sk89q.worldedit.session.storage.VoidStore;
import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors;
import com.sk89q.worldedit.util.eventbus.Subscribe;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@ -58,14 +53,22 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public class SessionManager {
@Deprecated
public static int EXPIRATION_GRACE = 600000;
private static final int FLUSH_PERIOD = 1000 * 30;
private static final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 5));
private static final Logger log = Logger.getLogger(SessionManager.class.getCanonicalName());
private final Timer timer = new Timer();
private final WorldEdit worldEdit;
private final Map<UUID, SessionHolder> sessions = new HashMap<UUID, SessionHolder>();
private final Map<UUID, SessionHolder> sessions = new ConcurrentHashMap<UUID, SessionHolder>();
private SessionStore store = new VoidStore();
private File path;
// Added //
// private final ConcurrentLinkedDeque<SessionHolder> toSave = new ConcurrentLinkedDeque<>();
///////////
/**
* Create a new session manager.
@ -77,7 +80,6 @@ public class SessionManager {
this.worldEdit = worldEdit;
worldEdit.getEventBus().register(this);
timer.schedule(new SessionTracker(), FLUSH_PERIOD, FLUSH_PERIOD);
}
/**
@ -191,46 +193,28 @@ public class SessionManager {
return session;
}
/**
* Save a map of sessions to disk.
*
* @param sessions a map of sessions to save
* @return a future that completes on save or error
*/
private ListenableFuture<?> commit(final Map<SessionKey, LocalSession> sessions) {
checkNotNull(sessions);
if (sessions.isEmpty()) {
return Futures.immediateFuture(sessions);
}
return executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Exception exception = null;
for (Map.Entry<SessionKey, LocalSession> entry : sessions.entrySet()) {
SessionKey key = entry.getKey();
private void save(SessionHolder holder) {
SessionKey key = holder.key;
if (key.isPersistent()) {
try {
store.save(getKey(key), entry.getValue());
if (holder.session.compareAndResetDirty()) {
if (holder.session.save()) {
store.save(getKey(key), holder.session);
} else if (path != null) {
File file = new File(path, getKey(key) + ".json");
if (file.exists()) {
if (!file.delete()) {
file.deleteOnExit();
}
}
}
}
} catch (IOException e) {
log.log(Level.WARNING, "Failed to write session for UUID " + getKey(key), e);
exception = e;
}
}
}
if (exception != null) {
throw exception;
}
return sessions;
}
});
}
/**
* Get the key to use in the map for an owner.
*
@ -264,13 +248,16 @@ public class SessionManager {
*/
public synchronized void remove(SessionOwner owner) {
checkNotNull(owner);
sessions.remove(getKey(owner));
save(sessions.remove(getKey(owner)));
}
/**
* Remove all sessions.
*/
public synchronized void clear() {
for (Map.Entry<UUID, SessionHolder> entry : sessions.entrySet()) {
save(entry.getValue());
}
sessions.clear();
}
@ -279,6 +266,7 @@ public class SessionManager {
LocalConfiguration config = event.getConfiguration();
File dir = new File(config.getWorkingDirectory(), "sessions");
store = new JsonFileSessionStore(dir);
this.path = dir;
}
/**
@ -295,42 +283,9 @@ public class SessionManager {
}
}
/**
* Removes inactive sessions after they have been inactive for a period
* of time. Commits them as well.
*/
private class SessionTracker extends TimerTask {
@Override
public void run() {
synchronized (SessionManager.this) {
long now = System.currentTimeMillis();
Iterator<SessionHolder> it = sessions.values().iterator();
Map<SessionKey, LocalSession> saveQueue = new HashMap<SessionKey, LocalSession>();
while (it.hasNext()) {
SessionHolder stored = it.next();
if (stored.key.isActive()) {
stored.lastActive = now;
if (stored.session.compareAndResetDirty()) {
saveQueue.put(stored.key, stored.session);
}
} else {
if (now - stored.lastActive > EXPIRATION_GRACE) {
if (stored.session.compareAndResetDirty()) {
saveQueue.put(stored.key, stored.session);
public static Class<?> inject() {
return SessionManager.class;
}
it.remove();
}
}
}
if (!saveQueue.isEmpty()) {
commit(saveQueue);
}
}
}
}
}

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import java.io.File;
import java.util.List;
@ -72,10 +71,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("lastWorld", event.getWorld());
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
}
}
}

View File

@ -10,7 +10,6 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
@ -18,6 +17,7 @@ import com.sk89q.jnbt.Tag;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -31,20 +31,16 @@ import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketChunkData;
import net.minecraft.network.play.server.SPacketDestroyEntities;
import net.minecraft.server.management.PlayerChunkMap;
import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.EnumSkyBlock;
@ -513,7 +509,9 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
}
@Override
public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) {
public void refreshChunk(FaweChunk fc) {
ForgeChunk_All fs = (ForgeChunk_All) fc;
Chunk nmsChunk = fs.getChunk();
if (!nmsChunk.isLoaded()) {
return;
}
@ -523,75 +521,44 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.chunkXPos;
int z = pos.chunkZPos;
if (!chunkMap.contains(x, z)) {
PlayerChunkMapEntry chunkMapEntry = chunkMap.getEntry(x, z);
if (chunkMapEntry == null) {
return;
}
EntityTracker tracker = w.getEntityTracker();
HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : w.playerEntities) {
if (player instanceof EntityPlayerMP) {
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) {
players.add((EntityPlayerMP) player);
}
}
}
if (players.size() == 0) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
ClassInheritanceMultiMap<Entity>[] entitieSlices = nmsChunk.getEntityLists();
IntHashMap<EntityTrackerEntry> entries = null;
for (Field field : tracker.getClass().getDeclaredFields()) {
if (field.getType() == IntHashMap.class) {
field.setAccessible(true);
entries = (IntHashMap<EntityTrackerEntry>) field.get(tracker);
break;
}
}
for (ClassInheritanceMultiMap<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null;
if (entry == null) {
continue;
}
entities.add(entry);
SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId());
final ArrayDeque<EntityPlayerMP> players = new ArrayDeque<>();
chunkMapEntry.hasPlayerMatching(input -> {
players.add(input);
return false;
});
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65280);
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
mask = 255;
}
}
// Send chunks
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535);
SPacketChunkData packet = new SPacketChunkData(nmsChunk, mask);
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
// send ents
for (EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayerMP player : players) {
boolean result = entry.trackingPlayers.remove(player);
if (result && entry.getTrackedEntity() != player) {
entry.updatePlayerEntity(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
public boolean hasEntities(Chunk nmsChunk) {
ClassInheritanceMultiMap<Entity>[] entities = nmsChunk.getEntityLists();
for (int i = 0; i < entities.length; i++) {
ClassInheritanceMultiMap<Entity> slice = entities[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) {
return new ForgeChunk_All(this, x, z);

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
@ -73,10 +72,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("lastWorld", event.world);
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
}
}
}

View File

@ -11,7 +11,6 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
@ -31,17 +30,13 @@ import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S13PacketDestroyEntities;
import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.server.management.PlayerManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.ChunkPosition;
@ -216,21 +211,21 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
};
@Override
public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) {
public void refreshChunk(FaweChunk fc) {
ForgeChunk_All fs = (ForgeChunk_All) fc;
Chunk nmsChunk = fs.getChunk();
if (!nmsChunk.isChunkLoaded) {
return;
}
try {
ChunkCoordIntPair pos = nmsChunk.getChunkCoordIntPair();
WorldServer w = (WorldServer) nmsChunk.worldObj;
PlayerManager chunkMap = w.getPlayerManager();
int x = pos.chunkXPos;
int z = pos.chunkZPos;
int x = nmsChunk.xPosition;
int z = nmsChunk.zPosition;
if (!chunkMap.func_152621_a(x, z)) {
return;
}
EntityTracker tracker = w.getEntityTracker();
final HashSet<EntityPlayerMP> players = new HashSet<>();
HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : (List<EntityPlayer>) w.playerEntities) {
if (player instanceof EntityPlayerMP) {
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) {
@ -241,60 +236,34 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
if (players.size() == 0) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
Collection<Entity>[] entitieSlices = nmsChunk.entityLists;
IntHashMap entries = null;
for (Field field : tracker.getClass().getDeclaredFields()) {
if (field.getType() == IntHashMap.class) {
field.setAccessible(true);
entries = (IntHashMap) field.get(tracker);
break;
}
}
for (Collection<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = entries != null ? (EntityTrackerEntry) entries.lookup(ent.getEntityId()) : null;
if (entry == null) {
continue;
}
entities.add(entry);
S13PacketDestroyEntities packet = new S13PacketDestroyEntities(ent.getEntityId());
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65280);
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
mask = 255;
}
}
// Send chunks
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65535);
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, mask);
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
// send ents
for (final EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayerMP player : players) {
boolean result = entry.trackingPlayers.remove(player);
if (result && entry.myEntity != player) {
entry.tryStartWachingThis(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
public boolean hasEntities(Chunk nmsChunk) {
for (int i = 0; i < nmsChunk.entityLists.length; i++) {
List slice = nmsChunk.entityLists[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
ForgeChunk_All fs = (ForgeChunk_All) fc;

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import java.io.File;
import java.util.List;
@ -73,10 +72,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("lastWorld", event.world);
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
}
}
}

View File

@ -10,7 +10,6 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
@ -29,19 +28,15 @@ import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S13PacketDestroyEntities;
import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.server.management.PlayerManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
@ -453,20 +448,20 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
}
@Override
public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) {
public void refreshChunk(FaweChunk fc) {
ForgeChunk_All fs = (ForgeChunk_All) fc;
Chunk nmsChunk = fs.getChunk();
if (!nmsChunk.isLoaded()) {
return;
}
try {
ChunkCoordIntPair pos = nmsChunk.getChunkCoordIntPair();
WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerManager chunkMap = w.getPlayerManager();
int x = pos.chunkXPos;
int z = pos.chunkZPos;
int x = nmsChunk.xPosition;
int z = nmsChunk.zPosition;
if (!chunkMap.hasPlayerInstance(x, z)) {
return;
}
EntityTracker tracker = w.getEntityTracker();
HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : w.playerEntities) {
if (player instanceof EntityPlayerMP) {
@ -478,60 +473,35 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
if (players.size() == 0) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
ClassInheritanceMultiMap<Entity>[] entitieSlices = nmsChunk.getEntityLists();
IntHashMap<EntityTrackerEntry> entries = null;
for (Field field : tracker.getClass().getDeclaredFields()) {
if (field.getType() == IntHashMap.class) {
field.setAccessible(true);
entries = (IntHashMap<EntityTrackerEntry>) field.get(tracker);
break;
}
}
for (ClassInheritanceMultiMap<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null;
if (entry == null) {
continue;
}
entities.add(entry);
S13PacketDestroyEntities packet = new S13PacketDestroyEntities(ent.getEntityId());
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65280);
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
mask = 255;
}
}
// Send chunks
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65535);
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, mask);
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
// send ents
for (EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayerMP player : players) {
boolean result = entry.trackingPlayers.remove(player);
if (result && entry.trackedEntity != player) {
entry.updatePlayerEntity(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
public boolean hasEntities(Chunk nmsChunk) {
ClassInheritanceMultiMap<Entity>[] entities = nmsChunk.getEntityLists();
for (int i = 0; i < entities.length; i++) {
ClassInheritanceMultiMap<Entity> slice = entities[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) {
return new ForgeChunk_All(this, x, z);

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import java.io.File;
import java.util.List;
@ -72,10 +71,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("lastWorld", event.getWorld());
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
}
}
}

View File

@ -10,7 +10,6 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
@ -18,6 +17,7 @@ import com.sk89q.jnbt.Tag;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -31,20 +31,16 @@ import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketChunkData;
import net.minecraft.network.play.server.SPacketDestroyEntities;
import net.minecraft.server.management.PlayerChunkMap;
import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.EnumSkyBlock;
@ -513,7 +509,9 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
}
@Override
public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) {
public void refreshChunk(FaweChunk fc) {
ForgeChunk_All fs = (ForgeChunk_All) fc;
Chunk nmsChunk = fs.getChunk();
if (!nmsChunk.isLoaded()) {
return;
}
@ -523,74 +521,42 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.chunkXPos;
int z = pos.chunkZPos;
if (!chunkMap.contains(x, z)) {
PlayerChunkMapEntry chunkMapEntry = chunkMap.getEntry(x, z);
if (chunkMapEntry == null) {
return;
}
EntityTracker tracker = w.getEntityTracker();
HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : w.playerEntities) {
if (player instanceof EntityPlayerMP) {
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) {
players.add((EntityPlayerMP) player);
}
}
}
if (players.size() == 0) {
return;
}
HashSet<EntityTrackerEntry> entities = new HashSet<>();
ClassInheritanceMultiMap<Entity>[] entitieSlices = nmsChunk.getEntityLists();
IntHashMap<EntityTrackerEntry> entries = null;
for (Field field : tracker.getClass().getDeclaredFields()) {
if (field.getType() == IntHashMap.class) {
field.setAccessible(true);
entries = (IntHashMap<EntityTrackerEntry>) field.get(tracker);
break;
}
}
for (ClassInheritanceMultiMap<Entity> slice : entitieSlices) {
if (slice == null) {
continue;
}
for (Entity ent : slice) {
EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null;
if (entry == null) {
continue;
}
entities.add(entry);
SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId());
final ArrayDeque<EntityPlayerMP> players = new ArrayDeque<>();
chunkMapEntry.hasPlayerMatching(input -> {
players.add(input);
return false;
});
int mask = fc.getBitMask();
if (mask == 65535 && hasEntities(nmsChunk)) {
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65280);
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
mask = 255;
}
}
// Send chunks
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535);
SPacketChunkData packet = new SPacketChunkData(nmsChunk, mask);
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
// send ents
for (EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
for (EntityPlayerMP player : players) {
boolean result = entry.trackingPlayers.remove(player);
if (result && entry.getTrackedEntity() != player) {
entry.updatePlayerEntity(player);
}
}
}
}, 2);
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
} catch (Throwable e) {
MainUtil.handleError(e);
public boolean hasEntities(Chunk nmsChunk) {
ClassInheritanceMultiMap<Entity>[] entities = nmsChunk.getEntityLists();
for (int i = 0; i < entities.length; i++) {
ClassInheritanceMultiMap<Entity> slice = entities[i];
if (slice != null && !slice.isEmpty()) {
return true;
}
}
return false;
}
@Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) {