diff --git a/src/main/java/com/skyblockexp/ezshops/bootstrap/EzShopsBootstrap.java b/src/main/java/com/skyblockexp/ezshops/bootstrap/EzShopsBootstrap.java index 672b92c..ecad547 100644 --- a/src/main/java/com/skyblockexp/ezshops/bootstrap/EzShopsBootstrap.java +++ b/src/main/java/com/skyblockexp/ezshops/bootstrap/EzShopsBootstrap.java @@ -208,12 +208,17 @@ public Set getBundledShopModes() { private boolean setupEconomy() { RegisteredServiceProvider registration = plugin.getServer().getServicesManager().getRegistration(Economy.class); - if (registration != null) { - economy = registration.getProvider(); - return economy != null; + if (registration == null) { + plugin.getLogger().severe("No Vault economy provider is registered. Install an economy " + + "plugin that provides a Vault economy service (e.g. EssentialsX, CMI)."); + return false; } - - return plugin.getServer().getPluginManager().getPlugin("Vault") != null; + economy = registration.getProvider(); + if (economy == null) { + plugin.getLogger().severe("Vault registered a null economy provider."); + return false; + } + return true; } private void saveDefaultResources() { diff --git a/src/main/java/com/skyblockexp/ezshops/bootstrap/PlayerShopComponent.java b/src/main/java/com/skyblockexp/ezshops/bootstrap/PlayerShopComponent.java index 02a6e15..df6db09 100644 --- a/src/main/java/com/skyblockexp/ezshops/bootstrap/PlayerShopComponent.java +++ b/src/main/java/com/skyblockexp/ezshops/bootstrap/PlayerShopComponent.java @@ -118,7 +118,7 @@ private PlayerShopRepository createRepository(EzShopsPlugin plugin) { plugin.getLogger().info("Player shops: using Jaloquent storage (JDBC adapter)."); return repo; } catch (Exception ex) { - plugin.getLogger().severe("Failed to initialise Jaloquent for player shops; falling back to YAML. " + ex.getMessage()); + plugin.getLogger().warning("Failed to initialise Jaloquent for player shops; falling back to YAML. " + ex.getMessage()); } } if ("mysql".equalsIgnoreCase(type)) { @@ -136,7 +136,7 @@ private PlayerShopRepository createRepository(EzShopsPlugin plugin) { plugin.getLogger().info("Player shops: using MySQL storage."); return repo; } catch (IllegalStateException ex) { - plugin.getLogger().severe("Failed to connect to MySQL for player shops; falling back to YAML. " + ex.getMessage()); + plugin.getLogger().warning("Failed to connect to MySQL for player shops; falling back to YAML. " + ex.getMessage()); } } return new YmlPlayerShopRepository(plugin.getDataFolder(), plugin.getLogger()); diff --git a/src/test/java/com/skyblockexp/ezshops/AbstractEzShopsTest.java b/src/test/java/com/skyblockexp/ezshops/AbstractEzShopsTest.java index 4200496..dcad0a8 100644 --- a/src/test/java/com/skyblockexp/ezshops/AbstractEzShopsTest.java +++ b/src/test/java/com/skyblockexp/ezshops/AbstractEzShopsTest.java @@ -101,6 +101,27 @@ protected void loadProviderPlugin(Economy econ) { } } + /** + * Load a minimal plugin with the given name that registers no services. + * Used to simulate an environment where a hard dependency (e.g. Vault) is + * present but no economy provider is registered. + */ + protected void loadBarePlugin(String name) { + try { + PluginDescriptionFile description = new PluginDescriptionFile( + name, "1.0", BarePlugin.class.getName()); + File dataFolder = Files.createTempDirectory("test-plugin-" + name).toFile(); + BarePlugin plugin = (BarePlugin) getUnsafe().allocateInstance(BarePlugin.class); + plugin.init(server, description, dataFolder, new File(""), + BarePlugin.class.getClassLoader(), description, + Logger.getLogger(name)); + server.getPluginManager().registerLoadedPlugin(plugin); + server.getPluginManager().enablePlugin(plugin); + } catch (IOException | InstantiationException e) { + throw new RuntimeException("Failed to load bare plugin " + name, e); + } + } + private static Unsafe getUnsafe() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); @@ -111,6 +132,13 @@ private static Unsafe getUnsafe() { } } + public static class BarePlugin extends JavaPlugin { + @Override + public void onEnable() { + // Intentionally registers no services. + } + } + public static class TestProviderPlugin extends JavaPlugin { static Economy econToRegister; diff --git a/src/test/java/com/skyblockexp/ezshops/EzShopsSetupEconomyFeatureTest.java b/src/test/java/com/skyblockexp/ezshops/EzShopsSetupEconomyFeatureTest.java index 07bdfed..f8603ef 100644 --- a/src/test/java/com/skyblockexp/ezshops/EzShopsSetupEconomyFeatureTest.java +++ b/src/test/java/com/skyblockexp/ezshops/EzShopsSetupEconomyFeatureTest.java @@ -17,4 +17,16 @@ void servicesManager_registration_before_plugin_enable_allows_economy_detection( assertNotNull(plugin); assertTrue(plugin.isEnabled(), "EzShops should enable when an Economy provider is registered in ServicesManager"); } + + @Test + void missingEconomyProvider_disablesPluginCleanly() { + // Vault present but no economy provider registered. This used to NPE in + // PlayerShopComponent. because setupEconomy() returned true while + // the economy field was still null. + loadBarePlugin("Vault"); + + EzShopsPlugin plugin = assertDoesNotThrow(() -> loadPlugin(EzShopsPlugin.class)); + assertFalse(plugin.isEnabled(), + "EzShops should disable cleanly when Vault has no economy provider"); + } }