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 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 {
|
public class Cooldown {
|
||||||
|
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
|
||||||
|
|
||||||
private enum Interval {
|
private enum Interval {
|
||||||
s(1000L),
|
s(1L),
|
||||||
m(60000L),
|
m(60L),
|
||||||
h(3600000L),
|
h(3600L),
|
||||||
d(86400000L),
|
d(86400L),
|
||||||
w(604800000L);
|
w(604800L);
|
||||||
|
|
||||||
final long factor;
|
final long factor;
|
||||||
Interval(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)
|
* @param cooldownStr The cooldown time as written in config.yml (7d, 30s, 5m, etc)
|
||||||
* @return The cooldown time in milliseconds
|
* @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 {
|
try {
|
||||||
long time = Long.parseLong(timeStr.substring(0, timeStr.length()-1));
|
long time = Long.parseLong(cooldownStr.substring(0, cooldownStr.length()-1));
|
||||||
String interval = timeStr.substring(timeStr.length()-1).toLowerCase();
|
String interval = cooldownStr.substring(cooldownStr.length()-1).toLowerCase();
|
||||||
return time * Interval.valueOf(interval).factor;
|
return time * Interval.valueOf(interval).factor;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.errorMsg(e);
|
Util.errorMsg(e);
|
||||||
}
|
}
|
||||||
return 0;
|
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;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class PlayerData {
|
public class PlayerData {
|
||||||
private final HashMap<String, Long> tradingCooldowns;
|
private final HashMap<String, String> tradingCooldowns;
|
||||||
private VillagerWrapper tradingVillager;
|
private VillagerWrapper tradingVillager;
|
||||||
|
|
||||||
public PlayerData() {
|
public PlayerData() {
|
||||||
@ -14,7 +14,7 @@ public class PlayerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return The map of items to timestamps for the player's trading history */
|
/** @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 */
|
/** @param tradingVillager The villager that this player is currently trading with */
|
||||||
public void setTradingVillager(final VillagerWrapper tradingVillager) { this.tradingVillager = tradingVillager; }
|
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 com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class DatabaseManager {
|
public class DatabaseManager {
|
||||||
@ -13,7 +15,7 @@ public class DatabaseManager {
|
|||||||
"CREATE TABLE IF NOT EXISTS vtl_cooldown("+
|
"CREATE TABLE IF NOT EXISTS vtl_cooldown("+
|
||||||
"uuid CHAR(36) NOT NULL,"+
|
"uuid CHAR(36) NOT NULL,"+
|
||||||
"item VARCHAR(255) NOT NULL,"+
|
"item VARCHAR(255) NOT NULL,"+
|
||||||
"time BIGINT NOT NULL,"+
|
"time TEXT NOT NULL,"+
|
||||||
"PRIMARY KEY(uuid, item));";
|
"PRIMARY KEY(uuid, item));";
|
||||||
private static final String SELECT_ITEMS = "SELECT * FROM vtl_cooldown;";
|
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
|
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]);
|
UUID uuid = UUID.fromString(tokens[0]);
|
||||||
String item = tokens[1];
|
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);
|
PlayerData playerData = instance.getPlayerData().get(uuid);
|
||||||
if(playerData == null) {
|
if(playerData == null) {
|
||||||
@ -54,9 +58,12 @@ public class DatabaseManager {
|
|||||||
instance.getPlayerData().put(uuid, playerData);
|
instance.getPlayerData().put(uuid, playerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
String cooldown = instance.getCfg().getString("Overrides."+item+".Cooldown", null);
|
final Date now = Date.from(Instant.now());
|
||||||
if(cooldown != null && System.currentTimeMillis() < time + Cooldown.parseTime(cooldown)) {
|
final String global = instance.getCfg().getString("Cooldown", "0");
|
||||||
playerData.getTradingCooldowns().put(item, time);
|
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 = "";
|
String values = "";
|
||||||
for(String item : playerData.getTradingCooldowns().keySet()) {
|
for(String item : playerData.getTradingCooldowns().keySet()) {
|
||||||
long time = playerData.getTradingCooldowns().get(item);
|
String time = playerData.getTradingCooldowns().get(item);
|
||||||
|
|
||||||
if(!values.isEmpty()) values += ",";
|
if(!values.isEmpty()) values += ",";
|
||||||
values += "('"+uuid+"','"+item+"','"+time+"')";
|
values += "('"+uuid+"','"+item+"','"+time+"')";
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.pretzel.dev.villagertradelimiter.listeners;
|
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||||
|
|
||||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
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.data.PlayerData;
|
||||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
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.ItemStack;
|
||||||
import org.bukkit.inventory.MerchantRecipe;
|
import org.bukkit.inventory.MerchantRecipe;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
public class InventoryListener implements Listener {
|
public class InventoryListener implements Listener {
|
||||||
|
|
||||||
private final VillagerTradeLimiter instance;
|
private final VillagerTradeLimiter instance;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
|
||||||
@ -74,7 +79,13 @@ public class InventoryListener implements Listener {
|
|||||||
if(overrides == null) return;
|
if(overrides == null) return;
|
||||||
|
|
||||||
final String type = settings.getType(result, ingredient1, ingredient2);
|
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
|
//Get the selected recipe by the items in the slots
|
||||||
final MerchantRecipe selectedRecipe = getSelectedRecipe((Villager)event.getInventory().getHolder(), ingredient1, ingredient2, result);
|
final MerchantRecipe selectedRecipe = getSelectedRecipe((Villager)event.getInventory().getHolder(), ingredient1, ingredient2, result);
|
||||||
@ -89,7 +100,7 @@ public class InventoryListener implements Listener {
|
|||||||
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
||||||
int uses = selectedRecipe.getUses();
|
int uses = selectedRecipe.getUses();
|
||||||
if(!playerData.getTradingCooldowns().containsKey(type) && uses >= selectedRecipe.getMaxUses()) {
|
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);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
|
|||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PlayerListener implements Listener {
|
public class PlayerListener implements Listener {
|
||||||
@ -172,9 +174,14 @@ public class PlayerListener implements Listener {
|
|||||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||||
if(overrides != null) {
|
if(overrides != null) {
|
||||||
final String type = settings.getType(recipe.getItemStack("sell"), recipe.getItemStack("buy"), recipe.getItemStack("buyB"));
|
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(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);
|
playerData.getTradingCooldowns().remove(type);
|
||||||
} else {
|
} else {
|
||||||
maxUses = 0;
|
maxUses = 0;
|
||||||
|
@ -66,24 +66,34 @@ public class Settings {
|
|||||||
final String resultType = result.getType().name().toLowerCase();
|
final String resultType = result.getType().name().toLowerCase();
|
||||||
final String ingredient1Type = ingredient1.getType().name().toLowerCase();
|
final String ingredient1Type = ingredient1.getType().name().toLowerCase();
|
||||||
final String ingredient2Type = ingredient2.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) {
|
if(result.getType() == Material.ENCHANTED_BOOK) {
|
||||||
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) result.getItemMeta();
|
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) result.getItemMeta();
|
||||||
if(meta == null) return null;
|
if(meta == null) return defaultType;
|
||||||
for(Enchantment key : meta.getStoredEnchants().keySet()) {
|
for(Enchantment key : meta.getStoredEnchants().keySet()) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
final String itemType = key.getKey().getKey() +"_"+meta.getStoredEnchantLevel(key);
|
final String itemType = key.getKey().getKey() +"_"+meta.getStoredEnchantLevel(key);
|
||||||
if(getItem(ingredient1, result, itemType) != null) return itemType;
|
if(getItem(ingredient1, result, itemType) != null) return itemType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return defaultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ItemStack ingredient = (ingredient1.getType() == Material.AIR ? ingredient2 : ingredient1);
|
final ItemStack ingredient = (ingredient1.getType() == Material.AIR ? ingredient2 : ingredient1);
|
||||||
if(getItem(ingredient, result, resultType) != null) return resultType;
|
if(getItem(ingredient, result, resultType) != null) return resultType;
|
||||||
if(getItem(ingredient, result, ingredient1Type) != null) return ingredient1Type;
|
if(getItem(ingredient, result, ingredient1Type) != null) return ingredient1Type;
|
||||||
if(getItem(ingredient, result, ingredient2Type) != null) return ingredient2Type;
|
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
|
# For more information, see https://minecraft.fandom.com/el/wiki/Trading#Java_Edition
|
||||||
MaxUses: -1
|
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 --------------------------------#
|
#-------------------------------- PER-ITEM SETTINGS --------------------------------#
|
||||||
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
||||||
|
Loading…
Reference in New Issue
Block a user