1.15.2 Best way to run asynchronous task on command execution

Discussion in 'Spigot Plugin Development' started by EVILCAT6, Mar 30, 2020.

  1. Hey. I'm trying to use the LuckPerms API to modify a player's permissions on a command, but this needs to be done asynchronously as to not block the main server thread and cause lag.

    I've referred to the API documentation here on how to obtain a user instance, but how would I load a user asynchronously, modify some permissions and update the user so the result of the command does not block the main thread?

    I've thought of putting everything to do with LuckPerms in a runTaskAsynchronously task, but I'm not sure if this is the best way about it (as the actual command is still running on the main thread?). I want to avoid blocking the main thread at all costs, because there could be a timeout issue or the SQL server just takes a while to respond and not blocking would hold up the entire server because of it.

    Help is appreciated, thanks!
     
  2. This doesn't really answer my question. If I use this in a command, is it going to block the server thread while the command waits for the result of the Future? If it does then there is absolutely no point in using them as that's what I'm wanting to avoid.
     
  3. As long as you do not use blocking methods like CompletableFuture#get() or CompletableFuture#join(), it won't.
     
  4. Would this be a step in the right direction? I've commented parts I'm not certain about.

    Code (Java):
    /*
    ALL OF THIS IS RUNNING IN A COMMAND
    */


    // Get Bukkit and PlotSquared player instances
    Player player = (Player) sender;
    PlotPlayer plotPlayer = PlotPlayer.wrap(player);

    int maxPlots = plotPlayer.getAllowedPlots();

    int plotsToBuy = 0;
    try {
        plotsToBuy = Integer.parseInt(args[0]);
    } catch(NumberFormatException e) {
        player.sendMessage(ChatUtils.prefix() + "Please only enter a whole number!");
        return true;
    }

    if(plotsToBuy == 0) {
        player.sendMessage(ChatUtils.prefix() + "You must buy at least 1 plot!");
        return true;
    }

    int costOfPlot = CreativeCore.plugin.getConfig().getInt("plot-cost");
    // Server currency
    int playerCredits = Credits.getAPI().getCredits(player);

    if(playerCredits >= plotsToBuy * costOfPlot) {
        // Player has already got a plots permission (previously bought plots)
        if(player.hasPermission("plots.plot." + maxPlots) && maxPlots != 1) {
            // Create permission nodes.
            PermissionNode node = PermissionNode.builder("plots.plot." + plotsToBuy).build();
            PermissionNode oldNode = PermissionNode.builder("plots.plot." + maxPlots).build();

            // Get LP API.
            LuckPerms api = LuckPermsProvider.get();

            final int finalToBuy = plotsToBuy;

            // Add the new permission to the user
            api.getUserManager().loadUser(player.getUniqueId()).thenApply(user ->
                    user.data().add(node));

            // Remove the old permission from the user.
            api.getUserManager().loadUser(player.getUniqueId()).thenApply(user ->
                    user.data().remove(oldNode)).thenRun(() -> {
                /*
                / Is this code in .thenRun() going to run once the future is complete without blocking?
                 */

                Credits.getAPI().setCredits(player, playerCredits - finalToBuy * costOfPlot);

                // Some database stuff on bungee using plugin channels
                int newPlayerCredits = Credits.getAPI().getCredits(player);
                if(finalToBuy == 1) Credits.getAPI().sendCreditsPurchaseHistory(player, "1 Plot", playerCredits, newPlayerCredits);
                else Credits.getAPI().sendCreditsPurchaseHistory(player, finalToBuy + " Plots", playerCredits, newPlayerCredits);

                player.sendMessage(ChatUtils.prefix() + "Successfully bought §a" + finalToBuy + " plots");
            });

            /*/ How would I check if the future failed to complete (such as user not loading, permissions not applying)
            and then send the user a message saying so?
             */

        } else {
            // Player has not bought a plot before

            // Create permission nodes.
            PermissionNode node = PermissionNode.builder("plots.plot." + plotsToBuy).build();
            PermissionNode oldNode = PermissionNode.builder("plots.plot." + maxPlots).build();

            // Get LP API.
            LuckPerms api = LuckPermsProvider.get();

            final int finalToBuy = plotsToBuy;

            // Add the new permission to the user
            api.getUserManager().loadUser(player.getUniqueId()).thenApply(user ->
                    user.data().add(node)).thenRun(() -> {
                /*
                / Is this code in .thenRun() going to run once the future is complete without blocking?
                 */

                Credits.getAPI().setCredits(player, playerCredits - finalToBuy * costOfPlot);

                // Some database stuff on bungee using plugin channels
                int newPlayerCredits = Credits.getAPI().getCredits(player);
                if(finalToBuy == 1) Credits.getAPI().sendCreditsPurchaseHistory(player, "1 Plot", playerCredits, newPlayerCredits);
                else Credits.getAPI().sendCreditsPurchaseHistory(player, finalToBuy + " Plots", playerCredits, newPlayerCredits);

                player.sendMessage(ChatUtils.prefix() + "Successfully bought §a" + finalToBuy + " plots");
            });
        }
    } else {
        player.sendMessage(ChatUtils.prefix() + "You do not have enough credits to buy " + plotsToBuy + " plots.");
    }