# [Tutorial] Better Cooldowns

Discussion in 'Resources' started by Loogeh, Aug 7, 2013.

Not open for further replies.
1. Offline

### Loogeh

Hello,
I saw another tutorial on cooldowns which display the time left on it but I didn't like them that much (personal opinion) because they would create lots of tasks and don't display an accurate time. The difference is that with mine every 1 tick (customizable) the server checks all of the cooldowns which are active and compares time instead of using a repeating scheduler to subtract from an integer.

The way I do it you only need one repeating task and it gives you an accurate time until the cooldown is completed.

First you'll need to write out two methods. Mine are in my utilMath and utilTime classes but it doesn't matter where you put them.

The first is a cut/trim method
class utilMath
Code:
```    public static double trim(double untrimmeded, int decimal) {
String format = "#.#";

for(int i = 1; i < decimal; i++) {
format = format + "#";
}
DecimalFormat twoDec = new DecimalFormat(format);
return Double.valueOf(twoDec.format(untrimmeded)).doubleValue();
}```
Next is a class to convert milliseconds into seconds, minutes, hours or days.

TimeUnit enum
class utilTime
Code:
```    public static enum TimeUnit {
BEST,
DAYS,
HOURS,
MINUTES,
SECONDS,
}```
The BEST unit will just convert milliseconds into whatever suits it best. Example 59000 milliseconds would be converted to seconds but 74000 would be converted to minutes.

The convert method

Code:
```    public static double convert(long time, TimeUnit unit, int decPoint) {
if(unit == TimeUnit.BEST) {
if(time < 60000L) unit = TimeUnit.SECONDS;
else if(time < 3600000L) unit = TimeUnit.MINUTES;
else if(time < 86400000L) unit = TimeUnit.HOURS;
else unit = TimeUnit.DAYS;
}
if(unit == TimeUnit.SECONDS) return utilMath.trim(time / 1000.0D, decPoint);
if(unit == TimeUnit.MINUTES) return utilMath.trim(time / 60000.0D, decPoint);
if(unit == TimeUnit.HOURS) return utilMath.trim(time / 3600000.0D, decPoint);
if(unit == TimeUnit.DAYS) return utilMath.trim(time / 86400000.0D, decPoint);
return utilMath.trim(time, decPoint);
}```
Next we need to create two classes. I called mine AbilityCooldown and Cooldown but you can name them whatever you want to.

In your first class create 4 variables like this
class AbilityCooldown
Code:
```    public String ability = "";
public String player = "";
public long seconds;
public long systime;```
The ability is just the name of the cooldown. Player and Seconds are pretty self explanatory and systime is the System.currentTimeMillis() when the cooldown was initiated.

Next create two constructors. The first one is the main one

Code:
```    public AbilityCooldown(String player, long seconds, long systime) {
this.player = player;
this.seconds = seconds;
this.systime = systime;
}```
This one stores the cooldown

The next constructor is later used to access a player's cooldowns.

Code:
```public AbilityCooldown(String player) {
this.player = player;
}```
We also need to create a HashMap which stores each player's cooldowns

Code:
`public HashMap<String, AbilityCooldown> cooldownMap = new HashMap<String, AbilityCooldown>();`
Now, we have to make the second class. This class creates cooldowns, get's the remaining time of a cooldown, handle's cooldowns which need to be removed and can check if a cooldown is cooling for a specific player.

Once you've created this class the first thing you do is create a new HashMap
class Cooldown
Code:
`public static HashMap<String, AbilityCooldown> cooldownPlayers = new HashMap<String, AbilityCooldown>();`
This HashMap stores the second AbilityCooldown constructor.

Within the new class file create a method called add
This is the method which is used to add a cooldown for a player.

Code:
```    public static void add(String player, String ability, long seconds, long systime) {
if(!cooldownPlayers.containsKey(player)) cooldownPlayers.put(player, new AbilityCooldown(player));
if(isCooling(player, ability)) return;
cooldownPlayers.get(player).cooldownMap.put(ability, new AbilityCooldown(player, seconds * 1000, System.currentTimeMillis()));
}```
Then, we'll make a method for checking if a cooldown is active for a specific player.

Code:
```public static boolean isCooling(String player, String ability) {
if(!cooldownPlayers.containsKey(player)) return false;
if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) return false;
return true;
}```
Now the good part, calculating the time remaining in a cooldown. Only takes a quick 5 lines of code.

Code:
```    public static double getRemaining(String player, String ability) {
if(!cooldownPlayers.containsKey(player)) return 0.0;
if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) return 0.0;
return utilTime.convert((cooldownPlayers.get(player).cooldownMap.get(ability).seconds + cooldownPlayers.get(player).cooldownMap.get(ability).systime) - System.currentTimeMillis(), TimeUnit.SECONDS, 1);
}```
This method is just used for sending a player the time remaining in their cooldown.

Code:
```    public static void coolDurMessage(Player player, String ability) {
if(player == null) {
return;
}
if(!isCooling(player.getName(), ability)) {
return;
}
player.sendMessage(ChatColor.GRAY + ability + " Cooldown " + ChatColor.AQUA + getRemaining(player.getName(), ability) + " Seconds");
}```
The method used for removing a cooldown from a player.

Code:
```    public static void removeCooldown(String player, String ability) {
if(!cooldownPlayers.containsKey(player)) {
return;
}
if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) {
return;
}
cooldownPlayers.get(player).cooldownMap.remove(ability);
Player cPlayer = Bukkit.getPlayer(player);
if(player != null) {
player.sendMessage(ChatColor.GRAY + "You can now use " + ChatColor.AQUA + ability);
}
}```
Next, the final method!
This method just iterates through all the cooldowns, compares the time and if it's less than or equal to 0.0 then it removes the cooldown from it's owner and notifies them.

Code:
```    public static void handleCooldowns() {
if(cooldownPlayers.isEmpty()) {
return;
}
for(Iterator<String> it = cooldownPlayers.keySet().iterator(); it.hasNext();) {
String key = it.next();
for(Iterator<String> iter = cooldownPlayers.get(key).cooldownMap.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
if(getRemaining(key, name) <= 0.0) {
removeCooldown(key, name);
}
}
}
}```
Once you've written (or copy and pasted ) it all we still need to do one more thing. But it's easy.

Go to your main class and create a repeating scheduler for every 1 tick. I believe you could also use every 2 ticks since my cooldowns display the decimal in tenths of a second. Inside the scheduler put the handleCooldowns() method.
class YourMainClass (in the onEnable())
Code:
```        Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {

@Override
public void run() {
Cooldown.handleCooldowns();
}
}, 1L, 1L);```
That's all the coding for the cooldowns but if you don't know how to use it then this will show you.

Code:
```            if(Cooldown.isCooling(player.getName(), "yourcooldownnamehere")) {
Cooldown.coolDurMessage(player, "yourcooldownnamehere");
return;
}
You could put that inside a PlayerInteractEvent. All it does is check if the cooldown yourcooldownnamehere is cooling and if it's not then it adds the potion effect Strength 1 to the player but if it is cooling then it sends the player a message containing the cooldown name and time remaining in seconds.

I hope i've helped people out in this tutorial. I know lots of people want to do this but just don't know how. You could also use this to help you figure out how to do temp bans (but I will say now that you cannot put a cooldown on a player which bans then because all of the cooldowns are cleared when the server reloads or restarts).

Goodluck!

EDIT: Added the class names for each section because it was requested + I changed all round() to trim() because of confusion

EDIT by Moderator: merged posts, please use the edit button instead of double posting.

Last edited by a moderator: Jun 3, 2016
#1
Johnylog, Phasesaber and Wizehh like this.
2. Offline

### bob7

Pretty cool! I simply use mills to get the remaining time. Like system.getmills - oldmills /1000 would give me the remaining time in seconds

#2
dxrknez likes this.
3. Offline

### Loogeh

bob7 Yeah that's basically what I do but mine just has more options with units

Edit: Just realized it's not exactly how I do it but the last part still is true

#3
4. Offline

### Loogeh

SkillSam Oh sorry, while I was making this tutorial I changed the round method to trim because it's not actually rounding it, it's just trimming it so there's less decimal places. Just either change the name of the trim method to round (which isn't really an accurate description of the method) or change all the places where round is used to trim

#4
5. Offline

### SkillSam

Ah, okay. And what about variable here?

#5
6. Offline

### Loogeh

SkillSam That was just an example. The variable could be set to the ability variable if you want or it could be set to something else, of your choice. Sorry for the confusion, I think i'll change that

#6
7. Offline

### SkillSam

Loogeh Oh, okay. I see. Thanks for the tutorial!

Edit: Apparently, after removing the variable component and adding the ability one, now it's saying that the method sendMessage(String) is undefined for the type String...

Edit 2: Lol, nevermind. I had to change player to cPlayer

#7
8. Offline

### xXMinecraftXx

How do i need to get the utilMath.round() imported? I don't see any imports in Eclipse fixes ?
Loogeh

#8
9. Offline

### Loogeh

xXMinecraftXx Sorry, the utilMath.round() is from my code. The utilMath.round() method is just the trim() method from the top. I changed the name in this tutorial because it's not actually rounding the number, rather, it's just trimming down the length of it. I'll fix it tomorrow.
Just change everywhere it says utilMath.round() to yourclasshere.trim().

#9
10. Offline

### ajs333

Ok, I tried this but uhh nothing worked so can you post what it should be!

#10
11. Offline

#11
12. Offline

### viper_monster

Dahwn use "." instead of ","

#12
13. Offline

### Dahwn

spoljo666
I use that always, I didn't replace anything. It seems the server/system does it on it's own?
-Dahwn

#13
14. Offline

### viper_monster

Dahwn could you show us some code where you use this?

#14
15. Offline

### Dahwn

spoljo666
I only use the code Loogeh posted above in a PlayerInteractEvent
Code:java
`        if (p.getInventory().getItemInHand().getType() == Material.COAL) {            if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {            if ((p.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.LEAVES) || (p.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.LEAVES_2) || (p.getWorld().getBlockAt(new Location(p.getWorld(), p.getLocation().getX(), p.getLocation().getY()+4, p.getLocation().getZ())).getType() != Material.STONE)) {                 if(Cooldown.isCooling(p.getName(), "Seismic Wave")) {                    Cooldown.coolDurMessage(p, "Seismic Wave");                    return;                }                Cooldown.add(p.getName(), "Seismic Wave", 15, System.currentTimeMillis());//only some of the code because I don't want to paste all! `

-Dahwn

#15
16. Offline

### viper_monster

Dahwn could you please post line 14 in utilMath and line 20 in utilTime classes?

#16
17. Offline

### Dahwn

spoljo666
Sure but I bet both are the same as posted above! xD
utilMath line 14:
Code:java
`        return Double.valueOf(twoDec.format(untrimmeded)).doubleValue();`

utilTime line 20:
Code:java
`        if(unit == TimeUnit.SECONDS) return utilMath.trim(time / 1000.0D, decPoint);`

#17
18. Offline

### Loogeh

Dahwn That's really weird, I use this code and it still works flawlessly for me. Can you post all of your code for cooldowns please? There must be something else doing it

#18
19. Offline

### Dahwn

Loogeh
There you go:
utilMath:
Code:java
`package me.mm98.Util; import java.text.DecimalFormat; public class utilMath {     public static double trim(double untrimmeded, int decimal) {        String format = "#.#";         for(int i = 1; i < decimal; i++) {            format = format + "#";        }        DecimalFormat twoDec = new DecimalFormat(format);        return Double.valueOf(twoDec.format(untrimmeded)).doubleValue();    }} `

utilTime:
Code:java
`package me.mm98.Util; public class utilTime {     public static enum TimeUnit {        BEST,        DAYS,        HOURS,        MINUTES,        SECONDS,}     public static double convert(long time, TimeUnit unit, int decPoint) {        if(unit == TimeUnit.BEST) {            if(time < 60000L) unit = TimeUnit.SECONDS;            else if(time < 3600000L) unit = TimeUnit.MINUTES;            else if(time < 86400000L) unit = TimeUnit.HOURS;            else unit = TimeUnit.DAYS;        }        if(unit == TimeUnit.SECONDS) return utilMath.trim(time / 1000.0D, decPoint);        if(unit == TimeUnit.MINUTES) return utilMath.trim(time / 60000.0D, decPoint);        if(unit == TimeUnit.HOURS) return utilMath.trim(time / 3600000.0D, decPoint);        if(unit == TimeUnit.DAYS) return utilMath.trim(time / 86400000.0D, decPoint);        return utilMath.trim(time, decPoint);    } } `

AbilityCooldown:
Code:java
`package me.mm98.Util; import java.util.HashMap; public class AbilityCooldown {     public String ability = "";    public String player = "";    public long seconds;    public long systime;     public HashMap<String, AbilityCooldown> cooldownMap = new HashMap<String, AbilityCooldown>();     public AbilityCooldown(String player, long seconds, long systime) {        this.player = player;        this.seconds = seconds;        this.systime = systime;    }     public AbilityCooldown(String player) {        this.player = player;    } } `

Cooldown:
Code:java
`package me.mm98.Util; import java.util.HashMap;import java.util.Iterator; import me.mm98.Util.utilTime.TimeUnit; import org.bukkit.Bukkit;import org.bukkit.ChatColor;import org.bukkit.entity.Player; public class Cooldown {     public static HashMap<String, AbilityCooldown> cooldownPlayers = new HashMap<String, AbilityCooldown>();     public static void add(String player, String ability, long seconds, long systime) {        if(!cooldownPlayers.containsKey(player)) cooldownPlayers.put(player, new AbilityCooldown(player));        if(isCooling(player, ability)) return;        cooldownPlayers.get(player).cooldownMap.put(ability, new AbilityCooldown(player, seconds * 1000, System.currentTimeMillis()));    }     public static boolean isCooling(String player, String ability) {        if(!cooldownPlayers.containsKey(player)) return false;        if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) return false;        return true;        }     public static double getRemaining(String player, String ability) {        if(!cooldownPlayers.containsKey(player)) return 0.0;        if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) return 0.0;        return utilTime.convert((cooldownPlayers.get(player).cooldownMap.get(ability).seconds + cooldownPlayers.get(player).cooldownMap.get(ability).systime) - System.currentTimeMillis(), TimeUnit.SECONDS, 1);    }     public static void coolDurMessage(Player player, String ability) {        if(player == null) {            return;        }        if(!isCooling(player.getName(), ability)) {            return;        }        player.sendMessage(ChatColor.GRAY + ability + " Cooldown " + ChatColor.AQUA + getRemaining(player.getName(), ability) + " Seconds");    }     public static void removeCooldown(String player, String ability) {        if(!cooldownPlayers.containsKey(player)) {            return;        }        if(!cooldownPlayers.get(player).cooldownMap.containsKey(ability)) {            return;        }        cooldownPlayers.get(player).cooldownMap.remove(ability);        Player cPlayer = Bukkit.getPlayer(player);        if(player != null) {            cPlayer.sendMessage(ChatColor.GRAY + "You can now use " + ChatColor.AQUA + ability);        }    }     public static void handleCooldowns() {        if(cooldownPlayers.isEmpty()) {            return;        }        for(Iterator<String> it = cooldownPlayers.keySet().iterator(); it.hasNext();) {            String key = it.next();            for(Iterator<String> iter = cooldownPlayers.get(key).cooldownMap.keySet().iterator(); iter.hasNext();) {                String name = iter.next();                if(getRemaining(key, name) <= 0.0) {                    removeCooldown(key, name);                }            }        }    }} `

InteractListener where I use it:
Code:java
`    @EventHandler    public void Interact(PlayerInteractEvent e) {         final Player p = e.getPlayer();        PvpPlayer pvpp = Kits.getInstance().getPvpPlayer(p);         if (pvpp.getKit() == Kit.EARTHQUAKE && Kits.getInstance().allowKit(p.getName())) {        if (p.getInventory().getItemInHand().getType() == Material.COAL) {            if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {            if ((p.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.LEAVES) || (p.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.LEAVES_2) || (p.getWorld().getBlockAt(new Location(p.getWorld(), p.getLocation().getX(), p.getLocation().getY()+4, p.getLocation().getZ())).getType() != Material.STONE)) {                 if(Cooldown.isCooling(p.getName(), "SeismicWave")) {                    Cooldown.coolDurMessage(p, "SeismicWave");                    return;                }                Cooldown.add(p.getName(), "SeismicWave", 16, System.currentTimeMillis());                 final Location center = p.getLocation().getBlock().getRelative(BlockFace.DOWN).getLocation();`

in onEnable():
Code:java
`  public void onEnable()  {       Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {           @Override          public void run() {              Cooldown.handleCooldowns();          }      }, 1L, 1L);  `

And one of the errors I get every time a number with a decimal point gets displayed which is not zero!
Code:
```[16:37:34 WARN]: [PlayKits] Task #2 for PlayKits v0.1 generated an exception
java.lang.NumberFormatException: For input string: "0,5"
.0_45]
at java.lang.Double.valueOf(Unknown Source) ~[?:1.7.0_45]
at me.mm98.Util.utilMath.trim(utilMath.java:14) ~[?:?]
at me.mm98.Util.utilTime.convert(utilTime.java:20) ~[?:?]
at me.mm98.Util.Cooldown.getRemaining(Cooldown.java:31) ~[?:?]
at me.mm98.Util.Cooldown.handleCooldowns(Cooldown.java:66) ~[?:?]
at me.mm98.PvPKits.Kits\$1.run(Kits.java:165) ~[?:?]
:53) ~[craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
rtbeat(CraftScheduler.java:345) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks
]
at net.minecraft.server.v1_7_R1.MinecraftServer.u(MinecraftServer.java:5
87) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
at net.minecraft.server.v1_7_R1.DedicatedServer.u(DedicatedServer.java:2
50) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
at net.minecraft.server.v1_7_R1.MinecraftServer.t(MinecraftServer.java:5
45) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
at net.minecraft.server.v1_7_R1.MinecraftServer.run(MinecraftServer.java
:457) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]
17) [craftbukkit.jar:git-Bukkit-1.7.2-R0.3-b3020jnks]```
If I right click the item and the cooldown is on 4.0 or 6.0 (example) I get the message. Also then it exists no error! The main system also works so that I can use the item again after the specific amount of time is over and the "you can now use this again" message also works. Just the messages between (for example) 4.0 and 5.0 don't work

-Dahwn

Loogeh
NVM. I did only not work on my localhost It works on the real server (Why? LOL)
Thanks anyways!
-Dahwn

EDIT by Moderator: merged posts, please use the edit button instead of double posting.

Last edited by a moderator: Jun 3, 2016
#19
20. Offline

### Loogeh

Dahwn Huh, that's weird. I have no idea why that's happening lol

#20
21. Offline

### Forge_User_62502025

@Dawhn
Are you inputting 0,5 some how on your localhost...

22. Offline

### Loogeh

Might come back and rewrite this tutorial because while the code works, it is not as clean and readable as it could be.

#22
23. Offline

### Zahachos

Loogeh WOW! Epic dude thanks!!
I transformed it a bit to use it with a temp ban command!

#23