Solved Creating an instance of a dungeon

Discussion in 'Plugin Development' started by Smegel, Nov 19, 2017.

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

    Smegel

    Hi,

    I want to let users create a portal and once they enter, they end up in a dungeon that is linked to that portal.
    How would I generate the dungeon? Should I save a selection of blocks that represent the dungeon, copy a whole world where the dungeon is located in, or not create a new world at all?

    I would like to know the best way to do this
     
  2. Offline

    MightyOne

    what is the purpose of a dungeon, and how shall it look like? where shall it be? I mean it is unclear what you mean with dungeon.
     
  3. Offline

    Smegel

    A structure. that's all I really want to generate/copy. I don't need world generation, or anything else.
     
  4. Offline

    CraftCreeper6

    @Smegel
    Will the dungeon be procedural?
     
  5. Offline

    Smegel

    @CraftCreeper6

    I want each instance of the dungeon to be seperated from the rest and deleted after completion.

    Currently I am copying the world of the dungeon (the template) and using the copied version so that the template will always remain untouched and ready for any new instances. This works, but I'm worried about the performance. I am copying a world each time a player has found a dungeon. Is this bad?
     
  6. Offline

    CraftCreeper6

    @Smegel

    Maybe not have a world for each dungeon. Have one world for all the dungeons and instantiate a new copy in an unoccupied location and teleport them there.

    OR

    Have a world for each dungeon, but keep track of the blocks that have been destroyed / changed and then revert them using a loop after the player leaves. (You can have more than one dungeon in the same world still)

    Understand?
     
    Smegel likes this.
  7. Offline

    Smegel

    @CraftCreeper6

    I understand, although I'll leave this idea for now. I'm still not sure about performance, even with your method (copying the blocks instead of the whole world).
     
  8. Offline

    CraftCreeper6

    @Smegel

    Well with the second method you don't necessarily have to copy the blocks. Just replace them. Which wouldn't be a difficult task.

    This would *probably* be the best in terms of performance.
     
  9. Offline

    Smegel

    @CraftCreeper6

    I'm not thinking of another method, which I don't know is possible or not;
    What if I create another server that is not joinable, that does all the heavy stuff such as generating worlds, copying worlds, data storing etc in the background? That sounds easy, although would there be a way to make the two servers communicate with eachother?
     
  10. Offline

    timtower Administrator Administrator Moderator

    @Smegel Communicate: yes.
    Do what you want: not really.
    Best to do is copying the blocks in a way that not everything gets copied in the same tick, so you can spread the load.
     
  11. Offline

    Smegel

    @timtower

    Hi,

    What about having another executable jar file copy the world folder?
     
  12. Offline

    timtower Administrator Administrator Moderator

    @Smegel Inefficient as it can be.
    There is a copy command in every operating system.
     
  13. Offline

    Smegel

    But if I call it directly from my plugin, it will run on the main thread, right? I thought by running a different executable that would handle the copying I'd avoid lag on the server.

    Another idea is having dungeon instances run on a separate server.. Once the dungeon is completed or everybody left, I'd get it ready for the next wave of players. This means I can only have X dungeons running at the same time though
     
  14. Offline

    timtower Administrator Administrator Moderator

    @Smegel You need to start the other executable somehow, a copy command does the same thing.
    You know you can run things on a different thread right?
     
  15. Offline

    Smegel

    @timtower

    I did not know. I thought Bukkit was single threaded which meant the same for all plugins.
    Could you point me in the right direction, so I can read more about multiple threads in a plugin?
     
  16. Offline

    timtower Administrator Administrator Moderator

    Smegel likes this.
  17. Offline

    Smegel

    @timtower

    Thanks :)

    So then I'd copy the worldfile in this scheduler, and check every X seconds if the file is available yet in the main thread, right?

    also, how long will messages that I post require admin approval?
     
  18. Offline

    timtower Administrator Administrator Moderator

    @Smegel No, you copy the worldfile in the async runnable. And after the copy command you make a sync runnable that does stuff on the main thread.
    And a couple days.
     
    Smegel likes this.
  19. Offline

    Smegel

    @timtower

    Hey, just to let you know;

    I've tried calling a function from the asynchronous runnable, and it's giving me errors about how I shouldn't call the spigot API from the runnable as you said, although this is unavoidable in my case. So right now I'm checking when a player enters a portal, get the world instance linked to that portal location and check if it's ready. if not, I give the user a heads up. This is the only way I could get it working. If you have any other ideas or suggestions, let me know!
     
  20. Offline

    timtower Administrator Administrator Moderator

    @Smegel And why would it be unavoidable?
    Then you did something wrong.
     
  21. Offline

    Smegel

    @timtower

    On EntityDeathEvent I am creating a new WorldInstance object, where a method is called that copies the world given as argument. After its done, it's trying to call an abstract method of the WorldInstance class that's trying to do things with the player from the EntityDeathEvent.
     
  22. Offline

    timtower Administrator Administrator Moderator

    @Smegel 1. PlayerDeathEvent.
    2. Please post your code.
     
  23. Offline

    Smegel

    @timtower

    I'm currently in the train on my way home, so I can't at the moment. But it is EntityDeathEvent, but I'm getting the player that killed the entity.

    By the way, thanks for your help

    @timtower

    Events being used
    Code:
        @EventHandler
        public void enterPortal(PlayerPortalEvent e) {
            e.setCancelled(true);
          
          
            e.getPlayer().sendMessage("entering");
          
            if (!Dungeon.active.containsKey(e.getFrom().getBlock().getLocation()))
                return;
          
            e.getPlayer().sendMessage("dungeon found");
          
            Dungeon dungeon = Dungeon.active.get(e.getFrom().getBlock().getLocation());
          
            if (!dungeon.getWorldInstance().initializeWorld()) {
                e.getPlayer().sendMessage("The dungeon is still loading. Please try again.");
                return;
            }
          
            e.getPlayer().teleport(dungeon.getWorldInstance().getWorld().getSpawnLocation());
        }
      
      
        @EventHandler
        public void onKill(EntityDeathEvent e) {
          
            if (e.getEntity() instanceof Player)
                return;
          
            if (e.getEntity().getKiller() == null)
                return;
          
            Player player = e.getEntity().getKiller();
          
            World world = Bukkit.getWorld("world_template");
          
          
            player.sendMessage("test");
          
            if (world == null)
                world = Bukkit.createWorld(new WorldCreator("world_template"));
          
            e.getEntity().getLocation().getBlock().setType(Material.PORTAL);
          
            new Dungeon(new WorldInstance(world) {
            }, e.getEntity().getLocation().getBlock().getLocation());
        }
      
      
        public void initializeCommands() {
            getCommand("finishmodal").setExecutor(new ModalCommandHandler());
        }    
    main part of worldInstance class
    Code:
        public WorldInstance(World world) {
            this.uuid = UUID.randomUUID();
            this.originalWorldName = world.getName();
            this.worldName = this.originalWorldName + "_" + this.uuid.toString();
          
            copyWorldFile(world);
          
            list.add(this);
        }
      
      
      
      
        private void copyWorldFile(World world) {
            setAvailable(false);
          
            File from = world.getWorldFolder();
            File to = new File(Bukkit.getWorldContainer(), this.worldName);
            String worldName = this.worldName;
          
            System.out.println(worldName);
    
            Bukkit.getScheduler().runTaskAsynchronously(Main.getPlugin(), new Runnable() {
                @Override
                public void run() {
                    try {
                        FileUtils.copyDirectory(from, to, new FileFilter() {
                            public boolean accept(File pathname) {
                                String name = pathname.getName();
                              
                                return (!name.equalsIgnoreCase("session.lock") && !name.equalsIgnoreCase("uid.dat"));
                            }
                        }, true);
                    } catch (IOException e) {
                        System.out.println(Color.RED + from.getName() + " could not be copied and caused an IOException.");
                    }
                  
                    setAvailable(true);
                }
            });
          
        }
      
      
      
      
      
        public boolean isnItializedWorld() {
            if (this.world == null)
                return false;
          
            return true;
        }
      
        public boolean initializeWorld() {
            if (!this.available)
                return false;
          
            this.world = Bukkit.createWorld(new WorldCreator(this.worldName));
            return true;
        }
      
        private void setAvailable(boolean state) {
            this.available = state;
        }
      
        public boolean isAvailable() {
            if (this.available)
                return true;
          
            return false;
        }
    Dungeon is only a wrapper for the location and the worldinstance, so I dont think theres any need to post, but if you need it ask me

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Nov 20, 2017
Thread Status:
Not open for further replies.

Share This Page