diff --git a/README.md b/README.md index e1fc51d..fe948e5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Core of economy system for Nukkit ## For developers +> [!WARNING] +> MySQL provider: +> Due to precision issues, MySQL uses bigint to store monetary amounts. The retrieved values need to be divided by 100 to convert them to the actual amounts. + ### Single Currency Economies Developers can access to EconomyAPI's API by using: diff --git a/src/main/java/me/onebone/economyapi/AsyncOperator.java b/src/main/java/me/onebone/economyapi/AsyncOperator.java new file mode 100644 index 0000000..0d10148 --- /dev/null +++ b/src/main/java/me/onebone/economyapi/AsyncOperator.java @@ -0,0 +1,537 @@ +package me.onebone.economyapi; + +import cn.nukkit.IPlayer; +import cn.nukkit.Player; +import cn.nukkit.Server; +import cn.nukkit.scheduler.AsyncTask; +import me.onebone.economyapi.event.money.AddMoneyEvent; +import me.onebone.economyapi.event.money.ReduceMoneyEvent; +import me.onebone.economyapi.event.money.SetMoneyEvent; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class AsyncOperator { + private static class MoneyLookupTask extends AsyncTask { + private final String id; + private final String currencyName; + private final CompletableFuture future; + + public MoneyLookupTask(String id, String currencyName, CompletableFuture future) { + this.id = id; + this.currencyName = currencyName; + this.future = future; + } + + @Override + public void onRun() { + double money = EconomyAPI.getInstance().provider.getMoney(currencyName, id.toLowerCase()); + this.setResult(money); + } + + @Override + public void onCompletion(Server server) { + future.complete((Double) this.getResult()); + } + } + + /** + * Returns money of player asynchronously + * + * @param player + * @return CompletableFuture containing money of player. -1 if player does not exist. + */ + public CompletableFuture myMoney(Player player) { + return this.myMoney(player.getUniqueId()); + } + + public CompletableFuture myMoney(IPlayer player) { + return this.myMoney(player.getUniqueId()); + } + + public CompletableFuture myMoney(UUID id) { + checkAndConvertLegacy(id); + return myMoneyInternal(id.toString()); + } + + public CompletableFuture myMoney(String id) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(this::myMoney) + .orElseGet(() -> myMoneyInternal(id)); + } + + private CompletableFuture myMoneyInternal(String id) { + return myMoneyInternal(id, EconomyAPI.MAIN_CONFIG.getDefaultCurrency().getName()); + } + + private static class MoneySetTask extends AsyncTask { + private final String id; + private final double amount; + private final String currencyName; + private final boolean force; + private final CompletableFuture future; + + public MoneySetTask(String id, double amount, String currencyName, boolean force, CompletableFuture future) { + this.id = id; + this.amount = amount; + this.currencyName = currencyName; + this.force = force; + this.future = future; + } + + @Override + public void onRun() { + String lowerId = id.toLowerCase(); + if (amount < 0) { + this.setResult(EconomyAPI.RET_INVALID); + return; + } + + SetMoneyEvent event = new SetMoneyEvent(lowerId, amount, currencyName); + Server.getInstance().getPluginManager().callEvent(event); + + if (!event.isCancelled() || force) { + if (EconomyAPI.getInstance().provider.accountExists(currencyName, lowerId)) { + double finalAmount = event.getAmount(); + if (finalAmount <= EconomyAPI.getInstance().getMaxMoney(currencyName)) { + EconomyAPI.getInstance().provider.setMoney(currencyName, lowerId, finalAmount); + this.setResult(EconomyAPI.RET_SUCCESS); + } else { + this.setResult(EconomyAPI.RET_INVALID); + } + } else { + this.setResult(EconomyAPI.RET_NO_ACCOUNT); + } + } else { + this.setResult(EconomyAPI.RET_CANCELLED); + } + } + + @Override + public void onCompletion(Server server) { + future.complete((Integer) this.getResult()); + } + } + + public CompletableFuture setMoney(Player player, double amount) { + return this.setMoney(player, amount, false); + } + + public CompletableFuture setMoney(Player player, double amount, boolean force) { + return this.setMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture setMoney(IPlayer player, double amount) { + return this.setMoney(player, amount, false); + } + + public CompletableFuture setMoney(IPlayer player, double amount, boolean force) { + return this.setMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture setMoney(UUID id, double amount) { + return setMoney(id, amount, false); + } + + public CompletableFuture setMoney(UUID id, double amount, boolean force) { + checkAndConvertLegacy(id); + return setMoneyInternal(id.toString(), amount, force); + } + + public CompletableFuture setMoney(String id, double amount) { + return this.setMoney(id, amount, false); + } + + public CompletableFuture setMoney(String id, double amount, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> setMoney(uuid1, amount, force)) + .orElseGet(() -> setMoneyInternal(id, amount, force)); + } + + private CompletableFuture setMoneyInternal(String id, double amount, boolean force) { + return setMoneyInternal(id, amount, EconomyAPI.MAIN_CONFIG.getDefaultCurrency().getName(), force); + } + + + private static class MoneyAddTask extends AsyncTask { + private final String id; + private final double amount; + private final String currencyName; + private final boolean force; + private final CompletableFuture future; + + public MoneyAddTask(String id, double amount, String currencyName, boolean force, CompletableFuture future) { + this.id = id; + this.amount = amount; + this.currencyName = currencyName; + this.force = force; + this.future = future; + } + + @Override + public void onRun() { + String lowerId = id.toLowerCase(); + if (amount < 0) { + this.setResult(EconomyAPI.RET_INVALID); + return; + } + + AddMoneyEvent event = new AddMoneyEvent(lowerId, amount, currencyName); + Server.getInstance().getPluginManager().callEvent(event); + + if (!event.isCancelled() || force) { + double money = EconomyAPI.getInstance().provider.getMoney(currencyName, lowerId); + if (money != -1) { + if (money + amount > EconomyAPI.getInstance().getMaxMoney(currencyName)) { + this.setResult(EconomyAPI.RET_INVALID); + } else { + EconomyAPI.getInstance().provider.addMoney(currencyName, lowerId, amount); + this.setResult(EconomyAPI.RET_SUCCESS); + } + } else { + this.setResult(EconomyAPI.RET_NO_ACCOUNT); + } + } else { + this.setResult(EconomyAPI.RET_CANCELLED); + } + } + + @Override + public void onCompletion(Server server) { + future.complete((Integer) this.getResult()); + } + } + + public CompletableFuture addMoney(Player player, double amount) { + return this.addMoney(player, amount, false); + } + + public CompletableFuture addMoney(Player player, double amount, boolean force) { + return this.addMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture addMoney(IPlayer player, double amount) { + return this.addMoney(player, amount, false); + } + + public CompletableFuture addMoney(IPlayer player, double amount, boolean force) { + return this.addMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture addMoney(UUID id, double amount) { + return addMoney(id, amount, false); + } + + public CompletableFuture addMoney(UUID id, double amount, boolean force) { + checkAndConvertLegacy(id); + return addMoneyInternal(id.toString(), amount, force); + } + + public CompletableFuture addMoney(String id, double amount) { + return this.addMoney(id, amount, false); + } + + public CompletableFuture addMoney(String id, double amount, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> addMoney(uuid1, amount, force)) + .orElseGet(() -> addMoneyInternal(id, amount, force)); + } + + private CompletableFuture addMoneyInternal(String id, double amount, boolean force) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneyAddTask(id, amount, EconomyAPI.MAIN_CONFIG.getDefaultCurrency().getName(), force, future) + ); + return future; + } + + private static class MoneyReduceTask extends AsyncTask { + private final String id; + private final double amount; + private final String currencyName; + private final boolean force; + private final CompletableFuture future; + + public MoneyReduceTask(String id, double amount, String currencyName, boolean force, CompletableFuture future) { + this.id = id; + this.amount = amount; + this.currencyName = currencyName; + this.force = force; + this.future = future; + } + + @Override + public void onRun() { + String lowerId = id.toLowerCase(); + if (amount < 0) { + this.setResult(EconomyAPI.RET_INVALID); + return; + } + + ReduceMoneyEvent event = new ReduceMoneyEvent(lowerId, amount, currencyName); + Server.getInstance().getPluginManager().callEvent(event); + + if (!event.isCancelled() || force) { + double finalAmount = event.getAmount(); + double money = EconomyAPI.getInstance().provider.getMoney(currencyName, lowerId); + if (money != -1) { + if (money - finalAmount < 0) { + this.setResult(EconomyAPI.RET_INVALID); + } else { + EconomyAPI.getInstance().provider.reduceMoney(currencyName, lowerId, finalAmount); + this.setResult(EconomyAPI.RET_SUCCESS); + } + } else { + this.setResult(EconomyAPI.RET_NO_ACCOUNT); + } + } else { + this.setResult(EconomyAPI.RET_CANCELLED); + } + } + + @Override + public void onCompletion(Server server) { + future.complete((Integer) this.getResult()); + } + } + + public CompletableFuture reduceMoney(Player player, double amount) { + return this.reduceMoney(player, amount, false); + } + + public CompletableFuture reduceMoney(Player player, double amount, boolean force) { + return this.reduceMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture reduceMoney(IPlayer player, double amount) { + return this.reduceMoney(player, amount, false); + } + + public CompletableFuture reduceMoney(IPlayer player, double amount, boolean force) { + return this.reduceMoney(player.getUniqueId(), amount, force); + } + + public CompletableFuture reduceMoney(UUID id, double amount) { + return reduceMoney(id, amount, false); + } + + public CompletableFuture reduceMoney(UUID id, double amount, boolean force) { + checkAndConvertLegacy(id); + return reduceMoneyInternal(id.toString(), amount, force); + } + + public CompletableFuture reduceMoney(String id, double amount) { + return this.reduceMoney(id, amount, false); + } + + public CompletableFuture reduceMoney(String id, double amount, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> reduceMoney(uuid1, amount, force)) + .orElseGet(() -> reduceMoneyInternal(id, amount, force)); + } + + private CompletableFuture reduceMoneyInternal(String id, double amount, boolean force) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneyReduceTask(id, amount, EconomyAPI.MAIN_CONFIG.getDefaultCurrency().getName(), force, future) + ); + return future; + } + + + // === mutiple currency + + public CompletableFuture myMoney(Player player, String currencyName) { + return this.myMoney(player.getUniqueId(), currencyName); + } + + public CompletableFuture myMoney(IPlayer player, String currencyName) { + return this.myMoney(player.getUniqueId(), currencyName); + } + + public CompletableFuture myMoney(UUID id, String currencyName) { + checkAndConvertLegacy(id); + return myMoneyInternal(id.toString(), currencyName); + } + + public CompletableFuture myMoney(String id, String currencyName) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> myMoney(uuid1, currencyName)) + .orElseGet(() -> myMoneyInternal(id, currencyName)); + } + + private CompletableFuture myMoneyInternal(String id, String currencyName) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneyLookupTask(id, currencyName, future) + ); + return future; + } + + public CompletableFuture setMoney(Player player, double amount, String currencyName) { + return this.setMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture setMoney(Player player, double amount, String currencyName, boolean force) { + return this.setMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture setMoney(IPlayer player, double amount, String currencyName) { + return this.setMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture setMoney(IPlayer player, double amount, String currencyName, boolean force) { + return this.setMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture setMoney(UUID id, double amount, String currencyName) { + return setMoney(id, amount, currencyName, false); + } + + public CompletableFuture setMoney(UUID id, double amount, String currencyName, boolean force) { + checkAndConvertLegacy(id); + return setMoneyInternal(id.toString(), amount, currencyName, force); + } + + public CompletableFuture setMoney(String id, double amount, String currencyName) { + return this.setMoney(id, amount, currencyName, false); + } + + public CompletableFuture setMoney(String id, double amount, String currencyName, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> setMoney(uuid1, amount, currencyName, force)) + .orElseGet(() -> setMoneyInternal(id, amount, currencyName, force)); + } + + private CompletableFuture setMoneyInternal(String id, double amount, String currencyName, boolean force) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneySetTask(id, amount, currencyName, force, future) + ); + return future; + } + + // Add Money methods + public CompletableFuture addMoney(Player player, double amount, String currencyName) { + return this.addMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture addMoney(Player player, double amount, String currencyName, boolean force) { + return this.addMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture addMoney(IPlayer player, double amount, String currencyName) { + return this.addMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture addMoney(IPlayer player, double amount, String currencyName, boolean force) { + return this.addMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture addMoney(UUID id, double amount, String currencyName) { + return addMoney(id, amount, currencyName, false); + } + + public CompletableFuture addMoney(UUID id, double amount, String currencyName, boolean force) { + checkAndConvertLegacy(id); + return addMoneyInternal(id.toString(), amount, currencyName, force); + } + + public CompletableFuture addMoney(String id, double amount, String currencyName) { + return this.addMoney(id, amount, currencyName, false); + } + + public CompletableFuture addMoney(String id, double amount, String currencyName, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> addMoney(uuid1, amount, currencyName, force)) + .orElseGet(() -> addMoneyInternal(id, amount, currencyName, force)); + } + + private CompletableFuture addMoneyInternal(String id, double amount, String currencyName, boolean force) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneyAddTask(id, amount, currencyName, force, future) + ); + return future; + } + + // Reduce Money methods + public CompletableFuture reduceMoney(Player player, double amount, String currencyName) { + return this.reduceMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture reduceMoney(Player player, double amount, String currencyName, boolean force) { + return this.reduceMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture reduceMoney(IPlayer player, double amount, String currencyName) { + return this.reduceMoney(player.getUniqueId(), amount, currencyName, false); + } + + public CompletableFuture reduceMoney(IPlayer player, double amount, String currencyName, boolean force) { + return this.reduceMoney(player.getUniqueId(), amount, currencyName, force); + } + + public CompletableFuture reduceMoney(UUID id, double amount, String currencyName) { + return reduceMoney(id, amount, currencyName, false); + } + + public CompletableFuture reduceMoney(UUID id, double amount, String currencyName, boolean force) { + checkAndConvertLegacy(id); + return reduceMoneyInternal(id.toString(), amount, currencyName, force); + } + + public CompletableFuture reduceMoney(String id, double amount, String currencyName) { + return this.reduceMoney(id, amount, currencyName, false); + } + + public CompletableFuture reduceMoney(String id, double amount, String currencyName, boolean force) { + Optional uuid = checkAndConvertLegacy(id); + return uuid.map(uuid1 -> reduceMoney(uuid1, amount, currencyName, force)) + .orElseGet(() -> reduceMoneyInternal(id, amount, currencyName, force)); + } + + private CompletableFuture reduceMoneyInternal(String id, double amount, String currencyName, boolean force) { + CompletableFuture future = new CompletableFuture<>(); + Server.getInstance().getScheduler().scheduleAsyncTask( + EconomyAPI.getInstance(), + new MoneyReduceTask(id, amount, currencyName, force, future) + ); + return future; + } + + // === other + + private void checkAndConvertLegacy(UUID uuid) { + IPlayer player = Server.getInstance().getOfflinePlayer(uuid); + if (player != null && player.getName() != null) { + checkAndConvertLegacy(uuid, player.getName()); + } + } + + private Optional checkAndConvertLegacy(String id) { + Optional uuid = Server.getInstance().lookupName(id); + uuid.ifPresent(uuid1 -> checkAndConvertLegacy(uuid1, id)); + return uuid; + } + + private void checkAndConvertLegacy(UUID uuid, String name) { + name = name.toLowerCase(); + if (!EconomyAPI.getInstance().provider.accountExists(name)) { + return; + } + if (EconomyAPI.getInstance().provider.accountExists(uuid.toString())) { + EconomyAPI.getInstance().provider.removeAccount(name); + return; + } + double money = EconomyAPI.getInstance().provider.getMoney(name); + EconomyAPI.getInstance().provider.createAccount(uuid.toString(), money); + EconomyAPI.getInstance().provider.removeAccount(name); + } +} \ No newline at end of file diff --git a/src/main/java/me/onebone/economyapi/EconomyAPI.java b/src/main/java/me/onebone/economyapi/EconomyAPI.java index 47f4033..7651048 100644 --- a/src/main/java/me/onebone/economyapi/EconomyAPI.java +++ b/src/main/java/me/onebone/economyapi/EconomyAPI.java @@ -35,6 +35,7 @@ import me.onebone.economyapi.event.money.AddMoneyEvent; import me.onebone.economyapi.event.money.ReduceMoneyEvent; import me.onebone.economyapi.event.money.SetMoneyEvent; +import me.onebone.economyapi.provider.MySQLProvider; import me.onebone.economyapi.provider.Provider; import me.onebone.economyapi.provider.SQLiteProvider; import me.onebone.economyapi.provider.YamlProvider; @@ -60,8 +61,9 @@ public class EconomyAPI extends PluginBase implements Listener { private static EconomyAPI instance; private static PluginI18n i18n; public static LangCode serverLangCode; - private Provider provider; - private final HashMap> providerClass = new HashMap<>(); + protected Provider provider; + protected final HashMap> providerClass = new HashMap<>(); + protected static AsyncOperator asyncOperator = new AsyncOperator(); static { MONEY_FORMAT.setMaximumFractionDigits(2); @@ -391,7 +393,7 @@ public double myMoney(String id, String currencyName) { } private double myMoneyInternal(String id, String currencyName) { - return this.provider.getMoney(id.toLowerCase(), currencyName); + return this.provider.getMoney(currencyName, id.toLowerCase()); } public int setMoney(Player player, double amount, String currencyName) { @@ -437,7 +439,7 @@ private int setMoneyInternal(String id, double amount, String currencyName, bool SetMoneyEvent event = new SetMoneyEvent(id, amount, currencyName); this.getServer().getPluginManager().callEvent(event); if (!event.isCancelled() || force) { - if (this.provider.accountExists(id, currencyName)) { + if (this.provider.accountExists(currencyName, id)) { amount = event.getAmount(); if (amount <= getMaxMoney(currencyName)) { this.provider.setMoney(currencyName, id, amount); @@ -643,8 +645,9 @@ public void onLoad() { this.addProvider("yaml", YamlProvider.class); if (this.getServer().getPluginManager().getPlugin("EasySQLX") != null) { this.addProvider("sqlite", SQLiteProvider.class); + this.addProvider("mysql", MySQLProvider.class); } else { - this.getLogger().warning("EasySQLX is not found, SQLite provider will not be available."); + this.getLogger().warning("EasySQLX is not found, SQLite and MySQL provider will not be available."); } } @@ -682,6 +685,9 @@ public void onJoin(PlayerJoinEvent event) { @Override public void onDisable() { this.saveAll(); + if (MAIN_CONFIG.getProvider().equals("mysql")) { + provider.close(); + } } private boolean initialize() { @@ -727,6 +733,19 @@ public boolean addProvider(String name, Class providerClass) return true; } + /** + * Gets the async operator for economy operations. + * + * @return AsyncOperator instance for performing asynchronous economy operations + * @example
+     * EconomyAPI.getAsyncOperator().myMoney(player).thenAccept(money -> {
+     *     player.sendMessage("Your balance: " + money);
+     * });
+     * 
+ */ + public static AsyncOperator getAsyncOperator() { + return asyncOperator; + } private void checkAndConvertLegacy(UUID uuid) { IPlayer player = getServer().getOfflinePlayer(uuid); diff --git a/src/main/java/me/onebone/economyapi/command/GiveMoneyCommand.java b/src/main/java/me/onebone/economyapi/command/GiveMoneyCommand.java index f52f117..0a0b607 100644 --- a/src/main/java/me/onebone/economyapi/command/GiveMoneyCommand.java +++ b/src/main/java/me/onebone/economyapi/command/GiveMoneyCommand.java @@ -56,13 +56,13 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (!this.plugin.isEnabled()) return false; LangCode langCode = sender instanceof Player ? ((Player) sender).getLanguageCode() : serverLangCode; if (!sender.hasPermission("economyapi.command.givemoney")) { - sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission")); + sender.sendMessage(new TranslationContainer(TextFormat.RED + "%cofalsemmands.generic.permission")); return false; } if (args.length < 2) { sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); - return true; + return false; } String player = args[0]; @@ -70,35 +70,39 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (p != null) { player = p.getName(); } - String currencyName = MAIN_CONFIG.getDefaultCurrency().getName(); - if (args.length >= 3) { - currencyName = args[2]; - } + + double amount; try { - double amount = Double.parseDouble(args[1]); + amount = Double.parseDouble(args[1]); if (amount <= 0 || !Double.isFinite(amount)) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-invalid-number")); - return true; + return false; } + } catch (NumberFormatException e) { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-must-be-number")); + return false; + } + + final String finalPlayer = player; + final double finalAmount = amount; + final String currencyName = args.length >= 3 ? args[2] : MAIN_CONFIG.getDefaultCurrency().getName(); - int result = this.plugin.addMoney(player, amount, currencyName); + EconomyAPI.getAsyncOperator().addMoney(player, amount, currencyName).thenAccept(result -> { switch (result) { case EconomyAPI.RET_INVALID: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "reached-max", EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit())); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "reached-max", EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit())); + break; case EconomyAPI.RET_NO_ACCOUNT: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", player)); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", finalPlayer)); + break; case EconomyAPI.RET_SUCCESS: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-gave-money", player, EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit())); + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-gave-money", finalPlayer, EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit())); if (p != null) { - p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "givemoney-money-given", EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit())); + p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "givemoney-money-given", EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit())); } - return true; + break; } - } catch (NumberFormatException e) { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-must-be-number")); - } + }); return true; } diff --git a/src/main/java/me/onebone/economyapi/command/MyMoneyCommand.java b/src/main/java/me/onebone/economyapi/command/MyMoneyCommand.java index f862d57..8594916 100644 --- a/src/main/java/me/onebone/economyapi/command/MyMoneyCommand.java +++ b/src/main/java/me/onebone/economyapi/command/MyMoneyCommand.java @@ -68,18 +68,19 @@ public boolean execute(CommandSender sender, String label, String[] args) { target = args[0]; } - double money = this.plugin.myMoney(target); - if (money == -1) { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", target)); - return true; - } + EconomyAPI.getAsyncOperator().myMoney(target).thenAccept(money -> { + if (money == -1) { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", target)); + return; + } - String moneyString = EconomyAPI.MONEY_FORMAT.format(money); - if (sender.getName().equals(target)) { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "mymoney-mymoney", moneyString, plugin.getMonetaryUnit())); - } else { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "seemoney-seemoney", target, moneyString, plugin.getMonetaryUnit())); - } + String moneyString = EconomyAPI.MONEY_FORMAT.format(money); + if (sender.getName().equals(target)) { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "mymoney-mymoney", moneyString, plugin.getMonetaryUnit())); + } else { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "seemoney-seemoney", target, moneyString, plugin.getMonetaryUnit())); + } + }); return true; } } diff --git a/src/main/java/me/onebone/economyapi/command/PayCommand.java b/src/main/java/me/onebone/economyapi/command/PayCommand.java index d3ee979..98cbb53 100644 --- a/src/main/java/me/onebone/economyapi/command/PayCommand.java +++ b/src/main/java/me/onebone/economyapi/command/PayCommand.java @@ -63,19 +63,19 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(new TranslationContainer("%commands.generic.ingame")); - return true; + return false; } if (args.length < 2) { sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); - return true; + return false; } String player = args[0]; Player p = this.plugin.getServer().getPlayer(player); if (p != null) { if (sender == p) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "pay-failed-self")); - return true; + return false; } player = p.getName(); @@ -84,27 +84,26 @@ public boolean execute(CommandSender sender, String label, String[] args) { try { amount = Double.parseDouble(args[1]); if (!Double.isFinite(amount)) { - throw new NumberFormatException(); + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-invalid-number")); + return false; } } catch (NumberFormatException e) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-must-be-number")); - return true; + return false; } if (amount < 0.01) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "pay-too-low")); - return true; + return false; } if (!this.plugin.hasAccount(player)) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", player)); - return true; + return false; } - String currencyName = MAIN_CONFIG.getDefaultCurrency().getName(); - if (args.length >= 3) { - currencyName = args[2]; - } + final String currencyName = args.length >= 3 ? args[2] : MAIN_CONFIG.getDefaultCurrency().getName(); + int result = this.plugin.reduceMoney((Player) sender, amount, currencyName); switch (result) { case EconomyAPI.RET_NO_ACCOUNT: diff --git a/src/main/java/me/onebone/economyapi/command/SetMoneyCommand.java b/src/main/java/me/onebone/economyapi/command/SetMoneyCommand.java index 107a795..8994860 100644 --- a/src/main/java/me/onebone/economyapi/command/SetMoneyCommand.java +++ b/src/main/java/me/onebone/economyapi/command/SetMoneyCommand.java @@ -63,7 +63,7 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (args.length < 2) { sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); - return true; + return false; } String player = args[0]; @@ -71,39 +71,42 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (p != null) { player = p.getName(); } - double amount = 0; + + double amount; try { amount = Double.parseDouble(args[1]); - if (!Double.isFinite(amount)) { - throw new NumberFormatException(); + if (amount <= 0 || !Double.isFinite(amount)) { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-invalid-number")); + return false; } } catch (NumberFormatException e) { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-invalid-number", amount, plugin.getMonetaryUnit())); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "givemoney-must-be-number")); + return false; } - String currencyName = MAIN_CONFIG.getDefaultCurrency().getName(); - if (args.length >= 3) { - currencyName = args[2]; - } - int result = this.plugin.setMoney(player, amount, currencyName); - switch (result) { - case EconomyAPI.RET_NO_ACCOUNT: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", player)); - return true; - case EconomyAPI.RET_CANCELLED: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-failed")); - return true; - case EconomyAPI.RET_INVALID: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "reached-max", EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit(currencyName))); - return true; - case EconomyAPI.RET_SUCCESS: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-setmoney", player, EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit(currencyName))); - if (p != null) { - p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "setmoney-set", EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit(currencyName))); - } - return true; - } + final String finalPlayer = player; + final double finalAmount = amount; + final String currencyName = args.length >= 3 ? args[2] : MAIN_CONFIG.getDefaultCurrency().getName(); + + EconomyAPI.getAsyncOperator().setMoney(player, amount, currencyName).thenAccept(result -> { + switch (result) { + case EconomyAPI.RET_NO_ACCOUNT: + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", finalPlayer)); + break; + case EconomyAPI.RET_CANCELLED: + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-failed")); + break; + case EconomyAPI.RET_INVALID: + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "reached-max", EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit(currencyName))); + break; + case EconomyAPI.RET_SUCCESS: + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "setmoney-setmoney", finalPlayer, EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit(currencyName))); + if (p != null) { + p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "setmoney-set", EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit(currencyName))); + } + break; + } + }); return true; } diff --git a/src/main/java/me/onebone/economyapi/command/TakeMoneyCommand.java b/src/main/java/me/onebone/economyapi/command/TakeMoneyCommand.java index 6191446..23e8ea6 100644 --- a/src/main/java/me/onebone/economyapi/command/TakeMoneyCommand.java +++ b/src/main/java/me/onebone/economyapi/command/TakeMoneyCommand.java @@ -63,7 +63,7 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (args.length < 2) { sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage())); - return true; + return false; } String player = args[0]; @@ -71,38 +71,43 @@ public boolean execute(CommandSender sender, String label, String[] args) { if (p != null) { player = p.getName(); } + + double amount = 0; try { - double amount = Double.parseDouble(args[1]); - if (amount < 0 || !Double.isFinite(amount)) { + amount = Double.parseDouble(args[1]); + if (amount <= 0 || !Double.isFinite(amount)) { sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-invalid-number")); - return true; + return false; } + } catch (NumberFormatException e) { + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-must-be-number")); + return false; + } - String currencyName = MAIN_CONFIG.getDefaultCurrency().getName(); - if (args.length >= 3) { - currencyName = args[2]; - } - int result = this.plugin.reduceMoney(player, amount, currencyName); + + final String finalPlayer = player; + final double finalAmount = amount; + final String currencyName = args.length >= 3 ? args[2] : MAIN_CONFIG.getDefaultCurrency().getName(); + + EconomyAPI.getAsyncOperator().reduceMoney(player, amount, currencyName).thenAccept(result -> { switch (result) { case EconomyAPI.RET_INVALID: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-player-lack-of-money", player, EconomyAPI.MONEY_FORMAT.format(amount), EconomyAPI.MONEY_FORMAT.format(this.plugin.myMoney(player, currencyName)), plugin.getMonetaryUnit(currencyName))); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-player-lack-of-money", finalPlayer, EconomyAPI.MONEY_FORMAT.format(finalAmount), EconomyAPI.MONEY_FORMAT.format(this.plugin.myMoney(finalPlayer, currencyName)), plugin.getMonetaryUnit(currencyName))); + break; case EconomyAPI.RET_NO_ACCOUNT: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", player)); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "player-never-connected", finalPlayer)); + break; case EconomyAPI.RET_CANCELLED: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-failed", player)); - return true; + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-failed", finalPlayer)); + break; case EconomyAPI.RET_SUCCESS: - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-took-money", player, EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit(currencyName))); + sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-took-money", finalPlayer, EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit(currencyName))); if (p != null) { - p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "takemoney-money-taken", EconomyAPI.MONEY_FORMAT.format(amount), plugin.getMonetaryUnit(currencyName))); + p.sendMessage(EconomyAPI.getI18n().tr(p.getLanguageCode(), "takemoney-money-taken", EconomyAPI.MONEY_FORMAT.format(finalAmount), plugin.getMonetaryUnit(currencyName))); } - return true; + break; } - } catch (NumberFormatException e) { - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "takemoney-must-be-number")); - } + }); return true; } diff --git a/src/main/java/me/onebone/economyapi/command/TopMoneyCommand.java b/src/main/java/me/onebone/economyapi/command/TopMoneyCommand.java index 6b1f170..69d51dd 100644 --- a/src/main/java/me/onebone/economyapi/command/TopMoneyCommand.java +++ b/src/main/java/me/onebone/economyapi/command/TopMoneyCommand.java @@ -70,7 +70,7 @@ private static String getName(String possibleUuid) { return possibleUuid; } - @Override + @Override public boolean execute(final CommandSender sender, String label, final String[] args) { if (!this.plugin.isEnabled()) return false; LangCode langCode = sender instanceof Player ? ((Player) sender).getLanguageCode() : serverLangCode; @@ -79,47 +79,41 @@ public boolean execute(final CommandSender sender, String label, final String[] return false; } - try { - int arg = args.length > 0 ? Integer.parseInt(args[0]) : 1; - String currencyName = MAIN_CONFIG.getDefaultCurrency().getName(); - if (args.length >= 2) { - currencyName = args[1]; - } + int arg = args.length > 0 ? Integer.parseInt(args[0]) : 1; + final String currencyName = args.length >= 2 ? args[1] : MAIN_CONFIG.getDefaultCurrency().getName(); + + sender.getServer().getScheduler().scheduleTask(EconomyAPI.getInstance(), () -> { final LinkedHashMap money = new LinkedHashMap<>(plugin.getAllMoney(currencyName)); - sender.getServer().getScheduler().scheduleTask(EconomyAPI.getInstance(), () -> { - int page = args.length > 0 ? Math.max(1, Math.min(arg, money.size())) : 1; - List list = new LinkedList<>(money.keySet()); - list.sort((s1, s2) -> Double.compare(money.get(s2), money.get(s1))); - StringBuilder output = new StringBuilder(); - output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-tag", Integer.toString(page), Integer.toString(((money.size() + 6) / 5)))).append("\n"); - if (page == 1) { - double total = 0; - for (double val : money.values()) { - total += val; - } - output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-total", EconomyAPI.MONEY_FORMAT.format(total))).append("\n\n"); + int page = args.length > 0 ? Math.max(1, Math.min(arg, money.size())) : 1; + List list = new LinkedList<>(money.keySet()); + list.sort((s1, s2) -> Double.compare(money.get(s2), money.get(s1))); + StringBuilder output = new StringBuilder(); + output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-tag", Integer.toString(page), Integer.toString(((money.size() + 6) / 5)))).append("\n"); + if (page == 1) { + double total = 0; + for (double val : money.values()) { + total += val; } - int duplicate = 0; - double prev = -1D; - for (int n = 0; n < list.size(); n++) { - int current = (int) Math.ceil((double) (n + 1) / 5); - if (page == current) { - double m = money.get(list.get(n)); - if (m == prev) duplicate++; - else duplicate = 0; - prev = m; - output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-format", Integer.toString(n + 1 - duplicate), getName(list.get(n)), EconomyAPI.MONEY_FORMAT.format(m))).append("\n"); - } else if (page < current) { - break; - } + output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-total", EconomyAPI.MONEY_FORMAT.format(total))).append("\n\n"); + } + int duplicate = 0; + double prev = -1D; + for (int n = 0; n < list.size(); n++) { + int current = (int) Math.ceil((double) (n + 1) / 5); + if (page == current) { + double m = money.get(list.get(n)); + if (m == prev) duplicate++; + else duplicate = 0; + prev = m; + output.append(EconomyAPI.getI18n().tr(langCode, "topmoney-format", Integer.toString(n + 1 - duplicate), getName(list.get(n)), EconomyAPI.MONEY_FORMAT.format(m))).append("\n"); + } else if (page < current) { + break; } + } - sender.sendMessage(output.substring(0, output.length() - 1)); - }, true); - } catch (NumberFormatException e) { + sender.sendMessage(output.substring(0, output.length() - 1)); + }, true); - sender.sendMessage(EconomyAPI.getI18n().tr(langCode, "topmoney-invalid-page-number")); - } return true; } } diff --git a/src/main/java/me/onebone/economyapi/config/UpgradeConfig.java b/src/main/java/me/onebone/economyapi/config/UpgradeConfig.java index b1a355a..a0cbaef 100644 --- a/src/main/java/me/onebone/economyapi/config/UpgradeConfig.java +++ b/src/main/java/me/onebone/economyapi/config/UpgradeConfig.java @@ -39,12 +39,12 @@ public static boolean tryUpgradeConfigVersion(int oldVersion) { return false; } - protected static String TEMP_CURRENCY_NAME = ""; + protected static String TEMP_CURRENCY_NAME = "USD"; public static boolean updateDoubleConfirmation() { System.out.println("An older version of the Money.yml configuration file has been detected. Would you like to upgrade?"); System.out.println("The upgrade process will perform the following steps:"); - System.out.println("1. Copy the Money.yml file to a new file under the money directory (money/.yml)"); + System.out.println("1. Copy the Money.yml file to a new file under the money directory (money/" + TEMP_CURRENCY_NAME + ".yml)"); System.out.println("2. Modify the version of the new file to 3"); System.out.println("3. Rename the old Money.yml file to Money.old.yml"); System.out.print("Please confirm whether to upgrade (yes/no): "); @@ -54,13 +54,6 @@ public static boolean updateDoubleConfirmation() { System.out.println("The plugin version is too high and does not support this configuration file. Please use version 2.0.6 of EconomyAPI!"); System.exit(1); } - // Input default currency - System.out.print("Please enter the default currency name (default: USD): "); - TEMP_CURRENCY_NAME = new Scanner(System.in).nextLine().trim(); - if (TEMP_CURRENCY_NAME.isEmpty()) { - TEMP_CURRENCY_NAME = "USD"; - } - System.out.println("Default currency name used: " + TEMP_CURRENCY_NAME); EconomyAPI.getInstance().saveDefaultConfig(); EconomyAPI.getInstance().getConfig().getSection("currencies").set( diff --git a/src/main/java/me/onebone/economyapi/provider/MySQLProvider.java b/src/main/java/me/onebone/economyapi/provider/MySQLProvider.java new file mode 100644 index 0000000..12aec4f --- /dev/null +++ b/src/main/java/me/onebone/economyapi/provider/MySQLProvider.java @@ -0,0 +1,213 @@ +package me.onebone.economyapi.provider; + +import cn.nukkit.Server; +import cn.nukkit.utils.ConfigSection; +import com.smallaswater.easysqlx.common.data.SqlData; +import com.smallaswater.easysqlx.common.data.SqlDataList; +import com.smallaswater.easysqlx.exceptions.MySqlLoginException; +import com.smallaswater.easysqlx.mysql.manager.SqlManager; +import com.smallaswater.easysqlx.mysql.utils.DataType; +import com.smallaswater.easysqlx.mysql.utils.TableType; +import com.smallaswater.easysqlx.mysql.utils.UserData; +import me.onebone.economyapi.EconomyAPI; + +import java.io.File; +import java.util.LinkedHashMap; + +import static me.onebone.economyapi.EconomyAPI.MAIN_CONFIG; + +public class MySQLProvider implements Provider { + private static SqlManager manager; + private static String TABLE_NAME_PREFIX = ""; + + public static void initTablePrefix(String prefix) { + MySQLProvider.TABLE_NAME_PREFIX = prefix; + } + + @Override + public void init(File path) { + if (MySQLProvider.manager != null && MySQLProvider.manager.isEnable()) { + EconomyAPI.getInstance().getLogger().warning("MySQL is already initialized."); + return; + } + + if (!MAIN_CONFIG.getConfig().exists("sql.mysql")) { + EconomyAPI.getInstance().getLogger().error("MySQL is not configured."); + return; + } + ConfigSection mysqlSection = MAIN_CONFIG.getConfig().getSection("sql.mysql"); + String host = mysqlSection.getString("host", "localhost"); + int port = mysqlSection.getInt("port", 3306); + String database = mysqlSection.getString("database", "economy"); + String username = mysqlSection.getString("username", "root"); + String password = mysqlSection.getString("password", "root123456"); + String tablePrefix = mysqlSection.getString("table-prefix", "v1_"); + MySQLProvider.initTablePrefix(tablePrefix); + + Server.getInstance().getScheduler().scheduleTask(EconomyAPI.getInstance(), () -> { + try { + SqlManager manager = new SqlManager(EconomyAPI.getInstance(), new UserData( + username, password, host, port, database + )); + MySQLProvider.manager = manager; + + MAIN_CONFIG.getCurrencyList().forEach(currencyName -> { + manager.createTable( + TABLE_NAME_PREFIX + currencyName, + new TableType("player", DataType.getUUID(), true), + new TableType("money", DataType.getBIGINT(), false) + ); + }); + EconomyAPI.getInstance().getLogger().info("MySQL initialized!"); + } catch (MySqlLoginException e) { + EconomyAPI.getInstance().getLogger().error("MySQL connection failed.", e); + Server.getInstance().getPluginManager().disablePlugin(EconomyAPI.getInstance()); + } + }, true); + } + + @Override + public void open() { + if (MySQLProvider.manager == null) return; + if (!MySQLProvider.manager.isEnable()) { + this.init(null); + } + } + + @Override + public void save() { + // not required in MySQL. + } + + @Override + public void close() { + if (MySQLProvider.manager == null) return; + MySQLProvider.manager.disable(); + } + + @Override + public boolean accountExists(String currencyName, String id) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + if (MySQLProvider.manager.isExistTable(TABLE_NAME_PREFIX + currencyName)) { + return MySQLProvider.manager.isExistsData(TABLE_NAME_PREFIX + currencyName, "player", id); + } + return false; + } + + @Override + public boolean accountExists(String id) { + return accountExists(MAIN_CONFIG.getDefaultCurrency().getName(), id); + } + + @Override + public boolean removeAccount(String currencyName, String id) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + if (MySQLProvider.manager.isExistTable(TABLE_NAME_PREFIX + currencyName)) { + return MySQLProvider.manager.deleteData(TABLE_NAME_PREFIX + currencyName, new SqlData("player", id)); + } + return false; + } + + @Override + public boolean removeAccount(String id) { + return removeAccount(MAIN_CONFIG.getDefaultCurrency().getName(), id); + } + + @Override + public boolean createAccount(String currencyName, String id, double defaultMoney) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + // convert money to bigint + long money = (long) defaultMoney * 100; + if (!accountExists(currencyName, id)) { + SqlData sqlData = new SqlData("player", id).put("money", money); + return MySQLProvider.manager.insertData(TABLE_NAME_PREFIX + currencyName, sqlData); + } + return false; + } + + @Override + public boolean createAccount(String id, double defaultMoney) { + return createAccount(MAIN_CONFIG.getDefaultCurrency().getName(), id, defaultMoney); + } + + @Override + public boolean setMoney(String currencyName, String id, double amount) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + long money = (long) amount * 100; + return MySQLProvider.manager.setData(TABLE_NAME_PREFIX + currencyName, new SqlData("money", money), new SqlData("player", id)); + } + + @Override + public boolean setMoney(String id, double amount) { + return setMoney(MAIN_CONFIG.getDefaultCurrency().getName(), id, amount); + } + + @Override + public boolean addMoney(String currencyName, String id, double amount) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + long money = (long) (getMoney(currencyName, id) + amount) * 100; + return MySQLProvider.manager.setData(TABLE_NAME_PREFIX + currencyName, new SqlData("money", money), new SqlData("player", id)); + } + + @Override + public boolean addMoney(String id, double amount) { + return addMoney(MAIN_CONFIG.getDefaultCurrency().getName(), id, amount); + } + + @Override + public boolean reduceMoney(String currencyName, String id, double amount) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return false; + long money = (long) (getMoney(currencyName, id) - amount) * 100; + return MySQLProvider.manager.setData(TABLE_NAME_PREFIX + currencyName, new SqlData("money", money), new SqlData("player", id)); + } + + @Override + public boolean reduceMoney(String id, double amount) { + return reduceMoney(MAIN_CONFIG.getDefaultCurrency().getName(), id, amount); + } + + @Override + public double getMoney(String currencyName, String id) { + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return 0; + SqlDataList sqlDataList = MySQLProvider.manager.getData(TABLE_NAME_PREFIX + currencyName, "money", new SqlData("player", id)); + if (sqlDataList.isEmpty()) return 0; + return sqlDataList.get(0).getLong("money") / 100.0; + } + + @Override + public double getMoney(String id) { + return getMoney(MAIN_CONFIG.getDefaultCurrency().getName(), id); + } + + @Override + public LinkedHashMap getAll(String currencyName) { + LinkedHashMap map = new LinkedHashMap<>(); + if (!MAIN_CONFIG.getCurrencyList().contains(currencyName)) return map; + SqlData emptyData = new SqlData(); + SqlDataList sqlDataList = MySQLProvider.manager.getData(TABLE_NAME_PREFIX + currencyName, "*", emptyData); + if (sqlDataList == null) { + return null; + } + for (SqlData sqlData : sqlDataList) { + LinkedHashMap data = sqlData.getData(); + try { + String playerId = (String) data.get("player"); + long moneyObj = (long) data.get("money"); + map.put(playerId, moneyObj / 100.0); + } catch (Exception e) { + EconomyAPI.getInstance().getLogger().error("Error processing SqlData: " + sqlData, e); + } + } + return map; + } + + @Override + public LinkedHashMap getAll() { + return getAll(MAIN_CONFIG.getDefaultCurrency().getName()); + } + + @Override + public String getName() { + return "MySQL"; + } +} diff --git a/src/main/java/me/onebone/economyapi/provider/SQLiteProvider.java b/src/main/java/me/onebone/economyapi/provider/SQLiteProvider.java index 1bfffe1..43d1fd8 100644 --- a/src/main/java/me/onebone/economyapi/provider/SQLiteProvider.java +++ b/src/main/java/me/onebone/economyapi/provider/SQLiteProvider.java @@ -2,6 +2,7 @@ import com.smallaswater.easysqlx.sqlite.SQLiteHelper; import com.smallaswater.easysqlx.sqlite.SQLiteHelper.DBTable; + import java.io.File; import java.util.HashMap; import java.util.LinkedHashMap; @@ -24,7 +25,7 @@ public class SQLiteProvider implements Provider { @Override public void init(File path) { try { - this.sqLiteHelper = new SQLiteHelper(path.getAbsolutePath() + File.separator +"MoneyV3.db"); + this.sqLiteHelper = new SQLiteHelper(path.getAbsolutePath() + File.separator + "MoneyV3.db"); if (!this.sqLiteHelper.exists(TABLE_NAME)) { this.sqLiteHelper.addTable(TABLE_NAME, DBTable.asDbTable(MoneyData.class)); } @@ -70,12 +71,12 @@ public boolean accountExists(String id) { @Override public boolean removeAccount(String currencyName, String id) { - if (accountExists(currencyName, id)) { - this.sqLiteHelper.remove(TABLE_NAME, COLUMN_PLAYER, id); // 移除账户时使用 player 作为 key,currency 通过 MoneyData 对象内部 currency 字段区分 - this.cache.remove(getCacheKey(currencyName, id)); // 从缓存中移除 - return true; + if (!accountExists(currencyName, id)) { + return false; } - return false; + this.sqLiteHelper.remove(TABLE_NAME, COLUMN_PLAYER, id); // 移除账户时使用 player 作为 key,currency 通过 MoneyData 对象内部 currency 字段区分 + this.cache.remove(getCacheKey(currencyName, id)); // 从缓存中移除 + return true; } @Override @@ -85,14 +86,14 @@ public boolean removeAccount(String id) { @Override public boolean createAccount(String currencyName, String id, double defaultMoney) { - if (!accountExists(currencyName, id)) { - MoneyData values = new MoneyData(id, defaultMoney); - values.setCurrency(currencyName); // 设置 currencyName - this.sqLiteHelper.add(TABLE_NAME, values); - this.cache.put(getCacheKey(currencyName, id), values); // 添加到缓存 - return true; + if (accountExists(currencyName, id)) { + return false; } - return false; + MoneyData values = new MoneyData(id, defaultMoney); + values.setCurrency(currencyName); // 设置 currencyName + this.sqLiteHelper.add(TABLE_NAME, values); + this.cache.put(getCacheKey(currencyName, id), values); // 添加到缓存 + return true; } @Override @@ -102,14 +103,14 @@ public boolean createAccount(String id, double defaultMoney) { @Override public boolean setMoney(String currencyName, String id, double amount) { - if (accountExists(currencyName, id)) { - MoneyData moneyData = this.getMoneyData(currencyName, id); - moneyData.setMoney(amount); - this.sqLiteHelper.set(TABLE_NAME, COLUMN_PLAYER, id, moneyData); // 使用 player 作为 key 更新,MoneyData 对象内部包含 currency 信息 - this.cache.put(getCacheKey(currencyName, id), moneyData); // 更新缓存 - return true; + if (!accountExists(currencyName, id)) { + return false; } - return false; + MoneyData moneyData = this.getMoneyData(currencyName, id); + moneyData.setMoney(amount); + this.sqLiteHelper.set(TABLE_NAME, COLUMN_PLAYER, id, moneyData); // 使用 player 作为 key 更新,MoneyData 对象内部包含 currency 信息 + this.cache.put(getCacheKey(currencyName, id), moneyData); // 更新缓存 + return true; } @Override @@ -119,11 +120,11 @@ public boolean setMoney(String id, double amount) { @Override public boolean addMoney(String currencyName, String id, double amount) { - if (accountExists(currencyName, id)) { - this.setMoney(currencyName, id, this.getMoney(currencyName, id) + amount); - return true; + if (!accountExists(currencyName, id)) { + return false; } - return false; + this.setMoney(currencyName, id, this.getMoney(currencyName, id) + amount); + return true; } @Override @@ -133,11 +134,11 @@ public boolean addMoney(String id, double amount) { @Override public boolean reduceMoney(String currencyName, String id, double amount) { - if (accountExists(currencyName, id)) { - this.setMoney(currencyName, id, this.getMoney(currencyName, id) - amount); - return true; + if (!accountExists(currencyName, id)) { + return false; } - return false; + this.setMoney(currencyName, id, this.getMoney(currencyName, id) - amount); + return true; } @Override diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e9fa06f..7efd63f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -18,4 +18,12 @@ currencies: data: default-currency: USD auto-save-interval: 10 # unit: minutes - provider: yaml # available values: yaml | sqlite + provider: yaml # available values: yaml | sqlite | mysql +sql: + mysql: + host: localhost + port: 3306 + database: economy + username: root + password: root123456 + table-prefix: "v1_"