1
0
mirror of https://github.com/essentials/Essentials.git synced 2025-09-01 18:53:27 +02:00

Better Location code for lazy loading worlds

This fixes problems with worlds that are loaded after the Location object is created and should prevent memory leaks when a world is unloaded.
This commit is contained in:
snowleo
2012-01-11 04:36:57 +01:00
parent 703c5741bd
commit ef659d5f26
6 changed files with 300 additions and 27 deletions

View File

@@ -19,6 +19,7 @@ package com.earth2me.essentials;
import static com.earth2me.essentials.I18n._;
import com.earth2me.essentials.api.*;
import com.earth2me.essentials.craftbukkit.BetterLocation;
import com.earth2me.essentials.craftbukkit.ItemDupeFix;
import com.earth2me.essentials.listener.*;
import com.earth2me.essentials.perm.PermissionsHandler;
@@ -231,6 +232,10 @@ public class Essentials extends JavaPlugin implements IEssentials
pm.registerEvent(Type.ENTITY_EXPLODE, tntListener, Priority.High, this);
pm.registerEvent(Type.WORLD_LOAD, BetterLocation.getListener(), Priority.Monitor, this);
pm.registerEvent(Type.WORLD_UNLOAD, BetterLocation.getListener(), Priority.Monitor, this);
getServer().getScheduler().scheduleAsyncRepeatingTask(this, BetterLocation.getListener(), 200, 200);
final EssentialsTimer timer = new EssentialsTimer(this);
getServer().getScheduler().scheduleSyncRepeatingTask(this, timer, 1, 100);
Economy.setEss(this);
@@ -249,6 +254,7 @@ public class Essentials extends JavaPlugin implements IEssentials
i18n.onDisable();
Economy.setEss(null);
Trade.closeLog();
BetterLocation.cleanup();
}
@Override

View File

@@ -0,0 +1,254 @@
package com.earth2me.essentials.craftbukkit;
import java.lang.ref.WeakReference;
import java.util.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
public class BetterLocation extends Location
{
private transient String worldName;
private static BetterLocationListener listener = new BetterLocationListener();
public static BetterLocationListener getListener()
{
return listener;
}
public static void cleanup()
{
synchronized (listener.locationMap)
{
listener.locationMap.clear();
}
}
public BetterLocation(final String worldName, final double x, final double y, final double z)
{
super(Bukkit.getWorld(worldName), x, y, z);
this.worldName = worldName;
addToMap(this);
}
public BetterLocation(final String worldName, final double x, final double y,
final double z, final float yaw, final float pitch)
{
super(Bukkit.getWorld(worldName), x, y, z, yaw, pitch);
this.worldName = worldName;
addToMap(this);
}
public BetterLocation(final World world, final double x, final double y, final double z)
{
super(world, x, y, z);
if (world == null)
{
throw new WorldNotLoadedException();
}
this.worldName = world.getName();
addToMap(this);
}
public BetterLocation(final World world, final double x, final double y,
final double z, final float yaw, final float pitch)
{
super(world, x, y, z, yaw, pitch);
if (world == null)
{
throw new WorldNotLoadedException();
}
this.worldName = world.getName();
addToMap(this);
}
public BetterLocation(final Location location)
{
super(location.getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
if (location.getWorld() == null)
{
throw new WorldNotLoadedException();
}
this.worldName = location.getWorld().getName();
addToMap(this);
}
@Override
public World getWorld()
{
World world = super.getWorld();
if (world == null)
{
world = Bukkit.getWorld(worldName);
}
if (world == null)
{
throw new WorldNotLoadedException();
}
else
{
super.setWorld(world);
}
return world;
}
@Override
public void setWorld(final World world)
{
if (world == null)
{
throw new WorldNotLoadedException();
}
if (!world.getName().equals(this.worldName))
{
getListener().removeLocation(this);
this.worldName = world.getName();
addToMap(this);
}
super.setWorld(world);
}
public String getWorldName()
{
return worldName;
}
private void addToMap(final BetterLocation location)
{
synchronized (listener.locationMap)
{
List<WeakReference<Location>> locations = listener.locationMap.get(location.getWorldName());
if (locations == null)
{
locations = new LinkedList<WeakReference<Location>>();
listener.locationMap.put(location.getWorldName(), locations);
}
locations.add(new WeakReference<Location>(location));
}
}
public static class WorldNotLoadedException extends RuntimeException
{
public WorldNotLoadedException()
{
super("World is not loaded.");
}
}
public static class BetterLocationListener extends org.bukkit.event.world.WorldListener implements Runnable
{
private static Random random = new Random();
private final transient Map<String, List<WeakReference<Location>>> locationMap = new HashMap<String, List<WeakReference<Location>>>();
@Override
public void onWorldLoad(final WorldLoadEvent event)
{
final String worldName = event.getWorld().getName();
synchronized (locationMap)
{
final List<WeakReference<Location>> locations = locationMap.get(worldName);
if (locations != null)
{
for (final Iterator<WeakReference<Location>> it = locations.iterator(); it.hasNext();)
{
final WeakReference<Location> weakReference = it.next();
final Location loc = weakReference.get();
if (loc == null)
{
it.remove();
}
else
{
loc.setWorld(event.getWorld());
}
}
}
}
}
@Override
public void onWorldUnload(final WorldUnloadEvent event)
{
final String worldName = event.getWorld().getName();
synchronized (locationMap)
{
final List<WeakReference<Location>> locations = locationMap.get(worldName);
if (locations != null)
{
for (final Iterator<WeakReference<Location>> it = locations.iterator(); it.hasNext();)
{
final WeakReference<Location> weakReference = it.next();
final Location loc = weakReference.get();
if (loc == null)
{
it.remove();
}
else
{
loc.setWorld(null);
}
}
}
}
}
@Override
public void run()
{
synchronized (locationMap)
{
// Pick a world by random
final Collection<List<WeakReference<Location>>> allWorlds = locationMap.values();
final int randomPick = (allWorlds.isEmpty() ? 0 : random.nextInt(allWorlds.size()));
List<WeakReference<Location>> locations = null;
final Iterator<List<WeakReference<Location>>> iterator = allWorlds.iterator();
for (int i = 0; iterator.hasNext() && i < randomPick; i++)
{
iterator.next();
}
if (iterator.hasNext())
{
locations = iterator.next();
}
if (locations != null)
{
for (final Iterator<WeakReference<Location>> it = locations.iterator(); it.hasNext();)
{
final WeakReference<Location> weakReference = it.next();
final Location loc = weakReference.get();
if (loc == null)
{
it.remove();
}
}
}
}
}
private void removeLocation(final BetterLocation location)
{
final String worldName = location.getWorldName();
synchronized (locationMap)
{
final List<WeakReference<Location>> locations = locationMap.get(worldName);
if (locations != null)
{
for (final Iterator<WeakReference<Location>> it = locations.iterator(); it.hasNext();)
{
final WeakReference<Location> weakReference = it.next();
final Location loc = weakReference.get();
if (loc == null || loc == location)
{
it.remove();
}
}
}
}
}
}
}

View File

@@ -38,7 +38,7 @@ public class OfflineBedLocation
{
spawnWorld = cserver.getWorlds().get(0).getName();
}
return new Location(cserver.getWorld(spawnWorld), playerStorage.getInt("SpawnX"), playerStorage.getInt("SpawnY"), playerStorage.getInt("SpawnZ"));
return new BetterLocation(spawnWorld, playerStorage.getInt("SpawnX"), playerStorage.getInt("SpawnY"), playerStorage.getInt("SpawnZ"));
}
return null;
}

View File

@@ -1,15 +1,14 @@
package com.earth2me.essentials.storage;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.craftbukkit.BetterLocation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
@@ -281,12 +280,7 @@ public class BukkitConstructor extends Constructor
{
return null;
}
final World world = Bukkit.getWorld(worldName);
if (world == null)
{
return null;
}
return new Location(world, x, y, z, yaw, pitch);
return new BetterLocation(worldName, x, y, z, yaw, pitch);
}
return super.construct(node);
}

View File

@@ -1,5 +1,6 @@
package com.earth2me.essentials.storage;
import com.earth2me.essentials.craftbukkit.BetterLocation;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -329,7 +330,14 @@ public class YamlStorageWriter implements IStorageWriter
writer.println();
writeIndention(depth);
writer.print("world: ");
if (entry instanceof BetterLocation)
{
writeScalar(((BetterLocation)entry).getWorldName());
}
else
{
writeScalar(entry.getWorld().getName());
}
writeIndention(depth);
writer.print("x: ");
writeScalar(entry.getX());

View File

@@ -4,6 +4,7 @@ import com.earth2me.essentials.Util;
import com.earth2me.essentials.api.IEssentials;
import com.earth2me.essentials.api.ISettings;
import com.earth2me.essentials.api.InvalidNameException;
import com.earth2me.essentials.craftbukkit.BetterLocation;
import com.earth2me.essentials.craftbukkit.OfflineBedLocation;
import com.earth2me.essentials.storage.AsyncStorageObjectHolder;
import java.io.File;
@@ -31,7 +32,7 @@ public abstract class UserBase extends AsyncStorageObjectHolder<UserData> implem
Player.class, Entity.class, CommandSender.class, ServerOperator.class,
HumanEntity.class, ConfigurationSerializable.class, LivingEntity.class,
Permissible.class
}, excludes = IOfflinePlayer.class)
}, excludes = {IOfflinePlayer.class, OtherExcludes.class})
protected Player base;
protected transient OfflinePlayer offlinePlayer;
@@ -436,4 +437,14 @@ public abstract class UserBase extends AsyncStorageObjectHolder<UserData> implem
unlock();
}
}
@Override
public Location getLocation()
{
return new BetterLocation(base.getLocation());
}
public static interface OtherExcludes {
Location getLocation();
}
}