Solved 1.8.8 Repeating Tasks w Actionbars

Discussion in 'Plugin Development' started by Lightcaster5, Sep 18, 2020.

  1. Offline

    Lightcaster5

    So I am developing an ability plugin that has a great assortment of kits along with specific abilities. One aspect of this plugin being recharge actionbars. Below I have some code that works most of the time and I was wondering if there was a more efficient way of going about this. I was reading on cooldowns and thought of using timestamps but found that wouldn't really be possible with what I am doing.

    Actionbar.java (open)
    Code:
    package me.lightcaster5.somepackage;
    
    
    import java.util.HashMap;
    
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    
    import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer;
    import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
    
    public class Actionbar {
    
        public static HashMap<Player, Integer> counterMap = new HashMap<Player, Integer>();
        public static HashMap<Player, Integer> runnableMap = new HashMap<Player, Integer>();
    
        public static void sendActionbarMessage(Player player, String message) {
            message = ChatColor.translateAlternateColorCodes('&', message);
            PacketPlayOutChat packet = new PacketPlayOutChat(ChatSerializer.a("{\"text\":\"" + message + "\"}"), (byte) 2);
            ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
        }
    
        @SuppressWarnings("deprecation")
        public static void sendLoadingActionbar(Player player, int durationInSeconds, String abilityName) {
            final int durationInTicks = 20 * durationInSeconds;
            counterMap.put(player, 0);
            runnableMap.put(player, Bukkit.getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() {
                @Override
                public void run() {
                    if (counterMap.get(player) == null) {
                        System.out.println("THERE WAS AN ERROR CANCELING " + player.getName() + "'s UPDATE TASK");
                        counterMap.remove(player);
                        runnableMap.remove(player);
                        return;
                    } else if (Bukkit.getPlayer(player.getName()) == null || counterMap.get(player) >= durationInTicks) {
                        counterMap.remove(player);
                        Bukkit.getScheduler().cancelTask(runnableMap.get(player));
                        runnableMap.remove(player);
                        return;
                    } else {
                        counterMap.put(player, (counterMap.get(player) + 1));
                        int currentCount = counterMap.get(player);
                        Integer percentLoaded = (int) Math.floor(((float) currentCount / durationInTicks) * 100);
                        String message = "LOADING - " + percentLoaded.toString();
                        sendActionbarMessage(player, message);
                    }
                }
            }, 0L, 1L));
        }
    
    }


    What I believe is happening and often causing the error is when sendLoadingActionbar() is called again, before the player has finished their previous actionbar, thus overwriting the runnableMap value. If anyone has a more efficient method of doing this or a way I could possible change what I already have, I am open to anything. Thanks!
     
  2. Offline

    KarimAKL

    @Lightcaster5
    1. Check if the player is already in the map, and if so, cancel the task gotten from them before adding the new one.
    2. If it's a countdown, you can probably make the task run every 20 ticks (1 second) instead of every 1 tick (20 times a second).

    Also, why would timestamps not work?
     
  3. Offline

    Lightcaster5

    1. That would probably fix the mem leaks when the map is overwritten
    2. I want it to be smoother than a 1 second update as the smallest cooldown time is 3 seconds. I don't want it to say 0, jump to 33, jump to 66, and then be done

    It isn't that they wouldn't work... I want to use them, I just don't really know how to implement them efficiently.
     
  4. Offline

    KarimAKL

    @Lightcaster5 For timestamps, you can use the long from System#currentTimeMillis(), then save that in a Map<UUID, Long>. You can then get that long and add the amount of milliseconds the loading should take, then compare that time with the current time to check the time left.
    Example (open)
    Code:Java
    1. /* Start the time */
    2. // Get the current time
    3. long time = System.currentTimeMillis();
    4.  
    5. // Save it to the map, with the player's uuid as the key
    6. map.put(uuid, time);
    7.  
    8. /* Check time left in runnable */
    9. // Get the timestamp we saved
    10. long time = map.get(uuid);
    11.  
    12. // Get the current time
    13. long current = System.currentTimeMillis();
    14.  
    15. // Check if the time is up
    16. if (current >= time + loadingTime) {
    17. // The time is up, do whatever you want
    18. } else {
    19. // If the time hasn't gone by, send them the time left
    20. player.sendMessage("Time left in milliseconds: " + (time + loadingTime - current));
    21. }
     
  5. Offline

    Lightcaster5

    Ohhhhh.... I thought the entire point of using System.currentTimeMillis() was to remove the need for runnables. That sounds much more efficient, I'll be sure to give that a shot! Thanks!
     

Share This Page