ExceptionWorldConflict

Discussion in 'Spigot Plugin Development' started by Rixterz, May 28, 2017.

  1. Hi,

    The setup is as follows;

    • When the server starts, the clean map is copied to sessionWorld.
    • sessionWorld is loaded and used for the game.

    However, when the last player leaves so nobody is online, I get tons of console spam saying:

    Code (Text):
    The save for world located at .\sessionWorld is being accessed from another location, aborting
    ... at WorldNBTStorage.checkSession
    Here's my code:

    Code (Text):
    package utils;

    import java.io.File;

    import org.apache.commons.io.FileUtils;
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.World.Environment;
    import org.bukkit.WorldCreator;

    public class WorldManager
    {
        private File cleanWorld;
        private File sessionWorld;
       
        public WorldManager()
        {
            cleanWorld = new File("./cleanWorld");
            sessionWorld = new File("./sessionWorld");
        }
       
        public void createSessionWorld()
        {
            try
            {
                FileUtils.copyDirectory(cleanWorld, sessionWorld);
            }
            catch(Exception e)
            {
                Bukkit.getConsoleSender().sendMessage(ChatColor.RED + " !!! CRITICAL: Could not copy new world to session; " + e.getMessage());
                //Utils.s.shutdown();
                return;
            }
           
            Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Created new session world");
           
            WorldCreator creator = new WorldCreator("sessionWorld");
            creator.environment(Environment.THE_END);
            creator.createWorld();
           
            Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Loaded new session world");
           
            LocationUtils.end = Bukkit.getWorld("sessionWorld");
        }
       
    }
     
    Code (Text):

        @EventHandler(priority = EventPriority.HIGHEST)
        public void onWorldLoad(WorldLoadEvent e) // load chunks from (7, 7) to (-8, -8)
        {
            for(int x = 7; x >= -8; x--)
            {
                for(int z = 7; z >= -8; z--)
                {
                    e.getWorld().loadChunk(x, z);
                }
            }
        }
       
        @EventHandler(priority = EventPriority.HIGHEST)
        public void onWorldUnload(WorldUnloadEvent e) // don't let the world unload
        {
            e.setCancelled(true);
        }
       
        @EventHandler(priority = EventPriority.HIGHEST)
        public void onChunkUnload(ChunkUnloadEvent e) // don't let any chunks unload between (7, 7) and (-8, -8)
        {
            Chunk c = e.getChunk();
           
            int cX = c.getX();
            int cZ = c.getZ();
           
            if(cX <= 7 && cX >= -8 && cZ <= 7 && cZ >= -8) e.setCancelled(true);
        }
       
     
  2. ScarabCoder

    ScarabCoder Retired Resource Staff
    Retired

    Try something like:
    Unload the Bukkit world (if you don't, you'll get those session lock errors)
    Delete the world folder
    Copy the clean world folder so as to replace the one that was deleted
    Load the Bukkit world.

    Here's my method for resetting an arena:
    Code (Text):
    public void resetWorld(){
            GameAPI.sendDebugMessage("Resetting arena world " + this.getWorld().getName() + "!", GameAPI.getPlugin());
            File worldFolder = world.getWorldFolder();
            String worldName = world.getName();
            for(Player player : this.getWorld().getPlayers()){
                player.kickPlayer("World resetting...");
            }
            GameAPI.sendDebugMessage("Unloading world " + worldName + "...", GameAPI.getPlugin());
            Bukkit.unloadWorld(worldName, true);
            Bukkit.getScheduler().scheduleSyncDelayedTask(GameAPI.getPlugin(), new Runnable(){

                @Override
                public void run() {
                    try {
                        GameAPI.sendDebugMessage("Deleting world " + worldName + "...", GameAPI.getPlugin());
                        FileUtils.deleteDirectory(worldFolder);
                        GameAPI.sendDebugMessage("Copying default world from GameWorlds/" + worldName + "...", GameAPI.getPlugin());
                        FileUtils.copyDirectory(new File(GameAPI.getGameWorldsFolder(), worldName), new File(GameAPI.getGameWorldsFolder().getParentFile(), worldName));
                        GameAPI.sendDebugMessage("Loading world " + worldName + " on server...", GameAPI.getPlugin());
                        WorldCreator creator = new WorldCreator(worldName);
                        creator.generatorSettings("3;minecraft:air;127;");
                        creator.type(WorldType.FLAT);
                        world = Bukkit.createWorld(creator);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
               
            }, 20);
           
           
        }
     
    • Winner Winner x 1
  3. Thanks, it seems to have worked! I wasn't unloading the world in onDisable(), so when I was reloading and the player joined, I suppose it was loading chunks which hadn't been unloaded by the previous session.

    EDIT: That's caused horrible side-effects, one time only it seems. The normal end has now merged with my custom one. Good thing I had a back up
     
    #3 Rixterz, May 28, 2017
    Last edited: May 28, 2017
  4. ScarabCoder

    ScarabCoder Retired Resource Staff
    Retired

    Yeah, expect a large amount of world corruption until you master it. After a certain point, though, it's mostly safe.
     
  5. Ok after that one mishap with world corruption, and no changes whatsoever to the code that caused it, it hasn't happened again after many server restarts. I think it may have been due to the old code doing weird things which only a manual replacement could sort out.

    Here's how I do it:

    1) onEnable: copy cleanWorld to sessionWorld. If this fails, shut down the server as all sorts of errors will arise with nullptrs when you create Locations etc.
    2) Load sessionWorld with WorldCreator using THE_END as the environment
    • Use that world for all Location instances
    3) onDisable: unload and delete sessionWorld. If this fails, it's no big deal as onEnable will replace it, but you will likely get a lot of console spam about the world being accessed from another location as I did

    .. and, as always, keep a backup because having a quarter of your map replaced with the normal end is very upsetting otherwise
     
  6. ScarabCoder

    ScarabCoder Retired Resource Staff
    Retired

    You can use vanilla world generators; for example, the flatworld generator. It enables you to not generate anything at all:
    Code (Text):
    WorldCreator creator = new WorldCreator(worldName);
    creator.generatorSettings("3;minecraft:air;127;");
    creator.type(WorldType.FLAT);
    world = Bukkit.createWorld(creator);
     
    • Useful Useful x 1
  7. That's quite useful, I'll definitely use that in the future