From f6f8d93b853c4283d7790995e843e61ac7acaa3d Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 4 Jan 2017 17:07:00 +0100 Subject: [PATCH] Improve performance while updating shops Inspired from the fork of @gonzalociocca but not exactly the same Closes #39 --- .../java/de/epiceric/shopchest/ShopChest.java | 21 +++++- .../de/epiceric/shopchest/config/Config.java | 5 ++ .../shopchest/event/ShopUpdateEvent.java | 41 +++++++++++ .../listeners/HologramUpdateListener.java | 71 ------------------- .../listeners/ShopInteractListener.java | 4 +- .../shopchest/listeners/ShopItemListener.java | 59 --------------- .../listeners/ShopUpdateListener.java | 25 +++++++ .../epiceric/shopchest/utils/ShopUpdater.java | 45 ++++++++++++ .../epiceric/shopchest/utils/ShopUtils.java | 60 +++++++++++++++- src/main/resources/config.yml | 12 ++-- 10 files changed, 202 insertions(+), 141 deletions(-) create mode 100644 src/main/java/de/epiceric/shopchest/event/ShopUpdateEvent.java delete mode 100644 src/main/java/de/epiceric/shopchest/listeners/HologramUpdateListener.java create mode 100644 src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java create mode 100644 src/main/java/de/epiceric/shopchest/utils/ShopUpdater.java diff --git a/src/main/java/de/epiceric/shopchest/ShopChest.java b/src/main/java/de/epiceric/shopchest/ShopChest.java index 51c028d..dcc49f6 100644 --- a/src/main/java/de/epiceric/shopchest/ShopChest.java +++ b/src/main/java/de/epiceric/shopchest/ShopChest.java @@ -5,6 +5,7 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.config.Regex; import de.epiceric.shopchest.event.ShopReloadEvent; +import de.epiceric.shopchest.event.ShopUpdateEvent; import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.LocalizedMessage; import de.epiceric.shopchest.listeners.*; @@ -26,6 +27,8 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; import java.io.*; import java.text.SimpleDateFormat; @@ -46,6 +49,7 @@ public class ShopChest extends JavaPlugin { private FileWriter fw; private WorldGuardPlugin worldGuard; private Towny towny; + private ShopUpdater updater; /** * @return An instance of ShopChest @@ -304,7 +308,7 @@ public class ShopChest extends JavaPlugin { } debug("Registering listeners..."); - getServer().getPluginManager().registerEvents(new HologramUpdateListener(this), this); + getServer().getPluginManager().registerEvents(new ShopUpdateListener(this), this); getServer().getPluginManager().registerEvents(new ShopItemListener(this), this); getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this); getServer().getPluginManager().registerEvents(new NotifyUpdateOnJoinListener(this), this); @@ -317,12 +321,20 @@ public class ShopChest extends JavaPlugin { getServer().getPluginManager().registerEvents(new WorldGuardListener(this), this); initializeShops(); + + updater = new ShopUpdater(this); + updater.start(); } @Override public void onDisable() { debug("Disabling ShopChest..."); + if (updater != null) { + debug("Stopping updater"); + updater.cancel(); + } + if (database != null) { for (Shop shop : shopUtils.getShops()) { shopUtils.removeShop(shop, false); @@ -383,6 +395,13 @@ public class ShopChest extends JavaPlugin { debug("Initialized " + count + " Shops"); } + /** + * @return The {@link ShopUpdater} that schedules hologram and item updates + */ + public ShopUpdater getUpdater() { + return updater; + } + /** * @return Whether the plugin 'Towny' is enabled */ diff --git a/src/main/java/de/epiceric/shopchest/config/Config.java b/src/main/java/de/epiceric/shopchest/config/Config.java index 960751c..0e0bce1 100644 --- a/src/main/java/de/epiceric/shopchest/config/Config.java +++ b/src/main/java/de/epiceric/shopchest/config/Config.java @@ -1,6 +1,7 @@ package de.epiceric.shopchest.config; import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.event.ShopUpdateEvent; import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.sql.Database; import org.bukkit.configuration.InvalidConfigurationException; @@ -20,6 +21,9 @@ public class Config { private LanguageConfiguration langConfig; + /** The quality of hologram and item updating (performance saving, or better quality) **/ + public ShopUpdateEvent.UpdateQuality update_quality; + /** The default value for the custom WorldGuard flag 'create-shop' **/ public boolean wg_allow_create_shop_default; @@ -299,6 +303,7 @@ public class Config { * Reload the configuration values from config.yml */ public void reload(boolean firstLoad, boolean langReload, boolean showMessages) { + update_quality = ShopUpdateEvent.UpdateQuality.valueOf(plugin.getConfig().getString("update-quality")); wg_allow_create_shop_default = plugin.getConfig().getBoolean("worldguard-default-flag-values.create-shop"); wg_allow_use_admin_shop_default = plugin.getConfig().getBoolean("worldguard-default-flag-values.use-admin-shop"); wg_allow_use_shop_default = plugin.getConfig().getBoolean("worldguard-default-flag-values.use-shop"); diff --git a/src/main/java/de/epiceric/shopchest/event/ShopUpdateEvent.java b/src/main/java/de/epiceric/shopchest/event/ShopUpdateEvent.java new file mode 100644 index 0000000..0f4b29d --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/event/ShopUpdateEvent.java @@ -0,0 +1,41 @@ +package de.epiceric.shopchest.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class ShopUpdateEvent extends Event { + + public enum UpdateQuality { + SLOWEST(31L), + SLOWER(24L), + SLOW(17L), + NORMAL(10L), + FAST(7L), + FASTER(4L), + FASTEST(1L); + + private long time; + + UpdateQuality(long time) { + this.time = time; + } + + public long getTime() { + return time; + } + } + + private static final HandlerList handlers = new HandlerList(); + + public ShopUpdateEvent() { + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/de/epiceric/shopchest/listeners/HologramUpdateListener.java b/src/main/java/de/epiceric/shopchest/listeners/HologramUpdateListener.java deleted file mode 100644 index 327a75b..0000000 --- a/src/main/java/de/epiceric/shopchest/listeners/HologramUpdateListener.java +++ /dev/null @@ -1,71 +0,0 @@ -package de.epiceric.shopchest.listeners; - -import de.epiceric.shopchest.ShopChest; -import de.epiceric.shopchest.shop.Shop; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerMoveEvent; - -public class HologramUpdateListener implements Listener { - - private ShopChest plugin; - - public HologramUpdateListener(ShopChest plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent e) { - updateHolograms(e.getPlayer()); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerMove(PlayerMoveEvent e) { - if (!plugin.getShopChestConfig().enable_quality_mode && - e.getFrom().getBlockX() == e.getTo().getBlockX() && - e.getFrom().getBlockY() == e.getTo().getBlockY() && - e.getFrom().getBlockZ() == e.getTo().getBlockZ()) { - return; - } - - updateHolograms(e.getPlayer()); - } - - private void updateHolograms(Player p) { - Location playerLocation = p.getLocation(); - double hologramDistanceSquared = Math.pow(plugin.getShopChestConfig().maximal_distance, 2); - - for (Shop shop : plugin.getShopUtils().getShops()) { - Block b = shop.getLocation().getBlock(); - - if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) { - plugin.getShopUtils().removeShop(shop, plugin.getShopChestConfig().remove_shop_on_error); - continue; - } - - if (shop.getHologram() == null) continue; - - Location shopLocation = shop.getLocation(); - - if (playerLocation.getWorld().getName().equals(shopLocation.getWorld().getName())) { - if (playerLocation.distanceSquared(shop.getHologram().getLocation()) <= hologramDistanceSquared) { - if (!shop.getHologram().isVisible(p)) { - shop.getHologram().showPlayer(p); - } - } else { - if (shop.getHologram().isVisible(p)) { - shop.getHologram().hidePlayer(p); - } - } - } - - } - } - -} diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 2e139e7..06585bd 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -374,7 +374,7 @@ public class ShopInteractListener implements Listener { if (hologram != null) { Block b = null; for (Shop shop : plugin.getShopUtils().getShops()) { - if (shop.getHologram().equals(hologram)) { + if (shop.getHologram() != null && shop.getHologram().equals(hologram)) { b = shop.getLocation().getBlock(); } } @@ -407,7 +407,7 @@ public class ShopInteractListener implements Listener { if (hologram != null) { Block b = null; for (Shop shop : plugin.getShopUtils().getShops()) { - if (shop.getHologram().equals(hologram)) { + if (shop.getHologram() != null && shop.getHologram().equals(hologram)) { b = shop.getLocation().getBlock(); } } diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java index a6a3b69..46b0ee9 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopItemListener.java @@ -28,43 +28,6 @@ public class ShopItemListener implements Listener { this.shopUtils = plugin.getShopUtils(); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerMove(PlayerMoveEvent e) { - if (e.getFrom().getBlockX() == e.getTo().getBlockX() - && e.getFrom().getBlockZ() == e.getTo().getBlockZ() - && e.getFrom().getBlockY() == e.getTo().getBlockY()) { - return; - } - - updateShopItemVisibility(e.getPlayer(), true, false); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerTeleport(final PlayerTeleportEvent e) { - if (e.getFrom().getWorld().equals(e.getTo().getWorld())) { - if (e.getFrom().distanceSquared(e.getTo()) > 22500) { - Bukkit.getScheduler().runTaskLater(plugin, new Runnable() { - @Override - public void run() { - updateShopItemVisibility(e.getPlayer(), true, true, e.getTo()); - } - }, 20L); - return; - } - updateShopItemVisibility(e.getPlayer(), true, false, e.getTo()); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerChangedWorld(PlayerChangedWorldEvent e) { - updateShopItemVisibility(e.getPlayer(), true, true); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent e) { - updateShopItemVisibility(e.getPlayer(), false, false); - } - @EventHandler public void onPlayerLeave(PlayerQuitEvent e) { for (Shop shop : plugin.getShopUtils().getShops()) { @@ -74,28 +37,6 @@ public class ShopItemListener implements Listener { } } - private void updateShopItemVisibility(Player p, boolean hideIfAway, boolean reset) { - updateShopItemVisibility(p, hideIfAway, reset, p.getLocation()); - } - - private void updateShopItemVisibility(Player p, boolean hideIfAway, boolean reset, Location playerLocation) { - double itemDistanceSquared = plugin.getShopChestConfig().maximal_item_distance; - itemDistanceSquared *= itemDistanceSquared; - World w = playerLocation.getWorld(); - - for (Shop shop : shopUtils.getShops()) { - Location shopLocation = shop.getLocation(); - if (w.equals(shopLocation.getWorld()) && shopLocation.distanceSquared(playerLocation) <= itemDistanceSquared) { - if (shop.getItem() != null) { - if (reset) shop.getItem().resetForPlayer(p); - else shop.getItem().setVisible(p, true); - } - } else if (hideIfAway) { - if (shop.getItem() != null) shop.getItem().setVisible(p, false); - } - } - } - @EventHandler(priority = EventPriority.HIGH) public void onBlockPlace(BlockPlaceEvent e) { Block b = e.getBlockPlaced(); diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java new file mode 100644 index 0000000..258d80b --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopUpdateListener.java @@ -0,0 +1,25 @@ +package de.epiceric.shopchest.listeners; + +import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.event.ShopUpdateEvent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class ShopUpdateListener implements Listener { + + private ShopChest plugin; + + public ShopUpdateListener(ShopChest plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onShopUpdate(ShopUpdateEvent e) { + for (Player p : Bukkit.getOnlinePlayers()) { + plugin.getShopUtils().updateShops(p, p.getLocation()); + } + } + +} diff --git a/src/main/java/de/epiceric/shopchest/utils/ShopUpdater.java b/src/main/java/de/epiceric/shopchest/utils/ShopUpdater.java new file mode 100644 index 0000000..0cd6222 --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/utils/ShopUpdater.java @@ -0,0 +1,45 @@ +package de.epiceric.shopchest.utils; + +import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.event.ShopUpdateEvent; +import org.bukkit.Bukkit; + +public class ShopUpdater extends Thread { + + private boolean running; + private long maxDelta; + private long lastTime; + + public ShopUpdater(ShopChest plugin) { + setMaxDelta(plugin.getShopChestConfig().update_quality.getTime()); + } + + public synchronized void setMaxDelta(long maxDelta) { + this.maxDelta = maxDelta * 50; + } + + @Override + public synchronized void start() { + super.start(); + running = true; + lastTime = System.currentTimeMillis(); + } + + public synchronized void cancel() { + running = false; + super.interrupt(); + } + + @Override + public void run() { + while(running) { + long timeNow = System.currentTimeMillis(); + long timeElapsed = timeNow - lastTime; + + if (timeElapsed >= maxDelta) { + Bukkit.getPluginManager().callEvent(new ShopUpdateEvent()); + lastTime = timeNow; + } + } + } +} diff --git a/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java b/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java index 357ef9f..efff8ee 100644 --- a/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java +++ b/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java @@ -3,9 +3,11 @@ package de.epiceric.shopchest.utils; import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.shop.Shop; -import de.epiceric.shopchest.sql.Database; +import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.block.Block; import org.bukkit.block.Chest; import org.bukkit.block.DoubleChest; import org.bukkit.entity.Player; @@ -188,7 +190,10 @@ public class ShopUtils { plugin.getShopDatabase().connect(); - if (reloadConfig) plugin.getShopChestConfig().reload(false, true, showConsoleMessages); + if (reloadConfig) { + plugin.getShopChestConfig().reload(false, true, showConsoleMessages); + plugin.getUpdater().setMaxDelta(plugin.getShopChestConfig().update_quality.getTime()); + } for (Shop shop : getShops()) { removeShop(shop, false); @@ -215,4 +220,55 @@ public class ShopUtils { return count; } + + /** + * Update hologram and item of all shops for a player + * @param player Player to show the updates + * @param location Location of the player + */ + public void updateShops(Player player, Location location) { + for (Shop shop : getShops()) { + updateShop(shop, player, location); + } + } + + /** + * Update hologram and item of the shop for a player + * @param shop Shop to update + * @param player Player to show the update + * @param location Location of the player + */ + public void updateShop(Shop shop, Player player, Location location) { + double holoDistSqr = Math.pow(plugin.getShopChestConfig().maximal_distance, 2); + double itemDistSqr = Math.pow(plugin.getShopChestConfig().maximal_item_distance, 2); + + if (location.getWorld().getName().equals(shop.getLocation().getWorld().getName())) { + double distSqr = shop.getLocation().distanceSquared(location); + + if (distSqr <= holoDistSqr) { + if (shop.getHologram() != null) { + Block b = shop.getLocation().getBlock(); + + if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) { + plugin.getShopUtils().removeShop(shop, plugin.getShopChestConfig().remove_shop_on_error); + return; + } + + if (!shop.getHologram().isVisible(player)) { + shop.getHologram().showPlayer(player); + } + } + } else { + if (shop.getHologram().isVisible(player)) shop.getHologram().hidePlayer(player); + } + + if (distSqr <= itemDistSqr) { + if (shop.getItem() != null) { + shop.getItem().setVisible(player, true); + } + } else { + if (shop.getItem() != null) shop.getItem().setVisible(player, false); + } + } + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2afb4fe..e1a9811 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -14,12 +14,12 @@ language-file: "en_US" # Set whether the floating shop items on top of the chest should be shown show-shop-items: true -# Set whether quality-mode should be enabled. -# Showing and hiding holograms will look better, -# but TPS will probably decrease. -# Not recommended on servers with low performance -# or on servers with a lot of shops! -enable-quality-mode: false +# Set the quality (speed) for hologram and item updating. +# Valid values are (from slowest to fastest): +# 'SLOWEST', 'SLOWER', 'SLOW', 'NORMAL', 'FAST', 'FASTER', 'FASTEST' +# The faster the value, the more performance will be used, which +# may lead to TPS loss. +update-quality: NORMAL # Set whether interaction with the hologram should be enabled. # If set to true, a player can do the exact same thing with the