Skip to content

Commit

Permalink
Refactor Vault economy integration.
Browse files Browse the repository at this point in the history
This commit reworks how MobArena interfaces with Vault economy provider
instances. Instead of affixing an Economy _instance_ to the main plugin
at "setup time", we create an instance of the new _Finance_ abstraction
at "(re)load time", and then use this new abstraction at every call site
(really just the MoneyThing) instead of Economy. This accomplishes two
important things:

1. Due to how the Bukkit load order works and how Vault providers are
registered with the plugin, the Finance abstraction gives us a means of
deferring the retrieval of the Economy provider needed to integrate with
the underlying economy plugin (or script). MobArena still uses Finance
as if it was a "complete" object and is completely unaware of what goes
on under the hodd. This is a much more elegant solution compared to the
terrible past proposals of fragmenting MobArena's bootstrapping phase,
especially considering the past proposals were server tick-centric...

2. It creates a simple and maintainable interface between MobArena and
Vault, effectively giving control back to MobArena as to how everything
should mesh together.

The biggest downside to this solution is that we no longer know if an
economy provider is available for when we need it at a convenient time
in the startup process. Instead, we need to react accordingly when an
economy operation is invoked. The implementation takes a somewhat mild
approach by simply _logging_ any issues encountered and defaulting to
"best effort" operations when an economy provider is not available. At
worst, this is a bunch of log spam and potentially a lot of work for
server owners to clean up after (compensating players for rewards and
stuff like that), but MobArena doesn't handle arbitrary exceptions in
its join/leave and start/end procedures very well, so it's probably a
better way to go about it than throwing exceptions.

If the server does not have Vault installed, an UnsupportedFinance is
created. This implementation just logs errors and "fails to work" on
every operation. If Vault is installed, a VaultFinance is created, and
this implementation should only log errors if a Vault economy provider
couldn't be found. We could have opted for a single implementation, but
this approach allows us to potentially support other economy registrars
in the future, so we (probably) won't have to make sweeping changes in
order to swap to something else.

Closes #797
  • Loading branch information
garbagemule committed Aug 18, 2024
1 parent 0be0cc6 commit da6202f
Show file tree
Hide file tree
Showing 12 changed files with 473 additions and 75 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ These changes will (most likely) be included in the next version.


## [Unreleased]
### Added
- MobArena now properly supports Vault economy providers registered after MobArena has started. This should make it possible to use custom economy providers that aren't built into Vault, such as those created with Denizen.

### Changed
- Recurrent waves can now be randomized. If two or more recurrent waves clash on wave number, frequency, _and_ priority, MobArena will now randomly pick between them. This should make it easier to create more varied and interesting wave setups without having to resort to only massively randomized default waves.
- Single waves can now be randomized. If two or more single waves clash on wave number, MobArena will now randomly pick between them. This means it is now possible to make randomly selected bosses for boss waves, for instance.
Expand Down
37 changes: 10 additions & 27 deletions src/main/java/com/garbagemule/MobArena/MobArena.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.garbagemule.MobArena.commands.CommandHandler;
import com.garbagemule.MobArena.config.LoadsConfigFile;
import com.garbagemule.MobArena.finance.Finance;
import com.garbagemule.MobArena.finance.FinanceFactory;
import com.garbagemule.MobArena.events.MobArenaPreReloadEvent;
import com.garbagemule.MobArena.events.MobArenaReloadEvent;
import com.garbagemule.MobArena.formula.FormulaMacros;
Expand All @@ -26,16 +28,12 @@
import com.garbagemule.MobArena.things.ThingPickerManager;
import com.garbagemule.MobArena.util.config.ConfigUtils;
import com.garbagemule.MobArena.waves.ability.AbilityManager;
import net.milkbowl.vault.economy.Economy;
import org.bstats.bukkit.Metrics;
import org.bukkit.ChatColor;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.File;
Expand All @@ -51,8 +49,7 @@ public class MobArena extends JavaPlugin
{
private ArenaMaster arenaMaster;

// Vault
private Economy economy;
private Finance finance;

private FileConfiguration config;
private LoadsConfigFile loadsConfigFile;
Expand Down Expand Up @@ -112,7 +109,6 @@ private void setup() {
setupArenaMaster();
setupCommandHandler();

setupVault();
setupBossAbilities();
setupListeners();
setupMetrics();
Expand Down Expand Up @@ -149,24 +145,6 @@ private void setupCommandHandler() {
getCommand("ma").setExecutor(new CommandHandler(this));
}

private void setupVault() {
Plugin vaultPlugin = this.getServer().getPluginManager().getPlugin("Vault");
if (vaultPlugin == null) {
getLogger().info("Vault was not found. Economy rewards will not work.");
return;
}

ServicesManager manager = this.getServer().getServicesManager();
RegisteredServiceProvider<Economy> e = manager.getRegistration(net.milkbowl.vault.economy.Economy.class);

if (e != null) {
economy = e.getProvider();
getLogger().info("Vault found; economy rewards enabled.");
} else {
getLogger().warning("Vault found, but no economy plugin detected. Economy rewards will not work!");
}
}

private void setupBossAbilities() {
AbilityManager.loadCoreAbilities();
AbilityManager.loadCustomAbilities(getDataFolder());
Expand All @@ -193,6 +171,7 @@ public void reload() {
getServer().getPluginManager().callEvent(pre);

try {
reloadFinance();
reloadConfig();
reloadGlobalMessenger();
reloadFormulaMacros();
Expand All @@ -209,6 +188,10 @@ public void reload() {
getServer().getPluginManager().callEvent(post);
}

private void reloadFinance() {
finance = FinanceFactory.create(getServer(), getLogger());
}

@Override
public void reloadConfig() {
if (loadsConfigFile == null) {
Expand Down Expand Up @@ -305,8 +288,8 @@ public ArenaMaster getArenaMaster() {
return arenaMaster;
}

public Economy getEconomy() {
return economy;
public Finance getFinance() {
return finance;
}

public Messenger getGlobalMessenger() {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/garbagemule/MobArena/finance/Finance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.garbagemule.MobArena.finance;

import org.bukkit.entity.Player;

public interface Finance {

double getBalance(Player player);

boolean deposit(Player player, double amount);

boolean withdraw(Player player, double amount);

String format(double amount);

}
25 changes: 25 additions & 0 deletions src/main/java/com/garbagemule/MobArena/finance/FinanceFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.garbagemule.MobArena.finance;

import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.ServicesManager;

import java.util.logging.Logger;

public class FinanceFactory {

FinanceFactory() {
// OK BOSS
}

public static Finance create(Server server, Logger log) {
Plugin plugin = server.getPluginManager().getPlugin("Vault");
if (plugin == null) {
return new UnsupportedFinance(log);
}

ServicesManager services = server.getServicesManager();
return new VaultFinance(services, log);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.garbagemule.MobArena.finance;

import org.bukkit.entity.Player;

import java.util.logging.Logger;

public class UnsupportedFinance implements Finance {

private final Logger log;

public UnsupportedFinance(Logger log) {
this.log = log;
}

@Override
public double getBalance(Player player) {
log.severe("Economy operations are only supported via Vault!");
return -1.0;
}

@Override
public boolean deposit(Player player, double amount) {
log.severe("Economy operations are only supported via Vault!");
return false;
}

@Override
public boolean withdraw(Player player, double amount) {
log.severe("Economy operations are only supported via Vault!");
return false;
}

@Override
public String format(double amount) {
log.severe("Economy operations are only supported via Vault!");
return "ERROR";
}

}
78 changes: 78 additions & 0 deletions src/main/java/com/garbagemule/MobArena/finance/VaultFinance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.garbagemule.MobArena.finance;

import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;

import java.util.logging.Logger;

public class VaultFinance implements Finance {

private final ServicesManager services;
private final Logger log;

private Economy economy;

public VaultFinance(ServicesManager services, Logger log) {
this.services = services;
this.log = log;
}

@Override
public double getBalance(Player player) {
try {
return getEconomy().getBalance(player);
} catch (IllegalStateException e) {
log.severe("Failed to check balance of player " + player.getName() + " because: " + e.getMessage());
return 0;
}
}

@Override
public boolean deposit(Player player, double amount) {
try {
EconomyResponse res = getEconomy().depositPlayer(player, amount);
return res.type == EconomyResponse.ResponseType.SUCCESS;
} catch (IllegalStateException e) {
log.severe("Failed to give " + amount + " economy money to player " + player.getName() + " because: " + e.getMessage());
return false;
}
}

@Override
public boolean withdraw(Player player, double amount) {
try {
EconomyResponse res = getEconomy().withdrawPlayer(player, amount);
return res.type == EconomyResponse.ResponseType.SUCCESS;
} catch (IllegalStateException e) {
log.severe("Failed to take " + amount + " economy money from player " + player.getName() + " because: " + e.getMessage());
return false;
}
}

@Override
public String format(double amount) {
try {
return getEconomy().format(amount);
} catch (IllegalStateException e) {
log.severe("Failed to format " + amount + " as economy money because: " + e.getMessage());
return "ERROR";
}
}

private Economy getEconomy() {
if (economy != null) {
return economy;
}

RegisteredServiceProvider<Economy> provider = services.getRegistration(Economy.class);
if (provider == null) {
throw new IllegalStateException("No Vault economy provider found!");
}

return (economy = provider.getProvider());
}

}
32 changes: 8 additions & 24 deletions src/main/java/com/garbagemule/MobArena/things/MoneyThing.java
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
package com.garbagemule.MobArena.things;

import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import net.milkbowl.vault.economy.EconomyResponse.ResponseType;
import com.garbagemule.MobArena.finance.Finance;
import org.bukkit.entity.Player;

public class MoneyThing implements Thing {

private final Economy economy;
private final Finance finance;
private final double amount;

public MoneyThing(Economy economy, double amount) {
this.economy = economy;
public MoneyThing(Finance finance, double amount) {
this.finance = finance;
this.amount = amount;
}

@Override
public boolean giveTo(Player player) {
if (economy == null) {
return false;
}
EconomyResponse result = economy.depositPlayer(player, amount);
return result.type == ResponseType.SUCCESS;
return finance.deposit(player, amount);
}

@Override
public boolean takeFrom(Player player) {
if (economy == null) {
return false;
}
EconomyResponse result = economy.withdrawPlayer(player, amount);
return result.type == ResponseType.SUCCESS;
return finance.withdraw(player, amount);
}

@Override
public boolean heldBy(Player player) {
if (economy == null) {
return false;
}
return economy.getBalance(player) >= amount;
return finance.getBalance(player) >= amount;
}

@Override
public String toString() {
if (economy == null) {
return "$" + amount;
}
return economy.format(amount);
return finance.format(amount);
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.garbagemule.MobArena.things;

import com.garbagemule.MobArena.MobArena;
import net.milkbowl.vault.economy.Economy;
import com.garbagemule.MobArena.finance.Finance;

class MoneyThingParser implements ThingParser {

Expand All @@ -20,11 +20,11 @@ public MoneyThing parse(String s) {
if (money == null) {
return null;
}
Economy economy = plugin.getEconomy();
if (economy == null) {
plugin.getLogger().severe("Vault or economy plugin missing while parsing: " + s);
}
return new MoneyThing(economy, Double.parseDouble(money));

Finance finance = plugin.getFinance();
double amount = Double.parseDouble(money);

return new MoneyThing(finance, amount);
}

private String trimPrefix(String s) {
Expand Down
Loading

0 comments on commit da6202f

Please sign in to comment.