Compare commits

..

1 Commits

Author SHA1 Message Date
58f8e617c8 Actualiser README.md 2025-08-27 17:15:00 +00:00
10 changed files with 23 additions and 169 deletions

View File

@ -4,7 +4,7 @@ Le plugin Minecraft fait pour montrer le regret de l'équipement perdu.
* Affiche l'élément le plus cher perdu * Affiche l'élément le plus cher perdu
* Utilise les prix du fichier worth.yml de Essentials (fallback) ou les prix de UltimateShop (recommandé) * Utilise les prix du fichier worth.yml de Essentials (fallback) ou les prix de UltimateShop (recommandé)
**Requiert d'avoir UltimateShop >= 3.10.2 !** **Dépendant d'UltimateShop >= 3.10.2 ou d'Essentials, fonctionne avec le nombre d'items en cas d'absence des deux premiers.**
Suite du README à venir. Suite du README à venir.

View File

@ -21,7 +21,6 @@ import java.util.logging.Level;
public final class PainfulLoss extends JavaPlugin { public final class PainfulLoss extends JavaPlugin {
public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
public static final String DEFAULT_LOCALE_TAG = "en-US"; public static final String DEFAULT_LOCALE_TAG = "en-US";
private static boolean debug = false;
@Getter private static PainfulLoss instance = null; @Getter private static PainfulLoss instance = null;
@Getter private static PriceProvider priceProvider = null; @Getter private static PriceProvider priceProvider = null;
@ -30,7 +29,6 @@ public final class PainfulLoss extends JavaPlugin {
PainfulLoss.instance = this; PainfulLoss.instance = this;
Message.init(DEFAULT_LOCALE); Message.init(DEFAULT_LOCALE);
configurationInit(); configurationInit();
PainfulLoss.debug = PropertiesEnum.DEBUG.getBoolean();
langInit(); langInit();
registerCommands(); registerCommands();
initPriceProvider(); initPriceProvider();
@ -111,7 +109,6 @@ public final class PainfulLoss extends JavaPlugin {
public void reload() { public void reload() {
reloadConfig(); reloadConfig();
PainfulLoss.debug = PropertiesEnum.DEBUG.getBoolean();
Message.clearCache(); Message.clearCache();
langInit(); langInit();
initPriceProvider(); initPriceProvider();
@ -124,8 +121,4 @@ public final class PainfulLoss extends JavaPlugin {
public static void info(String message) { public static void info(String message) {
PainfulLoss.log(Level.INFO, message); PainfulLoss.log(Level.INFO, message);
} }
public static void debug(String message) {
if (PainfulLoss.debug) Bukkit.getLogger().log(Level.INFO, "[Painful Loss DEBUG] {0}", message);
}
} }

View File

@ -76,12 +76,8 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter {
private boolean commandWorth(CommandSender commandSender, String[] args) { private boolean commandWorth(CommandSender commandSender, String[] args) {
if (args.length == 1 && commandSender instanceof Player p && commandSender.hasPermission("painfulloss.worth")) { if (args.length == 1 && commandSender instanceof Player p && commandSender.hasPermission("painfulloss.worth")) {
double t1 = System.currentTimeMillis();
PainfulLoss.debug("Starting clock for worth...");
commandSender.sendMessage(Message.of("command.worth", PriceCalculator.estimate(p) commandSender.sendMessage(Message.of("command.worth", PriceCalculator.estimate(p)
+ (PropertiesEnum.ENCHANT_ENABLED.getBoolean() ? PriceCalculator.enchantEstimate(p) : 0))); + (PropertiesEnum.ENCHANT_ENABLED.getBoolean() ? PriceCalculator.enchantEstimate(p) : 0)));
double t2 = System.currentTimeMillis();
PainfulLoss.debug("It took " + (t2-t1) + "ms to get the job done !");
} else if (args.length == 2 && commandSender.hasPermission("painfulloss.worth.other")) { } else if (args.length == 2 && commandSender.hasPermission("painfulloss.worth.other")) {
Player p = PainfulLoss.getInstance().getServer().getPlayer(args[1]); Player p = PainfulLoss.getInstance().getServer().getPlayer(args[1]);
if (p == null) { if (p == null) {
@ -93,7 +89,7 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter {
} else { } else {
return commandNotAutorised(commandSender); return commandNotAutorised(commandSender);
} }
return true; return false;
} }
@Override @Override

View File

@ -7,19 +7,19 @@ import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
public class CountPriceProvider extends PriceProvider{ public class CountPriceProvider implements PriceProvider{
@Override @Override
public String getName() { public String getName() {
return "Count"; return "Count";
} }
@Override @Override
public Optional<Double> getProviderPrice(ItemStack stack, @Nullable Player player) { public Optional<Double> getPrice(ItemStack stack, @Nullable Player player) {
return Optional.of((double) stack.getAmount()); return Optional.of((double) stack.getAmount());
} }
@Override @Override
public Optional<Double> getProviderPrices(List<ItemStack> stack, @org.jetbrains.annotations.Nullable Player player) { public Optional<Double> getPrices(List<ItemStack> stack, @org.jetbrains.annotations.Nullable Player player) {
return Optional.of(stack.stream().mapToDouble(ItemStack::getAmount).sum()); return Optional.of(stack.stream().mapToDouble(ItemStack::getAmount).sum());
} }

View File

@ -9,7 +9,7 @@ import javax.annotation.Nullable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Optional; import java.util.Optional;
public class EssentialsPriceProvider extends PriceProvider { public class EssentialsPriceProvider implements PriceProvider {
private final IEssentials plugin; private final IEssentials plugin;
private final Worth worth; private final Worth worth;
private boolean ready = false; private boolean ready = false;
@ -25,13 +25,13 @@ public class EssentialsPriceProvider extends PriceProvider {
} }
@Override @Override
public Optional<Double> getProviderPrice(ItemStack stack, @Nullable Player player) { public Optional<Double> getPrice(ItemStack stack, @Nullable Player player) {
if (stack == null || stack.getAmount() <= 0) return Optional.empty(); if (stack == null || stack.getAmount() <= 0) return Optional.empty();
try { try {
BigDecimal price = worth.getPrice(plugin, stack); BigDecimal price = worth.getPrice(plugin, stack);
if (price == null) return Optional.empty(); if (price == null) return Optional.empty();
if (price.doubleValue() <= 0) return Optional.empty(); if (price.signum() < 0) return Optional.empty();
return Optional.of(price.doubleValue()); return Optional.of(price.doubleValue());
} catch (Exception e) { } catch (Exception e) {
return Optional.empty(); return Optional.empty();

View File

@ -1,149 +1,20 @@
package fr.molzonas.painfulloss.provider; package fr.molzonas.painfulloss.provider;
import fr.molzonas.painfulloss.PainfulLoss;
import fr.molzonas.painfulloss.utils.PropertiesEnum;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.*; import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.math.BigDecimal; import java.util.List;
import java.util.*; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public abstract class PriceProvider { public interface PriceProvider {
private final Map<Material, BigDecimal> cachedPrices = new ConcurrentHashMap<>(); String getName();
private final Set<Material> tested = java.util.Collections.newSetFromMap(new ConcurrentHashMap<>()); Optional<Double> getPrice(ItemStack stack, @Nullable Player player);
default Optional<Double> getPrices(List<ItemStack> stack, @Nullable Player player) {
public abstract String getName();
protected abstract Optional<Double> getProviderPrice(ItemStack stack, @Nullable Player player);
protected Optional<Double> getProviderPrices(List<ItemStack> stack, @Nullable Player player) {
if (stack.isEmpty()) return Optional.empty();
return Optional.of(stack.stream().mapToDouble(x -> this.getProviderPrice(x, player).orElse(0d)).sum());
}
public Optional<Double> getPrice(ItemStack item, @Nullable Player player) {
if (item == null || item.getAmount() <= 0 || item.getType().isAir()) return Optional.empty();
ItemStack stack = item.clone();
stack.setAmount(1);
if (cachedPrices.containsKey(stack.getType())) {
BigDecimal price = cachedPrices.get(stack.getType());
return price.doubleValue() > 0d ? Optional.of(price.doubleValue() * item.getAmount()) : Optional.empty();
}
PainfulLoss.debug("Item not cached : " + stack.getType().name() + " ! Searching for values.");
Optional<Double> priceFromProvider = getProviderPrice(stack, player);
if (priceFromProvider.isPresent() && priceFromProvider.orElse(-1d) > 0) {
cachedPrices.put(stack.getType(), BigDecimal.valueOf(priceFromProvider.get()));
PainfulLoss.debug("Found " + stack.getType().name() + " price in the " + this.getName() + " provider for "+ priceFromProvider.get() + "$ per unit.");
return Optional.of(priceFromProvider.orElse(0d) * item.getAmount());
}
if (PropertiesEnum.RECIPE_COST_IF_NO_PRICE_FOUND.getBoolean()) {
// Anti-loop guard - if the material was already tested, return empty
if (!tested.add(stack.getType())) return Optional.empty();
PainfulLoss.debug("Time to search components of " + stack.getType().name() + " !");
try {
RecipePrice recipePrice = new RecipePrice(stack.clone(), player);
double priceFromIngredients = recipePrice.getLowestRecipePrice();
if (priceFromIngredients > 0) {
PainfulLoss.debug("And the components total price for " + stack.getType().name() + " is " + priceFromIngredients + "$ !");
cachedPrices.put(stack.getType(), BigDecimal.valueOf(priceFromIngredients));
return Optional.of(priceFromIngredients * item.getAmount());
}
} finally {
PainfulLoss.debug("Let's get back from " + stack.getType().name() + " after this search.");
tested.remove(stack.getType());
}
}
// If nothing is found : the price is NULL
cachedPrices.put(stack.getType(), BigDecimal.valueOf(-1));
PainfulLoss.debug("No price found for " + item.getType().name() + ". Registering it to 0$ !");
return Optional.empty();
}
public Optional<Double> getPrices(List<ItemStack> stack, @Nullable Player player) {
if (stack.isEmpty()) return Optional.empty(); if (stack.isEmpty()) return Optional.empty();
return Optional.of(stack.stream().mapToDouble(x -> this.getPrice(x, player).orElse(0d)).sum()); return Optional.of(stack.stream().mapToDouble(x -> this.getPrice(x, player).orElse(0d)).sum());
} }
public boolean isReady() { default boolean isReady() { return true; }
return true; default boolean isCountBased() { return false; }
} default void reload() { }
public boolean isCountBased() {
return false;
}
public void reload() {
this.cachedPrices.clear();
}
class RecipePrice {
final ItemStack item;
final Player player;
final int finalAmount;
RecipePrice(ItemStack item, @Nullable Player player) {
this.item = new ItemStack(item.getType(), 1);
this.player = player;
this.finalAmount = item.getAmount();
}
double getLowestRecipePrice() {
double lowest = 0d;
for (Recipe r : Bukkit.getRecipesFor(item)) {
double total = switch (r) {
case ShapedRecipe sr -> getShapedRecipePrice(sr);
case ShapelessRecipe sr -> getShapelessRecipePrice(sr);
case CookingRecipe<?> sr -> getRecipeChoicePrice(sr.getInputChoice());
default -> 0d;
};
if (total > 0d) {
double unit = total / Math.max(1, r.getResult().getAmount());
if (lowest == 0 || unit < lowest) lowest = unit;
}
}
PainfulLoss.debug("And the lowest price for the recipes was " + lowest + "$.");
return lowest;
}
double getShapedRecipePrice(ShapedRecipe sr) {
double sum = 0d;
Map<Character, RecipeChoice> map = sr.getChoiceMap();
Map<RecipeChoice, Integer> nbOfUnit = new HashMap<>();
for (String s : sr.getShape()) {
for (Character c : s.toCharArray()) {
RecipeChoice rc = map.getOrDefault(c, null);
if (c == ' ' || rc == null) continue;
nbOfUnit.put(rc, nbOfUnit.getOrDefault(rc, 0) + 1);
}
}
for (Map.Entry<RecipeChoice, Integer> entry : nbOfUnit.entrySet()) {
double price = getRecipeChoicePrice(entry.getKey()) * entry.getValue();
sum += price;
}
return sum;
}
double getShapelessRecipePrice(ShapelessRecipe sr) {
return sr.getChoiceList().stream().mapToDouble(this::getRecipeChoicePrice).sum();
}
double getRecipeChoicePrice(RecipeChoice choice) {
if (choice == null) return 0d;
double lowest = 0d;
List<ItemStack> stacks = new ArrayList<>();
if (choice instanceof RecipeChoice.MaterialChoice c) {
stacks = c.getChoices().stream().map(x -> new ItemStack(x, 1)).toList();
}
if (choice instanceof RecipeChoice.ExactChoice c) {
stacks = c.getChoices();
}
for (ItemStack stack : stacks) {
double price = getPrice(stack, player).orElse(0d);
if (price > 0 && (lowest == 0 || lowest > price)) lowest = price;
}
return lowest;
}
}
} }

View File

@ -13,7 +13,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
public class UltimateShopPriceProvider extends PriceProvider { public class UltimateShopPriceProvider implements PriceProvider {
private final Reflect reflect; private final Reflect reflect;
private String economyProvider; private String economyProvider;
private boolean ready = true; private boolean ready = true;
@ -39,10 +39,9 @@ public class UltimateShopPriceProvider extends PriceProvider {
} }
@Override @Override
public Optional<Double> getProviderPrice(ItemStack stack, @Nullable Player player) { public Optional<Double> getPrice(ItemStack stack, @Nullable Player player) {
try { try {
double price = reflect.priceFor(stack, player, economyProvider); return Optional.of(reflect.priceFor(stack, player, economyProvider));
return price > 0 ? Optional.of(price) : Optional.empty();
} catch (Exception e) { } catch (Exception e) {
return Optional.empty(); return Optional.empty();
} }

View File

@ -13,8 +13,7 @@ public enum PropertiesEnum {
PRICE_PROVIDER("priceProvider"), PRICE_PROVIDER("priceProvider"),
ENCHANT_ENABLED("estimation.enchant.enabled"), ENCHANT_ENABLED("estimation.enchant.enabled"),
ENCHANT_DEFAULT_PER_LEVEL("estimation.enchant.default_per_level"), ENCHANT_DEFAULT_PER_LEVEL("estimation.enchant.default_per_level"),
ECONOMY_PROVIDER("economyProvider", "Vault"), ECONOMY_PROVIDER("economyProvider", "Vault")
RECIPE_COST_IF_NO_PRICE_FOUND("recipeCostIfNoPriceFound", "false")
; ;
@Getter private final String path; @Getter private final String path;

View File

@ -11,10 +11,6 @@ priceProvider:
# Used to do estimations with UltimateShop, can only be Vault... for now. # Used to do estimations with UltimateShop, can only be Vault... for now.
economyProvider: Vault economyProvider: Vault
# If the product price isn't found, use the components of this product to get the price
# Disable this if you already defined every item or if the plugin is laggy
recipeCostIfNoPriceFound: true
estimation: estimation:
# Is enchants on items used in worth calculation ? # Is enchants on items used in worth calculation ?
enchant: enchant: