Complete World Unloading/Deletion

Discussion in 'Plugin Development' started by Goldbattle, Oct 7, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Goldbattle

    So I need to be able to directly control the creation and deletion of worlds. The problems is, is that when I try to delete the folder in the main directory it leaves some files behind. I know from searching that it is possible(MV does it but i can't trace their code). I believe it has to do with the server opening the file stream to the region files and then not closing them on the world unload. I tried using a region unloader that somebody said worked but the folder is still not deleting all the way. I have some files in the region folder and the the session.lock file. I am unable to restart the server so I need to do it live.

    delete methods (open)
    Code:
    /**Deletes the world
    *
    * @param sender the player that sent the cmd
    * @param worldName the name of the world
    */
    public boolean deleteWorld(CommandSender sender,String worldName)
    {
    if(plugin.getServer().getWorld(worldName+"_nether") != null){
    delete(new File(plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile()+File.separator+worldName+"_nether"));
    }
    if(plugin.getServer().getWorld(worldName+"_the_end") != null){
    delete(new File(plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile()+File.separator+worldName+"_the_end"));
    }
    if(delete(new File(plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile()+File.separator+worldName))){
    //remove world
    plugin.removeLinkedWorld(worldName);
    //save config
    plugin.saveConfigFile();
    //done
    sender.sendMessage(ChatColor.BLUE +"Sucessfull");
    return true;
    }
    else
    sender.sendMessage(ChatColor.GOLD +"Failed, please let an admin know");
    return false;
     
    }
    //recusivlly delete everything in the world folder
    private static boolean delete(File folder)
    {
    if (folder.isDirectory())
    {
    for (File f : folder.listFiles())
    {
    if (!delete(f)) return false;
    }
    }
    return folder.delete();
    }
     
    /**Unloads the world
    *
    * @param sender the player that sent the cmd
    * @param worldName the name of the world
    */
    public boolean unloadLinkedWorld(CommandSender sender,String worldName)
    {
    World wrd = null;
    try{
    if((wrd =plugin.getServer().getWorld(worldName)) != null)
    {
    removePlayers(wrd);
    for(Chunk c: plugin.getServer().getWorld(worldName).getLoadedChunks())
    c.unload(false, true);
    plugin.getServer().unloadWorld(worldName,false);
    }
    if((wrd = plugin.getServer().getWorld(worldName+"_nether")) != null)
    {
    removePlayers(wrd);
    for(Chunk c: plugin.getServer().getWorld(worldName+"_nether").getLoadedChunks())
    c.unload(false, true);
    plugin.getServer().unloadWorld(worldName+"_nether",false);
    }
    if((wrd = plugin.getServer().getWorld(worldName+"_the_end")) != null)
    {
    removePlayers(wrd);
    for(Chunk c: plugin.getServer().getWorld(worldName+"_the_end").getLoadedChunks())
    c.unload(false, true);
    plugin.getServer().unloadWorld(worldName+"_the_end",false);
    }
    return true;
    }
    catch(Exception e){
    return false;
    }
     
    }
     
    private void removePlayers(World wrd){
    List<Player> temp = wrd.getPlayers();
    World newWrd = plugin.getServer().getWorlds().get(0);
    Location loc = newWrd.getSpawnLocation();
    for(Player p:temp){
    plugin.getServer().getPlayer(p.getName()).teleport(loc);
    }
    }


    region listener class (open)

    Code:
     
    import java.io.File;
    import java.io.RandomAccessFile;
    import java.lang.ref.SoftReference;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.logging.Level;
     
    import net.minecraft.server.RegionFile;
     
    import org.bukkit.World;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.Listener;
    import org.bukkit.event.world.WorldUnloadEvent;
     
    public class WReferenceListener implements Listener
    {
        @SuppressWarnings("rawtypes")
        private static HashMap regionfiles;
        private static Field rafField;
        private static WorldControl plugin;
     
        @SuppressWarnings("rawtypes")
        public WReferenceListener(WorldControl pl)
        {
            plugin=pl;
            try
            {
                Field a = net.minecraft.server.RegionFileCache.class.getDeclaredField("a");
                a.setAccessible(true);
                regionfiles = (HashMap) a.get(null);
                rafField = net.minecraft.server.RegionFile.class.getDeclaredField("c");
                rafField.setAccessible(true);
                plugin.getLogger().log(Level.INFO, "Successfully bound variable to region file cache.");
                plugin.getLogger().log(Level.INFO, "File references to unloaded worlds will be cleared!");
            }
            catch (Throwable t)
            {
                plugin.getLogger().log(Level.WARNING, "Failed to bind to region file cache.");
                plugin.getLogger().log(Level.WARNING, "Files will stay referenced after being unloaded!");
                t.printStackTrace();
            }
        }
     
        //MAIN WORLD UNLOAD LISTENER
        @EventHandler(priority = EventPriority.HIGHEST)
        public void onWorldUnload(WorldUnloadEvent event) {
            if (!event.isCancelled())
            {
                clearWorldReference(event.getWorld());
            }
        }
     
        //clears referaces so the world can be deleted
        @SuppressWarnings("rawtypes")
        public static boolean clearWorldReference(World world)
        {
            String worldname = world.getName();
            if (regionfiles == null) return false;
            if (rafField == null) return false;
            ArrayList<Object> removedKeys = new ArrayList<Object>();
            try
            {
                for (Object o : regionfiles.entrySet())
                {
                    Map.Entry e = (Map.Entry) o;
                    File f = (File) e.getKey();
                    if (f.toString().startsWith("." + File.separator + worldname)) {
                        SoftReference ref = (SoftReference) e.getValue();
                        try {
                            RegionFile file = (RegionFile) ref.get();
                            if (file != null) {
                                RandomAccessFile raf = (RandomAccessFile) rafField.get(file);
                                raf.close();
                                removedKeys.add(f);
                            }
                        }
                        catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                plugin.getLogger().log(Level.WARNING, "Exception while removing world reference for '" + worldname + "'!");
                ex.printStackTrace();
            }
            for (Object key : removedKeys)
            {
                regionfiles.remove(key);
            }
            return true;
     
        }
    }
    


    I call the unload world method that tps players out then unloads it. I then call the delete method. The region class thing I think was supposed to listen to see if the world unloaded then close the streams.


    Any help on how to ensure that all the files are deleted would be great! Thanks!
     
Thread Status:
Not open for further replies.

Share This Page