mirror of
https://github.com/EssentialsX/Essentials.git
synced 2024-11-17 08:05:30 +01:00
Implement Command Cooldowns. Resolves #110
This commit is contained in:
parent
864dadab80
commit
09acbcdb05
@ -4,6 +4,7 @@ import com.earth2me.essentials.textreader.IText;
|
||||
import com.earth2me.essentials.textreader.KeywordReplacer;
|
||||
import com.earth2me.essentials.textreader.TextInput;
|
||||
import com.earth2me.essentials.textreader.TextPager;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.LocationUtil;
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.GameMode;
|
||||
@ -26,11 +27,15 @@ import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
@ -399,10 +404,48 @@ public class EssentialsPlayerListener implements Listener {
|
||||
broadcast = false;
|
||||
}
|
||||
}
|
||||
final User user = ess.getUser(player);
|
||||
if (update) {
|
||||
final User user = ess.getUser(player);
|
||||
user.updateActivity(broadcast);
|
||||
}
|
||||
|
||||
if (ess.getSettings().isCommandCooldownsEnabled() && pluginCommand != null) {
|
||||
int argStartIndex = event.getMessage().indexOf(" ");
|
||||
String args = argStartIndex == -1 ? event.getMessage() // No arguments present
|
||||
: event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
|
||||
String fullCommand = pluginCommand.getName() + " " + args;
|
||||
|
||||
// Used to determine whether a user already has an existing cooldown
|
||||
// If so, no need to check for (and write) new ones.
|
||||
boolean cooldownFound = false;
|
||||
|
||||
// Iterate over a copy of getCommandCooldowns in case of concurrent modifications
|
||||
for (Entry<Pattern, Long> entry : new HashMap<>(user.getCommandCooldowns()).entrySet()) {
|
||||
// Remove any expired cooldowns
|
||||
if (entry.getValue() <= System.currentTimeMillis()) {
|
||||
user.clearCommandCooldown(entry.getKey());
|
||||
// Don't break in case there are other command cooldowns left to clear.
|
||||
} else if (entry.getKey().matcher(fullCommand).matches()) {
|
||||
// User's current cooldown hasn't expired, inform and terminate cooldown code.
|
||||
if (entry.getValue() > System.currentTimeMillis()) {
|
||||
String commandCooldownTime = DateUtil.formatDateDiff(entry.getValue());
|
||||
user.sendMessage(tl("commandCooldown", commandCooldownTime));
|
||||
cooldownFound = true;
|
||||
event.setCancelled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cooldownFound) {
|
||||
Entry<Pattern, Long> cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand);
|
||||
|
||||
if (cooldownEntry != null) {
|
||||
Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue());
|
||||
user.addCommandCooldown(cooldownEntry.getKey(), expiry, ess.getSettings().isCommandCooldownPersistent(fullCommand));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
|
@ -10,7 +10,9 @@ import org.bukkit.event.EventPriority;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public interface ISettings extends IConf {
|
||||
@ -247,4 +249,12 @@ public interface ISettings extends IConf {
|
||||
boolean isSpawnOnJoin();
|
||||
|
||||
boolean isTeleportToCenterLocation();
|
||||
|
||||
boolean isCommandCooldownsEnabled();
|
||||
|
||||
long getCommandCooldownMs(String label);
|
||||
|
||||
Entry<Pattern, Long> getCommandCooldownEntry(String label);
|
||||
|
||||
boolean isCommandCooldownPersistent(String label);
|
||||
}
|
||||
|
@ -7,9 +7,11 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public interface IUser {
|
||||
@ -138,6 +140,14 @@ public interface IUser {
|
||||
Map<String, Object> getConfigMap();
|
||||
|
||||
Map<String, Object> getConfigMap(String node);
|
||||
|
||||
Map<Pattern, Long> getCommandCooldowns();
|
||||
|
||||
Date getCommandCooldownExpiry(String label);
|
||||
|
||||
void addCommandCooldown(Pattern pattern, Date expiresAt, boolean save);
|
||||
|
||||
boolean clearCommandCooldown(Pattern pattern);
|
||||
|
||||
/*
|
||||
* PlayerExtension
|
||||
|
@ -16,10 +16,13 @@ import org.bukkit.inventory.ItemStack;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
|
||||
public class Settings implements net.ess3.api.ISettings {
|
||||
@ -532,6 +535,7 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
customQuitMessage = _getCustomQuitMessage();
|
||||
isCustomQuitMessage = !customQuitMessage.equals("none");
|
||||
muteCommands = _getMuteCommands();
|
||||
commandCooldowns = _getCommandCooldowns();
|
||||
}
|
||||
|
||||
private List<Integer> itemSpawnBl = new ArrayList<Integer>();
|
||||
@ -1173,4 +1177,89 @@ public class Settings implements net.ess3.api.ISettings {
|
||||
public boolean isTeleportToCenterLocation() {
|
||||
return config.getBoolean("teleport-to-center", true);
|
||||
}
|
||||
|
||||
|
||||
private Map<Pattern, Long> commandCooldowns;
|
||||
|
||||
private Map<Pattern, Long> _getCommandCooldowns() {
|
||||
if (!config.isConfigurationSection("command-cooldowns")) {
|
||||
return null;
|
||||
}
|
||||
ConfigurationSection section = config.getConfigurationSection("command-cooldowns");
|
||||
Map<Pattern, Long> result = new LinkedHashMap<>();
|
||||
for (String cmdEntry : section.getKeys(false)) {
|
||||
Pattern pattern = null;
|
||||
|
||||
/* ================================
|
||||
* >> Regex
|
||||
* ================================ */
|
||||
if (cmdEntry.startsWith("^")) {
|
||||
try {
|
||||
pattern = Pattern.compile(cmdEntry.substring(1));
|
||||
} catch (PatternSyntaxException e) {
|
||||
ess.getLogger().warning("Command cooldown error: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// Escape above Regex
|
||||
if (cmdEntry.startsWith("\\^")) {
|
||||
cmdEntry = cmdEntry.substring(1);
|
||||
}
|
||||
String cmd = cmdEntry
|
||||
.replaceAll("\\*", ".*"); // Wildcards are accepted as asterisk * as known universally.
|
||||
pattern = Pattern.compile(cmd + "( .*)?"); // This matches arguments, if present, to "ignore" them from the feature.
|
||||
}
|
||||
|
||||
/* ================================
|
||||
* >> Process cooldown value
|
||||
* ================================ */
|
||||
Object value = section.get(cmdEntry);
|
||||
if (!(value instanceof Number) && value instanceof String) {
|
||||
try {
|
||||
value = Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
if (!(value instanceof Number)) {
|
||||
ess.getLogger().warning("Command cooldown error: '" + value + "' is not a valid cooldown");
|
||||
continue;
|
||||
}
|
||||
double cooldown = ((Number) value).doubleValue();
|
||||
if (cooldown < 1) {
|
||||
ess.getLogger().warning("Command cooldown with very short " + cooldown + " cooldown.");
|
||||
}
|
||||
|
||||
result.put(pattern, (long) cooldown * 1000); // convert to milliseconds
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommandCooldownsEnabled() {
|
||||
return commandCooldowns != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCommandCooldownMs(String label) {
|
||||
Entry<Pattern, Long> result = getCommandCooldownEntry(label);
|
||||
return result != null ? result.getValue() : -1; // return cooldown in milliseconds
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Pattern, Long> getCommandCooldownEntry(String label) {
|
||||
if (isCommandCooldownsEnabled()) {
|
||||
for (Entry<Pattern, Long> entry : this.commandCooldowns.entrySet()) {
|
||||
// Check if label matches current pattern (command-cooldown in config)
|
||||
if (entry.getKey().matcher(label).matches()) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommandCooldownPersistent(String label) {
|
||||
// TODO: enable per command cooldown specification for persistence.
|
||||
return config.getBoolean("command-cooldown-persistence", true);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.InvalidWorldException;
|
||||
import net.ess3.api.MaxMoneyException;
|
||||
@ -13,6 +14,8 @@ import org.bukkit.inventory.ItemStack;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
@ -85,6 +88,7 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
ignoredPlayers = _getIgnoredPlayers();
|
||||
logoutLocation = _getLogoutLocation();
|
||||
lastAccountName = _getLastAccountName();
|
||||
commandCooldowns = _getCommandCooldowns();
|
||||
}
|
||||
|
||||
private BigDecimal money;
|
||||
@ -792,6 +796,89 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
// Pattern, Date. Pattern for less pattern creations
|
||||
private Map<Pattern, Long> commandCooldowns;
|
||||
|
||||
private Map<Pattern, Long> _getCommandCooldowns() {
|
||||
if (!config.isConfigurationSection("timestamps.command-cooldowns")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// See saveCommandCooldowns() for deserialization explanation
|
||||
List<Map<?, ?>> section = config.getMapList("timestamps.command-cooldowns");
|
||||
HashMap<Pattern, Long> result = new HashMap<>();
|
||||
for (Map<?, ?> map : section) {
|
||||
Pattern pattern = Pattern.compile(map.get("pattern").toString());
|
||||
long expiry = ((Number) map.get("expiry")).longValue();
|
||||
result.put(pattern, expiry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<Pattern, Long> getCommandCooldowns() {
|
||||
if (this.commandCooldowns == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return Collections.unmodifiableMap(this.commandCooldowns);
|
||||
}
|
||||
|
||||
public Date getCommandCooldownExpiry(String label) {
|
||||
if (commandCooldowns != null) {
|
||||
for (Entry<Pattern, Long> entry : this.commandCooldowns.entrySet()) {
|
||||
if (entry.getKey().matcher(label).matches()) {
|
||||
return new Date(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addCommandCooldown(Pattern pattern, Date expiresAt, boolean save) {
|
||||
if (this.commandCooldowns == null) {
|
||||
this.commandCooldowns = new HashMap<>();
|
||||
}
|
||||
this.commandCooldowns.put(pattern, expiresAt.getTime());
|
||||
if (save) {
|
||||
saveCommandCooldowns();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean clearCommandCooldown(Pattern pattern) {
|
||||
if (this.commandCooldowns == null) {
|
||||
return false; // false for no modification
|
||||
}
|
||||
|
||||
if(this.commandCooldowns.remove(pattern) != null) {
|
||||
saveCommandCooldowns();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void saveCommandCooldowns() {
|
||||
// Serialization explanation:
|
||||
//
|
||||
// Serialization is done as a map list instead of a config section due to limitations.
|
||||
// When serializing patterns (which commonly include full stops .) Bukkit/Essentials config framework
|
||||
// interprets it as a path separator, thus it breaks up the regex into sub nodes causing invalid syntax.
|
||||
// Thus each command cooldown is instead stored as a Map of {pattern: .., expiry: ..} to work around this.
|
||||
List<Object> serialized = new ArrayList<>();
|
||||
for (Entry<Pattern, Long> entry : this.commandCooldowns.entrySet()) {
|
||||
// Don't save expired cooldowns
|
||||
if (entry.getValue() < System.currentTimeMillis()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<?, ?> map = ImmutableMap.builder()
|
||||
.put("pattern", entry.getKey().pattern())
|
||||
.put("expiry", entry.getValue())
|
||||
.build();
|
||||
serialized.add(map);
|
||||
}
|
||||
config.setProperty("timestamps.command-cooldowns", serialized);
|
||||
save();
|
||||
}
|
||||
|
||||
public UUID getConfigUUID() {
|
||||
return config.uuid;
|
||||
}
|
||||
|
@ -484,6 +484,24 @@ send-fly-enable-on-join: true
|
||||
# Give someone permission to teleport to a world with essentials.time.world.<worldname>.
|
||||
world-time-permissions: false
|
||||
|
||||
# Specify cooldown for both Essentials commands and external commands as well.
|
||||
# All commands do not start with a Forward Slash (/). Instead of /msg, write msg
|
||||
#
|
||||
# Wildcards are supported. E.g.
|
||||
# - '*i*': 50
|
||||
# adds a 50 second cooldown to all commands that include the letter i
|
||||
#
|
||||
# EssentialsX supports regex by starting the command with a caret ^
|
||||
# For example, to target commands starting with ban and not banip the following would be used:
|
||||
# '^ban([^ip])( .*)?': 60 # 60 seconds /ban cooldown.
|
||||
# Note: If you have a command that starts with ^, then you can escape it using backslash (\). e.g. \^command: 123
|
||||
command-cooldowns:
|
||||
# feed: 100 # 100 second cooldown on /feed command
|
||||
# '*': 5 # 5 Second cooldown on all commands
|
||||
|
||||
# Whether command cooldowns should be persistent past server shutdowns
|
||||
command-cooldown-persistence: true
|
||||
|
||||
############################################################
|
||||
# +------------------------------------------------------+ #
|
||||
# | EssentialsHome | #
|
||||
|
@ -573,3 +573,4 @@ msgEnabled=\u00a76Receiving messages \u00a7cenabled\u00a76.
|
||||
msgEnabledFor=\u00a76Receiving messages \u00a7cenabled \u00a76for \u00a7c{0}\u00a76.
|
||||
msgIgnore=\u00a7c{0} \u00a74has messages disabled.
|
||||
minimumPayAmount=\u00a7cThe minimum amount you can pay is {0}.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -566,3 +566,4 @@ spectator=Zuschauer
|
||||
kitContains=\u00a76Ausr\u00fcstung \u00a7c{0} \u00a76enth\u00e4lt:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Ung\u00fcltige Banner-Syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -566,3 +566,4 @@ msgDisabled=\u00a76Receiving messages \u00a7cdisabled\u00a76.
|
||||
msgDisabledFor=\u00a76Receiving messages \u00a7cdisabled \u00a76for \u00a7c{0}\u00a76.
|
||||
msgEnabled=\u00a76Receiving messages \u00a7cenabled\u00a76.
|
||||
msgEnabledFor=\u00a76Receiving messages \u00a7cenabled \u00a76for \u00a7c{0}\u00a76.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -567,3 +567,4 @@ msgDisabled=\u00a76R\u00e9ception des messages \u00a7cd\u00e9sactiv\u00e9e\u00a7
|
||||
msgDisabledFor=\u00a76R\u00e9ception des messages \u00a7cd\u00e9sactiv\u00e9e \u00a76pour \u00a7c{0}\u00a76.
|
||||
msgEnabled=\u00a76R\u00e9ception des messages \u00a7cactiv\u00e9e\u00a76.
|
||||
msgEnabledFor=\u00a76R\u00e9ception des messages \u00a7cactiv\u00e9e \u00a76pour \u00a7c{0}\u00a76.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
@ -561,4 +561,5 @@ createdKit=\u00a76Created kit \u00a7c{0} \u00a76with \u00a7c{1} \u00a76entries a
|
||||
spectator=spectator
|
||||
kitContains=\u00a76Kit \u00a7c{0} \u00a76contains:
|
||||
kitItem=\u00a76- \u00a7f{0}
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
invalidBanner=\u00a74Invalid banner syntax.
|
||||
commandCooldown=\u00a7cYou cannot type that command for {0}.
|
||||
|
Loading…
Reference in New Issue
Block a user