Chunklets optimization

This commit is contained in:
bm01 2012-06-28 23:20:03 +02:00
parent 189f23f407
commit c88ada489a
8 changed files with 140 additions and 42 deletions

View File

@ -18,7 +18,8 @@ Version 1.3.10-dev
= Fixed admin chat being seen by everyone
= Fixed issue with UTFDataFormatException occurring on occasion when trying to load Chunklets
= Fixed ArrayIndexOutOfBounds error caused when trying to use /xplock after logging in but before gaining XP
= Fixed custom tools not properly respecting the Ability_Enabled flag.
= Fixed custom tools not properly respecting the Ability_Enabled flag.
! Optimized how player placed blocks are tracked
Version 1.3.09
+ Added compatibility with AntiCheat (Which I highly recommend to prevent cheating)

View File

@ -224,7 +224,7 @@ public class BlockListener implements Listener {
}
//Remove metadata when broken
if (mcMMO.placeStore.isTrue(block) && BlockChecks.shouldBeWatched(block)) {
if (BlockChecks.shouldBeWatched(block)) {
mcMMO.placeStore.setFalse(block);
}

View File

@ -4,13 +4,13 @@ import java.io.File;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.ChunkletUnloader;
public class WorldListener implements Listener {
@EventHandler
@ -31,13 +31,8 @@ public class WorldListener implements Listener {
mcMMO.placeStore.saveWorld(event.getWorld());
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
mcMMO.placeStore.chunkLoaded(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk().getWorld());
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
mcMMO.placeStore.chunkUnloaded(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk().getWorld());
ChunkletUnloader.addToList(event.getChunk());
}
}

View File

@ -9,7 +9,6 @@ import java.util.List;
import net.shatteredlands.shatt.backup.ZipLibrary;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
@ -66,6 +65,7 @@ import com.gmail.nossr50.listeners.WorldListener;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.party.PartyManager;
import com.gmail.nossr50.runnables.BleedTimer;
import com.gmail.nossr50.runnables.ChunkletUnloader;
import com.gmail.nossr50.runnables.SaveTimer;
import com.gmail.nossr50.runnables.SkillMonitor;
import com.gmail.nossr50.runnables.SpoutStart;
@ -183,12 +183,14 @@ public class mcMMO extends JavaPlugin {
//Schedule Spout Activation 1 second after start-up
scheduler.scheduleSyncDelayedTask(this, new SpoutStart(this), 20);
//Periodic save timer (Saves every 10 minutes)
//Periodic save timer (Saves every 10 minutes by default)
scheduler.scheduleSyncRepeatingTask(this, new SaveTimer(this), 0, configInstance.getSaveInterval() * 1200);
//Regen & Cooldown timer (Runs every second)
scheduler.scheduleSyncRepeatingTask(this, new SkillMonitor(this), 0, 20);
//Bleed timer (Runs every two seconds)
scheduler.scheduleSyncRepeatingTask(this, new BleedTimer(), 0, 40);
//Chunklet unloader (Runs every 20 seconds by default)
scheduler.scheduleSyncRepeatingTask(this, new ChunkletUnloader(), 0, ChunkletUnloader.RUN_INTERVAL * 20);
registerCommands();
@ -224,10 +226,6 @@ public class mcMMO extends JavaPlugin {
// Get our ChunkletManager
placeStore = ChunkletManagerFactory.getChunkletManager();
for (World world : getServer().getWorlds()) {
placeStore.loadWorld(world);
}
}
/**

View File

@ -0,0 +1,53 @@
package com.gmail.nossr50.runnables;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Chunk;
import com.gmail.nossr50.mcMMO;
public class ChunkletUnloader implements Runnable {
private static Map<Chunk, Integer> unloadedChunks = new HashMap<Chunk, Integer>();
private static int minimumInactiveTime = 60; //Should be a multiple of RUN_INTERVAL for best performance
public static int RUN_INTERVAL = 20;
public static void addToList(Chunk chunk) {
//Unfortunately we can't use Map.contains() because Chunks are always new objects
//This method isn't efficient enough for me
for (Chunk otherChunk : unloadedChunks.keySet()) {
if (chunk.getX() == otherChunk.getX() && chunk.getZ() == otherChunk.getZ()) {
return;
}
}
unloadedChunks.put(chunk, 0);
}
@Override
public void run() {
for (Iterator<Entry<Chunk, Integer>> it = unloadedChunks.entrySet().iterator() ; it.hasNext() ; ) {
Entry<Chunk, Integer> entry = it.next();
Chunk chunk = entry.getKey();
if (!chunk.isLoaded()) {
int inactiveTime = entry.getValue() + RUN_INTERVAL;
//Chunklets are unloaded only if their chunk has been unloaded for minimumInactiveTime
if (inactiveTime >= minimumInactiveTime) {
mcMMO.placeStore.chunkUnloaded(chunk.getX(), chunk.getZ(), chunk.getWorld());
it.remove();
continue;
}
unloadedChunks.put(entry.getKey(), inactiveTime);
}
else {
//Just remove the entry if the chunk has been reloaded.
it.remove();
}
}
}
}

View File

@ -4,11 +4,21 @@ import org.bukkit.World;
import org.bukkit.block.Block;
public interface ChunkletManager {
/**
* Loads a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be loaded
* @param cy Chunklet Y coordinate that needs to be loaded
* @param cz Chunklet Z coordinate that needs to be loaded
* @param world World that the chunklet needs to be loaded in
*/
public void loadChunklet(int cx, int cy, int cz, World world);
/**
* Informs the ChunkletManager a chunk is loaded, it should load appropriate data
*
* @param cx Chunk X coordiate that is loaded
* @param cz Chunk Z coordiate that is loaded
* @param cx Chunk X coordinate that is loaded
* @param cz Chunk Z coordinate that is loaded
* @param world World that the chunk was loaded in
*/
public void chunkLoaded(int cx, int cz, World world);
@ -16,8 +26,8 @@ public interface ChunkletManager {
/**
* Informs the ChunkletManager a chunk is unloaded, it should unload and save appropriate data
*
* @param cx Chunk X coordiate that is unloaded
* @param cz Chunk Z coordiate that is unloaded
* @param cx Chunk X coordinate that is unloaded
* @param cz Chunk Z coordinate that is unloaded
* @param world World that the chunk was unloaded in
*/
public void chunkUnloaded(int cx, int cz, World world);

View File

@ -12,7 +12,6 @@ import java.io.UTFDataFormatException;
import java.util.HashMap;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
@ -22,26 +21,42 @@ public class HashChunkletManager implements ChunkletManager {
private HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
@Override
public void chunkLoaded(int cx, int cz, World world) {
public void loadChunklet(int cx, int cy, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
File cxDir = new File(dataDir, "" + cx);
if(!cxDir.exists()) return;
File czDir = new File(cxDir, "" + cz);
if(!czDir.exists()) return;
File yFile = new File(czDir, "" + cy);
if(!yFile.exists()) return;
for(int y = 0; y < 4; y++) {
File yFile = new File(czDir, "" + y);
if(!yFile.exists()) {
continue;
} else {
ChunkletStore in = deserializeChunkletStore(yFile);
if(in != null) {
store.put(world.getName() + "," + cx + "," + cz + "," + y, in);
}
}
ChunkletStore in = deserializeChunkletStore(yFile);
if(in != null) {
store.put(world.getName() + "," + cx + "," + cz + "," + cy, in);
}
}
@Override
public void chunkLoaded(int cx, int cz, World world) {
//File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
//File cxDir = new File(dataDir, "" + cx);
//if(!cxDir.exists()) return;
//File czDir = new File(cxDir, "" + cz);
//if(!czDir.exists()) return;
//for(int y = 0; y < 4; y++) {
// File yFile = new File(czDir, "" + y);
// if(!yFile.exists()) {
// continue;
// } else {
// ChunkletStore in = deserializeChunkletStore(yFile);
// if(in != null) {
// store.put(world.getName() + "," + cx + "," + cz + "," + y, in);
// }
// }
//}
}
@Override
public void chunkUnloaded(int cx, int cz, World world) {
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
@ -97,9 +112,9 @@ public class HashChunkletManager implements ChunkletManager {
@Override
public void loadWorld(World world) {
for(Chunk chunk : world.getLoadedChunks()) {
this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
}
//for(Chunk chunk : world.getLoadedChunks()) {
// this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
//}
}
@Override
@ -122,7 +137,15 @@ public class HashChunkletManager implements ChunkletManager {
int cx = x / 16;
int cz = z / 16;
int cy = y / 64;
if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) return false;
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
if (!store.containsKey(key)) {
return false;
}
ChunkletStore check = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
int ix = Math.abs(x) % 16;
@ -147,13 +170,20 @@ public class HashChunkletManager implements ChunkletManager {
int iz = Math.abs(z) % 16;
int iy = Math.abs(y) % 64;
ChunkletStore cStore;
if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) {
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
ChunkletStore cStore = store.get(key);
if (cStore == null) {
cStore = ChunkletStoreFactory.getChunkletStore();
store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore);
}
cStore = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
cStore.setTrue(ix, iy, iz);
}
@ -172,12 +202,18 @@ public class HashChunkletManager implements ChunkletManager {
int iz = Math.abs(z) % 16;
int iy = Math.abs(y) % 64;
ChunkletStore cStore;
if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) {
return; // No need to make a store for something we will be setting to false
String key = world.getName() + "," + cx + "," + cz + "," + cy;
if (!store.containsKey(key)) {
loadChunklet(cx, cy, cz, world);
}
ChunkletStore cStore = store.get(key);
if (cStore == null) {
return; //No need to make a store for something we will be setting to false
}
cStore = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
cStore.setFalse(ix, iy, iz);
}

View File

@ -9,6 +9,11 @@ import org.bukkit.block.Block;
* Useful for turning off Chunklets without actually doing much work
*/
public class NullChunkletManager implements ChunkletManager {
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {
return;
}
@Override
public void chunkLoaded(int cx, int cz, World world) {
return;