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) { public void onPlayerChangedWorld(PlayerChangedWorldEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
if (Settings.HISTORY.USE_DISK) {
fp.getSession().clearHistory();
fp.loadSessionsFromDisk(fp.getWorld());
}
} }
@Override @Override

View File

@ -134,9 +134,7 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
} }
@Override @Override
public void refreshChunk(World world, CHUNK chunk) { public void refreshChunk(FaweChunk fs) {}
return;
}
@Override @Override
public boolean regenerateChunk(World world, int x, int z) { 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.Block;
import net.minecraft.server.v1_10_R1.BlockPosition; import net.minecraft.server.v1_10_R1.BlockPosition;
import net.minecraft.server.v1_10_R1.Blocks; 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.ChunkSection;
import net.minecraft.server.v1_10_R1.DataBits; import net.minecraft.server.v1_10_R1.DataBits;
import net.minecraft.server.v1_10_R1.DataPaletteBlock; import net.minecraft.server.v1_10_R1.DataPaletteBlock;
import net.minecraft.server.v1_10_R1.Entity; 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.EntityPlayer;
import net.minecraft.server.v1_10_R1.EntityTracker; 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.EntityTypes;
import net.minecraft.server.v1_10_R1.EnumDifficulty; import net.minecraft.server.v1_10_R1.EnumDifficulty;
import net.minecraft.server.v1_10_R1.EnumGamemode; 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.MinecraftServer;
import net.minecraft.server.v1_10_R1.NBTTagCompound; import net.minecraft.server.v1_10_R1.NBTTagCompound;
import net.minecraft.server.v1_10_R1.NibbleArray; 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.PacketPlayOutMapChunk;
import net.minecraft.server.v1_10_R1.PlayerChunk; import net.minecraft.server.v1_10_R1.PlayerChunk;
import net.minecraft.server.v1_10_R1.PlayerChunkMap; import net.minecraft.server.v1_10_R1.PlayerChunkMap;
@ -246,86 +242,54 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<Chunk, ChunkSection[], Chunk
} }
@Override @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()) { if (!chunk.isLoaded()) {
return; return;
} }
try { try {
net.minecraft.server.v1_10_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); net.minecraft.server.v1_10_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
ChunkCoordIntPair pos = nmsChunk.k(); // getPosition()
WorldServer w = (WorldServer) nmsChunk.getWorld(); WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap(); PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(pos.x, pos.z); PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
if (playerChunk == null) { if (playerChunk == null) {
return; return;
} }
HashSet<EntityPlayer> set = new HashSet<EntityPlayer>(playerChunk.c); if (playerChunk.c.isEmpty()) {
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) {
return; 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 // Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535); int mask = fc.getBitMask();
for (EntityPlayer player : players) { if (mask == 65535 && hasEntities(nmsChunk)) {
player.playerConnection.sendPacket(packet); PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
mask = 255;
} }
// send ents PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, mask);
for (Collection<Entity> slice : entitieSlices) { for (EntityPlayer player : playerChunk.c) {
if (slice == null) { player.playerConnection.sendPacket(packet);
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);
}
}
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); 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 @Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode != RelightMode.NONE) { 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; net.minecraft.server.v1_10_R1.World nmsWorld = nmsChunk.world;
ChunkSection[] sections = nmsChunk.getSections(); ChunkSection[] sections = nmsChunk.getSections();
Class<? extends net.minecraft.server.v1_10_R1.Chunk> clazzChunk = nmsChunk.getClass(); Class<? extends net.minecraft.server.v1_10_R1.Chunk> clazzChunk = nmsChunk.getClass();
final Field ef = clazzChunk.getDeclaredField("entitySlices"); final Collection<Entity>[] entities = (Collection<Entity>[]) getEntitySlices.invoke(nmsChunk);
final Collection<Entity>[] entities = (Collection<Entity>[]) ef.get(nmsChunk);
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities(); Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
// Remove entities // Remove entities
for (int i = 0; i < entities.length; i++) { 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.Entity;
import net.minecraft.server.v1_7_R4.EntityPlayer; import net.minecraft.server.v1_7_R4.EntityPlayer;
import net.minecraft.server.v1_7_R4.EntityTracker; 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.EntityTypes;
import net.minecraft.server.v1_7_R4.EnumDifficulty; import net.minecraft.server.v1_7_R4.EnumDifficulty;
import net.minecraft.server.v1_7_R4.EnumGamemode; 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.MinecraftServer;
import net.minecraft.server.v1_7_R4.NBTTagCompound; import net.minecraft.server.v1_7_R4.NBTTagCompound;
import net.minecraft.server.v1_7_R4.NibbleArray; 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.PacketPlayOutMapChunk;
import net.minecraft.server.v1_7_R4.PlayerChunkMap; import net.minecraft.server.v1_7_R4.PlayerChunkMap;
import net.minecraft.server.v1_7_R4.ServerNBTManager; 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); fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount);
} }
@Override @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()) { if (!chunk.isLoaded()) {
return; return;
} }
@ -483,52 +485,34 @@ public class BukkitQueue17 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkSec
if (players.size() == 0) { if (players.size() == 0) {
return; 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 // 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);
}
mask = 255;
}
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, mask, 25);
for (EntityPlayer player : players) { for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
// send ents
for (final EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
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);
}
}
} catch (Throwable e) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode == RelightMode.ALL) { if (mode == RelightMode.ALL) {

View File

@ -30,12 +30,10 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import net.minecraft.server.v1_8_R3.Block; import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockPosition; 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.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity; import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer; import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EntityTracker; 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.EntityTypes;
import net.minecraft.server.v1_8_R3.EnumDifficulty; import net.minecraft.server.v1_8_R3.EnumDifficulty;
import net.minecraft.server.v1_8_R3.EnumSkyBlock; 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.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTTagCompound; import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.NibbleArray; 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.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerChunkMap; import net.minecraft.server.v1_8_R3.PlayerChunkMap;
import net.minecraft.server.v1_8_R3.ServerNBTManager; import net.minecraft.server.v1_8_R3.ServerNBTManager;
@ -429,24 +426,21 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], ChunkS
} }
@Override @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()) { if (!chunk.isLoaded()) {
return; return;
} }
try { try {
net.minecraft.server.v1_8_R3.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); net.minecraft.server.v1_8_R3.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
ChunkCoordIntPair pos = nmsChunk.j(); // getPosition()
WorldServer w = (WorldServer) nmsChunk.getWorld(); WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap(); PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.x; int x = nmsChunk.locX;
int z = pos.z; int z = nmsChunk.locZ;
if (!chunkMap.isChunkInUse(x, z)) { if (!chunkMap.isChunkInUse(x, z)) {
return; return;
} }
HashSet<EntityPlayer> set = new HashSet<EntityPlayer>();
EntityTracker tracker = w.getTracker();
// Get players
Field fieldChunkMap = chunkMap.getClass().getDeclaredField("d"); Field fieldChunkMap = chunkMap.getClass().getDeclaredField("d");
fieldChunkMap.setAccessible(true); fieldChunkMap.setAccessible(true);
LongHashMap<Object> map = (LongHashMap<Object>) fieldChunkMap.get(chunkMap); 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); Object playerChunk = map.getEntry(pair);
Field fieldPlayers = playerChunk.getClass().getDeclaredField("b"); Field fieldPlayers = playerChunk.getClass().getDeclaredField("b");
fieldPlayers.setAccessible(true); fieldPlayers.setAccessible(true);
final HashSet<EntityPlayer> players = new HashSet<>((Collection<EntityPlayer>)fieldPlayers.get(playerChunk)); Collection<EntityPlayer> players = (Collection<EntityPlayer>) fieldPlayers.get(playerChunk);
if (players.size() == 0) { if (players.isEmpty()) {
return; 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 // 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);
}
mask = 255;
}
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, mask);
for (EntityPlayer player : players) { for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
// send ents
for (final EntityTrackerEntry entry : entities) {
try {
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
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);
}
}
} catch (Throwable e) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) {
if (mode == RelightMode.ALL) { 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.Block;
import net.minecraft.server.v1_9_R2.BlockPosition; import net.minecraft.server.v1_9_R2.BlockPosition;
import net.minecraft.server.v1_9_R2.Blocks; 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.ChunkSection;
import net.minecraft.server.v1_9_R2.DataBits; import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R2.DataPalette; import net.minecraft.server.v1_9_R2.DataPalette;
import net.minecraft.server.v1_9_R2.DataPaletteBlock; import net.minecraft.server.v1_9_R2.DataPaletteBlock;
import net.minecraft.server.v1_9_R2.Entity; 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.EntityPlayer;
import net.minecraft.server.v1_9_R2.EntityTracker; 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.EntityTypes;
import net.minecraft.server.v1_9_R2.EnumDifficulty; import net.minecraft.server.v1_9_R2.EnumDifficulty;
import net.minecraft.server.v1_9_R2.EnumSkyBlock; 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.MinecraftServer;
import net.minecraft.server.v1_9_R2.NBTTagCompound; import net.minecraft.server.v1_9_R2.NBTTagCompound;
import net.minecraft.server.v1_9_R2.NibbleArray; 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.PacketPlayOutMapChunk;
import net.minecraft.server.v1_9_R2.PlayerChunk; import net.minecraft.server.v1_9_R2.PlayerChunk;
import net.minecraft.server.v1_9_R2.PlayerChunkMap; import net.minecraft.server.v1_9_R2.PlayerChunkMap;
@ -128,80 +124,49 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Chu
} }
@Override @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()) { if (!chunk.isLoaded()) {
return; return;
} }
net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); try {
ChunkCoordIntPair pos = nmsChunk.k(); // getPosition() net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
WorldServer w = (WorldServer) nmsChunk.getWorld(); WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerChunkMap chunkMap = w.getPlayerChunkMap(); PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(pos.x, pos.z); PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
if (playerChunk == null) { if (playerChunk == null) {
return; 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 (playerChunk.c.isEmpty()) {
if (players.size() == 0) { return;
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) { // Send chunks
EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId()); int mask = fc.getBitMask();
if (entry == null) { if (mask == 65535 && hasEntities(nmsChunk)) {
continue; PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280);
} for (EntityPlayer player : playerChunk.c) {
entities.add(entry);
PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId());
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }
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_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;
} }
} }
// Send chunks return false;
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(playerChunk.chunk, 65535);
for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet);
}
// send ents
for (List<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);
}
}
}
} }
@Override @Override

View File

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

View File

@ -257,7 +257,7 @@ public class FaweAPI {
* @return * @return
*/ */
public static List<DiskStorageHistory> getBDFiles(FaweLocation origin, UUID user, int radius, long timediff, boolean shallow) { 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()) { if (!history.exists()) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -37,6 +37,13 @@ public class Settings extends Config {
}) })
public static int MAX_MEMORY_PERCENT = 95; 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 @Create // This value will be generated automatically
public static ConfigBlock<LIMITS> LIMITS = null; public static ConfigBlock<LIMITS> LIMITS = null;
@ -129,7 +136,8 @@ public class Settings extends Config {
public static int CHUNK_WAIT_MS = 100; public static int CHUNK_WAIT_MS = 100;
@Comment("Delete history on disk after a number of days") @Comment("Delete history on disk after a number of days")
public static int DELETE_AFTER_DAYS = 7; 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; public static boolean DELETE_ON_LOGOUT = true;
@Comment({ @Comment({
"If history should be enabled by default for plugins using WorldEdit:", "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)", " - 1 = Optimal (Relight changed light sources and changed blocks)",
" - 2 = All (Slowly relight every blocks)" " - 2 = All (Slowly relight every blocks)"
}) })
public static int MODE = 2; public static int MODE = 1;
} }
public static void save(File file) { 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.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.util.MainUtil;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -42,7 +43,7 @@ public class RollbackDatabase {
public RollbackDatabase(final String world) throws SQLException, ClassNotFoundException { public RollbackDatabase(final String world) throws SQLException, ClassNotFoundException {
this.prefix = ""; this.prefix = "";
this.world = world; 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(); 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))"; 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(?,?,?,?,?,?,?,?,?)"; 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[] air;
public short[] relight; public short[] relight;
public int[][] biomes; public int[][] biomes;
private int bitMask = -1;
public T chunk; public T chunk;
@ -103,6 +104,23 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return total; 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 * Get the raw data for a section
* @param i * @param i

View File

@ -66,7 +66,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
@Override @Override
public void sendChunk(final FaweChunk fc) { public void sendChunk(final FaweChunk fc) {
refreshChunk(getWorld(), (CHUNK) fc.getChunk()); refreshChunk(fc);
} }
public abstract void setFullbright(CHUNKSECTION sections); 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 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; 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() { public void sendChunks() {
for (Map.Entry<Long, RelightSkyEntry> entry : skyToRelight.entrySet()) { for (Map.Entry<Long, RelightSkyEntry> entry : skyToRelight.entrySet()) {
RelightSkyEntry chunk = entry.getValue(); 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(); public abstract char[][] getCombinedIdArrays();
/**
* The modified sections
* @return
*/
public abstract int getBitMask();
/** /**
* Get the combined block id at a location<br> * Get the combined block id at a location<br>
* combined = (id <<<< 4) + data * combined = (id <<<< 4) + data

View File

@ -4,8 +4,6 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; 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.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
@ -30,9 +28,6 @@ import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.registry.WorldData;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -111,23 +106,6 @@ public abstract class FawePlayer<T> {
if (Settings.CLIPBOARD.USE_DISK) { if (Settings.CLIPBOARD.USE_DISK) {
loadClipboardFromDisk(); 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 * - Should already be called if history on disk is enabled
*/ */
public void loadClipboardFromDisk() { 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 { try {
if (file.exists() && file.length() > 5) { if (file.exists() && file.length() > 5) {
DiskOptimizedClipboard doc = new DiskOptimizedClipboard(file); 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> * 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 * @param world
*/ */
public void loadSessionsFromDisk(final World world) { public void loadSessionsFromDisk(final World world) {
if (world == null) { if (world == null) {
return; return;
} }
final long start = System.currentTimeMillis(); getSession().loadSessionHistoryFromDisk(getUUID(), world);
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);
}
});
}
} }
/** /**
@ -417,9 +364,10 @@ public abstract class FawePlayer<T> {
*/ */
public void unregister() { public void unregister() {
if (Settings.HISTORY.DELETE_ON_LOGOUT) { if (Settings.HISTORY.DELETE_ON_LOGOUT) {
getSession().setClipboard(null); session = getSession();
getSession().clearHistory();
WorldEdit.getInstance().removeSession(getPlayer()); WorldEdit.getInstance().removeSession(getPlayer());
session.setClipboard(null);
session.clearHistory();
} }
Fawe.get().unregister(getName()); Fawe.get().unregister(getName());
} }

View File

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

View File

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

View File

@ -56,7 +56,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable {
private int last; private int last;
public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) { 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 { 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) { 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() { public void close() {

View File

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

View File

@ -19,7 +19,13 @@
package com.sk89q.worldedit; 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.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.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil; 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.session.request.Request;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.snapshot.Snapshot; 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.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.swing.filechooser.FileNameExtensionFilter;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -78,14 +93,37 @@ public class LocalSession {
// Session related // Session related
private transient RegionSelector selector = new CuboidRegionSelector(); private transient RegionSelector selector = new CuboidRegionSelector();
private transient boolean placeAtPos1 = false; private transient boolean placeAtPos1 = false;
private transient List<EditSession> history = Collections.synchronizedList(new LinkedList<EditSession>()); private transient List<Object> history = Collections.synchronizedList(new LinkedList<Object>() {
private transient volatile int historyPointer = 0; @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 volatile long historySize = 0;
private transient ClipboardHolder clipboard; private transient ClipboardHolder clipboard;
private transient boolean toolControl = true; private transient boolean toolControl = true;
private transient boolean superPickaxe = false; private transient boolean superPickaxe = false;
private transient BlockTool pickaxeMode = new SinglePickaxe(); 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 int maxBlocksChanged = -1;
private transient boolean useInventory; private transient boolean useInventory;
private transient Snapshot snapshot; private transient Snapshot snapshot;
@ -95,6 +133,10 @@ public class LocalSession {
private transient Mask mask; private transient Mask mask;
private transient TimeZone timezone = TimeZone.getDefault(); private transient TimeZone timezone = TimeZone.getDefault();
// May be null
private transient World currentWorld;
private transient UUID uuid;
// Saved properties // Saved properties
private String lastScript; private String lastScript;
private RegionSelectorType defaultSelector; 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 * Get whether this session is "dirty" and has changes that needs to
* be committed. * be committed.
@ -153,6 +286,29 @@ public class LocalSession {
dirty.set(true); 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 * Get whether this session is "dirty" and has changes that needs to
* be committed, and reset it to {@code false}. * be committed, and reset it to {@code false}.
@ -187,7 +343,7 @@ public class LocalSession {
*/ */
public void clearHistory() { public void clearHistory() {
history.clear(); history.clear();
historyPointer = 0; historyNegativeIndex = 0;
historySize = 0; historySize = 0;
} }
@ -203,6 +359,16 @@ public class LocalSession {
remember(editSession, true, false, limit); 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) { 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)) { if (editSession == null || editSession.getChangeSet() == null || limitMb == 0 || ((historySize >> 20) > limitMb && !append)) {
return; return;
@ -213,27 +379,41 @@ public class LocalSession {
if (editSession.size() == 0 || editSession.hasFastMode()) { if (editSession.size() == 0 || editSession.hasFastMode()) {
return; return;
} }
FawePlayer fp = editSession.getPlayer();
if (fp != null) {
loadSessionHistoryFromDisk(fp.getUUID(), editSession.getWorld());
}
// Destroy any sessions after this undo point // Destroy any sessions after this undo point
if (append) { if (append) {
while (historyPointer < history.size()) { int size = getHistoryNegativeIndex();
EditSession item = history.get(historyPointer); ListIterator<Object> iter = history.listIterator();
historySize -= MainUtil.getSizeInMemory(item.getChangeSet()); int i = 0;
history.remove(historyPointer); 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) { if (append) {
history.add(editSession); history.add(changeSet);
historyPointer = history.size(); if (getHistoryNegativeIndex() != 0) {
setDirty();
historyNegativeIndex = 0;
}
} else { } else {
history.add(0, editSession); history.add(0, changeSet);
historyPointer++;
} }
while ((history.size() > MAX_HISTORY_SIZE || (historySize >> 20) > limitMb) && history.size() > 1) { while ((history.size() > MAX_HISTORY_SIZE || (historySize >> 20) > limitMb) && history.size() > 1) {
EditSession item = history.get(0); FaweChangeSet item = (FaweChangeSet) history.remove(0);
historySize -= MainUtil.getSizeInMemory(item.getChangeSet()); historySize -= MainUtil.getSizeInMemory(item);
history.remove(0);
historyPointer--;
} }
} }
@ -257,20 +437,26 @@ public class LocalSession {
*/ */
public EditSession undo(@Nullable BlockBag newBlockBag, Player player) { public EditSession undo(@Nullable BlockBag newBlockBag, Player player) {
checkNotNull(player); checkNotNull(player);
--historyPointer; loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld());
if (historyPointer >= 0) { if (getHistoryNegativeIndex() < history.size()) {
EditSession editSession = history.get(historyPointer); FaweChangeSet changeSet = (FaweChangeSet) history.get(getHistoryIndex());
EditSession newEditSession = new EditSessionBuilder(editSession.getWorld()) EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
.allowedRegionsEverywhere() .allowedRegionsEverywhere()
.checkMemory(false) .checkMemory(false)
.changeSetNull() .changeSet(changeSet)
.fastmode(true) .fastmode(false)
.limitUnlimited() .limitUnlimited()
.build(); .build();
editSession.undo(newEditSession); newEditSession.undo(newEditSession);
return editSession; setDirty();
historyNegativeIndex++;
return newEditSession;
} else { } else {
historyPointer = 0; int size = history.size();
if (getHistoryNegativeIndex() != size) {
historyNegativeIndex = history.size();
setDirty();
}
return null; return null;
} }
} }
@ -295,20 +481,21 @@ public class LocalSession {
*/ */
public EditSession redo(@Nullable BlockBag newBlockBag, Player player) { public EditSession redo(@Nullable BlockBag newBlockBag, Player player) {
checkNotNull(player); checkNotNull(player);
if (historyPointer < history.size()) { loadSessionHistoryFromDisk(player.getUniqueId(), player.getWorld());
EditSession editSession = history.get(historyPointer); if (getHistoryNegativeIndex() > 0) {
EditSession newEditSession = new EditSessionBuilder(editSession.getWorld()) setDirty();
historyNegativeIndex--;
FaweChangeSet changeSet = (FaweChangeSet) history.get(getHistoryIndex());
EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
.allowedRegionsEverywhere() .allowedRegionsEverywhere()
.checkMemory(false) .checkMemory(false)
.changeSetNull() .changeSet(changeSet)
.fastmode(true) .fastmode(false)
.limitUnlimited() .limitUnlimited()
.build(); .build();
editSession.redo(newEditSession); newEditSession.redo(newEditSession);
++historyPointer; return newEditSession;
return editSession;
} }
return null; return null;
} }

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge; package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -72,10 +71,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) { if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("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.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
@ -18,6 +17,7 @@ import com.sk89q.jnbt.Tag;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -31,20 +31,16 @@ import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList; 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.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketChunkData; 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.PlayerChunkMap;
import net.minecraft.server.management.PlayerChunkMapEntry; import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.EnumSkyBlock;
@ -513,7 +509,9 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
} }
@Override @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()) { if (!nmsChunk.isLoaded()) {
return; return;
} }
@ -523,75 +521,44 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
PlayerChunkMap chunkMap = w.getPlayerChunkMap(); PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.chunkXPos; int x = pos.chunkXPos;
int z = pos.chunkZPos; int z = pos.chunkZPos;
if (!chunkMap.contains(x, z)) { PlayerChunkMapEntry chunkMapEntry = chunkMap.getEntry(x, z);
if (chunkMapEntry == null) {
return; return;
} }
EntityTracker tracker = w.getEntityTracker(); final ArrayDeque<EntityPlayerMP> players = new ArrayDeque<>();
HashSet<EntityPlayerMP> players = new HashSet<>(); chunkMapEntry.hasPlayerMatching(input -> {
for (EntityPlayer player : w.playerEntities) { players.add(input);
if (player instanceof EntityPlayerMP) { return false;
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { });
players.add((EntityPlayerMP) player); 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;
} }
if (players.size() == 0) { SPacketChunkData packet = new SPacketChunkData(nmsChunk, mask);
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());
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
}
}
// Send chunks
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535);
for (EntityPlayerMP player : players) { for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet); 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) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) { public FaweChunk<Chunk> getFaweChunk(int x, int z) {
return new ForgeChunk_All(this, x, z); return new ForgeChunk_All(this, x, z);

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge; package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod;
@ -73,10 +72,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) { if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("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.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
@ -31,17 +30,13 @@ import java.util.UUID;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList; 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.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S13PacketDestroyEntities;
import net.minecraft.network.play.server.S21PacketChunkData; import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.server.management.PlayerManager; import net.minecraft.server.management.PlayerManager;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.LongHashMap; import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.ChunkPosition; import net.minecraft.world.ChunkPosition;
@ -216,21 +211,21 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
}; };
@Override @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) { if (!nmsChunk.isChunkLoaded) {
return; return;
} }
try { try {
ChunkCoordIntPair pos = nmsChunk.getChunkCoordIntPair();
WorldServer w = (WorldServer) nmsChunk.worldObj; WorldServer w = (WorldServer) nmsChunk.worldObj;
PlayerManager chunkMap = w.getPlayerManager(); PlayerManager chunkMap = w.getPlayerManager();
int x = pos.chunkXPos; int x = nmsChunk.xPosition;
int z = pos.chunkZPos; int z = nmsChunk.zPosition;
if (!chunkMap.func_152621_a(x, z)) { if (!chunkMap.func_152621_a(x, z)) {
return; return;
} }
EntityTracker tracker = w.getEntityTracker(); HashSet<EntityPlayerMP> players = new HashSet<>();
final HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : (List<EntityPlayer>) w.playerEntities) { for (EntityPlayer player : (List<EntityPlayer>) w.playerEntities) {
if (player instanceof EntityPlayerMP) { if (player instanceof EntityPlayerMP) {
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) {
@ -241,60 +236,34 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
if (players.size() == 0) { if (players.size() == 0) {
return; return;
} }
HashSet<EntityTrackerEntry> entities = new HashSet<>(); int mask = fc.getBitMask();
Collection<Entity>[] entitieSlices = nmsChunk.entityLists; if (mask == 65535 && hasEntities(nmsChunk)) {
IntHashMap entries = null; S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65280);
for (Field field : tracker.getClass().getDeclaredFields()) { for (EntityPlayerMP player : players) {
if (field.getType() == IntHashMap.class) { player.playerNetServerHandler.sendPacket(packet);
field.setAccessible(true);
entries = (IntHashMap) field.get(tracker);
break;
} }
mask = 255;
} }
for (Collection<Entity> slice : entitieSlices) { S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, mask);
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());
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
}
}
// Send chunks
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65535);
for (EntityPlayerMP player : players) { for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet); 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) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) { public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
ForgeChunk_All fs = (ForgeChunk_All) fc; ForgeChunk_All fs = (ForgeChunk_All) fc;

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge; package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -73,10 +72,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.world) { if (fp.getMeta("lastWorld") != event.world) {
fp.setMeta("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.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
@ -29,19 +28,15 @@ import java.util.UUID;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList; 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.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.S13PacketDestroyEntities;
import net.minecraft.network.play.server.S21PacketChunkData; import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.server.management.PlayerManager; import net.minecraft.server.management.PlayerManager;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -453,20 +448,20 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
} }
@Override @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()) { if (!nmsChunk.isLoaded()) {
return; return;
} }
try { try {
ChunkCoordIntPair pos = nmsChunk.getChunkCoordIntPair();
WorldServer w = (WorldServer) nmsChunk.getWorld(); WorldServer w = (WorldServer) nmsChunk.getWorld();
PlayerManager chunkMap = w.getPlayerManager(); PlayerManager chunkMap = w.getPlayerManager();
int x = pos.chunkXPos; int x = nmsChunk.xPosition;
int z = pos.chunkZPos; int z = nmsChunk.zPosition;
if (!chunkMap.hasPlayerInstance(x, z)) { if (!chunkMap.hasPlayerInstance(x, z)) {
return; return;
} }
EntityTracker tracker = w.getEntityTracker();
HashSet<EntityPlayerMP> players = new HashSet<>(); HashSet<EntityPlayerMP> players = new HashSet<>();
for (EntityPlayer player : w.playerEntities) { for (EntityPlayer player : w.playerEntities) {
if (player instanceof EntityPlayerMP) { if (player instanceof EntityPlayerMP) {
@ -478,60 +473,35 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
if (players.size() == 0) { if (players.size() == 0) {
return; return;
} }
HashSet<EntityTrackerEntry> entities = new HashSet<>(); int mask = fc.getBitMask();
ClassInheritanceMultiMap<Entity>[] entitieSlices = nmsChunk.getEntityLists(); if (mask == 65535 && hasEntities(nmsChunk)) {
IntHashMap<EntityTrackerEntry> entries = null; S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65280);
for (Field field : tracker.getClass().getDeclaredFields()) { for (EntityPlayerMP player : players) {
if (field.getType() == IntHashMap.class) { player.playerNetServerHandler.sendPacket(packet);
field.setAccessible(true);
entries = (IntHashMap<EntityTrackerEntry>) field.get(tracker);
break;
} }
mask = 255;
} }
for (ClassInheritanceMultiMap<Entity> slice : entitieSlices) { S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, mask);
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());
for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet);
}
}
}
// Send chunks
S21PacketChunkData packet = new S21PacketChunkData(nmsChunk, false, 65535);
for (EntityPlayerMP player : players) { for (EntityPlayerMP player : players) {
player.playerNetServerHandler.sendPacket(packet); 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) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) { public FaweChunk<Chunk> getFaweChunk(int x, int z) {
return new ForgeChunk_All(this, x, z); return new ForgeChunk_All(this, x, z);

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.forge; package com.boydti.fawe.forge;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
@ -72,10 +71,6 @@ public class ForgeMain {
FawePlayer fp = FawePlayer.wrap(player); FawePlayer fp = FawePlayer.wrap(player);
if (fp.getMeta("lastWorld") != event.getWorld()) { if (fp.getMeta("lastWorld") != event.getWorld()) {
fp.setMeta("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.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
@ -18,6 +17,7 @@ import com.sk89q.jnbt.Tag;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -31,20 +31,16 @@ import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList; 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.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketChunkData; 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.PlayerChunkMap;
import net.minecraft.server.management.PlayerChunkMapEntry; import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.EnumSkyBlock;
@ -513,7 +509,9 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
} }
@Override @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()) { if (!nmsChunk.isLoaded()) {
return; return;
} }
@ -523,75 +521,43 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
PlayerChunkMap chunkMap = w.getPlayerChunkMap(); PlayerChunkMap chunkMap = w.getPlayerChunkMap();
int x = pos.chunkXPos; int x = pos.chunkXPos;
int z = pos.chunkZPos; int z = pos.chunkZPos;
if (!chunkMap.contains(x, z)) { PlayerChunkMapEntry chunkMapEntry = chunkMap.getEntry(x, z);
if (chunkMapEntry == null) {
return; return;
} }
EntityTracker tracker = w.getEntityTracker(); final ArrayDeque<EntityPlayerMP> players = new ArrayDeque<>();
HashSet<EntityPlayerMP> players = new HashSet<>(); chunkMapEntry.hasPlayerMatching(input -> {
for (EntityPlayer player : w.playerEntities) { players.add(input);
if (player instanceof EntityPlayerMP) { return false;
if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { });
players.add((EntityPlayerMP) player); 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;
} }
if (players.size() == 0) { SPacketChunkData packet = new SPacketChunkData(nmsChunk, mask);
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());
for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet);
}
}
}
// Send chunks
SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535);
for (EntityPlayerMP player : players) { for (EntityPlayerMP player : players) {
player.connection.sendPacket(packet); 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) { } catch (Throwable e) {
MainUtil.handleError(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 @Override
public FaweChunk<Chunk> getFaweChunk(int x, int z) { public FaweChunk<Chunk> getFaweChunk(int x, int z) {
return new ForgeChunk_All(this, x, z); return new ForgeChunk_All(this, x, z);