forked from Upstream/VillagerTradeLimiter
Version 1.5.0-pre3:
* Added global setting for per-player cooldowns To-do: * Add per-villager restock cooldowns * Add enchantments, data, etc. to ingredients and result * Add config editor GUI in-game
This commit is contained in:
parent
0e63df608f
commit
3aba0ff1c7
@ -2,13 +2,20 @@ package com.pretzel.dev.villagertradelimiter.data;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Cooldown {
|
||||
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
|
||||
|
||||
private enum Interval {
|
||||
s(1000L),
|
||||
m(60000L),
|
||||
h(3600000L),
|
||||
d(86400000L),
|
||||
w(604800000L);
|
||||
s(1L),
|
||||
m(60L),
|
||||
h(3600L),
|
||||
d(86400L),
|
||||
w(604800L);
|
||||
|
||||
final long factor;
|
||||
Interval(long factor) {
|
||||
@ -17,17 +24,31 @@ public class Cooldown {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeStr The cooldown time as written in config.yml (7d, 30s, 5m, etc)
|
||||
* @return The cooldown time in milliseconds
|
||||
* @param cooldownStr The cooldown time as written in config.yml (7d, 30s, 5m, etc)
|
||||
* @return The cooldown time in seconds
|
||||
*/
|
||||
public static long parseTime(final String timeStr) {
|
||||
public static long parseCooldown(final String cooldownStr) {
|
||||
if(cooldownStr.equals("0")) return 0;
|
||||
try {
|
||||
long time = Long.parseLong(timeStr.substring(0, timeStr.length()-1));
|
||||
String interval = timeStr.substring(timeStr.length()-1).toLowerCase();
|
||||
long time = Long.parseLong(cooldownStr.substring(0, cooldownStr.length()-1));
|
||||
String interval = cooldownStr.substring(cooldownStr.length()-1).toLowerCase();
|
||||
return time * Interval.valueOf(interval).factor;
|
||||
} catch (Exception e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static String formatTime(Date date) {
|
||||
return FORMAT.format(date);
|
||||
}
|
||||
|
||||
public static Date parseTime(final String timeStr) {
|
||||
try {
|
||||
return FORMAT.parse(timeStr);
|
||||
} catch (ParseException e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import com.pretzel.dev.villagertradelimiter.wrappers.VillagerWrapper;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PlayerData {
|
||||
private final HashMap<String, Long> tradingCooldowns;
|
||||
private final HashMap<String, String> tradingCooldowns;
|
||||
private VillagerWrapper tradingVillager;
|
||||
|
||||
public PlayerData() {
|
||||
@ -14,7 +14,7 @@ public class PlayerData {
|
||||
}
|
||||
|
||||
/** @return The map of items to timestamps for the player's trading history */
|
||||
public HashMap<String, Long> getTradingCooldowns() { return this.tradingCooldowns; }
|
||||
public HashMap<String, String> getTradingCooldowns() { return this.tradingCooldowns; }
|
||||
|
||||
/** @param tradingVillager The villager that this player is currently trading with */
|
||||
public void setTradingVillager(final VillagerWrapper tradingVillager) { this.tradingVillager = tradingVillager; }
|
||||
|
@ -6,6 +6,8 @@ import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DatabaseManager {
|
||||
@ -13,7 +15,7 @@ public class DatabaseManager {
|
||||
"CREATE TABLE IF NOT EXISTS vtl_cooldown("+
|
||||
"uuid CHAR(36) NOT NULL,"+
|
||||
"item VARCHAR(255) NOT NULL,"+
|
||||
"time BIGINT NOT NULL,"+
|
||||
"time TEXT NOT NULL,"+
|
||||
"PRIMARY KEY(uuid, item));";
|
||||
private static final String SELECT_ITEMS = "SELECT * FROM vtl_cooldown;";
|
||||
private static final String INSERT_ITEM = "INSERT OR IGNORE INTO vtl_cooldown(uuid,item,time) VALUES?;"; //INSERT IGNORE INTO for MySQL
|
||||
@ -46,7 +48,9 @@ public class DatabaseManager {
|
||||
|
||||
UUID uuid = UUID.fromString(tokens[0]);
|
||||
String item = tokens[1];
|
||||
long time = Long.parseLong(tokens[2]);
|
||||
final Date date = Cooldown.parseTime(tokens[2]);
|
||||
if(date == null) continue;
|
||||
long time = date.getTime();
|
||||
|
||||
PlayerData playerData = instance.getPlayerData().get(uuid);
|
||||
if(playerData == null) {
|
||||
@ -54,9 +58,12 @@ public class DatabaseManager {
|
||||
instance.getPlayerData().put(uuid, playerData);
|
||||
}
|
||||
|
||||
String cooldown = instance.getCfg().getString("Overrides."+item+".Cooldown", null);
|
||||
if(cooldown != null && System.currentTimeMillis() < time + Cooldown.parseTime(cooldown)) {
|
||||
playerData.getTradingCooldowns().put(item, time);
|
||||
final Date now = Date.from(Instant.now());
|
||||
final String global = instance.getCfg().getString("Cooldown", "0");
|
||||
final String local = instance.getCfg().getString("Overrides."+item+".Cooldown", global);
|
||||
long cooldown = Cooldown.parseCooldown(local);
|
||||
if(cooldown != 0 && now.getTime()/1000L < time/1000L + cooldown) {
|
||||
playerData.getTradingCooldowns().put(item, Cooldown.formatTime(date));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,7 +88,7 @@ public class DatabaseManager {
|
||||
|
||||
String values = "";
|
||||
for(String item : playerData.getTradingCooldowns().keySet()) {
|
||||
long time = playerData.getTradingCooldowns().get(item);
|
||||
String time = playerData.getTradingCooldowns().get(item);
|
||||
|
||||
if(!values.isEmpty()) values += ",";
|
||||
values += "('"+uuid+"','"+item+"','"+time+"')";
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||
@ -18,7 +19,11 @@ import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.MerchantRecipe;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
public class InventoryListener implements Listener {
|
||||
|
||||
private final VillagerTradeLimiter instance;
|
||||
private final Settings settings;
|
||||
|
||||
@ -74,7 +79,13 @@ public class InventoryListener implements Listener {
|
||||
if(overrides == null) return;
|
||||
|
||||
final String type = settings.getType(result, ingredient1, ingredient2);
|
||||
if(type == null || !overrides.contains(type+".Cooldown")) return;
|
||||
if(instance.getCfg().getString("Cooldown", "0").equals("0")) {
|
||||
Util.consoleMsg("global cooldown is 0");
|
||||
if(overrides.getString(type+".Cooldown", "0").equals("0")) {
|
||||
Util.consoleMsg("local cooldown is 0");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Get the selected recipe by the items in the slots
|
||||
final MerchantRecipe selectedRecipe = getSelectedRecipe((Villager)event.getInventory().getHolder(), ingredient1, ingredient2, result);
|
||||
@ -89,7 +100,7 @@ public class InventoryListener implements Listener {
|
||||
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
||||
int uses = selectedRecipe.getUses();
|
||||
if(!playerData.getTradingCooldowns().containsKey(type) && uses >= selectedRecipe.getMaxUses()) {
|
||||
playerData.getTradingCooldowns().put(type, System.currentTimeMillis());
|
||||
playerData.getTradingCooldowns().put(type, Cooldown.formatTime(Date.from(Instant.now())));
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
@ -172,9 +174,14 @@ public class PlayerListener implements Listener {
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
if(overrides != null) {
|
||||
final String type = settings.getType(recipe.getItemStack("sell"), recipe.getItemStack("buy"), recipe.getItemStack("buyB"));
|
||||
if(type != null && overrides.contains(type+".Cooldown")) {
|
||||
final String global = instance.getCfg().getString("Cooldown", "0");
|
||||
final String local = overrides.getString(type+".Cooldown", global);
|
||||
if(type != null && !local.equals("0")) {
|
||||
if(playerData.getTradingCooldowns().containsKey(type)) {
|
||||
if(System.currentTimeMillis() >= playerData.getTradingCooldowns().get(type) + Cooldown.parseTime(overrides.getString(type+".Cooldown"))) {
|
||||
final Date now = Date.from(Instant.now());
|
||||
final Date lastTrade = Cooldown.parseTime(playerData.getTradingCooldowns().get(type));
|
||||
long cooldown = Cooldown.parseCooldown(local);
|
||||
if(lastTrade != null && (now.getTime()/1000L >= lastTrade.getTime()/1000L + cooldown)) {
|
||||
playerData.getTradingCooldowns().remove(type);
|
||||
} else {
|
||||
maxUses = 0;
|
||||
|
@ -66,24 +66,34 @@ public class Settings {
|
||||
final String resultType = result.getType().name().toLowerCase();
|
||||
final String ingredient1Type = ingredient1.getType().name().toLowerCase();
|
||||
final String ingredient2Type = ingredient2.getType().name().toLowerCase();
|
||||
final String defaultType;
|
||||
if(result.getType() == Material.EMERALD) {
|
||||
if(ingredient1.getType() == Material.BOOK || ingredient1.getType() == Material.AIR) {
|
||||
defaultType = ingredient2Type;
|
||||
} else {
|
||||
defaultType = ingredient1Type;
|
||||
}
|
||||
} else {
|
||||
defaultType = resultType;
|
||||
}
|
||||
|
||||
if(result.getType() == Material.ENCHANTED_BOOK) {
|
||||
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) result.getItemMeta();
|
||||
if(meta == null) return null;
|
||||
if(meta == null) return defaultType;
|
||||
for(Enchantment key : meta.getStoredEnchants().keySet()) {
|
||||
if (key != null) {
|
||||
final String itemType = key.getKey().getKey() +"_"+meta.getStoredEnchantLevel(key);
|
||||
if(getItem(ingredient1, result, itemType) != null) return itemType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
final ItemStack ingredient = (ingredient1.getType() == Material.AIR ? ingredient2 : ingredient1);
|
||||
if(getItem(ingredient, result, resultType) != null) return resultType;
|
||||
if(getItem(ingredient, result, ingredient1Type) != null) return ingredient1Type;
|
||||
if(getItem(ingredient, result, ingredient2Type) != null) return ingredient2Type;
|
||||
return null;
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,18 @@ MaxDemand: -1
|
||||
# For more information, see https://minecraft.fandom.com/el/wiki/Trading#Java_Edition
|
||||
MaxUses: -1
|
||||
|
||||
# The per-player, per-trade cooldown in real-world time.
|
||||
# After a player makes a trade <MaxUses> times, the trade will be disabled for the player until the cooldown is over.
|
||||
# * Set to 0 to disable this feature and keep vanilla behavior
|
||||
# * Set to a number and interval to add a per-player, per-trade cooldown for all trades (see below)
|
||||
# A valid cooldown follows the <Number><Interval> format, such as 7d or 30s. The valid intervals are:
|
||||
# * s = seconds (e.g. 30s)
|
||||
# * m = minutes (e.g. 10m)
|
||||
# * h = hours (e.g. 1h)
|
||||
# * d = days (e.g. 3d)
|
||||
# * w = weeks (e.g. 2w)
|
||||
Cooldown: 0
|
||||
|
||||
|
||||
#-------------------------------- PER-ITEM SETTINGS --------------------------------#
|
||||
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
||||
|
Loading…
Reference in New Issue
Block a user