diff --git a/CHANGELOG.md b/CHANGELOG.md index 77faed165..77b95662e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Date format: (YYYY-MM-DD) * Changed: Some entity attributes are setup prior to entity spawning now (such as metadata, non-persist flag and name (if it has/uses one)). This should help other plugins to identify Shopkeeper entities during spawning. * Changed: Added setting 'increment-villager-statistics' (default: false) which controls whether opening the trading menu and trading with shopkeepers increment minecraft's 'talked-to-villager' and 'traded-with-villager' statistics. Previously the talked-to-villager statistics would always get incremented and the traded-with-villager statistic was not used. * Added: The previous, current and next page items inside the editor view will now use their stack size to visualize the previous, current and next page number. This even works for items which are usually not stackable. +* Added: Added a system property 'shopkeepers.skip-wg-allow-shop-flag' which can be used to disable the registration of the 'allow-shop' WorldGuard flag. This should usually not be required though. API: * API: Added interfaces for the different shopkeeper types and their offers to the API. They allow modifying the shopkeepers' trades. Factory methods for the different types of offers are provided via ShopkeepersPlugin and ShopkeepersAPI. The internal shopkeeper classes got renamed. @@ -44,6 +45,7 @@ API: * API/Fixed: ShopkeepersAPI was missing getDefaultUITypes. Internal: +* Internal: Moved some initialization and config loading into the onLoad phase. * Internal: Avoiding ItemStack#hasItemMeta calls before getting an item's ItemMeta, since this might be heavier than simply getting the ItemMeta directly and performing only the relevant checks on that. Internally ItemStack#hasItemMeta checks emptiness for all item attributes and might (for CraftItemStacks) even first copy all the item's data into a new ItemMeta object. And even if the item actually has no data (Bukkit ItemStack with null ItemMeta), ItemStack#getItemMeta will simply create a new empty ItemMeta object without having to copy any data, so this is still a similarly lightweight operation anyways. * Internal: Made all priorities and ignoring of cancelled events explicit. * Internal: Moved code for checking chest access into util package. diff --git a/src/main/java/com/nisovin/shopkeepers/SKShopkeepersPlugin.java b/src/main/java/com/nisovin/shopkeepers/SKShopkeepersPlugin.java index ba90c2043..2d85bc565 100644 --- a/src/main/java/com/nisovin/shopkeepers/SKShopkeepersPlugin.java +++ b/src/main/java/com/nisovin/shopkeepers/SKShopkeepersPlugin.java @@ -113,18 +113,10 @@ public static SKShopkeepersPlugin getInstance() { private final SignShops signShops = new SignShops(this); private final CitizensShops citizensShops = new CitizensShops(this); - @Override - public void onLoad() { - // WorldGuard only allows registering flags before it got enabled. - // The config gets loaded later, so we always attempt to register the flag. - WorldGuardHandler.registerAllowShopFlag(); - } - - @Override - public void onEnable() { - plugin = this; - ShopkeepersAPI.enable(this); + private boolean outdatedServer = false; + private boolean incompatibleServer = false; + private void loadRequiredClasses() { // making sure that certain classes, that are needed during shutdown, are loaded: // this helps for hot reloads (when the plugin gets disabled, but the original jar got replaced and is therefore // no longer available) @@ -136,48 +128,52 @@ public void onEnable() { } catch (ClassNotFoundException e) { e.printStackTrace(); } + } - // validate that this is running a minimum required version of Spigot: + // returns true if server is outdated + private boolean isOutdatedServerVersion() { + // validate that this server is running a minimum required version: // TODO add proper version parsing - /*String cbVersion = Utils.getServerCBVersion(); // 1_13_R2 - String bukkitVersion = Bukkit.getBukkitVersion(); // 1.13.1-R0.1-SNAPSHOT*/ + /*String cbVersion = Utils.getServerCBVersion(); // eg. 1_13_R2 + String bukkitVersion = Bukkit.getBukkitVersion(); // eg. 1.13.1-R0.1-SNAPSHOT*/ try { - // this has been added with the recent changes to PlayerBedEnterEvent: + // this has been added with the recent changes to PlayerBedEnterEvent: TODO outdated Class.forName("org.bukkit.event.player.PlayerBedEnterEvent$BedEnterResult"); - } catch (ClassNotFoundException e1) { - Log.severe("Outdated server version (" + Bukkit.getVersion() - + "): Shopkeepers cannot be enabled. Please update your server!"); - this.setEnabled(false); - return; + return false; + } catch (ClassNotFoundException e) { + return true; } + } - // try to load suitable NMS code: + // returns false if no compatible NMS version, nor the fallback handler could be setup + private boolean setupNMS() { NMSManager.load(this); - if (NMSManager.getProvider() == null) { - Log.severe("Incompatible server version: Shopkeepers cannot be enabled."); - this.setEnabled(false); - return; - } + return (NMSManager.getProvider() != null); + } + + private void loadConfig() { + Log.info("Loading config."); + // save default config in case the config file doesn't exist + this.saveDefaultConfig(); // load config: - File configFile = new File(this.getDataFolder(), "config.yml"); - if (!configFile.exists()) { - this.saveDefaultConfig(); - } this.reloadConfig(); + + // load settings from config: Configuration config = this.getConfig(); boolean configChanged = Settings.loadConfiguration(config); if (configChanged) { - // if missing settings were added -> save the modified config + // if the config was modified (migrations, adding missing settings, ..), save it: // TODO persist comments somehow this.saveConfig(); } // load language config: String lang = Settings.language; - File langFile = new File(this.getDataFolder(), "language-" + lang + ".yml"); - if (!langFile.exists() && this.getResource("language-" + lang + ".yml") != null) { - this.saveResource("language-" + lang + ".yml", false); + String langFileName = "language-" + lang + ".yml"; + File langFile = new File(this.getDataFolder(), langFileName); + if (!langFile.exists() && this.getResource(langFileName) != null) { + this.saveResource(langFileName, false); } if (langFile.exists()) { try { @@ -188,6 +184,74 @@ public void onEnable() { e.printStackTrace(); } } + } + + @Override + public void onLoad() { + // setting plugin reference early, so it is also available for any code running here: + plugin = this; + ShopkeepersAPI.enable(this); + + // making sure that certain classes, that are needed during shutdown, are loaded: + // this helps for hot reloads (when the plugin gets disabled, but the original jar got replaced and is therefore + // no longer available) + this.loadRequiredClasses(); + + // validate that this server is running a minimum required version: + this.outdatedServer = this.isOutdatedServerVersion(); + if (this.outdatedServer) { + return; + } + + // try to load suitable NMS (or fallback) code: + this.incompatibleServer = !this.setupNMS(); + if (this.incompatibleServer) { + return; + } + + // load config: + this.loadConfig(); + + // WorldGuard only allows registering flags before it gets enabled. + // By default, we always attempt to register the flag. There is no config setting for this, because this is not + // expected to be required. And a config setting would also not work for later config reloads. + // Instead, in case this is really required for some reason, a system property can be used to disable the flag + // registration. + if (System.getProperty("shopkeepers.skip-wg-allow-shop-flag", "false").equals("false")) { + WorldGuardHandler.registerAllowShopFlag(); + } + } + + @Override + public void onEnable() { + // plugin instance and API might already have been set during onLoad: + boolean alreadySetup = true; + if (plugin == null) { + alreadySetup = false; + plugin = this; + ShopkeepersAPI.enable(this); + } + + // validate that this server is running a minimum required version: + if (this.outdatedServer) { + Log.severe("Outdated server version (" + Bukkit.getVersion() + "): Shopkeepers cannot be enabled. Please update your server!"); + this.setEnabled(false); // also calls onDisable + return; + } + + // check if the server version is incompatible: + if (this.incompatibleServer) { + Log.severe("Incompatible server version: Shopkeepers cannot be enabled."); + this.setEnabled(false); // also calls onDisable + return; + } + + // load config (if it hasn't just been loaded already during onLoad): + if (!alreadySetup) { + this.loadConfig(); + } else { + Log.info("Config already loaded."); + } // process additional permissions String[] perms = Settings.maxShopsPermOptions.replace(" ", "").split(","); diff --git a/src/main/java/com/nisovin/shopkeepers/pluginhandlers/WorldGuardHandler.java b/src/main/java/com/nisovin/shopkeepers/pluginhandlers/WorldGuardHandler.java index d9522fbb3..ba83c491b 100644 --- a/src/main/java/com/nisovin/shopkeepers/pluginhandlers/WorldGuardHandler.java +++ b/src/main/java/com/nisovin/shopkeepers/pluginhandlers/WorldGuardHandler.java @@ -50,14 +50,14 @@ public static boolean isShopAllowed(Player player, Location loc) { private static class Link { public static void registerAllowShopFlag() { - Log.debug("Registering WorldGuard flag '" + FLAG_ALLOW_SHOP + "'."); + Log.info("Registering WorldGuard flag '" + FLAG_ALLOW_SHOP + "'."); try { StateFlag allowShopFlag = new StateFlag(FLAG_ALLOW_SHOP, false); WorldGuard.getInstance().getFlagRegistry().register(allowShopFlag); } catch (FlagConflictException | IllegalStateException e) { // another plugin has probably already registered this flag, // or this plugin got hard reloaded by some plugin manager plugin - Log.debug("Couldn't register WorldGuard flag '" + FLAG_ALLOW_SHOP + "': " + e.getMessage()); + Log.info("Couldn't register WorldGuard flag '" + FLAG_ALLOW_SHOP + "': " + e.getMessage()); } }