Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
AppleDash | ed3ccceb1d | |
A248 | 78d6c70658 | |
AppleDash | 2756e0c2d9 | |
AppleDash | bb02d78b88 | |
AppleDash | 77ba58e333 | |
AppleDash | c47524f863 | |
AppleDash | afa4e9d36e | |
AppleDash | 854c2f5f10 |
|
@ -9,7 +9,7 @@
|
|||
<version>0</version>
|
||||
</parent>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.0-SNAPSHOT</version>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
|||
import org.appledash.saneeconomy.vault.VaultHook;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/18/16.
|
||||
|
@ -27,4 +28,6 @@ public interface ISaneEconomy {
|
|||
Optional<TransactionLogger> getTransactionLogger();
|
||||
|
||||
VaultHook getVaultHook();
|
||||
|
||||
String getLastName(UUID uuid);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class SaneEconomy extends SanePlugin implements ISaneEconomy {
|
|||
private TransactionLogger transactionLogger;
|
||||
private GithubVersionChecker versionChecker;
|
||||
|
||||
private final Map<String, SaneCommand> COMMANDS = new HashMap<String, SaneCommand>() {
|
||||
private final Map<String, SaneCommand> commands = new HashMap<String, SaneCommand>() {
|
||||
{
|
||||
this.put("balance", new BalanceCommand(SaneEconomy.this));
|
||||
this.put("ecoadmin", new EconomyAdminCommand(SaneEconomy.this));
|
||||
|
@ -184,7 +184,7 @@ public class SaneEconomy extends SanePlugin implements ISaneEconomy {
|
|||
|
||||
private void loadCommands() {
|
||||
this.getLogger().info("Initializing commands...");
|
||||
this.COMMANDS.forEach((name, command) -> this.getCommand(name).setExecutor(command));
|
||||
this.commands.forEach((name, command) -> this.getCommand(name).setExecutor(command));
|
||||
this.getLogger().info("Initialized commands.");
|
||||
}
|
||||
|
||||
|
@ -241,4 +241,9 @@ public class SaneEconomy extends SanePlugin implements ISaneEconomy {
|
|||
public VaultHook getVaultHook() {
|
||||
return this.vaultHook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(UUID uuid) {
|
||||
return this.economyManager.getBackend().getLastName("player:" + uuid.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class BalanceTopCommand extends SaneCommand {
|
|||
try {
|
||||
page = Math.abs(Integer.parseInt(args[0]));
|
||||
} catch (NumberFormatException e) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "{1} is not a valid number.");
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "{1} is not a valid number.", args[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,12 @@ public class BalanceTopCommand extends SaneCommand {
|
|||
AtomicInteger index = new AtomicInteger(offset + 1); /* I know it's stupid, but you can't do some_int++ from within the lambda. */
|
||||
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Top {1} players on page {2}:", topBalances.size(), page);
|
||||
topBalances.forEach((player, balance) -> this.saneEconomy.getMessenger().sendMessage(sender, "[{1:02d}] {2} - {3}", index.getAndIncrement(), player == null ? "<unknown>" : player, this.saneEconomy.getEconomyManager().getCurrency().formatAmount(balance)));
|
||||
|
||||
topBalances.forEach((player, balance) ->
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "[{1:02d}] {2} - {3}",
|
||||
index.getAndIncrement(),
|
||||
player == null ? "<unknown>" : player,
|
||||
this.saneEconomy.getEconomyManager().getCurrency().formatAmount(balance))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,11 @@ public class PayCommand extends SaneCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.saneEconomy.getConfig().getConfigurationSection("economy").getBoolean("pay-offline-players", true) && !toPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You cannot pay an offline player.");
|
||||
return;
|
||||
}
|
||||
|
||||
String sAmount = args[1];
|
||||
BigDecimal amount = NumberUtils.parseAndFilter(ecoMan.getCurrency(), sAmount);
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import org.bukkit.configuration.ConfigurationSection;
|
|||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
|
@ -78,7 +77,7 @@ public class Currency {
|
|||
*/
|
||||
public String formatAmount(BigDecimal amount) {
|
||||
return ChatColor.translateAlternateColorCodes('&',
|
||||
MessageUtils.indexedFormat(this.balanceFormat, this.format.format(amount), amount.equals(BigDecimal.ONE) ? this.nameSingular : this.namePlural)
|
||||
MessageUtils.indexedFormat(this.balanceFormat, this.format.format(amount), amount.compareTo(BigDecimal.ONE) == 0 ? this.nameSingular : this.namePlural)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.appledash.saneeconomy.event.SaneEconomyTransactionEvent;
|
|||
import org.appledash.saneeconomy.utils.MapUtil;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -28,6 +29,8 @@ public class EconomyManager {
|
|||
private final Currency currency;
|
||||
private final EconomyStorageBackend backend;
|
||||
private final String serverAccountName;
|
||||
|
||||
private static final BigDecimal REQUIRED_BALANCE_ACCURACY = new BigDecimal("0.0001");
|
||||
|
||||
public EconomyManager(ISaneEconomy saneEconomy, Currency currency, EconomyStorageBackend backend, String serverAccountName) {
|
||||
this.saneEconomy = saneEconomy;
|
||||
|
@ -83,8 +86,25 @@ public class EconomyManager {
|
|||
* @return True if they have requiredBalance or more, false otherwise
|
||||
*/
|
||||
public boolean hasBalance(Economable targetPlayer, BigDecimal requiredBalance) {
|
||||
return (EconomableConsole.isConsole(targetPlayer)) || (this.getBalance(targetPlayer).compareTo(requiredBalance) >= 0);
|
||||
|
||||
return (EconomableConsole.isConsole(targetPlayer)) || (hasBalance(this.getBalance(targetPlayer), requiredBalance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare account balance and required balance to a reasonable degree of accuracy. <br>
|
||||
* <b>Visible for testing</b>
|
||||
*
|
||||
* @param accountBalance account balance
|
||||
* @param requiredBalance required balance
|
||||
* @return true if the account has the required balance to some degree of accuracy, false otherwise
|
||||
*/
|
||||
public boolean hasBalance(BigDecimal accountBalance, BigDecimal requiredBalance) {
|
||||
if (accountBalance.compareTo(requiredBalance) >= 0) {
|
||||
return true;
|
||||
}
|
||||
// Must compare to degree of accuracy
|
||||
// See https://github.com/AppleDash/SaneEconomy/issues/100
|
||||
BigDecimal difference = requiredBalance.subtract(accountBalance);
|
||||
return difference.compareTo(REQUIRED_BALANCE_ACCURACY) < 0; // difference < PRECISION
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,20 +212,20 @@ public class EconomyManager {
|
|||
* @return Map of OfflinePlayer to Double
|
||||
*/
|
||||
public Map<String, BigDecimal> getTopBalances(int amount, int offset) {
|
||||
LinkedHashMap<String, BigDecimal> uuidBalances = this.backend.getTopBalances();
|
||||
LinkedHashMap<String, BigDecimal> playerNamesToBalances = this.backend.getTopBalances();
|
||||
|
||||
/* TODO
|
||||
uuidBalances.forEach((uuid, balance) -> {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getServer().getOfflinePlayer(uuid);
|
||||
if (offlinePlayer != null) {
|
||||
/*uuidBalances.re((uuid, balance) -> {
|
||||
String playerName = this.backend.getLastName(uuid);
|
||||
|
||||
if (playerName != null) {
|
||||
if ((this.saneEconomy.getVaultHook() == null) || !this.saneEconomy.getVaultHook().hasPermission(offlinePlayer, "saneeconomy.balancetop.hide")) {
|
||||
playerBalances.put(Bukkit.getServer().getOfflinePlayer(uuid), balance);
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
});*/
|
||||
|
||||
return MapUtil.skipAndTake(uuidBalances, offset, amount);
|
||||
|
||||
return MapUtil.skipAndTake(playerNamesToBalances, offset, amount);
|
||||
}
|
||||
|
||||
public EconomyStorageBackend getBackend() {
|
||||
|
|
|
@ -67,6 +67,14 @@ public interface EconomyStorageBackend {
|
|||
*/
|
||||
void waitUntilFlushed();
|
||||
|
||||
/**
|
||||
* Get the last name associated with a unique ID.
|
||||
*
|
||||
* @param uuid Unique ID.
|
||||
* @return Last name, or null if none.
|
||||
*/
|
||||
String getLastName(String uuid);
|
||||
|
||||
enum EconomableReloadReason {
|
||||
CROSS_SERVER_SYNC, PLAYER_JOIN
|
||||
}
|
||||
|
|
|
@ -44,9 +44,9 @@ public abstract class EconomyStorageBackendCaching implements EconomyStorageBack
|
|||
public void reloadTopPlayerBalances() {
|
||||
Map<String, BigDecimal> balances = new HashMap<>();
|
||||
|
||||
this.balances.forEach((identifier, balance) -> {
|
||||
balances.put(this.uuidToName.get(identifier), balance);
|
||||
});
|
||||
this.balances.forEach((identifier, balance) ->
|
||||
balances.put(this.uuidToName.get(identifier), balance)
|
||||
);
|
||||
|
||||
this.topBalances = MapUtil.sortByValue(balances);
|
||||
}
|
||||
|
@ -64,4 +64,9 @@ public abstract class EconomyStorageBackendCaching implements EconomyStorageBack
|
|||
|
||||
this.reloadDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(String uuid) {
|
||||
return this.uuidToName.get(uuid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ public class EconomyStorageBackendJSON extends EconomyStorageBackendCaching {
|
|||
@Override
|
||||
public void setBalance(Economable economable, BigDecimal newBalance) {
|
||||
this.balances.put(economable.getUniqueIdentifier(), newBalance);
|
||||
this.uuidToName.put(economable.getUniqueIdentifier(), economable.getName());
|
||||
this.saveDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void reloadDatabase() {
|
||||
if (!this.file.exists()) {
|
||||
return;
|
||||
|
@ -42,9 +42,9 @@ public class EconomyStorageBackendJSON extends EconomyStorageBackendCaching {
|
|||
this.balances = new ConcurrentHashMap<>();
|
||||
this.uuidToName = new ConcurrentHashMap<>(dataHolder.uuidToName);
|
||||
|
||||
dataHolder.balances.forEach((s, bal) -> {
|
||||
this.balances.put(s, new BigDecimal(bal));
|
||||
});
|
||||
dataHolder.balances.forEach((s, bal) ->
|
||||
this.balances.put(s, new BigDecimal(bal))
|
||||
);
|
||||
|
||||
this.saveDatabase();
|
||||
} catch (FileNotFoundException e) {
|
||||
|
@ -55,7 +55,7 @@ public class EconomyStorageBackendJSON extends EconomyStorageBackendCaching {
|
|||
this.balances = new ConcurrentHashMap<>(dataHolder.balances);
|
||||
this.uuidToName = new ConcurrentHashMap<>(dataHolder.uuidToName);
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new RuntimeException("Failed to load database!", e);
|
||||
throw new RuntimeException("Failed to load database!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ public class EconomyStorageBackendJSON extends EconomyStorageBackendCaching {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
@SuppressWarnings({"FieldMayBeFinal", "CanBeFinal"})
|
||||
private static class DataHolderOld {
|
||||
@SerializedName("balances")
|
||||
private Map<String, Double> balances;
|
||||
|
|
|
@ -105,6 +105,7 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
|||
|
||||
while (rs.next()) {
|
||||
this.balances.put(rs.getString("unique_identifier"), new BigDecimal(rs.getString("balance")));
|
||||
this.uuidToName.put(rs.getString("unique_identifier"), rs.getString("last_name"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed to reload data from SQL.", e);
|
||||
|
@ -115,17 +116,18 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
|||
public void setBalance(Economable economable, BigDecimal newBalance) {
|
||||
BigDecimal oldBalance = this.getBalance(economable);
|
||||
this.balances.put(economable.getUniqueIdentifier(), newBalance);
|
||||
this.uuidToName.put(economable.getUniqueIdentifier(), economable.getName());
|
||||
|
||||
this.dbConn.executeAsyncOperation("set_balance_" + economable.getUniqueIdentifier(), (conn) -> {
|
||||
try {
|
||||
this.ensureAccountExists(economable, conn);
|
||||
conn.prepareStatement("LOCK TABLE " + this.dbConn.getTable(SANEECONOMY_BALANCES) + " WRITE").execute();
|
||||
this.dbConn.lockTable(conn, SANEECONOMY_BALANCES);
|
||||
PreparedStatement statement = this.dbConn.prepareStatement(conn, String.format("UPDATE `%s` SET balance = ?, last_name = ? WHERE `unique_identifier` = ?", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
statement.setString(1, newBalance.toString());
|
||||
statement.setString(2, economable.getName());
|
||||
statement.setString(3, economable.getUniqueIdentifier());
|
||||
statement.executeUpdate();
|
||||
conn.prepareStatement("UNLOCK TABLES").execute();
|
||||
this.dbConn.unlockTables(conn);
|
||||
} catch (Exception e) {
|
||||
this.balances.put(economable.getUniqueIdentifier(), oldBalance);
|
||||
throw new RuntimeException("SQL error has occurred.", e);
|
||||
|
|
|
@ -16,7 +16,7 @@ public class EconomableConsole implements Economable {
|
|||
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return "console:" + CONSOLE_UUID.toString();
|
||||
return "console:" + CONSOLE_UUID;
|
||||
}
|
||||
|
||||
public static boolean isConsole(Economable economable) {
|
||||
|
|
|
@ -18,6 +18,6 @@ public class EconomableGeneric implements Economable {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.uniqueIdentifier.substring(16);
|
||||
return this.uniqueIdentifier.substring(16); // FIXME: Why 16?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class JoinQuitListener implements Listener {
|
|||
public void onPlayerJoin(PlayerJoinEvent evt) {
|
||||
Player player = evt.getPlayer();
|
||||
Economable economable = Economable.wrap((OfflinePlayer) player);
|
||||
BigDecimal startBalance = new BigDecimal(this.plugin.getConfig().getDouble("economy.start-balance", 0.0D));
|
||||
BigDecimal startBalance = BigDecimal.valueOf(this.plugin.getConfig().getDouble("economy.start-balance", 0.0D));
|
||||
|
||||
/* A starting balance is configured AND they haven't been given it yet. */
|
||||
if ((startBalance.compareTo(BigDecimal.ZERO) > 0) && !this.plugin.getEconomyManager().accountExists(economable)) {
|
||||
|
|
|
@ -26,6 +26,7 @@ public final class PlayerUtils {
|
|||
return player;
|
||||
}
|
||||
|
||||
//noinspection ReuseOfLocalVariable
|
||||
player = Bukkit.getServer().getPlayer(playerNameOrUUID);
|
||||
|
||||
if (player == null) {
|
||||
|
|
|
@ -105,13 +105,15 @@ public class SaneEconomyConfiguration {
|
|||
/**
|
||||
* Convert one EconomyStorageBackend to another.
|
||||
* Right now, this just consists of converting all player balances. Data in the old backend is kept.
|
||||
* Why is this in here?
|
||||
* @param oldBackend Old backend
|
||||
* @param newBackend New backend
|
||||
*/
|
||||
private void convertBackends(EconomyStorageBackend oldBackend, EconomyStorageBackend newBackend) {
|
||||
oldBackend.getAllBalances().forEach((uniqueId, balance) -> {
|
||||
newBackend.setBalance(new EconomableGeneric(uniqueId), balance);
|
||||
});
|
||||
oldBackend.getAllBalances().forEach((uniqueId, balance) ->
|
||||
newBackend.setBalance(new EconomableGeneric(uniqueId), balance)
|
||||
);
|
||||
|
||||
newBackend.waitUntilFlushed();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ public final class WebUtils {
|
|||
|
||||
return out.toString();
|
||||
} catch (IOException e) {
|
||||
SaneEconomy.logger().warning("Failed to get contents of URL " + url);
|
||||
throw new RuntimeException("Failed to get URL contents!", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@ import java.util.logging.Logger;
|
|||
*/
|
||||
public class MySQLConnection {
|
||||
private static final Logger LOGGER = Logger.getLogger("MySQLConnection");
|
||||
public static final int FIVE_SECONDS = 5000;
|
||||
private final DatabaseCredentials dbCredentials;
|
||||
private final SaneDatabase saneDatabase;
|
||||
private boolean canLockTables = true;
|
||||
|
||||
public MySQLConnection(DatabaseCredentials dbCredentials) {
|
||||
this.dbCredentials = dbCredentials;
|
||||
|
@ -48,6 +50,31 @@ public class MySQLConnection {
|
|||
}
|
||||
}
|
||||
|
||||
public void lockTable(Connection conn, String tableName) throws SQLException {
|
||||
if (!this.canLockTables) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
conn.prepareStatement("LOCK TABLE " + this.getTable(tableName) + " WRITE").execute();
|
||||
this.canLockTables = true;
|
||||
} catch (SQLException e) {
|
||||
if (this.canLockTables) {
|
||||
LOGGER.warning("Your MySQL user does not have privileges to LOCK TABLES - this may cause issues if you are running this plugin with the same database on multiple servers.");
|
||||
}
|
||||
|
||||
this.canLockTables = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockTables(Connection conn) throws SQLException {
|
||||
if (!this.canLockTables) {
|
||||
return;
|
||||
}
|
||||
|
||||
conn.prepareStatement("UNLOCK TABLES").execute();
|
||||
}
|
||||
|
||||
public void executeAsyncOperation(String tag, Consumer<Connection> callback) {
|
||||
this.saneDatabase.runDatabaseOperationAsync(tag, () -> this.doExecuteAsyncOperation(1, callback));
|
||||
}
|
||||
|
@ -79,7 +106,7 @@ public class MySQLConnection {
|
|||
public void waitUntilFlushed() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
while (!this.saneDatabase.areAllTransactionsDone()) {
|
||||
if ((System.currentTimeMillis() - startTime) > 5000) {
|
||||
if ((System.currentTimeMillis() - startTime) > FIVE_SECONDS) {
|
||||
LOGGER.warning("Took too long to flush all transactions - something has probably hung :(");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ economy:
|
|||
notify-admin-give: false # Whether to notify players when /ecoadmin give is used on them.
|
||||
notify-admin-take: false # Whether to notify players when /ecoadmin take is used on them.
|
||||
notify-admin-set: false # Whether to notify players when /ecoadmin set is used on them.
|
||||
pay-offline-players: true # Whether to allow paying offline players or not.
|
||||
|
||||
multi-server-sync: false # Experimental balance syncing without player rejoins, across BungeeCord networks.
|
||||
update-check: true # Whether to check for updates to the plugin and notify admins about them.
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
# The order of placeholders can be changed. Anything after the :, like the '02d' in {1:02d} is a Java String.format specifier. If you don't know what that is, I recommend leaving it as-is.
|
||||
# IMPORTANT: If your translation has a colon ( : ) character inside of it, you must enclose the entire part after "translation: " in single quotes ( ' ).
|
||||
# If this file doesn't work for some reason, check your console for errors with "SnakeYAML" included in them.
|
||||
####################################################################################################
|
||||
########## READ ABOVE IF YOU INTEND TO EDIT THIS FILE, BEFORE ASKING FOR HELP! #####################
|
||||
####################################################################################################
|
||||
messages:
|
||||
- message: "You don't have permission to check the balance of {1}."
|
||||
- message: "That player is not online."
|
||||
|
|
|
@ -15,7 +15,7 @@ public class CurrencyTest {
|
|||
@Test
|
||||
public void testCurrencyFormat() {
|
||||
Currency currency = new Currency("test dollar", "test dollars", new DecimalFormat("0.00"));
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal(1.0D)), "1.00 test dollar");
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal(1337.0D)), "1337.00 test dollars");
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal("1.0")), "1.00 test dollar");
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal("1337.0")), "1337.00 test dollars");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ public class EconomableTest {
|
|||
@Test
|
||||
public void testWrapFaction() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
Economable economable = Economable.wrap(String.format("faction-%s", uuid.toString()));
|
||||
Economable economable = Economable.wrap(String.format("faction-%s", uuid));
|
||||
Assert.assertEquals(economable.getClass(), EconomableFaction.class);
|
||||
Assert.assertEquals(economable.getUniqueIdentifier(), String.format("faction:%s", uuid.toString()));
|
||||
Assert.assertEquals(economable.getUniqueIdentifier(), String.format("faction:%s", uuid));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.junit.Test;
|
|||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -45,7 +46,7 @@ public class EconomyManagerTest {
|
|||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerOne));
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerTwo));
|
||||
|
||||
this.economyManager.setBalance(playerOne, new BigDecimal(100.0));
|
||||
this.economyManager.setBalance(playerOne, new BigDecimal("100.0"));
|
||||
|
||||
// Now one should have an account, but two should not
|
||||
Assert.assertTrue(this.economyManager.accountExists(playerOne));
|
||||
|
@ -56,7 +57,7 @@ public class EconomyManagerTest {
|
|||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerTwo));
|
||||
|
||||
// One should be able to transfer to two
|
||||
Assert.assertSame(this.economyManager.transact(new Transaction(this.economyManager.getCurrency(), playerOne, playerTwo, new BigDecimal(50.0), TransactionReason.PLAYER_PAY)).getStatus(), TransactionResult.Status.SUCCESS);
|
||||
Assert.assertSame(this.economyManager.transact(new Transaction(this.economyManager.getCurrency(), playerOne, playerTwo, new BigDecimal("50.0"), TransactionReason.PLAYER_PAY)).getStatus(), TransactionResult.Status.SUCCESS);
|
||||
|
||||
// One should now have only 50 left, two should have 50 now
|
||||
SaneEcoAssert.assertEquals("Player one should have 50 dollars", new BigDecimal("50.00"), this.economyManager.getBalance(playerOne));
|
||||
|
@ -117,4 +118,25 @@ public class EconomyManagerTest {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasRequiredBalance() {
|
||||
for (int n = 0; n < 20; n++) { // in the absence of Junit 5's @RepeatedTest
|
||||
|
||||
BigDecimal bigDecimal = randomBigDecimal();
|
||||
// We MUST modify the BigDecimal in some way otherwise the test will always succeed
|
||||
// See https://github.com/AppleDash/SaneEconomy/issues/100
|
||||
for (int m = 0; m < 20; m++) {
|
||||
bigDecimal = bigDecimal.add(randomBigDecimal()).subtract(randomBigDecimal());
|
||||
}
|
||||
//
|
||||
|
||||
Assert.assertTrue("Account must have required balance despite loss of precision (repeat " + n + ")",
|
||||
economyManager.hasBalance(bigDecimal, new BigDecimal(bigDecimal.doubleValue())));
|
||||
}
|
||||
}
|
||||
|
||||
private static BigDecimal randomBigDecimal() {
|
||||
return new BigDecimal(ThreadLocalRandom.current().nextDouble());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
|||
import org.appledash.saneeconomy.vault.VaultHook;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/18/16.
|
||||
|
@ -26,4 +27,9 @@ public class MockSaneEconomy implements ISaneEconomy {
|
|||
public VaultHook getVaultHook() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(UUID uuid) {
|
||||
return uuid.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.logging.Logger;
|
|||
* Created by appledash on 7/15/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class MockServer implements Server {
|
||||
public static MockServer instance;
|
||||
|
||||
|
@ -501,12 +502,12 @@ public class MockServer implements Server {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CachedServerIcon loadServerIcon(File file) throws Exception {
|
||||
public CachedServerIcon loadServerIcon(File file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedServerIcon loadServerIcon(BufferedImage bufferedImage) throws Exception {
|
||||
public CachedServerIcon loadServerIcon(BufferedImage bufferedImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.0-SNAPSHOT</version>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.bukkit.event.Listener;
|
|||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
@ -83,11 +84,11 @@ public class EntityDamageListener implements Listener {
|
|||
|
||||
if (offlinePlayer.isOnline()) {
|
||||
Player player = Bukkit.getServer().getPlayer(offlinePlayer.getUniqueId());
|
||||
this.plugin.getMessenger().sendMessage(player, "You have been awarded {1} for doing {2:.2f}% of the damage required to kill that {3}!", this.plugin.getSaneEconomy().getEconomyManager().getCurrency().formatAmount(thisAmount), thisPercent, entity.getName());
|
||||
this.plugin.getMessenger().sendMessage(player, "You have been awarded {1} for doing {2:.2f}% of the damage required to kill that {3}!", this.plugin.getSaneEconomy().getEconomyManager().getCurrency().formatAmount(BigDecimal.valueOf(thisAmount)), thisPercent, entity.getName());
|
||||
}
|
||||
|
||||
this.plugin.getSaneEconomy().getEconomyManager().transact(new Transaction(
|
||||
this.plugin.getSaneEconomy().getEconomyManager().getCurrency(), Economable.PLUGIN, Economable.wrap(offlinePlayer), thisAmount, TransactionReason.PLUGIN_GIVE
|
||||
this.plugin.getSaneEconomy().getEconomyManager().getCurrency(), Economable.PLUGIN, Economable.wrap(offlinePlayer), BigDecimal.valueOf(thisAmount), TransactionReason.PLUGIN_GIVE
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.0-SNAPSHOT</version>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.appledash.saneeconomy.onlinetime;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -8,14 +9,14 @@ import java.util.Map;
|
|||
*/
|
||||
public class Payout {
|
||||
private final int secondsInterval;
|
||||
private final double amount;
|
||||
private final BigDecimal amount;
|
||||
private final String message;
|
||||
private String permission;
|
||||
private final long reportInterval;
|
||||
|
||||
public Payout(int secondsInterval, double amount, String message, long reportInterval) {
|
||||
this.secondsInterval = secondsInterval;
|
||||
this.amount = amount;
|
||||
this.amount = BigDecimal.valueOf(amount);
|
||||
this.message = message;
|
||||
this.reportInterval = reportInterval;
|
||||
}
|
||||
|
@ -24,7 +25,7 @@ public class Payout {
|
|||
return this.secondsInterval;
|
||||
}
|
||||
|
||||
public double getAmount() {
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.bukkit.event.EventHandler;
|
|||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -18,7 +19,7 @@ import java.util.*;
|
|||
*/
|
||||
public class SaneEconomyOnlineTime extends SanePlugin implements Listener {
|
||||
private final Map<UUID, Long> onlineSeconds = new HashMap<>();
|
||||
private final Map<UUID, Double> reportingAmounts = new HashMap<>();
|
||||
private final Map<UUID, BigDecimal> reportingAmounts = new HashMap<>();
|
||||
private final List<Payout> payouts = new ArrayList<>();
|
||||
private SaneEconomy saneEconomy;
|
||||
|
||||
|
@ -47,7 +48,7 @@ public class SaneEconomyOnlineTime extends SanePlugin implements Listener {
|
|||
|
||||
if ((onlineSeconds % payout.getSecondsInterval()) == 0) {
|
||||
if (this.reportingAmounts.containsKey(player.getUniqueId())) {
|
||||
this.reportingAmounts.put(player.getUniqueId(), this.reportingAmounts.get(player.getUniqueId()) + payout.getAmount());
|
||||
this.reportingAmounts.put(player.getUniqueId(), this.reportingAmounts.get(player.getUniqueId()).add(payout.getAmount()));
|
||||
} else {
|
||||
this.reportingAmounts.put(player.getUniqueId(), payout.getAmount());
|
||||
}
|
||||
|
@ -56,8 +57,8 @@ public class SaneEconomyOnlineTime extends SanePlugin implements Listener {
|
|||
}
|
||||
|
||||
if ((onlineSeconds % payout.getReportInterval()) == 0) {
|
||||
this.getMessenger().sendMessage(player, payout.getMessage(), this.saneEconomy.getEconomyManager().getCurrency().formatAmount(this.reportingAmounts.getOrDefault(player.getUniqueId(), 0.0D)), payout.getReportInterval());
|
||||
this.reportingAmounts.put(player.getUniqueId(), 0.0D);
|
||||
this.getMessenger().sendMessage(player, payout.getMessage(), this.saneEconomy.getEconomyManager().getCurrency().formatAmount(this.reportingAmounts.getOrDefault(player.getUniqueId(), BigDecimal.ZERO)), payout.getReportInterval());
|
||||
this.reportingAmounts.put(player.getUniqueId(), BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.0-SNAPSHOT</version>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
|
|||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -114,7 +115,7 @@ public class InteractListener implements Listener {
|
|||
private void doSell(SignShop shop, Player player) { // TODO: Selling enchanted items
|
||||
EconomyManager ecoMan = this.plugin.getSaneEconomy().getEconomyManager();
|
||||
int quantity = player.isSneaking() ? 1 : shop.getQuantity();
|
||||
double price = shop.getSellPrice(quantity);
|
||||
BigDecimal price = shop.getSellPrice(quantity);
|
||||
|
||||
if (!player.getInventory().containsAtLeast(new ItemStack(shop.getItemStack()), quantity)) {
|
||||
this.plugin.getMessenger().sendMessage(player, "You do not have {1} {2}!", quantity, shop.getItemStack().getType().name());
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
|||
import org.appledash.saneeconomysignshop.util.ItemInfo;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/1/17.
|
||||
* Blackjack is still best pony.
|
||||
|
@ -18,9 +20,9 @@ public class ShopTransaction {
|
|||
private final Player player;
|
||||
private final ItemInfo item;
|
||||
private final int quantity;
|
||||
private final double price;
|
||||
private final BigDecimal price;
|
||||
|
||||
public ShopTransaction(Currency currency, TransactionDirection direction, Player player, ItemInfo item, int quantity, double price) {
|
||||
public ShopTransaction(Currency currency, TransactionDirection direction, Player player, ItemInfo item, int quantity, BigDecimal price) {
|
||||
this.currency = currency;
|
||||
this.direction = direction;
|
||||
this.player = player;
|
||||
|
@ -45,7 +47,7 @@ public class ShopTransaction {
|
|||
return this.quantity;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
@ -20,8 +21,8 @@ public class SignShop implements Serializable {
|
|||
private final SerializableLocation location;
|
||||
private final ItemInfo item;
|
||||
private final int quantity;
|
||||
private final double buyPrice;
|
||||
private final double sellPrice;
|
||||
private final BigDecimal buyPrice;
|
||||
private final BigDecimal sellPrice;
|
||||
|
||||
public SignShop(UUID ownerUuid, Location location, ItemStack item, int quantity, double buyPrice, double sellPrice) {
|
||||
if ((ownerUuid == null) || (location == null) || (item == null)) {
|
||||
|
@ -36,8 +37,8 @@ public class SignShop implements Serializable {
|
|||
this.location = new SerializableLocation(location);
|
||||
this.item = new ItemInfo(item);
|
||||
this.quantity = quantity;
|
||||
this.buyPrice = buyPrice;
|
||||
this.sellPrice = sellPrice;
|
||||
this.buyPrice = BigDecimal.valueOf(buyPrice);
|
||||
this.sellPrice = BigDecimal.valueOf(sellPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +69,7 @@ public class SignShop implements Serializable {
|
|||
* Get the price that the player can buy this item from the server for
|
||||
* @return Buy price for this.getQuantity() items
|
||||
*/
|
||||
public double getBuyPrice() {
|
||||
public BigDecimal getBuyPrice() {
|
||||
return this.buyPrice;
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,7 @@ public class SignShop implements Serializable {
|
|||
* Get the price that the player can sell this item to the server for
|
||||
* @return Buy price for this.getQuantity() items
|
||||
*/
|
||||
public double getSellPrice() {
|
||||
public BigDecimal getSellPrice() {
|
||||
return this.sellPrice;
|
||||
}
|
||||
|
||||
|
@ -86,8 +87,8 @@ public class SignShop implements Serializable {
|
|||
* @param quantity Quantity of items to price
|
||||
* @return Price to buy that number of items at this shop
|
||||
*/
|
||||
public double getBuyPrice(int quantity) {
|
||||
return this.buyPrice * ((float)quantity / (float)this.quantity); // TODO: Is this okay?
|
||||
public BigDecimal getBuyPrice(int quantity) {
|
||||
return this.buyPrice.multiply(BigDecimal.valueOf((double) quantity / this.quantity)); // TODO: Is this okay?
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,8 +97,8 @@ public class SignShop implements Serializable {
|
|||
* @param quantity Quantity of items to price
|
||||
* @return Price to sell that number of items at this shop
|
||||
*/
|
||||
public double getSellPrice(int quantity) {
|
||||
return this.sellPrice * ((float)quantity / (float)this.quantity); // TODO: Is this okay?
|
||||
public BigDecimal getSellPrice(int quantity) {
|
||||
return this.sellPrice.multiply(BigDecimal.valueOf((double) quantity / this.quantity)); // TODO: Is this okay?
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +106,7 @@ public class SignShop implements Serializable {
|
|||
* @return True if they can, false if they can't
|
||||
*/
|
||||
public boolean canBuy() {
|
||||
return this.buyPrice >= 0;
|
||||
return this.buyPrice.compareTo(BigDecimal.ZERO) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,7 +114,7 @@ public class SignShop implements Serializable {
|
|||
* @return True if they can, false if they can't
|
||||
*/
|
||||
public boolean canSell() {
|
||||
return this.sellPrice >= 0;
|
||||
return this.sellPrice.compareTo(BigDecimal.ZERO) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue