diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java index 928b7dc..7e61337 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -101,6 +101,8 @@ import org.apache.commons.io.Charsets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.service.Economy; import org.ultramine.core.service.InjectService; import org.ultramine.server.WorldConstants; import org.ultramine.server.event.PlayerDeathEvent; @@ -1001,6 +1003,8 @@ private PlayerData playerData; @InjectService private static Permissions perms; + @InjectService + private static Economy economy; public boolean hasPermission(String permission) { @@ -1011,6 +1015,11 @@ { return perms.getMeta(this, key); } + + public Account getAccount() + { + return economy.getPlayerAccount(this); + } @Override public boolean isEntityPlayerMP() diff --git a/src/main/java/org/ultramine/core/economy/Currency.java b/src/main/java/org/ultramine/core/economy/Currency.java new file mode 100644 index 0000000..0f640e3 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/Currency.java @@ -0,0 +1,40 @@ +package org.ultramine.core.economy; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; + +@Immutable +public interface Currency +{ + /** + * @return the currency's unique id + */ + @Nonnull + String getId(); + + /** + * @return the currency's display name, in singular form. Ex: Dollar. + */ + @Nonnull + String getDisplayName(); + + /** + * @return the currency's display name in plural form. Ex: Dollars. + */ + @Nonnull + String getPluralDisplayName(); + + /** + * @return the currency's symbol. Ex. $ + */ + @Nonnull + String getSymbol(); + + /** + * @return number of digits after the decimal point kept or -1 if any number + */ + int getFractionalDigits(); + + @Nonnull + String format(double amount); +} diff --git a/src/main/java/org/ultramine/core/economy/account/Account.java b/src/main/java/org/ultramine/core/economy/account/Account.java new file mode 100644 index 0000000..d317062 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/account/Account.java @@ -0,0 +1,29 @@ +package org.ultramine.core.economy.account; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.holdings.Holdings; +import org.ultramine.core.economy.exception.CurrencyNotFoundException; +import org.ultramine.core.economy.exception.CurrencyNotSupportedException; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public interface Account +{ + @Nonnull + String getName(); + + @Nonnull + Collection getSupportedCurrencies(); + + boolean isCurrencySupported(@Nonnull Currency currency); + + @Nonnull + Currency getSupportedCurrency(@Nonnull String id) throws CurrencyNotFoundException, CurrencyNotSupportedException; + + @Nonnull + Holdings getHoldings(@Nonnull Currency currency) throws CurrencyNotSupportedException; + + @Nonnull + Holdings getDefaultHoldings(); +} diff --git a/src/main/java/org/ultramine/core/economy/account/PlayerAccount.java b/src/main/java/org/ultramine/core/economy/account/PlayerAccount.java new file mode 100644 index 0000000..949547f --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/account/PlayerAccount.java @@ -0,0 +1,13 @@ +package org.ultramine.core.economy.account; + +import com.mojang.authlib.GameProfile; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +public interface PlayerAccount extends Account +{ + @Nonnull + GameProfile getProfile(); +} diff --git a/src/main/java/org/ultramine/core/economy/exception/AccountTypeNotSupportedException.java b/src/main/java/org/ultramine/core/economy/exception/AccountTypeNotSupportedException.java new file mode 100644 index 0000000..256b692 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/AccountTypeNotSupportedException.java @@ -0,0 +1,31 @@ +package org.ultramine.core.economy.exception; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.account.Account; + +import javax.annotation.Nonnull; + +public class AccountTypeNotSupportedException extends EconomyException +{ + @Nonnull private final Account account; + @Nonnull private final Currency currency; + + public AccountTypeNotSupportedException(@Nonnull Account account, @Nonnull Currency currency) + { + super("ultramine.economy.fail.account_type_not_supported", account.getName(), currency.getId()); + this.account = account; + this.currency = currency; + } + + @Nonnull + public Account getAccount() + { + return account; + } + + @Nonnull + public Currency getCurrency() + { + return currency; + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/CurrencyNotFoundException.java b/src/main/java/org/ultramine/core/economy/exception/CurrencyNotFoundException.java new file mode 100644 index 0000000..5b9c698 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/CurrencyNotFoundException.java @@ -0,0 +1,17 @@ +package org.ultramine.core.economy.exception; + +public class CurrencyNotFoundException extends EconomyException +{ + private final String currencyId; + + public CurrencyNotFoundException(String currencyId) + { + super("ultramine.economy.fail.currency_not_exists", currencyId); + this.currencyId = currencyId; + } + + public String getCurrencyId() + { + return currencyId; + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/CurrencyNotSupportedException.java b/src/main/java/org/ultramine/core/economy/exception/CurrencyNotSupportedException.java new file mode 100644 index 0000000..36e4484 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/CurrencyNotSupportedException.java @@ -0,0 +1,30 @@ +package org.ultramine.core.economy.exception; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.account.Account; + +import javax.annotation.Nonnull; + +public class CurrencyNotSupportedException extends EconomyException +{ + @Nonnull private final Account account; + @Nonnull private final Currency currency; + + public CurrencyNotSupportedException(@Nonnull Account account, @Nonnull Currency currency) + { + super("ultramine.economy.fail.currency_not_supported", account.getName(), currency.getId()); + this.account = account; + this.currency = currency; + } + + @Nonnull + public Account getAccount() + { + return account; + } + + public Currency getCurrency() + { + return currency; + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/EconomyException.java b/src/main/java/org/ultramine/core/economy/exception/EconomyException.java new file mode 100644 index 0000000..5a7f773 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/EconomyException.java @@ -0,0 +1,22 @@ +package org.ultramine.core.economy.exception; + +import net.minecraft.command.CommandException; + +public class EconomyException extends CommandException +{ + public EconomyException(String translationKey, Object... args) + { + super(translationKey, args); + } + + public EconomyException(Throwable t) + { + this("Unexpected exception occurred during economy operation", t); + } + + public EconomyException(String message, Throwable t) + { + this(message); + initCause(t); + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/InsufficientFundsException.java b/src/main/java/org/ultramine/core/economy/exception/InsufficientFundsException.java new file mode 100644 index 0000000..3f2f33a --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/InsufficientFundsException.java @@ -0,0 +1,36 @@ +package org.ultramine.core.economy.exception; + +import org.ultramine.core.economy.holdings.Holdings; + +/** + * The exception thrown if you try to withdraw more than account contains + */ +public class InsufficientFundsException extends EconomyException +{ + private final Holdings holdings; + private final double balance; + private final double amount; + + public InsufficientFundsException(Holdings holdings, double balance, double amount) + { + super("ultramine.economy.fail.insufficient_funds", balance, amount); + this.holdings = holdings; + this.balance = balance; + this.amount = amount; + } + + public Holdings getHoldings() + { + return holdings; + } + + public double getBalance() + { + return balance; + } + + public double getAmount() + { + return amount; + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/InternalEconomyException.java b/src/main/java/org/ultramine/core/economy/exception/InternalEconomyException.java new file mode 100644 index 0000000..6fd819f --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/InternalEconomyException.java @@ -0,0 +1,19 @@ +package org.ultramine.core.economy.exception; + +public class InternalEconomyException extends EconomyException +{ + public InternalEconomyException(String translationKey, Object... args) + { + super(translationKey, args); + } + + public InternalEconomyException(Throwable t) + { + super(t); + } + + public InternalEconomyException(String message, Throwable t) + { + super(message, t); + } +} diff --git a/src/main/java/org/ultramine/core/economy/exception/NegativeAmountException.java b/src/main/java/org/ultramine/core/economy/exception/NegativeAmountException.java new file mode 100644 index 0000000..67ef9c2 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/exception/NegativeAmountException.java @@ -0,0 +1,9 @@ +package org.ultramine.core.economy.exception; + +public class NegativeAmountException extends EconomyException +{ + public NegativeAmountException(double amount) + { + super("ultramine.economy.fail.negative_amount", amount); + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/AbstractAsyncHoldings.java b/src/main/java/org/ultramine/core/economy/holdings/AbstractAsyncHoldings.java new file mode 100644 index 0000000..38d4abb --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/AbstractAsyncHoldings.java @@ -0,0 +1,84 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.Currency; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public abstract class AbstractAsyncHoldings implements AsyncHoldings +{ + private final Holdings holdings; + + public AbstractAsyncHoldings(@Nonnull Holdings holdings) + { + this.holdings = holdings; + } + + @Nonnull + protected abstract CompletableFuture execute(@Nonnull Supplier action); + + @Override + public Account getAccount() + { + return holdings.getAccount(); + } + + @Nonnull + @Override + public Currency getCurrency() + { + return holdings.getCurrency(); + } + + @Nonnull + @Override + public CompletableFuture getBalance() + { + return execute(holdings::getBalance); + } + + @Nonnull + @Override + public CompletableFuture setBalance(double balance, @Nullable String comment) + { + return execute(() -> holdings.setBalance(balance, comment)); + } + + @Nonnull + @Override + public CompletableFuture deposit(double amount, @Nullable String comment) + { + return execute(() -> holdings.deposit(amount, comment)); + } + + @Nonnull + @Override + public CompletableFuture withdrawUnchecked(double amount, @Nullable String comment) + { + return execute(() -> holdings.withdrawUnchecked(amount, comment)); + } + + @Nonnull + @Override + public CompletableFuture withdraw(double amount, @Nullable String comment) + { + return execute(() -> holdings.withdraw(amount, comment)); + } + + @Nonnull + @Override + public CompletableFuture transferUnchecked(@Nonnull Account to, double amount, @Nullable String comment) + { + return execute(() -> holdings.transferUnchecked(to, amount, comment)); + } + + @Nonnull + @Override + public CompletableFuture transfer(@Nonnull Account to, double amount, @Nullable String comment) + { + return execute(() -> holdings.transfer(to, amount, comment)); + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/AsyncHoldings.java b/src/main/java/org/ultramine/core/economy/holdings/AsyncHoldings.java new file mode 100644 index 0000000..a4bfe11 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/AsyncHoldings.java @@ -0,0 +1,61 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.account.Account; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.CompletableFuture; + +@ThreadSafe +public interface AsyncHoldings +{ + @Nonnull Account getAccount(); + + @Nonnull Currency getCurrency(); + + @Nonnull CompletableFuture getBalance(); + + @Nonnull CompletableFuture setBalance(double balance, @Nullable String comment); + + @Nonnull CompletableFuture deposit(double amount, @Nullable String comment); + + @Nonnull CompletableFuture withdrawUnchecked(double amount, @Nullable String comment); + + @Nonnull CompletableFuture withdraw(double amount, @Nullable String comment); + + @Nonnull CompletableFuture transferUnchecked(Account to, double amount, @Nullable String comment); + + @Nonnull CompletableFuture transfer(@Nonnull Account to, double amount, @Nullable String comment); + + @Nonnull default CompletableFuture setBalance(double balance) + { + return setBalance(balance, null); + } + + @Nonnull default CompletableFuture deposit(double amount) + { + return deposit(amount, null); + } + + @Nonnull default CompletableFuture withdrawUnchecked(double amount) + { + return withdrawUnchecked(amount, null); + } + + @Nonnull default CompletableFuture withdraw(double amount) + { + return withdraw(amount, null); + } + + @Nonnull default CompletableFuture transferUnchecked(@Nonnull Account to, double amount) + { + return transferUnchecked(to, amount, null); + } + + @Nonnull default CompletableFuture transfer(@Nonnull Account to, double amount) + { + return transfer(to, amount, null); + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/FakeAsyncHoldings.java b/src/main/java/org/ultramine/core/economy/holdings/FakeAsyncHoldings.java new file mode 100644 index 0000000..152c51b --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/FakeAsyncHoldings.java @@ -0,0 +1,29 @@ +package org.ultramine.core.economy.holdings; + +import javax.annotation.Nonnull; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class FakeAsyncHoldings extends AbstractAsyncHoldings +{ + public FakeAsyncHoldings(Holdings holdings) + { + super(holdings); + } + + @Nonnull + @Override + protected CompletableFuture execute(@Nonnull Supplier action) + { + try + { + return CompletableFuture.completedFuture(action.get()); + } + catch (Throwable t) + { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(t); + return future; + } + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/Holdings.java b/src/main/java/org/ultramine/core/economy/holdings/Holdings.java new file mode 100644 index 0000000..a3a17be --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/Holdings.java @@ -0,0 +1,234 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.exception.CurrencyNotSupportedException; +import org.ultramine.core.economy.exception.InsufficientFundsException; +import org.ultramine.core.economy.exception.NegativeAmountException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import java.util.function.DoubleUnaryOperator; + +/** + * Holdings represents a single balance associated with pair (account, currency). It must always be fully thread-safe, so + * constructions like setBalance(getBalance() - ...) will not work anyway. Use {@link Holdings#computeBalance(DoubleUnaryOperator, String)} + * if you want to perform some custom operation with balance + */ +@ThreadSafe +public interface Holdings +{ + @Nonnull + Account getAccount(); + + @Nonnull + Currency getCurrency(); + + /** + * Return the balance of this holdings. This method may return not relevant balance, for example, in sql-based holdings balance may be cached. + * So, never use constructions like setBalance(getBalance() - ...); + * @return the current balance of this holdings + */ + double getBalance(); + + /** + * Sets this holdings balance + * @param balance the balance to be set + * @param comment the comment to be logged with this transaction + * @return previous balance + */ + double setBalance(double balance, @Nullable String comment); + + /** + * Deposits the specified amount to this holdings. + * @param amount the amount to deposit + * @param comment the comment to be logged with this transaction + * @return new balance + * @throws org.ultramine.core.economy.exception.NegativeAmountException if the specified amount is less than zero + */ + double deposit(double amount, @Nullable String comment) throws NegativeAmountException; + + /** + * Withdraws the specified amount from this holdings. Unlike {@link Holdings#withdraw}, the amount may be greater than + * the current holdings balance and after transaction balance may be negative. + * @param amount the amount to withdraw + * @param comment the comment to be logged with this transaction + * @return new balance + * @throws NegativeAmountException if the specified amount is less than zero + */ + double withdrawUnchecked(double amount, @Nullable String comment) throws NegativeAmountException; + + /** + * Withdraws the specified amount from this holdings. Unlike {@link Holdings#withdrawUnchecked}, the amount may not + * be greater than the current holdings balance: InsufficientFundsException will be thrown + * @param amount the amount to withdraw + * @param comment the comment to be logged with this transaction + * @return new balance + * @throws NegativeAmountException if the specified amount is less than zero + * @throws org.ultramine.core.economy.exception.InsufficientFundsException if the current holdings balance is less than the specified amount + */ + double withdraw(double amount, @Nullable String comment) throws NegativeAmountException, InsufficientFundsException; + + /** + * Performs custom operation on balance in thread-safe, transactional way. Use it always instead of constructions + * like setBalance(getBalance() - ...) + * @return new balance + * @param operation the action to be performed on a balance + * @param comment the comment to be logged with this transaction + */ + double computeBalance(DoubleUnaryOperator operation, @Nullable String comment); + + /** + * Sets this holdings balance + * @param balance the balance to be set + * @return previous balance + */ + default double setBalance(double balance) + { + return setBalance(balance, null); + } + + /** + * Deposits the specified amount to this holdings. + * @param amount the amount to deposit + * @return new balance + * @throws NegativeAmountException if the specified amount is less than zero + */ + default double deposit(double amount) throws NegativeAmountException + { + return deposit(amount, null); + } + + /** + * Withdraws the specified amount from this holdings. Unlike {@link Holdings#withdraw}, the amount may be greater than + * the current holdings balance and after transaction balance may be negative. + * @param amount the amount to withdraw + * @return new balance + * @throws NegativeAmountException if the specified amount is less than zero + */ + default double withdrawUnchecked(double amount) throws NegativeAmountException + { + return withdrawUnchecked(amount, null); + } + + /** + * Withdraws the specified amount from this holdings. Unlike {@link Holdings#withdrawUnchecked}, the amount may not + * be greater than the current holdings balance: InsufficientFundsException will be thrown + * @param amount the amount to withdraw + * @return new balance + * @throws NegativeAmountException if the specified amount is less than zero + * @throws InsufficientFundsException if the current holdings balance is less than the specified amount + */ + default double withdraw(double amount) throws NegativeAmountException, InsufficientFundsException + { + return withdraw(amount, null); + } + + /** + * Performs custom operation on balance in thread-safe, transactional way. Use it always instead of constructions + * like setBalance(getBalance() - ...). Note that {@code operation} may be executed multiple times. + * @return new balance + * @param operation the action to be performed on a balance + */ + default double computeBalance(DoubleUnaryOperator operation) + { + return computeBalance(operation, null); + } + + /** + * @return true if balance is less than zero + */ + default boolean isNegative() + { + return getBalance() < 0; + } + + /** + * @return true only if balance is greater or equals to the specified amount + */ + default boolean hasEnough(double amount) + { + return getBalance() >= amount; + } + + /** + * Transfers the specified amount of the current {@link Currency} from this holdings to the holdings for the same + * currency of the destination account. Unlike {@link Holdings#transfer}, the amount may be greater than the + * current holdings balance and after this transaction the current holdings balance may be negative. + * @param to the recipient account + * @param amount the amount to transfer + * @param comment the comment to be logged with this transaction + * @throws NegativeAmountException if the specified amount is less than zero + * @throws org.ultramine.core.economy.exception.CurrencyNotSupportedException if the recipient account do not support the current {@link Currency} + */ + default double transferUnchecked(@Nonnull Account to, double amount, @Nullable String comment) throws NegativeAmountException, CurrencyNotSupportedException + { + Holdings holdingsTo = to.getHoldings(getCurrency()); + double ret = withdrawUnchecked(amount, comment); + holdingsTo.deposit(amount, comment); + return ret; + } + + /** + * Transfers the specified amount of the current {@link Currency} from this holdings to the holdings for the same + * currency of the destination account. Unlike {@link Holdings#transfer}, the amount may be greater than the + * current holdings balance and after this transaction the current holdings balance may be negative. + * @param to the recipient account + * @param amount the amount to transfer + * @throws NegativeAmountException if the specified amount is less than zero + * @throws CurrencyNotSupportedException if the recipient account do not support the current {@link Currency} + */ + default double transferUnchecked(@Nonnull Account to, double amount) throws NegativeAmountException, CurrencyNotSupportedException + { + return transferUnchecked(to, amount, null); + } + + /** + * Transfers the specified amount of the current {@link Currency} from this holdings to the holdings for the same + * currency of the destination account. Unlike {@link Holdings#transferUnchecked}, the amount may not + * be greater than the current holdings balance: InsufficientFundsException will be thrown + * @param to the recipient account + * @param amount the amount to transfer + * @param comment the comment to be logged with this transaction + * @throws NegativeAmountException if the specified amount is less than zero + * @throws InsufficientFundsException if the current holdings balance is less than the specified amount + * @throws CurrencyNotSupportedException if the recipient account do not support the current {@link Currency} + */ + default double transfer(@Nonnull Account to, double amount, @Nullable String comment) throws NegativeAmountException, InsufficientFundsException, CurrencyNotSupportedException + { + Holdings holdingsTo = to.getHoldings(getCurrency()); + double ret = withdraw(amount, comment); + holdingsTo.deposit(amount, comment); + return ret; + } + + /** + * Transfers the specified amount of the current {@link Currency} from this holdings to the holdings for the same + * currency of the destination account. Unlike {@link Holdings#transferUnchecked}, the amount may not + * be greater than the current holdings balance: InsufficientFundsException will be thrown + * @param to the recipient account + * @param amount the amount to transfer + * @throws NegativeAmountException if the specified amount is less than zero + * @throws InsufficientFundsException if the current holdings balance is less than the specified amount + * @throws CurrencyNotSupportedException if the recipient account do not support the current {@link Currency} + */ + default double transfer(@Nonnull Account to, double amount) throws NegativeAmountException, InsufficientFundsException, CurrencyNotSupportedException + { + return transfer(to, amount, null); + } + + /** + * @return true if this implementation of Holdings may block caller thread for a long time. For example, if it is SQL-based Holdings + */ + default boolean isAsyncPreferred() + { + return false; + } + + @Nonnull + default AsyncHoldings asAsync() + { + return isAsyncPreferred() ? new RealAsyncHoldings(this) : new FakeAsyncHoldings(this); + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/HoldingsFactory.java b/src/main/java/org/ultramine/core/economy/holdings/HoldingsFactory.java new file mode 100644 index 0000000..4837d92 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/HoldingsFactory.java @@ -0,0 +1,13 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.exception.AccountTypeNotSupportedException; + +import javax.annotation.Nonnull; + +public interface HoldingsFactory +{ + @Nonnull + Holdings createHoldings(@Nonnull Account account, @Nonnull Currency currency) throws AccountTypeNotSupportedException; +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/MemoryHoldings.java b/src/main/java/org/ultramine/core/economy/holdings/MemoryHoldings.java new file mode 100644 index 0000000..e3fb284 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/MemoryHoldings.java @@ -0,0 +1,140 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.exception.InsufficientFundsException; +import org.ultramine.core.economy.exception.NegativeAmountException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.function.DoubleUnaryOperator; + +@ThreadSafe +public class MemoryHoldings implements Holdings +{ + private static final AtomicLongFieldUpdater BALANCE_FIELD_UPDATER = AtomicLongFieldUpdater.newUpdater(MemoryHoldings.class, "balance_field"); + private final Account account; + private final Currency currency; + + @SuppressWarnings("unused") + private volatile long balance_field; // must not be used directly + protected final double factor; + + public MemoryHoldings(@Nonnull Account account, @Nonnull Currency currency) + { + this.account = account; + this.currency = currency; + this.factor = Math.pow(10, currency.getFractionalDigits()); + } + + public long getBalanceInternal() + { + return BALANCE_FIELD_UPDATER.get(this); + } + + public void setBalanceInternal(long balance) + { + BALANCE_FIELD_UPDATER.set(this, balance); + } + + public void setBalanceSilently(double balance) + { + setBalanceInternal(floor(balance * factor)); + } + + @Nonnull + @Override + public Account getAccount() + { + return account; + } + + @Nonnull + @Override + public Currency getCurrency() + { + return currency; + } + + @Override + public double getBalance() + { + return getBalanceInternal() / factor; + } + + @Override + public double setBalance(double balance, @Nullable String comment) + { + long lastBalance = BALANCE_FIELD_UPDATER.getAndSet(this, floor(balance * factor)); + onHoldingsBalanceChange(); + return lastBalance / factor; + } + + @Override + public double deposit(double amount, @Nullable String comment) throws NegativeAmountException + { + checkAmount(amount); + long toAdd = floor(amount * factor); + long ret = BALANCE_FIELD_UPDATER.updateAndGet(this, balance -> Math.addExact(balance, toAdd)); + onHoldingsBalanceChange(); + return ret / factor; + } + + @Override + public double withdrawUnchecked(double amount, @Nullable String comment) throws NegativeAmountException + { + checkAmount(amount); + long toSubtract = ceiling(amount * factor); + long ret = BALANCE_FIELD_UPDATER.updateAndGet(this, balance -> Math.subtractExact(balance, toSubtract)); + onHoldingsBalanceChange(); + return ret / factor; + } + + @Override + public double withdraw(double amount, @Nullable String comment) throws NegativeAmountException, InsufficientFundsException + { + checkAmount(amount); + long toSubtract = ceiling(amount * factor); + long ret = BALANCE_FIELD_UPDATER.updateAndGet(this, balance -> { + long newBalance = Math.subtractExact(balance, toSubtract); + if(newBalance < 0) + throw new InsufficientFundsException(this, balance, amount); + return newBalance; + }); + onHoldingsBalanceChange(); + return ret / factor; + } + + @Override + public double computeBalance(DoubleUnaryOperator operation, @Nullable String comment) + { + long ret = BALANCE_FIELD_UPDATER.updateAndGet(this, balance -> floor(operation.applyAsDouble(balance / factor) * factor)); + onHoldingsBalanceChange(); + return ret / factor; + } + + protected static void checkAmount(double amount) + { + if(amount < 0.0d) + throw new NegativeAmountException(amount); + } + + protected static long ceiling(double arg) + { + long i = (long)arg; + return arg > (double)i ? Math.addExact(i, 1) : i; + } + + protected static long floor(double arg) + { + long i = (long)arg; + return arg < (double)i ? Math.subtractExact(i, 1) : i; + } + + protected void onHoldingsBalanceChange() + { + + } +} diff --git a/src/main/java/org/ultramine/core/economy/holdings/RealAsyncHoldings.java b/src/main/java/org/ultramine/core/economy/holdings/RealAsyncHoldings.java new file mode 100644 index 0000000..31fbf99 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/holdings/RealAsyncHoldings.java @@ -0,0 +1,22 @@ +package org.ultramine.core.economy.holdings; + +import org.ultramine.server.util.GlobalExecutors; + +import javax.annotation.Nonnull; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class RealAsyncHoldings extends AbstractAsyncHoldings +{ + public RealAsyncHoldings(Holdings holdings) + { + super(holdings); + } + + @Nonnull + @Override + protected CompletableFuture execute(@Nonnull Supplier action) + { + return CompletableFuture.supplyAsync(action, GlobalExecutors.cachedIO()); + } +} diff --git a/src/main/java/org/ultramine/core/economy/service/DefaultCurrencyService.java b/src/main/java/org/ultramine/core/economy/service/DefaultCurrencyService.java new file mode 100644 index 0000000..b526cee --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/service/DefaultCurrencyService.java @@ -0,0 +1,13 @@ +package org.ultramine.core.economy.service; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.service.Service; + +import javax.annotation.Nonnull; + +@Service +public interface DefaultCurrencyService +{ + @Nonnull + Currency getDefaultCurrency(); +} diff --git a/src/main/java/org/ultramine/core/economy/service/DefaultHoldingsProvider.java b/src/main/java/org/ultramine/core/economy/service/DefaultHoldingsProvider.java new file mode 100644 index 0000000..aa16f94 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/service/DefaultHoldingsProvider.java @@ -0,0 +1,13 @@ +package org.ultramine.core.economy.service; + +import org.ultramine.core.economy.holdings.HoldingsFactory; +import org.ultramine.core.service.Service; + +import javax.annotation.Nonnull; + +@Service +public interface DefaultHoldingsProvider +{ + @Nonnull + HoldingsFactory getDefaultHoldingsFactory(); +} diff --git a/src/main/java/org/ultramine/core/economy/service/Economy.java b/src/main/java/org/ultramine/core/economy/service/Economy.java new file mode 100644 index 0000000..dd808d3 --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/service/Economy.java @@ -0,0 +1,23 @@ +package org.ultramine.core.economy.service; + +import com.mojang.authlib.GameProfile; +import net.minecraft.entity.player.EntityPlayerMP; +import org.ultramine.core.economy.account.PlayerAccount; +import org.ultramine.core.service.Service; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; + +@Service +@ThreadSafe +public interface Economy +{ + @Nonnull + PlayerAccount getPlayerAccount(@Nonnull GameProfile profile); + + @Nonnull + default PlayerAccount getPlayerAccount(@Nonnull EntityPlayerMP player) + { + return getPlayerAccount(player.getGameProfile()); + } +} diff --git a/src/main/java/org/ultramine/core/economy/service/EconomyRegistry.java b/src/main/java/org/ultramine/core/economy/service/EconomyRegistry.java new file mode 100644 index 0000000..8745bfb --- /dev/null +++ b/src/main/java/org/ultramine/core/economy/service/EconomyRegistry.java @@ -0,0 +1,60 @@ +package org.ultramine.core.economy.service; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.exception.CurrencyNotFoundException; +import org.ultramine.core.economy.holdings.HoldingsFactory; +import org.ultramine.core.service.Service; +import org.ultramine.core.util.Undoable; +import org.ultramine.core.util.UndoableValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; + +@Service +@ThreadSafe +public interface EconomyRegistry +{ + @Nonnull + UndoableValue registerCurrency( + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits, + int priority + ); + + @Nonnull + UndoableValue registerCurrency( + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits, + int priority, + @Nonnull HoldingsFactory factory + ); + + @Nullable + Currency getCurrencyNullable(@Nonnull String id); + + boolean isCurrencyRegistered(@Nonnull String id); + + @Nonnull + Collection getRegisteredCurrencies(); + + @Nonnull + default Currency getCurrency(@Nonnull String id) throws CurrencyNotFoundException + { + Currency currency = getCurrencyNullable(id); + if(currency == null) + throw new CurrencyNotFoundException(id); + return currency; + } + + Undoable registerStartPlayerBalance(Currency currency, double balance); + + double getStartPlayerBalance(Currency currency); +} diff --git a/src/main/java/org/ultramine/economy/Account.java b/src/main/java/org/ultramine/economy/Account.java deleted file mode 100644 index 3f7958a..0000000 --- a/src/main/java/org/ultramine/economy/Account.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.ultramine.economy; - -import java.util.HashMap; -import java.util.Map; - -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; - -public abstract class Account -{ - protected final Map holdings = new HashMap(); - - public abstract String getName(); - - public abstract void onHoldingsChange(IHoldings holdings); - - public abstract void onHoldingsCreate(IHoldings holdings); - - public IHoldings getHoldingsOf(Currency cur) - { - IHoldings hlds = holdings.get(cur); - if(hlds == null) - { - hlds = cur.createHoldings(this); - holdings.put(cur, hlds); - onHoldingsCreate(hlds); - } - return hlds; - } - - public void writeToNBT(NBTTagCompound nbt) - { - for(Map.Entry ent : holdings.entrySet()) - { - NBTTagCompound nbt1 = new NBTTagCompound(); - ent.getValue().writeToNBT(nbt1); - nbt.setTag(ent.getKey().getCode(), nbt1); - } - } - - public void readFromNBT(NBTTagCompound nbt) - { - for(Object code : nbt.func_150296_c()) - { - Currency cur = CurrencyRegistry.getCurrency((String)code); - if(cur != null) - { - IHoldings hlds = cur.createHoldings(this); - NBTBase b = nbt.getTag((String)code); - if(b instanceof NBTTagCompound) - hlds.readFromNBT((NBTTagCompound)b); - else - hlds.setBalanceSilently(nbt.getDouble((String)code));//TODO remove backward compatibility - holdings.put(cur, hlds); - } - } - } -} diff --git a/src/main/java/org/ultramine/economy/Accounts.java b/src/main/java/org/ultramine/economy/Accounts.java deleted file mode 100644 index caea8fb..0000000 --- a/src/main/java/org/ultramine/economy/Accounts.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.ultramine.economy; - -import org.ultramine.server.data.player.PlayerData; - -import net.minecraft.server.MinecraftServer; - -import com.mojang.authlib.GameProfile; - -public class Accounts -{ - public static Account getPlayer(GameProfile profile) - { - PlayerData data = MinecraftServer.getServer().getConfigurationManager().getDataLoader().getPlayerData(profile); - return data == null ? null : data.core().getAccount(); - } - - public static Account getPlayer(String username) - { - PlayerData data = MinecraftServer.getServer().getConfigurationManager().getDataLoader().getPlayerData(username); - return data == null ? null : data.core().getAccount(); - } -} diff --git a/src/main/java/org/ultramine/economy/BasicHoldings.java b/src/main/java/org/ultramine/economy/BasicHoldings.java deleted file mode 100644 index e6eada9..0000000 --- a/src/main/java/org/ultramine/economy/BasicHoldings.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.ultramine.economy; - -import net.minecraft.command.CommandException; -import net.minecraft.nbt.NBTTagCompound; - -public class BasicHoldings implements IHoldings -{ - private final Account acc; - private final Currency cur; - - private long balance; - - public BasicHoldings(Account acc, Currency cur) - { - this.acc = acc; - this.cur = cur; - } - - public BasicHoldings(Account acc, Currency cur, double balance) - { - this(acc, cur); - this.balance = (int)(balance*100); - } - - public BasicHoldings(Account acc, Currency cur, long balanceInternal) - { - this(acc, cur); - this.balance = balanceInternal; - } - - long getBalanceInternal() - { - return this.balance; - } - - @Override - public Account getAccount() - { - return acc; - } - - @Override - public Currency getCurrency() - { - return cur; - } - - @Override - public double getBalance() - { - return balance / 100.0d; - } - - @Override - public void setBalanceSilently(double balance) - { - this.balance = (int)(balance*100); - } - - @Override - public void setBalance(double balance) - { - this.balance = (int)(balance*100); - acc.onHoldingsChange(this); - } - - @Override - public void add(double amount) - { - if(amount <= 0.0d) - throw new CommandException("economy.fail.negativeamount"); - this.balance = Math.addExact(this.balance, floor(amount*100)); - acc.onHoldingsChange(this); - } - - @Override - public void subtract(double amount) - { - if(amount <= 0.0d) - throw new CommandException("economy.fail.negativeamount"); - this.balance = Math.subtractExact(this.balance, ceiling(amount*100)); - acc.onHoldingsChange(this); - } - - @Override - public void subtractChecked(double amount) - { - if(Math.subtractExact(balance, ceiling(amount*100)) < 0L) - throw new CommandException("economy.fail.notenough"); - subtract(amount); - } - - @Override - public void divide(double amount) - { - this.balance /= amount; - acc.onHoldingsChange(this); - } - - @Override - public void multiply(double amount) - { - this.balance *= amount; - acc.onHoldingsChange(this); - } - - @Override - public boolean isNegative() - { - return this.balance < 0L; - } - - @Override - public boolean hasEnough(double amount) - { - return ceiling(amount*100) <= this.balance; - } - - @Override - public boolean hasOver(double amount) - { - return ceiling(amount*100) < this.balance; - } - - @Override - public void transact(IHoldings to, double amount) - { - this.subtract(amount); - to.add(amount); - } - - @Override - public void transactChecked(IHoldings to, double amount) - { - this.subtractChecked(amount); - to.add(amount); - } - - @Override - public void writeToNBT(NBTTagCompound nbt) - { - nbt.setLong("b", this.balance); - } - - @Override - public void readFromNBT(NBTTagCompound nbt) - { - this.balance = nbt.getLong("b"); - } - - private static long ceiling(double arg) - { - long i = (long)arg; - return arg > (double)i ? Math.addExact(i, 1) : i; - } - - private static long floor(double arg) - { - long i = (long)arg; - return arg < (double)i ? Math.subtractExact(i, 1) : i; - } -} diff --git a/src/main/java/org/ultramine/economy/Currency.java b/src/main/java/org/ultramine/economy/Currency.java deleted file mode 100644 index d868d23..0000000 --- a/src/main/java/org/ultramine/economy/Currency.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.ultramine.economy; - -import java.lang.reflect.Constructor; -import java.text.DecimalFormat; -import java.text.NumberFormat; - -public final class Currency -{ - private static final NumberFormat dformat = new DecimalFormat("#0.##"); - - private final Constructor cls; - private final String code; - private final String sign; - private final String dispName; - - Currency(Class cls, String code, String sign, String dispName) - { - try - { - this.cls = cls.getDeclaredConstructor(Account.class, Currency.class); - } - catch(NoSuchMethodException e) - { - throw new RuntimeException(e); - } - this.code = code; - this.sign = sign; - this.dispName = dispName; - } - - public String getCode() - { - return code; - } - - public String getSign() - { - return sign; - } - - public String getDisplayName() - { - return dispName; - } - - public String format(double amount) - { - return dformat.format(amount) + sign; - } - - IHoldings createHoldings(Account acc) - { - try - { - return cls.newInstance(acc, this); - } - catch(Exception e) - { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/org/ultramine/economy/CurrencyRegistry.java b/src/main/java/org/ultramine/economy/CurrencyRegistry.java deleted file mode 100644 index ff7129a..0000000 --- a/src/main/java/org/ultramine/economy/CurrencyRegistry.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.ultramine.economy; - -import java.util.HashMap; -import java.util.Map; - -public final class CurrencyRegistry -{ - private static final Map currencies = new HashMap(); - - public static final Currency GSC = registerCurrency(BasicHoldings.class, "GSC", "$", "dollar"); - - public static Currency registerCurrency(Class cls, String code, String sign, String dispName) - { - code = code.toUpperCase(); - if(currencies.containsKey(code)) - throw new IllegalStateException("Currency with code "+code+" is already registered"); - Currency cur = new Currency(cls, code, sign, dispName); - currencies.put(code, cur); - return cur; - } - - public static Currency getCurrency(String code) - { - return currencies.get(code.toUpperCase()); - } -} diff --git a/src/main/java/org/ultramine/economy/EconomyCommands.java b/src/main/java/org/ultramine/economy/EconomyCommands.java deleted file mode 100644 index 088ddfa..0000000 --- a/src/main/java/org/ultramine/economy/EconomyCommands.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.ultramine.economy; - -import static net.minecraft.util.EnumChatFormatting.*; - -import org.ultramine.commands.Command; -import org.ultramine.commands.CommandContext; - -public class EconomyCommands -{ - @Command( - name = "pay", - aliases = {"mpay", "madd"}, - group = "economy", - permissions = {"command.economy.pay"}, - syntax = { - " ", - " " - } - ) - public static void pay(CommandContext ctx) - { - Currency cur = ctx.contains("currency") ? CurrencyRegistry.getCurrency(ctx.get("currency").asString()) : CurrencyRegistry.GSC; - ctx.check(cur != null, "economy.fail.currency"); - IHoldings from = ctx.getSenderAsPlayer().getData().core().getAccount().getHoldingsOf(cur); - IHoldings to = ctx.get("player").asPlayerData().core().getAccount().getHoldingsOf(cur); - double amount = ctx.get("amount").asDouble(); - from.transactChecked(to, amount); - ctx.sendMessage(DARK_GREEN, GREEN, "command.pay.sended", ctx.get("player").asString(), cur.format(amount)); - ctx.get("player").asOfflinePlayer().sendMessage(DARK_GREEN, GREEN, "command.pay.received", cur.format(amount), ctx.getSenderAsPlayer().func_145748_c_()); - } - - @Command( - name = "money", - group = "economy", - permissions = {"command.economy.money", "command.economy.money.other"}, - syntax = { - "", - "", - " " - } - ) - public static void money(CommandContext ctx) - { - ctx.checkPermissionIfArg("player", "command.economy.money.other", "command.money.other.fail.perm"); - Currency cur = ctx.contains("currency") ? CurrencyRegistry.getCurrency(ctx.get("currency").asString()) :CurrencyRegistry.GSC; - ctx.check(cur != null, "economy.fail.currency"); - IHoldings holdings = (ctx.contains("player") ? ctx.get("player").asPlayerData() : ctx.getSenderAsPlayer().getData()).core().getAccount().getHoldingsOf(cur); - ctx.sendMessage(DARK_GREEN, GREEN, "command.money.info", holdings.getAccount().getName(), cur.format(holdings.getBalance())); - } - - @Command( - name = "msub", - aliases = {"msubtract"}, - group = "economy", - permissions = {"command.economy.msub"}, - syntax = { - " ", - " " - } - ) - public static void msub(CommandContext ctx) - { - Currency cur = ctx.contains("currency") ? CurrencyRegistry.getCurrency(ctx.get("currency").asString()) : CurrencyRegistry.GSC; - ctx.check(cur != null, "economy.fail.currency"); - IHoldings from = ctx.getSenderAsPlayer().getData().core().getAccount().getHoldingsOf(cur); - IHoldings to = ctx.get("player").asPlayerData().core().getAccount().getHoldingsOf(cur); - double amount = ctx.get("amount").asDouble(); - ctx.check(amount > 0.0d, "economy.fail.negativeamount"); - to.subtract(amount); //result may be negative - from.add(amount); - ctx.sendMessage(DARK_GREEN, GREEN, "command.msub.sended", cur.format(amount), ctx.get("player").asString()); - ctx.get("player").asOfflinePlayer().sendMessage(DARK_GREEN, GREEN, "command.msub.received", cur.format(amount)); - } - - @Command( - name = "mgive", - group = "economy", - permissions = {"command.economy.mgive"}, - syntax = { - " ", - " " - } - ) - public static void mgive(CommandContext ctx) - { - Currency cur = ctx.contains("currency") ? CurrencyRegistry.getCurrency(ctx.get("currency").asString()) : CurrencyRegistry.GSC; - ctx.check(cur != null, "economy.fail.currency"); - IHoldings holdings = ctx.get("player").asPlayerData().core().getAccount().getHoldingsOf(cur); - double amount = ctx.get("amount").asDouble(); - holdings.add(amount); - ctx.sendMessage(DARK_GREEN, GREEN, "command.mgive.sended", ctx.get("player").asString(), cur.format(amount)); - ctx.get("player").asOfflinePlayer().sendMessage(DARK_GREEN, GREEN, "command.mgive.received", cur.format(amount)); - } - - @Command( - name = "mset", - group = "economy", - permissions = {"command.economy.mset"}, - syntax = { - " ", - " " - } - ) - public static void mset(CommandContext ctx) - { - Currency cur = ctx.contains("currency") ? CurrencyRegistry.getCurrency(ctx.get("currency").asString()) : CurrencyRegistry.GSC; - ctx.check(cur != null, "economy.fail.currency"); - IHoldings holdings = ctx.get("player").asPlayerData().core().getAccount().getHoldingsOf(cur); - double amount = ctx.get("amount").asDouble(); //may be negative - double last = holdings.getBalance(); - holdings.setBalance(amount); - ctx.sendMessage(DARK_GREEN, GREEN, "command.mset.sended", ctx.get("player").asString(), cur.format(last), cur.format(amount)); - ctx.get("player").asOfflinePlayer().sendMessage(DARK_GREEN, GREEN, "command.mset.received", cur.format(last), cur.format(amount)); - } -} diff --git a/src/main/java/org/ultramine/economy/HoldingsEvent.java b/src/main/java/org/ultramine/economy/HoldingsEvent.java deleted file mode 100644 index 9f4b9f0..0000000 --- a/src/main/java/org/ultramine/economy/HoldingsEvent.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.ultramine.economy; - -import cpw.mods.fml.common.eventhandler.Event; - -public class HoldingsEvent extends Event -{ - public final IHoldings holdings; - - protected HoldingsEvent(IHoldings holdings) - { - this.holdings = holdings; - } -} diff --git a/src/main/java/org/ultramine/economy/IHoldings.java b/src/main/java/org/ultramine/economy/IHoldings.java deleted file mode 100644 index 03df020..0000000 --- a/src/main/java/org/ultramine/economy/IHoldings.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.ultramine.economy; - -import net.minecraft.nbt.NBTTagCompound; - -public interface IHoldings -{ - Account getAccount(); - - public Currency getCurrency(); - - double getBalance(); - - void setBalanceSilently(double balance); - - void setBalance(double balance); - - void add(double amount); - - void subtract(double amount); - - void subtractChecked(double amount); - - void divide(double amount); - - void multiply(double amount); - - boolean isNegative(); - - boolean hasEnough(double amount); - - boolean hasOver(double amount); - - void transact(IHoldings to, double amount); - - void transactChecked(IHoldings to, double amount); - - void writeToNBT(NBTTagCompound nbt); - - void readFromNBT(NBTTagCompound nbt); -} diff --git a/src/main/java/org/ultramine/economy/PlayerAccount.java b/src/main/java/org/ultramine/economy/PlayerAccount.java deleted file mode 100644 index 2a70e57..0000000 --- a/src/main/java/org/ultramine/economy/PlayerAccount.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.ultramine.economy; - -import net.minecraftforge.common.MinecraftForge; - -import org.ultramine.server.data.player.PlayerData; - -public class PlayerAccount extends Account -{ - private final PlayerData data; - - public PlayerAccount(PlayerData data) - { - this.data = data; - } - - @Override - public String getName() - { - return data.getProfile().getName(); - } - - @Override - public void onHoldingsChange(IHoldings holdings) - { - MinecraftForge.EVENT_BUS.post(new PlayerHoldingsEvent.ChangeEvent(holdings, data)); - data.save(); - } - - @Override - public void onHoldingsCreate(IHoldings holdings) - { - MinecraftForge.EVENT_BUS.post(new PlayerHoldingsEvent.CreateEvent(holdings, data)); - } -} diff --git a/src/main/java/org/ultramine/economy/PlayerHoldingsEvent.java b/src/main/java/org/ultramine/economy/PlayerHoldingsEvent.java deleted file mode 100644 index a71cf7f..0000000 --- a/src/main/java/org/ultramine/economy/PlayerHoldingsEvent.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.ultramine.economy; - -import org.ultramine.server.data.player.PlayerData; - -public class PlayerHoldingsEvent extends HoldingsEvent -{ - public final PlayerData player; - - protected PlayerHoldingsEvent(IHoldings holdings, PlayerData player) - { - super(holdings); - this.player = player; - } - - public static class CreateEvent extends PlayerHoldingsEvent - { - public CreateEvent(IHoldings holdings, PlayerData player) - { - super(holdings, player); - } - } - - public static class ChangeEvent extends PlayerHoldingsEvent - { - public ChangeEvent(IHoldings holdings, PlayerData player) - { - super(holdings, player); - } - } -} diff --git a/src/main/java/org/ultramine/server/UltramineServerModContainer.java b/src/main/java/org/ultramine/server/UltramineServerModContainer.java index 6155ba0..9aebabe 100644 --- a/src/main/java/org/ultramine/server/UltramineServerModContainer.java +++ b/src/main/java/org/ultramine/server/UltramineServerModContainer.java @@ -16,14 +16,19 @@ import org.ultramine.commands.basic.TechCommands; import org.ultramine.commands.basic.VanillaCommands; import org.ultramine.commands.syntax.DefaultCompleters; +import org.ultramine.core.economy.service.DefaultHoldingsProvider; +import org.ultramine.core.economy.service.Economy; +import org.ultramine.core.economy.service.EconomyRegistry; import org.ultramine.core.service.InjectService; import org.ultramine.core.service.ServiceManager; -import org.ultramine.economy.EconomyCommands; import org.ultramine.server.chunk.ChunkGenerationQueue; import org.ultramine.server.chunk.ChunkProfiler; import org.ultramine.server.data.Databases; import org.ultramine.server.data.ServerDataLoader; import org.ultramine.server.data.player.PlayerCoreData; +import org.ultramine.server.economy.UMIntegratedHoldingsProvider; +import org.ultramine.server.economy.UMEconomy; +import org.ultramine.server.economy.UMEconomyRegistry; import org.ultramine.server.event.ForgeModIdMappingEvent; import org.ultramine.server.internal.SyncServerExecutorImpl; import org.ultramine.server.internal.UMEventHandler; @@ -59,17 +64,16 @@ public class UltramineServerModContainer extends DummyModContainer { private static UltramineServerModContainer instance; - @InjectService - private static ServiceManager services; - @InjectService - private static Permissions perms; - + @InjectService private static ServiceManager services; + @InjectService private static Permissions perms; + @InjectService private static EconomyRegistry economyRegistry; + private LoadController controller; @SideOnly(Side.SERVER) private ButtonCommand buttonCommand; private ItemBlocker itemBlocker; private final RecipeCache recipeCache = new RecipeCache(); - + public UltramineServerModContainer() { super(new ModMetadata()); @@ -79,7 +83,7 @@ meta.name = "Ultramine Server"; meta.version = "@version@"; } - + public static UltramineServerModContainer getInstance() { return instance; @@ -92,7 +96,7 @@ bus.register(this); return true; } - + @Subscribe public void modConstruction(FMLConstructionEvent evt) { @@ -110,11 +114,16 @@ Databases.init(); MinecraftServer.getServer().getMultiWorld().preloadConfigs(); ConfigurationHandler.postWorldDescsLoad(); + OpBasedPermissions vanPerms = new OpBasedPermissions(); vanPerms.addDefault("command.vanilla.help"); vanPerms.addDefault("command.vanilla.msg"); vanPerms.addDefault("command.vanilla.reply"); services.register(Permissions.class, vanPerms, 0); + + services.register(EconomyRegistry.class, new UMEconomyRegistry(), 0); + services.register(Economy.class, new UMEconomy(), 0); + services.register(DefaultHoldingsProvider.class, new UMIntegratedHoldingsProvider(), 0); } } catch (Throwable t) @@ -122,7 +131,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void init(FMLInitializationEvent e) { @@ -152,7 +161,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void serverAboutToStart(FMLServerAboutToStartEvent e) { @@ -171,7 +180,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void serverStarting(FMLServerStartingEvent e) { @@ -183,9 +192,8 @@ e.registerCommands(BasicCommands.class); e.registerCommands(TechCommands.class); e.registerCommands(GenWorldCommand.class); - e.registerCommands(EconomyCommands.class); e.registerCommands(OpenInvCommands.class); - + if(e.getSide().isServer()) { buttonCommand.load(e); @@ -199,7 +207,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void serverStarted(FMLServerStartedEvent e) { @@ -222,7 +230,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void serverStopped(FMLServerStoppedEvent e) { @@ -232,7 +240,7 @@ ChunkGenerationQueue.instance().unregister(); ChunkProfiler.instance().setEnabled(false); ((SyncServerExecutorImpl) GlobalExecutors.nextTick()).unregister(); - + if(e.getSide().isServer()) { buttonCommand.unload(); @@ -244,7 +252,7 @@ controller.errorOccurred(this, t); } } - + @Subscribe public void remap(FMLModIdMappingEvent e) { @@ -258,7 +266,7 @@ controller.errorOccurred(this, t); } } - + @NetworkCheckHandler public boolean networkCheck(Map map, Side side) { @@ -286,12 +294,12 @@ { return this; } - + public RecipeCache getRecipeCache() { return recipeCache; } - + public void reloadToolsCfg() { getRecipeCache().setEnabled(ConfigurationHandler.getServerConfig().settings.other.recipeCacheEnabled); diff --git a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java index 6c228ed..3f20093 100644 --- a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java +++ b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java @@ -228,7 +228,9 @@ private PlayerData readPlayerData(NBTTagCompound nbt) { PlayerData pdata = new PlayerData(mgr.getDataLoader()); - + if(nbt != null && nbt.hasKey("id") && nbt.hasKey("name")) + pdata.setProfile(new GameProfile(UUID.fromString(nbt.getString("id")), nbt.getString("name"))); + List infos = mgr.getDataLoader().getDataExtProviders(); List data = new ArrayList(infos.size()); @@ -238,8 +240,6 @@ } pdata.loadExtensions(data); - if(nbt != null && nbt.hasKey("id") && nbt.hasKey("name")) - pdata.setProfile(new GameProfile(UUID.fromString(nbt.getString("id")), nbt.getString("name"))); return pdata; } diff --git a/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java b/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java index 8b09db5..0a4acdb 100644 --- a/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java +++ b/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java @@ -3,20 +3,29 @@ import java.util.HashMap; import java.util.Map; -import org.ultramine.economy.Account; -import org.ultramine.economy.PlayerAccount; +import net.minecraft.nbt.NBTBase; +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.service.Economy; +import org.ultramine.core.economy.service.EconomyRegistry; +import org.ultramine.core.economy.holdings.Holdings; +import org.ultramine.core.economy.account.PlayerAccount; +import org.ultramine.core.service.InjectService; import org.ultramine.server.Teleporter; +import org.ultramine.server.economy.CurrencyImpl; +import org.ultramine.server.economy.UMIntegratedPlayerHoldings; import org.ultramine.server.util.WarpLocation; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import javax.annotation.Nullable; + public class PlayerCoreData extends PlayerDataExtension { private long firstLoginTime = System.currentTimeMillis(); private long lastLoginTime = firstLoginTime; private final Map homes = new HashMap(); - private final PlayerAccount account; + private final PlayerAccountHolder account; private long unmuteTime; private boolean commandsMuted; private boolean hidden; @@ -30,7 +39,7 @@ public PlayerCoreData(PlayerData data) { super(data); - this.account = new PlayerAccount(data); + this.account = new PlayerAccountHolder(data); } public long getFirstLoginTime() @@ -62,10 +71,16 @@ { return homes; } - - public Account getAccount() + + @Nullable + public UMIntegratedPlayerHoldings getHoldingsInternal(Currency currency) { - return account; + return account.getHoldings(currency); + } + + public void setHoldingsInternal(UMIntegratedPlayerHoldings holdings) + { + account.setHoldings(holdings); } public Teleporter getTeleporter() @@ -205,4 +220,93 @@ commandsMuted = nbt.getBoolean("mc"); hidden = nbt.getBoolean("h"); } + + private static class PlayerAccountHolder + { + @InjectService + private static EconomyRegistry economyRegistry; + @InjectService + private static Economy economy; + + private final Map holdingsMap = new HashMap<>(); + private final PlayerData data; + private PlayerAccountHolder(PlayerData data) + { + this.data = data; + } + + @Nullable + public UMIntegratedPlayerHoldings getHoldings(Currency currency) + { + Object val = holdingsMap.get(currency.getId()); + if(val instanceof UMIntegratedPlayerHoldings) + { + UMIntegratedPlayerHoldings holdings = (UMIntegratedPlayerHoldings) val; + if(holdings.getCurrency() != currency) + { + UMIntegratedPlayerHoldings newHoldings = new UMIntegratedPlayerHoldings(holdings.getAccount(), holdings.getCurrency(), data); + newHoldings.setBalanceInternal(holdings.getBalanceInternal()); + holdingsMap.put(currency.getId(), newHoldings); + return newHoldings; + } + return holdings; + } + else if(val != null) + { + PlayerAccount realAccount = economy.getPlayerAccount(data.getProfile()); + UMIntegratedPlayerHoldings holdings = new UMIntegratedPlayerHoldings(realAccount, currency, data); + holdings.readFromNBT((NBTTagCompound)val); + holdingsMap.put(currency.getId(), holdings); + return holdings; + } + + return null; + } + + public void setHoldings(UMIntegratedPlayerHoldings holdings) + { + holdingsMap.put(holdings.getCurrency().getId(), holdings); + } + + public void writeToNBT(NBTTagCompound nbt) + { + for(Map.Entry ent : holdingsMap.entrySet()) + { + Object val = ent.getValue(); + if(val instanceof UMIntegratedPlayerHoldings) + { + NBTTagCompound nbt1 = new NBTTagCompound(); + ((UMIntegratedPlayerHoldings) val).writeToNBT(nbt1); + nbt.setTag(ent.getKey(), nbt1); + } + else + { + nbt.setTag(ent.getKey(), (NBTTagCompound) val); + } + } + } + + public void readFromNBT(NBTTagCompound nbt) + { + PlayerAccount realAccount = economy.getPlayerAccount(data.getProfile()); + + for(Object code : nbt.func_150296_c()) + { + NBTBase b = nbt.getTag((String)code); + if(!(b instanceof NBTTagCompound)) + continue; + Currency currency = economyRegistry.getCurrencyNullable((String)code); + if(currency != null && currency instanceof CurrencyImpl) + { + UMIntegratedPlayerHoldings holdings = new UMIntegratedPlayerHoldings(realAccount, currency, data); + holdings.readFromNBT((NBTTagCompound)b); + holdingsMap.put(currency.getId(), holdings); + } + else + { + holdingsMap.put((String)code, nbt.getTag((String)code)); + } + } + } + } } diff --git a/src/main/java/org/ultramine/server/economy/CurrencyImpl.java b/src/main/java/org/ultramine/server/economy/CurrencyImpl.java new file mode 100644 index 0000000..4a2e6bd --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/CurrencyImpl.java @@ -0,0 +1,87 @@ +package org.ultramine.server.economy; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.holdings.HoldingsFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.text.DecimalFormat; + +public class CurrencyImpl implements Currency +{ + @Nullable private final HoldingsFactory factory; + @Nonnull private final String id; + @Nonnull private final String name; + @Nonnull private final String pluralName; + @Nonnull private final String sign; + @Nonnull private final int fractionalDigits; + @Nonnull private final DecimalFormat format; + + public CurrencyImpl( + @Nullable HoldingsFactory factory, + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits + ) { + this.factory = factory; + this.id = id; + this.name = name; + this.pluralName = pluralName; + this.sign = sign; + this.fractionalDigits = fractionalDigits; + + StringBuilder sb = new StringBuilder(fractionalDigits); + for(int i = 0; i < fractionalDigits; i++) + sb.append("#"); + format = new DecimalFormat("#0." + sb.toString()); + } + + @Nonnull + @Override + public String getId() + { + return id; + } + + @Nonnull + @Override + public String getDisplayName() + { + return name; + } + + @Nonnull + @Override + public String getPluralDisplayName() + { + return pluralName; + } + + @Nonnull + @Override + public String getSymbol() + { + return sign; + } + + @Override + public int getFractionalDigits() + { + return fractionalDigits; + } + + @Nonnull + @Override + public String format(double amount) + { + return format.format(amount) + getSymbol(); + } + + @Nullable + public HoldingsFactory getFactory() + { + return factory; + } +} diff --git a/src/main/java/org/ultramine/server/economy/DefaultCurrencyServiceProvider.java b/src/main/java/org/ultramine/server/economy/DefaultCurrencyServiceProvider.java new file mode 100644 index 0000000..a0fb347 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/DefaultCurrencyServiceProvider.java @@ -0,0 +1,23 @@ +package org.ultramine.server.economy; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.service.DefaultCurrencyService; + +import javax.annotation.Nonnull; + +public class DefaultCurrencyServiceProvider implements DefaultCurrencyService +{ + private final Currency currency; + + public DefaultCurrencyServiceProvider(Currency currency) + { + this.currency = currency; + } + + @Nonnull + @Override + public Currency getDefaultCurrency() + { + return currency; + } +} diff --git a/src/main/java/org/ultramine/server/economy/PlayerAccountImpl.java b/src/main/java/org/ultramine/server/economy/PlayerAccountImpl.java new file mode 100644 index 0000000..9861fb3 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/PlayerAccountImpl.java @@ -0,0 +1,88 @@ +package org.ultramine.server.economy; + +import com.mojang.authlib.GameProfile; +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.service.DefaultCurrencyService; +import org.ultramine.core.economy.service.DefaultHoldingsProvider; +import org.ultramine.core.economy.service.EconomyRegistry; +import org.ultramine.core.economy.holdings.Holdings; +import org.ultramine.core.economy.holdings.HoldingsFactory; +import org.ultramine.core.economy.account.PlayerAccount; +import org.ultramine.core.economy.exception.CurrencyNotFoundException; +import org.ultramine.core.economy.exception.CurrencyNotSupportedException; +import org.ultramine.core.service.InjectService; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@ThreadSafe +public class PlayerAccountImpl implements PlayerAccount +{ + @InjectService private static EconomyRegistry economyRegistry; + @InjectService private static DefaultHoldingsProvider defaultHoldings; + @InjectService private static DefaultCurrencyService defaultCurrency; + + private final GameProfile profile; + private final Map holdingsMap = new ConcurrentHashMap<>(); + + public PlayerAccountImpl(GameProfile profile) + { + this.profile = profile; + } + + @Nonnull + @Override + public String getName() + { + return profile.getName(); + } + + @Nonnull + @Override + public Collection getSupportedCurrencies() + { + return economyRegistry.getRegisteredCurrencies(); + } + + @Override + public boolean isCurrencySupported(@Nonnull Currency currency) + { + return economyRegistry.isCurrencyRegistered(currency.getId()); + } + + @Nonnull + @Override + public Currency getSupportedCurrency(@Nonnull String id) throws CurrencyNotFoundException, CurrencyNotSupportedException + { + return economyRegistry.getCurrency(id); + } + + @Nonnull + @Override + public Holdings getHoldings(@Nonnull Currency currency) + { + return holdingsMap.computeIfAbsent(currency, id -> { + HoldingsFactory factory = ((CurrencyImpl)currency).getFactory(); + if(factory == null) + factory = defaultHoldings.getDefaultHoldingsFactory(); + return factory.createHoldings(this, currency); + }); + } + + @Nonnull + @Override + public Holdings getDefaultHoldings() + { + return getHoldings(defaultCurrency.getDefaultCurrency()); + } + + @Nonnull + @Override + public GameProfile getProfile() + { + return profile; + } +} diff --git a/src/main/java/org/ultramine/server/economy/UMEconomy.java b/src/main/java/org/ultramine/server/economy/UMEconomy.java new file mode 100644 index 0000000..1a7ff25 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/UMEconomy.java @@ -0,0 +1,25 @@ +package org.ultramine.server.economy; + +import com.mojang.authlib.GameProfile; +import org.ultramine.core.economy.service.Economy; +import org.ultramine.core.economy.account.PlayerAccount; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@ThreadSafe +public class UMEconomy implements Economy +{ + private final Map accounts = new ConcurrentHashMap<>(); + + @Nonnull + @Override + public PlayerAccount getPlayerAccount(@Nonnull GameProfile profile) + { + profile.getClass(); // NPE + return accounts.computeIfAbsent(profile.getId(), k -> new PlayerAccountImpl(profile)); + } +} diff --git a/src/main/java/org/ultramine/server/economy/UMEconomyRegistry.java b/src/main/java/org/ultramine/server/economy/UMEconomyRegistry.java new file mode 100644 index 0000000..1fa47e4 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/UMEconomyRegistry.java @@ -0,0 +1,117 @@ +package org.ultramine.server.economy; + +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.service.DefaultCurrencyService; +import org.ultramine.core.economy.service.EconomyRegistry; +import org.ultramine.core.economy.holdings.HoldingsFactory; +import org.ultramine.core.service.InjectService; +import org.ultramine.core.service.ServiceManager; +import org.ultramine.core.util.Undoable; +import org.ultramine.core.util.UndoableValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@ThreadSafe +public class UMEconomyRegistry implements EconomyRegistry +{ + private static final Double DEFAULT_PLAYER_BALANCE = 0.0d; + @InjectService + private static ServiceManager services; + + private final Map currencies = new ConcurrentHashMap<>(); + private final Map startPlayerBalances = new ConcurrentHashMap<>(); + + @Nonnull + @Override + public UndoableValue registerCurrency( + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits, + int priority + ) { + return registerCurrencyInternal(id, name, pluralName, sign, fractionalDigits, priority, null); + } + + @Override + public UndoableValue registerCurrency( + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits, + int priority, + @Nonnull HoldingsFactory factory + ) { + factory.getClass(); // NPE + return registerCurrencyInternal(id, name, pluralName, sign, fractionalDigits, priority, factory); + } + + private UndoableValue registerCurrencyInternal( + @Nonnull String id, + @Nonnull String name, + @Nonnull String pluralName, + @Nonnull String sign, + int fractionalDigits, + int priority, + @Nullable HoldingsFactory factory + ) { + id.getClass(); // NPE + name.getClass(); // NPE + pluralName.getClass(); // NPE + sign.getClass(); // NPE + + CurrencyImpl currency = new CurrencyImpl(factory, id, name, pluralName, sign, fractionalDigits); + currencies.compute(id, (s, currency1) -> { + if(currency1 != null) + throw new IllegalStateException("Currency with id: " + s + " is already registered"); + return currency; + }); + Undoable undoService = services.register(DefaultCurrencyService.class, new DefaultCurrencyServiceProvider(currency), priority); + return UndoableValue.of(currency, Undoable.ofAll(undoService, () -> currencies.computeIfPresent(id, (s, currency1) -> currency1 == currency ? null : currency1))); + } + + @Override + public Currency getCurrencyNullable(@Nonnull String id) + { + id.getClass(); // NPE + return currencies.get(id); + } + + @Override + public boolean isCurrencyRegistered(@Nonnull String id) + { + return currencies.containsKey(id); + } + + @Nonnull + @Override + public Collection getRegisteredCurrencies() + { + return currencies.values(); + } + + @Override + public Undoable registerStartPlayerBalance(Currency currency, double balance) + { + startPlayerBalances.compute(currency, (s, currency1) -> { + if(currency1 != null) + throw new IllegalStateException("Start player balance for currency with id: " + s + " is already registered"); + return balance; + }); + + return () -> startPlayerBalances.remove(currency); + } + + @Override + public double getStartPlayerBalance(Currency currency) + { + return startPlayerBalances.getOrDefault(currency, DEFAULT_PLAYER_BALANCE); + } +} diff --git a/src/main/java/org/ultramine/server/economy/UMIntegratedHoldingsProvider.java b/src/main/java/org/ultramine/server/economy/UMIntegratedHoldingsProvider.java new file mode 100644 index 0000000..9067a30 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/UMIntegratedHoldingsProvider.java @@ -0,0 +1,15 @@ +package org.ultramine.server.economy; + +import org.ultramine.core.economy.service.DefaultHoldingsProvider; +import org.ultramine.core.economy.holdings.HoldingsFactory; + +public class UMIntegratedHoldingsProvider implements DefaultHoldingsProvider +{ + private final HoldingsFactory factory = new UMIntegratedPlayerHoldingsFactory(); + + @Override + public HoldingsFactory getDefaultHoldingsFactory() + { + return factory; + } +} diff --git a/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldings.java b/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldings.java new file mode 100644 index 0000000..b5a0a95 --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldings.java @@ -0,0 +1,49 @@ +package org.ultramine.server.economy; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.holdings.MemoryHoldings; +import org.ultramine.server.data.player.PlayerData; +import org.ultramine.server.util.GlobalExecutors; + +import javax.annotation.Nonnull; + +public class UMIntegratedPlayerHoldings extends MemoryHoldings +{ + // Current implementation is a legacy of old ultramine versions + + private final PlayerData playerData; + + public UMIntegratedPlayerHoldings(@Nonnull Account account, @Nonnull Currency currency, @Nonnull PlayerData playerData) + { + super(account, currency); + this.playerData = playerData; + } + + public void writeToNBT(@Nonnull NBTTagCompound nbt) + { + nbt.setLong("b", getBalanceInternal()); + } + + public void readFromNBT(@Nonnull NBTTagCompound nbt) + { + setBalanceInternal(nbt.getLong("b")); + } + + @Override + protected void onHoldingsBalanceChange() + { + if(Thread.currentThread() == MinecraftServer.getServer().getServerThread()) + save(); + else + GlobalExecutors.syncServer().execute(this::save); + } + + private void save() + { + playerData.core().setHoldingsInternal(this); + playerData.save(); + } +} diff --git a/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldingsFactory.java b/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldingsFactory.java new file mode 100644 index 0000000..27eb3cb --- /dev/null +++ b/src/main/java/org/ultramine/server/economy/UMIntegratedPlayerHoldingsFactory.java @@ -0,0 +1,42 @@ +package org.ultramine.server.economy; + +import net.minecraft.server.MinecraftServer; +import org.ultramine.core.economy.account.Account; +import org.ultramine.core.economy.exception.AccountTypeNotSupportedException; +import org.ultramine.core.economy.Currency; +import org.ultramine.core.economy.holdings.Holdings; +import org.ultramine.core.economy.holdings.HoldingsFactory; +import org.ultramine.core.economy.account.PlayerAccount; +import org.ultramine.core.economy.service.EconomyRegistry; +import org.ultramine.core.service.InjectService; +import org.ultramine.server.data.player.PlayerData; + +import javax.annotation.Nonnull; + +public class UMIntegratedPlayerHoldingsFactory implements HoldingsFactory +{ + @InjectService + private static EconomyRegistry economyRegistry; + + @Nonnull + @Override + public Holdings createHoldings(@Nonnull Account account, @Nonnull Currency currency) throws AccountTypeNotSupportedException + { + if(!(account instanceof PlayerAccount)) + throw new AccountTypeNotSupportedException(account, currency); + PlayerData data = MinecraftServer.getServer().getConfigurationManager().getDataLoader().getPlayerData(((PlayerAccount)account).getProfile()); // TODO service + UMIntegratedPlayerHoldings holdings = data.core().getHoldingsInternal(currency); + if(holdings != null) + return holdings; + holdings = new UMIntegratedPlayerHoldings(account, currency, data); + holdings.setBalanceSilently(economyRegistry.getStartPlayerBalance(currency)); + data.core().setHoldingsInternal(holdings); + onHoldingsCreate(holdings); + return holdings; + } + + protected void onHoldingsCreate(UMIntegratedPlayerHoldings holdings) + { + + } +} diff --git a/src/main/java/org/ultramine/server/internal/UMEventHandler.java b/src/main/java/org/ultramine/server/internal/UMEventHandler.java index 3dc4ed9..89c0663 100644 --- a/src/main/java/org/ultramine/server/internal/UMEventHandler.java +++ b/src/main/java/org/ultramine/server/internal/UMEventHandler.java @@ -3,8 +3,6 @@ import net.minecraft.util.DamageSource; import org.ultramine.commands.basic.GenWorldCommand; import org.ultramine.core.service.InjectService; -import org.ultramine.economy.CurrencyRegistry; -import org.ultramine.economy.PlayerHoldingsEvent; import org.ultramine.server.ConfigurationHandler; import org.ultramine.server.Teleporter; import org.ultramine.server.UltramineServerConfig.ToolsConf.AutoBroacastConf; @@ -315,14 +313,6 @@ } @SideOnly(Side.SERVER) - @SubscribeEvent(priority = EventPriority.HIGH) - public void onHoldingsCreate(PlayerHoldingsEvent.CreateEvent e) - { - if(e.holdings.getCurrency() == CurrencyRegistry.GSC) - e.holdings.setBalance(ConfigurationHandler.getServerConfig().tools.economy.startBalance); - } - - @SideOnly(Side.SERVER) @SubscribeEvent(priority=EventPriority.LOWEST) public void onPlayerLoggedIn(PlayerLoggedInEvent e) { diff --git a/src/main/resources/assets/ultramine/lang/en_US.lang b/src/main/resources/assets/ultramine/lang/en_US.lang index c95910d..2ebe1fd 100644 --- a/src/main/resources/assets/ultramine/lang/en_US.lang +++ b/src/main/resources/assets/ultramine/lang/en_US.lang @@ -48,6 +48,13 @@ command.generic.itemstack.data=Failed to parse item data: %s command.generic.itemstack.size=Failed to parse stack size: %s +#Economy generic +ultramine.economy.fail.currency_not_exists=Specified currency is not exists: %s +ultramine.economy.fail.currency_not_supported=Specified currency (%2$s) is not supported by account: %1$s +ultramine.economy.fail.account_type_not_supported=The type of the specified account is not supported for currency: %1$s +ultramine.economy.fail.negative_amount=Amount must be greater than zero (specified %s) +ultramine.economy.fail.insufficient_funds=Your account is not enough money to carry out this operation + #Vanilla replacements command.tp.usage=/tp [target player] OR /tp [target player] [world] command.tp.description=Teleports target player (or you) to another player or coordinate @@ -78,35 +85,6 @@ command.say.server=Server -#Economy commands -economy.fail.currency=Specified currency is not exists -economy.fail.negativeamount=Amount must be greater than zero -economy.fail.notenough=Your account is not enough money to carry out this operation - -command.pay.usage=/pay [currency] -command.pay.description=Transfers specified amount to the account of another player -command.pay.sended=You are transferred to the account of the player %s amount of %s -command.pay.received=Received %s from the player %s - -command.money.usage=/money [player] [currency] -command.money.description=Displays information about your account or account of another player -command.money.info=On account of the player %s is %s - -command.mgive.usage=/mgive [currency] -command.mgive.description=Gives specified amount to the player's account -command.mgive.sended=Вы выдали игроку %s сумму в %s -command.mgive.received=Вам выдано %s - -command.mset.usage=/mset [currency] -command.mset.description=Sets the specified account balance for player -command.mset.sended=You has been changed balance of player %s account from %s to %s -command.mset.received=Your account balance is changed from %s to %s - -command.msub.usage=/msub [currency] -command.msub.description=Confiscates player's account specified amount -command.msub.sendedYou confiscated %s from the players account %s -command.msub.received=With your account confiscated %s - #Basic commands command.home.usage=/home [name] command.home.description=Teleports you to your home, sets with /sethome diff --git a/src/main/resources/assets/ultramine/lang/ru_RU.lang b/src/main/resources/assets/ultramine/lang/ru_RU.lang index 0bde975..d6b1eb3 100644 --- a/src/main/resources/assets/ultramine/lang/ru_RU.lang +++ b/src/main/resources/assets/ultramine/lang/ru_RU.lang @@ -48,6 +48,13 @@ command.generic.itemstack.data=Не удалось разобрать предмет: %s command.generic.itemstack.size=Не удалось разобрать количество предметов: %s +#Economy generic +ultramine.economy.fail.currency_not_exists=Указанная валюта не существует: %s +ultramine.economy.fail.currency_not_supported=Указанная валюта (%2$s) не поддерживается аккаунтом: %1$s +ultramine.economy.fail.account_type_not_supported=Типа указанного счета не поддерживается валютой: %1$s +ultramine.economy.fail.negative_amount=Сумма должна быть больше нуля (указано %s) +ultramine.economy.fail.insufficient_funds=На вашем счету недостаточно средств для совершения данной операции + #Vanilla replacements command.tp.usage=/tp [кого] <к кому> ИЛИ /tp [кого] [мир] command.tp.description=Телепортация указанного игрока (или вас) к другому игроку или по координатам @@ -78,35 +85,6 @@ command.say.server=Сервер -#Economy commands -economy.fail.currency=Указанная валюта не существует -economy.fail.negativeamount=Сумма должна быть больше нуля -economy.fail.notenough=На вашем счету недостаточно средств для совершения данной операции - -command.pay.usage=/pay <игрок> [валюта] <сумма> -command.pay.description=Перечисляет указанную сумму на счет другого игрока -command.pay.sended=Вы перечислили на счет игрока %s сумму в %s -command.pay.received=Получено %s от игрока %s - -command.money.usage=/money [игрок] [валюта] -command.money.description=Показывает информацию о вашем счете или счете другого игрока -command.money.info=На счету игрока %s находится %s - -command.mgive.usage=/mgive <игрок> [валюта] <сумма> -command.mgive.description=Выдает игроку указанную сумму -command.mgive.sended=Вы выдали игроку %s сумму в %s -command.mgive.received=Вам выдано %s - -command.mset.usage=/mset <игрок> [валюта] <сумма> -command.mset.description=Устанавливает указанный баланс на счету игрока -command.mset.sended=Вы изменили баланс счета игрока %s с %s до %s -command.mset.received=Баланс вашего счета изменен с %s до %s - -command.msub.usage=/msub <игрок> [валюта] <сумма> -command.msub.description=Конфискует со счета игрока указанную сумму -command.msub.sended=Вы конфисковали %s со счета игрока %s -command.msub.received=С вашего счета конфисковано %s - #Basic commands command.home.usage=/home [название] command.home.description=Телепортация на точку дома, устанавливаемую командой /sethome