Skip to content

Instantly share code, notes, and snippets.

@Sxtanna
Last active February 10, 2021 21:47
Show Gist options
  • Save Sxtanna/af4f01d21cd5c35ce047a9e64a0dab48 to your computer and use it in GitHub Desktop.
Save Sxtanna/af4f01d21cd5c35ce047a9e64a0dab48 to your computer and use it in GitHub Desktop.
Vault Economy Wrapper
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.math.BigDecimal;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Drag and Drop Vault Economy Wrapper
*
* @see Response
*/
public final class Economy
{
/**
* Represents a transaction response from Vault
*
* @see Response#getResult()
* @see Response#getReason()
*/
public static final class Response
{
/**
* Represents whether the result of the transaction was passing or failing
*/
public enum Result
{
PASS,
FAIL,
}
@NotNull
private final Result result;
@NotNull
private final String reason;
@Contract(pure = true)
private Response(@NotNull final Result result, @NotNull final String reason)
{
this.result = result;
this.reason = reason;
}
/**
* @return Whether the transaction passed or failed
*/
@Contract(pure = true)
public @NotNull Result getResult()
{
return result;
}
/**
* @return The reason the transaction failed, if it failed...
*/
@Contract(pure = true)
public @NotNull String getReason()
{
return reason;
}
/**
* @return true if this transaction was successful, false otherwise
*/
@Contract(pure = true)
public boolean isPassing()
{
return getResult() == Result.PASS;
}
/**
* @return true if this transaction failed, false otherwise
*/
@Contract(pure = true)
public boolean isFailing()
{
return getResult() == Result.FAIL;
}
/**
* Convenience function for handling the state of this response
*
* @param passing What to do if this transaction was successful
* @param failing What to do if this transaction failed
*/
public void handle(@NotNull final Runnable passing, @NotNull final Consumer<String> failing)
{
// switch over the possible results
switch (getResult())
{
case PASS:
// run the passing runnable when this is a passing response
passing.run();
break;
case FAIL:
// provide the reason to the failing consumer when this is a failing response
failing.accept(getReason());
break;
default:
// throw an exception when no applicable case is found (should not happen)
throw new UnsupportedOperationException(String.format("unexpected response result: %s?", getResult()));
}
}
/**
* Creates a new response denoting a successful transaction
*
* @return The new response holding {@link Result#PASS}
*/
@Contract(value = " -> new", pure = true)
public static @NotNull Response passing()
{
return new Response(Result.PASS, ""); /* todo: this could probably be a single instance, like Optional.empty()*/
}
/**
* Creates a new response denoting a failing transaction
*
* @param reason The reason the transaction failed
* @return The new response holding {@link Result#FAIL} and a Reason
*/
@Contract(value = "_ -> new", pure = true)
public static @NotNull Response failing(@NotNull final String reason)
{
return new Response(Result.FAIL, reason);
}
}
/**
* Attempts to retrieve the current balance of the supplied {@link OfflinePlayer}
*
* @param player The player whose balance to retrieve.
* @return An {@link Optional} containing the player's balance if available, otherwise empty.
*/
public static @NotNull Optional<BigDecimal> get(@NotNull final OfflinePlayer player)
{
try
{
// attempt to retrieve the registered provider for vault's economy, will either return null or throw NCDFE
final RegisteredServiceProvider<net.milkbowl.vault.economy.Economy> registration = Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (registration == null)
{
// this will be null when there is no economy plugin, simply return an empty optional
return Optional.empty();
}
// wrap the result of their current balance in a big decimal, because using floating point values for money is DUMB
return Optional.of(BigDecimal.valueOf(registration.getProvider().getBalance(player)));
}
catch (final NoClassDefFoundError ignored)
{
// this error will be thrown if vault is not loaded on this server, simply return an empty optional
return Optional.empty();
}
}
/**
* Attempts to take the supplied amount of money from the player's balance. (withdraw)
*
* @param player The player whose balance to take from
* @param amount The amount of money to take from them
* @return A response denoting the outcome of this withdrawal attempt
*/
public static @NotNull Response take(@NotNull final OfflinePlayer player, @NotNull final BigDecimal amount)
{
try
{
// attempt to retrieve the registered provider for vault's economy, will either return null or throw NCDFE
final RegisteredServiceProvider<net.milkbowl.vault.economy.Economy> registration = Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (registration == null)
{
// this will be null when there is no economy plugin, simply return an "no economy" response
return Response.failing("no economy");
}
// attempt to withdraw the double value of the provided BigDecimal amount
final net.milkbowl.vault.economy.EconomyResponse response = registration.getProvider().withdrawPlayer(player, amount.doubleValue());
// return either a passing response, or a failing response containing the error message
return response.transactionSuccess() ? Response.passing() : Response.failing(response.errorMessage);
}
catch (final NoClassDefFoundError error)
{
// return a failing response with the NCDFE message (probably not ideal? maybe just say "no vault")
return Response.failing(String.format("withdraw failed: %s", error.getMessage()));
}
}
/**
* Attempts to give the supplied amount of money to the player. (deposit)
*
* @param player The player whose balance to give to
* @param amount The amount of money to give them
* @return A response denoting the outcome of this deposit attempt
*/
public static @NotNull Response give(@NotNull final OfflinePlayer player, @NotNull final BigDecimal amount)
{
try
{
// attempt to retrieve the registered provider for vault's economy, will either return null or throw NCDFE
final RegisteredServiceProvider<net.milkbowl.vault.economy.Economy> registration = Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (registration == null)
{
// this will be null when there is no economy plugin, simply return an "no economy" response
return Response.failing("no economy");
}
// attempt to deposit the double value of the provided BigDecimal amount
final net.milkbowl.vault.economy.EconomyResponse response = registration.getProvider().depositPlayer(player, amount.doubleValue());
// return either a passing response, or a failing response containing the error message
return response.transactionSuccess() ? Response.passing() : Response.failing(response.errorMessage);
}
catch (final NoClassDefFoundError error)
{
// return a failing response with the NCDFE message (probably not ideal? maybe just say "no vault")
return Response.failing(String.format("deposit failed: %s", error.getMessage()));
}
}
}
@Sxtanna
Copy link
Author

Sxtanna commented Feb 7, 2021

Plugin Description

// ....
softdepend:
  - "Vault"

Repository and Dependency

Gradle

repositories {
    // ....
    maven {
        url = uri("https://jitpack.io")
    }
}

dependencies {
    // ....
    compileOnly("com.github.MilkBowl:VaultAPI:1.7") {
	transitive false // ignore transitive dependencies (we don't need them)
    }
}

Maven

<repositories>
    // ....
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependencies>
    // ....
    <dependency>
        <groupId>com.github.MilkBowl</groupId>
        <artifactId>VaultAPI</artifactId>
        <version>1.7</version>
        <scope>provided</scope>
        <exclusions>
            <exclusion>
                <groupId>*</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

@Sxtanna
Copy link
Author

Sxtanna commented Feb 7, 2021

Example of this resource's usage

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public final class EconomyExample
{

    public static final class Product
    {

        @NotNull
        private final String     name;
        @NotNull
        private final BigDecimal cost;
        @NotNull
        private final ItemStack  item;


        @Contract(pure = true)
        public Product(@NotNull final String name, @NotNull final BigDecimal cost, @NotNull final ItemStack item)
        {
            this.name = name;
            this.cost = cost;
            this.item = item;
        }


        @Contract(pure = true)
        public @NotNull String getName()
        {
            return name;
        }

        @Contract(pure = true)
        public @NotNull BigDecimal getCost()
        {
            return cost;
        }

        @Contract(pure = true)
        public @NotNull ItemStack getItem()
        {
            return item;
        }


        public @NotNull BigDecimal getCostAtAmount(final int amount)
        {
            final int        count = getItem().getAmount();
            final BigDecimal price = getCost().divide(BigDecimal.valueOf(count), BigDecimal.ROUND_HALF_EVEN);

            return price.multiply(BigDecimal.valueOf(amount));
        }

        public @NotNull ItemStack getItemAtAmount(final int amount)
        {
            final ItemStack stack = getItem().clone();
            stack.setAmount(amount);

            return stack;
        }


        @Override
        public String toString()
        {
            return String.format("Product['%s'{%s}: %s]", name, cost, item);
        }


        @Override
        public boolean equals(final Object o)
        {
            if (this == o)
            {
                return true;
            }
            if (!(o instanceof Product))
            {
                return false;
            }

            final Product that = (Product) o;
            return this.getName().equals(that.getName()) &&
                   this.getCost().equals(that.getCost()) &&
                   this.getItem().equals(that.getItem());
        }

        @Override
        public int hashCode()
        {
            return Objects.hash(this.getName(),
                                this.getCost(),
                                this.getItem());
        }

    }


    public static void main(String[] args)
    {
        final List<Product> products = Arrays.asList(
                new Product("Stone x64", BigDecimal.valueOf(100), new ItemStack(Material.STONE, 64)),
                new Product("Grass x32", BigDecimal.valueOf(300), new ItemStack(Material.GRASS_BLOCK, 32))
        );


        final Player player = Bukkit.getOnlinePlayers().iterator().next();

        Economy.give(player, BigDecimal.valueOf(350)).handle(() -> {}, reason -> {
            // imagine not being able to have $350
        });


        for (final Product product : products)
        {
            final Economy.Response response = Economy.take(player, product.getCost());

            switch (response.getResult())
            {
                case FAIL:
                    player.sendMessage(String.format("Failed to purchase %s for $%s: %s", product.getName(), product.getCost().toPlainString(), response.getReason()));
                    break;
                case PASS:
                    player.sendMessage(String.format("Successfully purchased %s for $%s", product.getName(), product.getCost().toPlainString()));

                    final Map<Integer, ItemStack> failed = player.getInventory().addItem(product.getItem());
                    if (failed.isEmpty())
                    {
                        return;
                    }

                    final ItemStack  remain = failed.get(0);
                    final BigDecimal refund = product.getCostAtAmount(remain.getAmount());

                    if (refund.compareTo(BigDecimal.ZERO) <= 0)
                    {
                        return;
                    }

                    Economy.give(player, refund);

                    player.sendMessage(String.format("You have been refunded $%s because your inventory could not hold x%d of the purchased items!", refund.toPlainString(), remain.getAmount()));

                    break;

            }
        }

    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment