/**
* @author Aleksey Terzi
*
*/
package com.lishid.orebfuscator.obfuscation;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bukkit.World;
import org.bukkit.entity.Player;
import com.lishid.orebfuscator.Orebfuscator;
import com.lishid.orebfuscator.cache.ObfuscatedCachedChunk;
import com.lishid.orebfuscator.cache.ObfuscatedDataCache;
import com.lishid.orebfuscator.config.ProximityHiderConfig;
import com.lishid.orebfuscator.nms.IChunkManager;
import com.lishid.orebfuscator.types.ChunkCoord;
public class ChunkReloader extends Thread implements Runnable {
private static final Map<World, HashSet<ChunkCoord>> loadedChunks = new WeakHashMap<World, HashSet<ChunkCoord>>();
private static final Map<World, HashSet<ChunkCoord>> unloadedChunks = new WeakHashMap<World, HashSet<ChunkCoord>>();
private static final Map<World, HashSet<ChunkCoord>> chunksForReload = new WeakHashMap<World, HashSet<ChunkCoord>>();
private static ChunkReloader thread = new ChunkReloader();
private long lastExecute = System.currentTimeMillis();
private AtomicBoolean kill = new AtomicBoolean(false);
public static void Load() {
if (thread == null || thread.isInterrupted() || !thread.isAlive()) {
thread = new ChunkReloader();
thread.setName("Orebfuscator ChunkReloader Thread");
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
}
public static void terminate() {
if (thread != null) {
thread.kill.set(true);
}
}
public void run() {
HashSet<ChunkCoord> localLoadedChunks = new HashSet<ChunkCoord>();
HashSet<ChunkCoord> localUnloadedChunks = new HashSet<ChunkCoord>();
Map<World, HashSet<ChunkCoord>> localChunksForReload = new WeakHashMap<World, HashSet<ChunkCoord>>();
ArrayList<World> localWorldsToCheck = new ArrayList<World>();
while (!this.isInterrupted() && !kill.get()) {
try {
// Wait until necessary
long timeWait = lastExecute + Orebfuscator.config.getChunkReloaderRate() - System.currentTimeMillis();
lastExecute = System.currentTimeMillis();
if (timeWait > 0) {
Thread.sleep(timeWait);
}
if (!Orebfuscator.config.isUseChunkReloader()) {
return;
}
synchronized (loadedChunks) {
localWorldsToCheck.addAll(loadedChunks.keySet());
}
for(World world : localWorldsToCheck) {
HashSet<ChunkCoord> localChunksForReloadForWorld = localChunksForReload.get(world);
if(localChunksForReloadForWorld == null) {
localChunksForReload.put(world, localChunksForReloadForWorld = new HashSet<ChunkCoord>());
}
synchronized (chunksForReload) {
HashSet<ChunkCoord> chunksForReloadForWorld = chunksForReload.get(world);
if(chunksForReloadForWorld != null && !chunksForReloadForWorld.isEmpty()) {
localChunksForReloadForWorld.addAll(chunksForReloadForWorld);
chunksForReloadForWorld.clear();
}
}
synchronized (unloadedChunks) {
HashSet<ChunkCoord> unloadedChunksForWorld = unloadedChunks.get(world);
if(unloadedChunksForWorld != null && !unloadedChunksForWorld.isEmpty()) {
localUnloadedChunks.addAll(unloadedChunksForWorld);
unloadedChunksForWorld.clear();
}
}
for(ChunkCoord unloadedChunk : localUnloadedChunks) {
localChunksForReloadForWorld.remove(unloadedChunk);
}
localUnloadedChunks.clear();
synchronized (loadedChunks) {
HashSet<ChunkCoord> loadedChunksForWorld = loadedChunks.get(world);
if(loadedChunksForWorld != null && !loadedChunksForWorld.isEmpty()) {
localLoadedChunks.addAll(loadedChunksForWorld);
loadedChunksForWorld.clear();
}
}
for(ChunkCoord loadedChunk : localLoadedChunks) {
ChunkCoord chunk1 = new ChunkCoord(loadedChunk.x - 1, loadedChunk.z);
ChunkCoord chunk2 = new ChunkCoord(loadedChunk.x + 1, loadedChunk.z);
ChunkCoord chunk3 = new ChunkCoord(loadedChunk.x, loadedChunk.z - 1);
ChunkCoord chunk4 = new ChunkCoord(loadedChunk.x, loadedChunk.z + 1);
localChunksForReloadForWorld.add(chunk1);
localChunksForReloadForWorld.add(chunk2);
localChunksForReloadForWorld.add(chunk3);
localChunksForReloadForWorld.add(chunk4);
}
localLoadedChunks.clear();
if(!localChunksForReloadForWorld.isEmpty()) {
scheduleReloadChunks(world, localChunksForReloadForWorld);
localChunksForReloadForWorld.clear();
}
}
localWorldsToCheck.clear();
} catch (Exception e) {
Orebfuscator.log(e);
}
}
}
private static void scheduleReloadChunks(final World world, HashSet<ChunkCoord> chunksForReloadForWorld) {
File cacheFolder = new File(ObfuscatedDataCache.getCacheFolder(), world.getName());
final IChunkManager chunkManager = Orebfuscator.nms.getChunkManager(world);
for(final ChunkCoord chunk : chunksForReloadForWorld) {
if(Orebfuscator.config.isUseCache()) {
ObfuscatedCachedChunk cache = new ObfuscatedCachedChunk(cacheFolder, chunk.x, chunk.z);
if(cache.getHash() != 0) continue;
}
//Orebfuscator.log("Add chunk x = " + chunk.x + ", z = " + chunk.z + " to schedule for reload for players");/*debug*/
Orebfuscator.instance.runTask(new Runnable() {
public void run() {
runReloadChunk(world, chunkManager, chunk);
}
});
}
}
private static void runReloadChunk(World world, IChunkManager chunkManager, ChunkCoord chunk) {
//Reload chunk for players
HashSet<Player> affectedPlayers = new HashSet<Player>();
if(!world.isChunkLoaded(chunk.x, chunk.z)) return;
if(!chunkManager.resendChunk(chunk.x, chunk.z, affectedPlayers)) {
synchronized (chunksForReload) {
HashSet<ChunkCoord> chunksForReloadForWorld = chunksForReload.get(world);
if(chunksForReloadForWorld == null) {
chunksForReload.put(world, chunksForReloadForWorld = new HashSet<ChunkCoord>());
}
chunksForReloadForWorld.add(chunk);
}
//Orebfuscator.log("Is not possible to reload chunk x = " + chunk.x + ", z = " + chunk.z + ", add for later reload");/*debug*/
} else {
//Orebfuscator.log("Force chunk x = " + chunk.x + ", z = " + chunk.z + " to reload for players");/*debug*/
}
if(affectedPlayers.size() > 0) {
ProximityHiderConfig proximityHider = Orebfuscator.configManager.getWorld(world).getProximityHiderConfig();
if(proximityHider.isEnabled()) {
ProximityHider.addPlayersToReload(affectedPlayers);
}
}
}
private static void restart() {
synchronized (thread) {
if (thread.isInterrupted() || !thread.isAlive()) {
ChunkReloader.Load();
}
}
}
public static void addLoadedChunk(World world, int chunkX, int chunkZ) {
if (!Orebfuscator.config.isEnabled() // Plugin enabled
|| !Orebfuscator.config.isUseChunkReloader()
|| !Orebfuscator.configManager.getWorld(world).isEnabled() // World not enabled
)
{
return;
}
restart();
synchronized (loadedChunks) {
HashSet<ChunkCoord> chunks = loadedChunks.get(world);
if(chunks == null) {
loadedChunks.put(world, chunks = new HashSet<ChunkCoord>());
}
chunks.add(new ChunkCoord(chunkX, chunkZ));
}
}
public static void addUnloadedChunk(World world, int chunkX, int chunkZ) {
if (!Orebfuscator.config.isEnabled() // Plugin enabled
|| !Orebfuscator.config.isUseChunkReloader()
|| !Orebfuscator.configManager.getWorld(world).isEnabled() // World not enabled
)
{
return;
}
restart();
synchronized (unloadedChunks) {
HashSet<ChunkCoord> chunks = unloadedChunks.get(world);
if(chunks == null) {
unloadedChunks.put(world, chunks = new HashSet<ChunkCoord>());
}
chunks.add(new ChunkCoord(chunkX, chunkZ));
}
}
}