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
@ -18,7 +18,8 @@ Version 1.3.10-dev
|
|||||||
= Fixed admin chat being seen by everyone
|
= Fixed admin chat being seen by everyone
|
||||||
= Fixed issue with UTFDataFormatException occurring on occasion when trying to load Chunklets
|
= 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 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
|
Version 1.3.09
|
||||||
+ Added compatibility with AntiCheat (Which I highly recommend to prevent cheating)
|
+ Added compatibility with AntiCheat (Which I highly recommend to prevent cheating)
|
||||||
|
@ -224,7 +224,7 @@ public class BlockListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Remove metadata when broken
|
//Remove metadata when broken
|
||||||
if (mcMMO.placeStore.isTrue(block) && BlockChecks.shouldBeWatched(block)) {
|
if (BlockChecks.shouldBeWatched(block)) {
|
||||||
mcMMO.placeStore.setFalse(block);
|
mcMMO.placeStore.setFalse(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ import java.io.File;
|
|||||||
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.world.ChunkLoadEvent;
|
|
||||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||||
import org.bukkit.event.world.WorldLoadEvent;
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
import org.bukkit.event.world.WorldSaveEvent;
|
import org.bukkit.event.world.WorldSaveEvent;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
|
|
||||||
import com.gmail.nossr50.mcMMO;
|
import com.gmail.nossr50.mcMMO;
|
||||||
|
import com.gmail.nossr50.runnables.ChunkletUnloader;
|
||||||
|
|
||||||
public class WorldListener implements Listener {
|
public class WorldListener implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -31,13 +31,8 @@ public class WorldListener implements Listener {
|
|||||||
mcMMO.placeStore.saveWorld(event.getWorld());
|
mcMMO.placeStore.saveWorld(event.getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onChunkLoad(ChunkLoadEvent event) {
|
|
||||||
mcMMO.placeStore.chunkLoaded(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk().getWorld());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onChunkUnload(ChunkUnloadEvent event) {
|
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 net.shatteredlands.shatt.backup.ZipLibrary;
|
||||||
|
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
import org.bukkit.plugin.PluginManager;
|
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.locale.LocaleLoader;
|
||||||
import com.gmail.nossr50.party.PartyManager;
|
import com.gmail.nossr50.party.PartyManager;
|
||||||
import com.gmail.nossr50.runnables.BleedTimer;
|
import com.gmail.nossr50.runnables.BleedTimer;
|
||||||
|
import com.gmail.nossr50.runnables.ChunkletUnloader;
|
||||||
import com.gmail.nossr50.runnables.SaveTimer;
|
import com.gmail.nossr50.runnables.SaveTimer;
|
||||||
import com.gmail.nossr50.runnables.SkillMonitor;
|
import com.gmail.nossr50.runnables.SkillMonitor;
|
||||||
import com.gmail.nossr50.runnables.SpoutStart;
|
import com.gmail.nossr50.runnables.SpoutStart;
|
||||||
@ -183,12 +183,14 @@ public class mcMMO extends JavaPlugin {
|
|||||||
|
|
||||||
//Schedule Spout Activation 1 second after start-up
|
//Schedule Spout Activation 1 second after start-up
|
||||||
scheduler.scheduleSyncDelayedTask(this, new SpoutStart(this), 20);
|
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);
|
scheduler.scheduleSyncRepeatingTask(this, new SaveTimer(this), 0, configInstance.getSaveInterval() * 1200);
|
||||||
//Regen & Cooldown timer (Runs every second)
|
//Regen & Cooldown timer (Runs every second)
|
||||||
scheduler.scheduleSyncRepeatingTask(this, new SkillMonitor(this), 0, 20);
|
scheduler.scheduleSyncRepeatingTask(this, new SkillMonitor(this), 0, 20);
|
||||||
//Bleed timer (Runs every two seconds)
|
//Bleed timer (Runs every two seconds)
|
||||||
scheduler.scheduleSyncRepeatingTask(this, new BleedTimer(), 0, 40);
|
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();
|
registerCommands();
|
||||||
|
|
||||||
@ -224,10 +226,6 @@ public class mcMMO extends JavaPlugin {
|
|||||||
|
|
||||||
// Get our ChunkletManager
|
// Get our ChunkletManager
|
||||||
placeStore = ChunkletManagerFactory.getChunkletManager();
|
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;
|
import org.bukkit.block.Block;
|
||||||
|
|
||||||
public interface ChunkletManager {
|
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
|
* Informs the ChunkletManager a chunk is loaded, it should load appropriate data
|
||||||
*
|
*
|
||||||
* @param cx Chunk X coordiate that is loaded
|
* @param cx Chunk X coordinate that is loaded
|
||||||
* @param cz Chunk Z coordiate that is loaded
|
* @param cz Chunk Z coordinate that is loaded
|
||||||
* @param world World that the chunk was loaded in
|
* @param world World that the chunk was loaded in
|
||||||
*/
|
*/
|
||||||
public void chunkLoaded(int cx, int cz, World world);
|
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
|
* Informs the ChunkletManager a chunk is unloaded, it should unload and save appropriate data
|
||||||
*
|
*
|
||||||
* @param cx Chunk X coordiate that is unloaded
|
* @param cx Chunk X coordinate that is unloaded
|
||||||
* @param cz Chunk Z coordiate that is unloaded
|
* @param cz Chunk Z coordinate that is unloaded
|
||||||
* @param world World that the chunk was unloaded in
|
* @param world World that the chunk was unloaded in
|
||||||
*/
|
*/
|
||||||
public void chunkUnloaded(int cx, int cz, World world);
|
public void chunkUnloaded(int cx, int cz, World world);
|
||||||
|
@ -12,7 +12,6 @@ import java.io.UTFDataFormatException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
|
||||||
@ -22,26 +21,42 @@ public class HashChunkletManager implements ChunkletManager {
|
|||||||
private HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
|
private HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
|
||||||
|
|
||||||
@Override
|
@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 dataDir = new File(world.getWorldFolder(), "mcmmo_data");
|
||||||
File cxDir = new File(dataDir, "" + cx);
|
File cxDir = new File(dataDir, "" + cx);
|
||||||
if(!cxDir.exists()) return;
|
if(!cxDir.exists()) return;
|
||||||
File czDir = new File(cxDir, "" + cz);
|
File czDir = new File(cxDir, "" + cz);
|
||||||
if(!czDir.exists()) return;
|
if(!czDir.exists()) return;
|
||||||
|
File yFile = new File(czDir, "" + cy);
|
||||||
|
if(!yFile.exists()) return;
|
||||||
|
|
||||||
for(int y = 0; y < 4; y++) {
|
ChunkletStore in = deserializeChunkletStore(yFile);
|
||||||
File yFile = new File(czDir, "" + y);
|
if(in != null) {
|
||||||
if(!yFile.exists()) {
|
store.put(world.getName() + "," + cx + "," + cz + "," + cy, in);
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
ChunkletStore in = deserializeChunkletStore(yFile);
|
|
||||||
if(in != null) {
|
|
||||||
store.put(world.getName() + "," + cx + "," + cz + "," + y, 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
|
@Override
|
||||||
public void chunkUnloaded(int cx, int cz, World world) {
|
public void chunkUnloaded(int cx, int cz, World world) {
|
||||||
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
|
File dataDir = new File(world.getWorldFolder(), "mcmmo_data");
|
||||||
@ -97,9 +112,9 @@ public class HashChunkletManager implements ChunkletManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadWorld(World world) {
|
public void loadWorld(World world) {
|
||||||
for(Chunk chunk : world.getLoadedChunks()) {
|
//for(Chunk chunk : world.getLoadedChunks()) {
|
||||||
this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
|
// this.chunkLoaded(chunk.getX(), chunk.getZ(), world);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,7 +137,15 @@ public class HashChunkletManager implements ChunkletManager {
|
|||||||
int cx = x / 16;
|
int cx = x / 16;
|
||||||
int cz = z / 16;
|
int cz = z / 16;
|
||||||
int cy = y / 64;
|
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);
|
ChunkletStore check = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
|
||||||
int ix = Math.abs(x) % 16;
|
int ix = Math.abs(x) % 16;
|
||||||
@ -147,13 +170,20 @@ public class HashChunkletManager implements ChunkletManager {
|
|||||||
int iz = Math.abs(z) % 16;
|
int iz = Math.abs(z) % 16;
|
||||||
int iy = Math.abs(y) % 64;
|
int iy = Math.abs(y) % 64;
|
||||||
|
|
||||||
ChunkletStore cStore;
|
String key = world.getName() + "," + cx + "," + cz + "," + cy;
|
||||||
if(!store.containsKey(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();
|
cStore = ChunkletStoreFactory.getChunkletStore();
|
||||||
|
|
||||||
store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore);
|
store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
cStore = store.get(world.getName() + "," + cx + "," + cz + "," + cy);
|
|
||||||
cStore.setTrue(ix, iy, iz);
|
cStore.setTrue(ix, iy, iz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,12 +202,18 @@ public class HashChunkletManager implements ChunkletManager {
|
|||||||
int iz = Math.abs(z) % 16;
|
int iz = Math.abs(z) % 16;
|
||||||
int iy = Math.abs(y) % 64;
|
int iy = Math.abs(y) % 64;
|
||||||
|
|
||||||
ChunkletStore cStore;
|
String key = world.getName() + "," + cx + "," + cz + "," + cy;
|
||||||
if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) {
|
|
||||||
return; // No need to make a store for something we will be setting to false
|
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);
|
cStore.setFalse(ix, iy, iz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,11 @@ import org.bukkit.block.Block;
|
|||||||
* Useful for turning off Chunklets without actually doing much work
|
* Useful for turning off Chunklets without actually doing much work
|
||||||
*/
|
*/
|
||||||
public class NullChunkletManager implements ChunkletManager {
|
public class NullChunkletManager implements ChunkletManager {
|
||||||
|
@Override
|
||||||
|
public void loadChunklet(int cx, int cy, int cz, World world) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void chunkLoaded(int cx, int cz, World world) {
|
public void chunkLoaded(int cx, int cz, World world) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user