Solved Better way to do this?

Discussion in 'Plugin Development' started by microgeek, May 10, 2013.

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

    microgeek

    I'm running a MySQL query when a player joins, to check if they are banned. The server is running in offline mode(due to the program I'm using to proxy my servers together), so I cannot use an AsyncPlayerPreLoginEvent, because it's not fired. Below is what I'm using at the moment, but feel that the call should be async, so the server does not grind to a halt when three people join. When I tried running the code below in an Async BukkitRunnable, it seemed to not have an effect. Any thoughts?
    Code:
        @EventHandler
        public void onJoin(PlayerLoginEvent event) {
            if(BanUtilities.isPlayerBanned(event.getPlayer().getName())) {
                event.disallow(Result.KICK_BANNED, BanUtilities.getBanMessage());
            }
        }
     
  2. Offline

    TheE

    You answered yourself: there no way this could be done.

    The PlayerLoginEvent is called in the main server-thread and therefore blocks this thread until it is processed. Everything you could do (executing the database call in an async thread and use Future<Result> to return the result or hand over the event to an async thread that deals with it) will block the main thread until the async thread has completed since the event itself is hold by the main-thread.
     
  3. Offline

    LucasEmanuel

    You could do something like this:
    Code:
    @EventHandler
    public void onJoin(PlayerLoginEvent event) {
     
        final Player player = event.getPlayer();
        final String playername = player.getName();
     
        plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
            public void run() {
         
                if(BanUtilities.isPlayerBanned(playername)) {
             
                    plugin.getServer().getScheduler().runTask(plugin, new Runnable() {
                        public void run() {
                            player.kickPlayer(BanUtilities.getBanMessage());
                        }
                    });
                }
            }
        }
    }
    While this would stop the server from freezing over during the database-lookup, the player would be able to move around until the check is finished and the scheduled task kicks the player if he is banned.
     
  4. Offline

    TheE

    player.kickPlayer() is (as far as I am informed) not threadsafe.
     
  5. Offline

    microgeek

    I used LucasEmanuel 's idea, but I implemented a cache.
    Code:
        @EventHandler
        public void onJoin(final PlayerLoginEvent event) {
            if(BanCache.bans.contains(event.getPlayer().getName())) {
                event.disallow(Result.KICK_BANNED, BanUtilities.getBanMessage(BanCache.bans.get(event.getPlayer().getName())));
                updateCache(event.getPlayer().getName());
            }else {
                new BukkitRunnable() {
                   
                    @Override
                    public void run() {
                        final Ban ban = BanUtilities.getPlayerBan(event.getPlayer().getName());
                        if(ban != null) {
                            new BukkitRunnable() {
                                public void run() {
                                    event.getPlayer().kickPlayer(BanUtilities.getBanMessage(ban));
                                }
                            }.runTask(plugin);
                        }
                    }
                }.runTaskAsynchronously(plugin);
            }
        }
     
        private void updateCache(final String player) {
            new BukkitRunnable() {
               
                @Override
                public void run() {
                    final Ban ban = BanUtilities.getPlayerBan(player);
                    if(ban == null) {
                        BanCache.bans.remove(player);
                    }
                }
            }.runTaskAsynchronously(plugin);
        }
     
  6. Offline

    LucasEmanuel

    That's why I schedule a synchronous task that kicks the player.

    I'm referring to point 3:
    1. Asynchronous tasks should never access any API in Bukkit.
    2. Do not access or modify shared collections from your asynchronous tasks. Normal collections are not thread-safe. This also applies to objects which are not thread safe.
    3. An asynchronous task can schedule a synchronous task.
    4. A synchronous task can schedule an asynchronous task.
    Source: http://wiki.bukkit.org/Scheduler_Programming
     
    microgeek likes this.
  7. Offline

    WizKhalifa

    good point.
     
Thread Status:
Not open for further replies.

Share This Page