Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ release.properties
dependency-reduced-pom.xml
buildNumber.properties

# Test artifacts
database/
database_backup/

# Intellij
*.iml
*.java___jb_tmp___
Expand Down
32 changes: 19 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@
<byte-buddy.version>1.17.5</byte-buddy.version>
<!-- More visible way how to change dependency versions -->
<paper.version>1.21.11-R0.1-SNAPSHOT</paper.version>
<bentobox.version>2.7.1-SNAPSHOT</bentobox.version>
<bentobox.version>3.17.0-SNAPSHOT</bentobox.version>
<vault.version>1.7</vault.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.17.1</build.version>
<build.version>1.18.0</build.version>
<!-- Sonar Cloud -->
<sonar.projectKey>BentoBoxWorld_addon-invSwitcher</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
Expand Down Expand Up @@ -114,14 +115,6 @@
</profiles>

<repositories>
<!-- jitpack first so MockBukkit snapshots resolve without hitting other repos -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
Expand All @@ -134,6 +127,11 @@
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<!-- Vault's own maven repo is unreliable, so we pull it from jitpack -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<dependencies>
Expand All @@ -146,9 +144,9 @@
</dependency>
<!-- MockBukkit -->
<dependency>
<groupId>com.github.MockBukkit</groupId>
<artifactId>MockBukkit</artifactId>
<version>v1.21-SNAPSHOT</version>
<groupId>org.mockbukkit.mockbukkit</groupId>
<artifactId>mockbukkit-v1.21</artifactId>
<version>4.110.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
Expand Down Expand Up @@ -196,6 +194,14 @@
<version>${bentobox.version}</version>
<scope>provided</scope>
</dependency>
<!-- Vault: pulled from jitpack as their maven repo is unreliable -->
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>${vault.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
116 changes: 116 additions & 0 deletions src/main/java/com/wasteofplastic/invswitcher/InvSwitcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@

import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;

import com.wasteofplastic.invswitcher.commands.admin.AdminMoneyCommand;
import com.wasteofplastic.invswitcher.commands.user.BalanceCommand;
import com.wasteofplastic.invswitcher.commands.user.PayCommand;
import com.wasteofplastic.invswitcher.economy.InvEconomy;
import com.wasteofplastic.invswitcher.listeners.PlayerListener;

import net.milkbowl.vault.economy.Economy;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
import world.bentobox.bentobox.hooks.VaultHook;

/**
* Inventory switcher for worlds. Switches advancements too.
Expand All @@ -31,6 +39,8 @@ public class InvSwitcher extends Addon {

private Set<World> worlds = new HashSet<>();

private InvEconomy economy;

@Override
public void onLoad() {
// Save default config.yml
Expand Down Expand Up @@ -74,6 +84,12 @@ public void allLoaded() {
store = new Store(this);
// Register the listeners
registerListener(new PlayerListener(this));
// Now that worlds are known, register the economy commands and placeholders. The economy
// provider itself was registered earlier, in onEnable, so it beats shop plugins that cache
// their Vault provider during their own startup.
if (economy != null) {
registerEconomyCommands();
}
}

@Override
Expand All @@ -82,19 +98,119 @@ public void onEnable() {
if (this.getPlugin().getSettings().getDatabaseType().equals(DatabaseType.YAML)) {
this.setState(State.DISABLED);
this.logError("This addon is incompatible with YAML database. Please use another type, like JSON.");
return;
}
// Register the Vault economy provider as early as possible (here in onEnable, not in
// allLoaded) so it is in place before economy-consuming plugins (e.g. QuickShop) resolve
// and cache their provider. The store, worlds and delegate are resolved lazily by
// InvEconomy, so they do not need to exist yet.
if (getSettings() != null && getSettings().isMoney()) {
if (Bukkit.getPluginManager().getPlugin("Vault") == null) {
logError("options.money is enabled but the Vault plugin is not installed - per-world money disabled.");
} else {
registerEconomyProvider();
}
}
}

/**
* Creates and registers InvSwitcher's per-world economy at the highest Vault priority so it
* intercepts every economy call. Runs once. The provider is lazy - it resolves the store and
* the delegate economy on first use - so this can run before those are ready.
*/
private void registerEconomyProvider() {
if (economy != null) {
return;
}
economy = new InvEconomy(this);
Bukkit.getServicesManager().register(Economy.class, economy, getPlugin(), ServicePriority.Highest);

// BentoBox captured its VaultHook during early hook registration, before us, so it (and
// addons that use it, e.g. Bank) still points at the previous economy. Re-run the hook so
// it re-reads the now-highest provider (us). The VaultHook is a single shared instance held
// by those addons, so refreshing it updates them too.
refreshBentoBoxVaultHook();

// Dump the current economy provider chain (debug only) so it is clear we win the registration.
if (getSettings().isEconomyDebug()) {
logEconomyRegistrations();
}
}

/**
* Registers the economy commands and placeholders against the game modes whose worlds
* InvSwitcher manages. Called from allLoaded, once worlds are known.
*/
private void registerEconomyCommands() {
PhManager phManager = new PhManager(this);
getPlugin().getAddonsManager().getGameModeAddons().stream()
.filter(gm -> worlds.contains(gm.getOverWorld()))
.forEach(gm -> {
gm.getPlayerCommand().ifPresent(pc -> {
new BalanceCommand(this, pc);
new PayCommand(this, pc);
});
gm.getAdminCommand().ifPresent(ac -> new AdminMoneyCommand(this, ac));
if (!phManager.registerPlaceholders(gm)) {
logWarning("Could not register economy placeholders - no PlaceholderManager available.");
}
log("Per-world economy hooking into " + gm.getDescription().getName());
});
}


@Override
public void onDisable() {
// Unregister our economy so a reload does not stack providers
if (economy != null) {
Bukkit.getServicesManager().unregister(Economy.class, economy);
economy = null;
// Re-point BentoBox's VaultHook at whatever economy remains (e.g. EssentialsC)
refreshBentoBoxVaultHook();
}
// save cache
if (store != null) {
getStore().saveOnShutdown();
}

}

/**
* Re-runs BentoBox's VaultHook so it re-reads the highest-priority economy currently
* registered with the services manager. BentoBox addons such as Bank hold this same hook
* instance, so they pick up the change without needing to re-hook themselves.
*/
private void refreshBentoBoxVaultHook() {
getPlugin().getVault().ifPresent(VaultHook::hook);
}

/**
* Logs all registered Vault economy providers (highest priority first) and which one Vault
* will hand out. Useful for confirming InvSwitcher won the registration and for spotting
* consumers that cached a different provider before we registered.
*/
private void logEconomyRegistrations() {
log("Vault economy providers now registered (used by new lookups):");
for (RegisteredServiceProvider<Economy> r : Bukkit.getServicesManager().getRegistrations(Economy.class)) {
log(" - " + r.getProvider().getName() + " (" + r.getProvider().getClass().getName()
+ ") priority=" + r.getPriority() + " registeredBy=" + r.getPlugin().getName());
}
RegisteredServiceProvider<Economy> top = Bukkit.getServicesManager().getRegistration(Economy.class);
if (top != null) {
log("Vault.getRegistration(Economy) returns: " + top.getProvider().getName() + " ("
+ top.getProvider().getClass().getName() + ")");
}
log("If a shop/economy plugin still uses the old balance, it cached its provider before now "
+ "and must be loaded after BentoBox (or re-resolve on ServiceRegisterEvent).");
}

/**
* @return the per-world economy, or null if money is disabled or Vault is absent
*/
public InvEconomy getEconomy() {
return economy;
}


/**
* @return the store
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/wasteofplastic/invswitcher/PhManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.wasteofplastic.invswitcher;

import com.wasteofplastic.invswitcher.economy.InvEconomy;

import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.user.User;

/**
* Registers PlaceholderAPI placeholders for a player's per-world balance.
* @author tastybento
*/
public class PhManager {

private final BentoBox plugin;
private final InvSwitcher addon;

public PhManager(InvSwitcher addon) {
this.addon = addon;
this.plugin = addon.getPlugin();
}

/**
* Register the balance placeholders for a game mode.
* @param gm - game mode addon
* @return true if registered, false if no placeholder manager is available
*/
public boolean registerPlaceholders(GameModeAddon gm) {
if (plugin.getPlaceholdersManager() == null) {
return false;
}
String prefix = gm.getDescription().getName().toLowerCase() + "_invswitcher_";
// Balance is scoped to this game mode's world, so the placeholder is stable regardless of
// where the player currently is (e.g. bskyblock_invswitcher_balance is the BSkyBlock balance).
String worldName = gm.getOverWorld().getName();
// Raw balance number
plugin.getPlaceholdersManager().registerPlaceholder(addon, prefix + "balance",
user -> balance(user, worldName, false));
// Formatted balance (currency name + decimals)
plugin.getPlaceholdersManager().registerPlaceholder(addon, prefix + "balance_formatted",
user -> balance(user, worldName, true));
return true;
}

private String balance(User user, String worldName, boolean formatted) {
InvEconomy eco = addon.getEconomy();
if (eco == null || user == null || !user.isPlayer()) {
return "";
}
double bal = eco.getBalance(user.getPlayer(), worldName);
return formatted ? eco.format(bal) : String.valueOf(bal);
}
}
Loading
Loading