diff --git a/resources/config.yml b/resources/config.yml index bf9dd77..f8abd1c 100644 --- a/resources/config.yml +++ b/resources/config.yml @@ -26,11 +26,18 @@ MaxSize: 6 # AllowedGameModes: [ "SURVIVAL", "ADVENTURE" ] AllowedGameModes: [ "SURVIVAL" ] -# Defines how long a player have to wait till he can reopen his backpack. -# Time is in seconds. Values < 1 disable the cooldown. -CommandCooldown: -1 -# If enabled whe cooldown will be synced between servers (BungeeCord network). It will also prevent players from leaving and joining to bypass the cooldown. -SyncCooldown: false +Cooldown: + # Defines how long a player have to wait till he can reopen his backpack. + # Time is in seconds. Values < 1 disable the cooldown. + Command: -1 + # If enabled whe cooldown will be synced between servers (BungeeCord network). It will also allow to keep cooldown through server restarts. + # You should only use it with long lasting cooldowns. + Sync: false + AddOnJoin: true + # You can turn this on when using sync to reduce the memory consumption a little bit + ClearOnLeave: false + # Removes old cooldowns from the cache to free memory. Time in seconds. + CleanupInterval: 600 # Controls for the auto pickup on full inventory function FullInventory: diff --git a/resources/lang/en.yml b/resources/lang/en.yml index 2de96a1..eb31c11 100644 --- a/resources/lang/en.yml +++ b/resources/lang/en.yml @@ -11,8 +11,8 @@ Language: InvalidBackpack: "Invalid backpack." NotAllowedInBackpack: "&c{ItemName} is not allowed in the backpack." Open: - #Parameter: {TimeLeft} time in seconds till he can reopen his backpack - Cooldown: "&2Please wait {TimeLeft} seconds till you reopen your backpack." + #Parameter: {TimeLeft} time in seconds till he can reopen his backpack, {TimeSpanLeft} + Cooldown: "&2Please wait {TimeSpanLeft} seconds till you reopen your backpack." #Parameter: {CurrentGameMode}, {AllowedGameModes} WrongGameMode: "You are not allowed to open your backpack in your current game-mode." Clean: diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Backpack.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Backpack.java index 9f0c36d..6acff16 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Backpack.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Backpack.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016, 2017 GeorgH93 + * Copyright (C) 2016-2018 GeorgH93 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Command/OpenCommand.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Command/OpenCommand.java index c36bbb3..6926d1b 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Command/OpenCommand.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Command/OpenCommand.java @@ -18,6 +18,7 @@ package at.pcgamingfreaks.Minepacks.Bukkit.Command; import at.pcgamingfreaks.Bukkit.Message.Message; +import at.pcgamingfreaks.Calendar.TimeSpan; import at.pcgamingfreaks.Command.HelpData; import at.pcgamingfreaks.Minepacks.Bukkit.API.MinepacksCommand; import at.pcgamingfreaks.Minepacks.Bukkit.Minepacks; @@ -35,8 +36,6 @@ public class OpenCommand extends MinepacksCommand { private final Message messageCooldown, messageWrongGameMode; private final String allowedGameModes, descriptionOpenOthers, helpParam; - private final long cooldown; - private final boolean syncCooldown; private final Minepacks plugin; public OpenCommand(Minepacks plugin) @@ -44,14 +43,11 @@ public OpenCommand(Minepacks plugin) super(plugin, "open", plugin.getLanguage().getTranslated("Commands.Description.Backpack"), "backpack.use", true, plugin.getLanguage().getCommandAliases("Open")); this.plugin = plugin; - messageCooldown = plugin.getLanguage().getMessage("Ingame.Open.Cooldown").replaceAll("\\{TimeLeft}", "%1\\$.1f"); - messageWrongGameMode = plugin.getLanguage().getMessage("Ingame.Open.WrongGameMode").replaceAll("\\{CurrentGameMode}", "%1\\$s").replaceAll("\\{AllowedGameModes}", "%1\\$s"); + messageCooldown = plugin.getLanguage().getMessage("Ingame.Open.Cooldown").replaceAll("\\{TimeLeft}", "%1\\$.1f").replaceAll("\\{TimeSpanLeft}", "%2\\$s"); + messageWrongGameMode = plugin.getLanguage().getMessage("Ingame.Open.WrongGameMode").replaceAll("\\{CurrentGameMode}", "%1\\$s").replaceAll("\\{AllowedGameModes}", "%1\\$s"); descriptionOpenOthers = plugin.getLanguage().getTranslated("Commands.Description.OpenOthers"); helpParam = "<" + plugin.getLanguage().get("Commands.PlayerNameVariable") + ">"; - cooldown = plugin.getConfiguration().getCommandCooldown(); - syncCooldown = plugin.getConfiguration().isCommandCooldownSyncEnabled(); - StringBuilder allowedGameModesBuilder = new StringBuilder(); for(GameMode gameMode : plugin.getConfiguration().getAllowedGameModes()) { @@ -72,24 +68,16 @@ public void execute(@NotNull CommandSender sender, @NotNull String main, @NotNul { if(getMinepacksPlugin().isPlayerGameModeAllowed(player)) { - if(cooldown > 0 && !player.hasPermission("backpack.noCooldown")) + if(plugin.getCooldownManager() != null && !player.hasPermission("backpack.noCooldown")) { - if(plugin.cooldowns.containsKey(player.getUniqueId())) + long cd = plugin.getCooldownManager().getRemainingCooldown(player); + if(cd > 0) { - long cd = plugin.cooldowns.get(player.getUniqueId()); - if(cd < System.currentTimeMillis()) - { - cd = cd - System.currentTimeMillis(); - messageCooldown.send(sender, cd / 1000f); - return; - } + TimeSpan ts = new TimeSpan(cd, true); + messageCooldown.send(sender, cd / 1000f, ts.toString()); + return; } - final long cooldownTime = System.currentTimeMillis() + cooldown; - if(syncCooldown) - { - plugin.getDatabase().syncCooldown(player, cooldownTime); - } - plugin.cooldowns.put(player.getUniqueId(), cooldownTime); + plugin.getCooldownManager().setCooldown(player); } plugin.openBackpack(player, player, true); } diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/CooldownManager.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/CooldownManager.java new file mode 100644 index 0000000..d2881d5 --- /dev/null +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/CooldownManager.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 GeorgH93 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package at.pcgamingfreaks.Minepacks.Bukkit; + +import at.pcgamingfreaks.Minepacks.Bukkit.API.Callback; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class CooldownManager extends BukkitRunnable implements Listener +{ + private final Minepacks plugin; + private final Map cooldowns = new HashMap<>(); + private final long cooldown; + private final boolean syncCooldown, addOnJoin, clearOnLeave; + + public CooldownManager(Minepacks plugin) + { + this.plugin = plugin; + + cooldown = plugin.getConfiguration().getCommandCooldown(); + syncCooldown = plugin.getConfiguration().isCommandCooldownSyncEnabled(); + addOnJoin = plugin.getConfiguration().isCommandCooldownAddOnJoinEnabled(); + clearOnLeave = plugin.getConfiguration().isCommandCooldownClearOnLeaveEnabled(); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + + runTaskTimer(plugin, plugin.getConfiguration().getCommandCooldownCleanupInterval(), plugin.getConfiguration().getCommandCooldownCleanupInterval()); + } + + public void close() + { + cancel(); + HandlerList.unregisterAll(this); + } + + public void setCooldown(Player player) + { + final long cooldownTime = System.currentTimeMillis() + cooldown; + if(syncCooldown) + { + plugin.getDatabase().syncCooldown(player, cooldownTime); + } + cooldowns.put(player.getUniqueId(), cooldownTime); + } + + @SuppressWarnings("unused") + public boolean isInCooldown(Player player) + { + return cooldowns.getOrDefault(player.getUniqueId(), 0L) > System.currentTimeMillis(); + } + + public long getRemainingCooldown(Player player) + { + long cd = cooldowns.getOrDefault(player.getUniqueId(), 0L); + if(cd > System.currentTimeMillis()) + { + return cd - System.currentTimeMillis(); + } + return 0; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoinEvent(PlayerJoinEvent event) + { + if(syncCooldown) + { + final UUID uuid = event.getPlayer().getUniqueId(); + cooldowns.put(uuid, System.currentTimeMillis() + cooldown); // Temporary cooldown till the data is loaded from the database + plugin.getDatabase().getCooldown(event.getPlayer(), new Callback() { + @Override + public void onResult(Long dbCooldownTime) + { + if(dbCooldownTime > System.currentTimeMillis()) + { + cooldowns.put(uuid, dbCooldownTime); + } + } + + @Override + public void onFail() {} + }); + } + else if(addOnJoin) + { + setCooldown(event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerLeaveEvent(PlayerQuitEvent event) + { + if(clearOnLeave) cooldowns.remove(event.getPlayer().getUniqueId()); + } + + @Override + public void run() + { + cooldowns.entrySet().removeIf(entry -> entry.getValue() < System.currentTimeMillis()); + } +} \ No newline at end of file diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Config.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Config.java index efd0630..c16cd47 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Config.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Config.java @@ -198,12 +198,27 @@ public boolean isBungeeCordModeEnabled() public long getCommandCooldown() { - return getConfig().getInt("CommandCooldown", -1) * 1000L; + return getConfig().getInt("Cooldown.Command", -1) * 1000L; } public boolean isCommandCooldownSyncEnabled() { - return getConfig().getBoolean("SyncCooldown", false); + return getConfig().getBoolean("Cooldown.Sync", false); + } + + public boolean isCommandCooldownClearOnLeaveEnabled() + { + return getConfig().getBoolean("Cooldown.ClearOnLeave", false); + } + + public boolean isCommandCooldownAddOnJoinEnabled() + { + return getConfig().getBoolean("Cooldown.AddOnJoin", true); + } + + public long getCommandCooldownCleanupInterval() + { + return getConfig().getInt("Cooldown.CleanupInterval", 600) * 20L; } public Collection getAllowedGameModes() diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Database.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Database.java index 83f3b25..f370a5a 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Database.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Database.java @@ -222,7 +222,9 @@ public void updatePlayerAndLoadBackpack(Player player) public abstract void saveBackpack(Backpack backpack); - public abstract void syncCooldown(Player player, long time); + public void syncCooldown(Player player, long time) {} + + public void getCooldown(final Player player, final Callback callback) {} protected abstract void loadBackpack(final OfflinePlayer player, final Callback callback); } \ No newline at end of file diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Files.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Files.java index 5b30870..3c530b0 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Files.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Files.java @@ -143,12 +143,6 @@ public void saveBackpack(Backpack backpack) } } - @Override - public void syncCooldown(Player player, long time) - { - // There is no reason for cooldown syncing in file mode - } - @Override protected void loadBackpack(final OfflinePlayer player, final Callback callback) { diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Helper/OldFileUpdater.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Helper/OldFileUpdater.java index 712ed4f..73d5800 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Helper/OldFileUpdater.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/Helper/OldFileUpdater.java @@ -41,8 +41,8 @@ public static void updateConfig(YAML oldYAML, YAML newYAML) } switch(key) { - case "CommandCooldown": oldKey = "command_cooldown"; break; - case "SyncCooldown": oldKey = "sync_cooldown"; break; + case "Cooldown.Command": oldKey = "command_cooldown"; break; + case "Cooldown.Sync": oldKey = "sync_cooldown"; break; case "DropOnDeath": oldKey = "drop_on_death"; break; case "MaxSize": oldKey = "max_size"; break; case "AllowedGameModes": oldKey = "allowed_game_modes"; break; diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/SQL.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/SQL.java index 141ae18..31ea80f 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/SQL.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Database/SQL.java @@ -40,7 +40,7 @@ import java.util.*; public abstract class SQL extends Database -{ //TODO load cooldown +{ private HikariDataSource dataSource; protected String tablePlayers, tableBackpacks, tableCooldowns; // Table Names @@ -212,6 +212,7 @@ protected final void buildQuerys() queryGetPlayerID = "SELECT {FieldPlayerID} FROM {TablePlayers} WHERE {FieldUUID}=?;"; queryGetBP += "{FieldUUID}=?;"; querySyncCooldown = "INSERT INTO {TableCooldowns} ({FieldCDPlayer},{FieldCDTime}) SELECT {FieldPlayerID},? FROM {TablePlayers} WHERE {FieldUUID}=?;"; + queryGetCooldown = "SELECT * FROM {TableCooldowns} WHERE {FieldCDPlayer} IN (SELECT {FieldPlayerID} FROM {TablePlayers} WHERE {FieldUUID}=?);"; } else { @@ -219,6 +220,7 @@ protected final void buildQuerys() queryGetPlayerID = "SELECT {FieldPlayerID} FROM {TablePlayers} WHERE {FieldName}=?;"; queryGetBP += "{FieldName}=?;"; querySyncCooldown = "INSERT INTO {TableCooldowns} ({FieldCDPlayer},{FieldCDTime}) SELECT {FieldPlayerID},? FROM {TablePlayers} WHERE {FieldName}=?;"; + queryGetCooldown = "SELECT * FROM {TableCooldowns} WHERE {FieldCDPlayer} IN (SELECT {FieldPlayerID} FROM {TablePlayers} WHERE {FieldName}=?);"; } queryInsertBp = "REPLACE INTO {TableBackpacks} ({FieldBPOwner},{FieldBPITS},{FieldBPVersion}) VALUES (?,?,?);"; queryUpdateBp = "UPDATE {TableBackpacks} SET {FieldBPITS}=?,{FieldBPVersion}=?,{FieldBPLastUpdate}={NOW} WHERE {FieldBPOwner}=?;"; @@ -251,6 +253,7 @@ protected void setTableAndFieldNames() queryDeleteOldBackpacks = replacePlaceholders(queryDeleteOldBackpacks.replaceAll("\\{VarMaxAge}", maxAge + "")); queryGetUnsetOrInvalidUUIDs = replacePlaceholders(queryGetUnsetOrInvalidUUIDs); querySyncCooldown = replacePlaceholders(querySyncCooldown); + queryGetCooldown = replacePlaceholders(queryGetCooldown); queryDeleteOldCooldowns = replacePlaceholders(queryDeleteOldCooldowns); } @@ -320,7 +323,7 @@ public void saveBackpack(final Backpack backpack) { if(rs.next()) { - final int newID = rs.getInt(1); + final int newID = rs.getInt(fieldPlayerID); DBTools.runStatement(connection, queryInsertBp, newID, data, usedSerializer); plugin.getServer().getScheduler().runTask(plugin, () -> backpack.setOwnerID(newID)); } @@ -374,9 +377,9 @@ protected void loadBackpack(final OfflinePlayer player, final Callback { if(rs.next()) { - bpID = rs.getInt(1); - version = rs.getInt(3); - data = rs.getBytes(2); + bpID = rs.getInt(fieldBpOwner); + version = rs.getInt(fieldBpVersion); + data = rs.getBytes(fieldBpIts); } else { @@ -410,4 +413,25 @@ public void syncCooldown(Player player, long cooldownTime) { runStatementAsync(querySyncCooldown, new Timestamp(cooldownTime), getPlayerNameOrUUID(player)); } + + @Override + public void getCooldown(final Player player, final Callback callback) + { + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { + try(Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(queryGetCooldown)) + { + ps.setString(1, getPlayerNameOrUUID(player)); + try(ResultSet rs = ps.executeQuery()) + { + final long time = (rs.next()) ? rs.getLong(fieldCdTime) : 0; + plugin.getServer().getScheduler().runTask(plugin, () -> callback.onResult(time)); + } + } + catch(SQLException e) + { + e.printStackTrace(); + plugin.getServer().getScheduler().runTask(plugin, () -> callback.onResult(0L)); + } + }); + } } \ No newline at end of file diff --git a/src/at/pcgamingfreaks/Minepacks/Bukkit/Minepacks.java b/src/at/pcgamingfreaks/Minepacks/Bukkit/Minepacks.java index b8aa15a..42da5b4 100644 --- a/src/at/pcgamingfreaks/Minepacks/Bukkit/Minepacks.java +++ b/src/at/pcgamingfreaks/Minepacks/Bukkit/Minepacks.java @@ -55,9 +55,6 @@ import java.io.File; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; public class Minepacks extends JavaPlugin implements MinepacksPlugin { @@ -69,8 +66,6 @@ public class Minepacks extends JavaPlugin implements MinepacksPlugin private Language lang; private Database database; - public final Map cooldowns = new HashMap<>(); - public String backpackTitleOther = "%s Backpack", backpackTitle = "Backpack"; public Message messageNoPermission, messageInvalidBackpack, messageWorldDisabled, messageNotFromConsole; @@ -80,6 +75,7 @@ public class Minepacks extends JavaPlugin implements MinepacksPlugin private ItemsCollector collector; private CommandManager commandManager; private Collection gameModes; + private CooldownManager cooldownManager = null; public static Minepacks getInstance() { @@ -194,6 +190,7 @@ private void load() } gameModes = config.getAllowedGameModes(); + if(config.getCommandCooldown() > 0) cooldownManager = new CooldownManager(this); } private void unload() @@ -204,7 +201,8 @@ private void unload() HandlerList.unregisterAll(this); // Stop the listeners getServer().getScheduler().cancelTasks(this); // Kill all running task database.close(); // Close the DB connection, we won't need them any longer - cooldowns.clear(); + if(cooldownManager != null) cooldownManager.close(); + cooldownManager = null; } public void reload() @@ -328,4 +326,9 @@ public boolean isPlayerGameModeAllowed(Player player) { return gameModes.contains(player.getGameMode()) || player.hasPermission("backpack.ignoreGameMode"); } + + public @Nullable CooldownManager getCooldownManager() + { + return cooldownManager; + } } \ No newline at end of file