소스 검색

Chunklets optimization

bm01 13 년 전
부모
커밋
c88ada489a

+ 2 - 1
Changelog.txt

@@ -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)

+ 1 - 1
src/main/java/com/gmail/nossr50/listeners/BlockListener.java

@@ -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);
         }
 

+ 2 - 7
src/main/java/com/gmail/nossr50/listeners/WorldListener.java

@@ -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());
     }
 }

+ 4 - 6
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -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);
-        }
     }
 
     /**

+ 53 - 0
src/main/java/com/gmail/nossr50/runnables/ChunkletUnloader.java

@@ -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();
+            }
+        }
+    }
+}

+ 14 - 4
src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletManager.java

@@ -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);

+ 59 - 23
src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java

@@ -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);
     }
 

+ 5 - 0
src/main/java/com/gmail/nossr50/util/blockmeta/NullChunkletManager.java

@@ -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;