mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2024-11-26 15:16:45 +01:00
Chunklets optimization
This commit is contained in:
parent
189f23f407
commit
c88ada489a
@ -19,6 +19,7 @@ Version 1.3.10-dev
|
||||
= 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.
|
||||
! Optimized how player placed blocks are tracked
|
||||
|
||||
Version 1.3.09
|
||||
+ Added compatibility with AntiCheat (Which I highly recommend to prevent cheating)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user