Improve performance while updating shops

Inspired from the fork of @gonzalociocca but not exactly the same
Closes #39
This commit is contained in:
Eric 2017-01-04 17:07:00 +01:00
parent e08b09fe3f
commit f6f8d93b85
10 changed files with 202 additions and 141 deletions

View File

@ -5,6 +5,7 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.config.Regex; import de.epiceric.shopchest.config.Regex;
import de.epiceric.shopchest.event.ShopReloadEvent; import de.epiceric.shopchest.event.ShopReloadEvent;
import de.epiceric.shopchest.event.ShopUpdateEvent;
import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.LocalizedMessage; import de.epiceric.shopchest.language.LocalizedMessage;
import de.epiceric.shopchest.listeners.*; import de.epiceric.shopchest.listeners.*;
@ -26,6 +27,8 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import java.io.*; import java.io.*;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -46,6 +49,7 @@ public class ShopChest extends JavaPlugin {
private FileWriter fw; private FileWriter fw;
private WorldGuardPlugin worldGuard; private WorldGuardPlugin worldGuard;
private Towny towny; private Towny towny;
private ShopUpdater updater;
/** /**
* @return An instance of ShopChest * @return An instance of ShopChest
@ -304,7 +308,7 @@ public class ShopChest extends JavaPlugin {
} }
debug("Registering listeners..."); 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 ShopItemListener(this), this);
getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this); getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this);
getServer().getPluginManager().registerEvents(new NotifyUpdateOnJoinListener(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); getServer().getPluginManager().registerEvents(new WorldGuardListener(this), this);
initializeShops(); initializeShops();
updater = new ShopUpdater(this);
updater.start();
} }
@Override @Override
public void onDisable() { public void onDisable() {
debug("Disabling ShopChest..."); debug("Disabling ShopChest...");
if (updater != null) {
debug("Stopping updater");
updater.cancel();
}
if (database != null) { if (database != null) {
for (Shop shop : shopUtils.getShops()) { for (Shop shop : shopUtils.getShops()) {
shopUtils.removeShop(shop, false); shopUtils.removeShop(shop, false);
@ -383,6 +395,13 @@ public class ShopChest extends JavaPlugin {
debug("Initialized " + count + " Shops"); 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 * @return Whether the plugin 'Towny' is enabled
*/ */

View File

@ -1,6 +1,7 @@
package de.epiceric.shopchest.config; package de.epiceric.shopchest.config;
import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.event.ShopUpdateEvent;
import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.sql.Database; import de.epiceric.shopchest.sql.Database;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
@ -20,6 +21,9 @@ public class Config {
private LanguageConfiguration langConfig; 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' **/ /** The default value for the custom WorldGuard flag 'create-shop' **/
public boolean wg_allow_create_shop_default; public boolean wg_allow_create_shop_default;
@ -299,6 +303,7 @@ public class Config {
* Reload the configuration values from config.yml * Reload the configuration values from config.yml
*/ */
public void reload(boolean firstLoad, boolean langReload, boolean showMessages) { 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_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_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"); wg_allow_use_shop_default = plugin.getConfig().getBoolean("worldguard-default-flag-values.use-shop");

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}
}

View File

@ -374,7 +374,7 @@ public class ShopInteractListener implements Listener {
if (hologram != null) { if (hologram != null) {
Block b = null; Block b = null;
for (Shop shop : plugin.getShopUtils().getShops()) { for (Shop shop : plugin.getShopUtils().getShops()) {
if (shop.getHologram().equals(hologram)) { if (shop.getHologram() != null && shop.getHologram().equals(hologram)) {
b = shop.getLocation().getBlock(); b = shop.getLocation().getBlock();
} }
} }
@ -407,7 +407,7 @@ public class ShopInteractListener implements Listener {
if (hologram != null) { if (hologram != null) {
Block b = null; Block b = null;
for (Shop shop : plugin.getShopUtils().getShops()) { for (Shop shop : plugin.getShopUtils().getShops()) {
if (shop.getHologram().equals(hologram)) { if (shop.getHologram() != null && shop.getHologram().equals(hologram)) {
b = shop.getLocation().getBlock(); b = shop.getLocation().getBlock();
} }
} }

View File

@ -28,43 +28,6 @@ public class ShopItemListener implements Listener {
this.shopUtils = plugin.getShopUtils(); 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 @EventHandler
public void onPlayerLeave(PlayerQuitEvent e) { public void onPlayerLeave(PlayerQuitEvent e) {
for (Shop shop : plugin.getShopUtils().getShops()) { 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) @EventHandler(priority = EventPriority.HIGH)
public void onBlockPlace(BlockPlaceEvent e) { public void onBlockPlace(BlockPlaceEvent e) {
Block b = e.getBlockPlaced(); Block b = e.getBlockPlaced();

View File

@ -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());
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -3,9 +3,11 @@ package de.epiceric.shopchest.utils;
import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.config.Config;
import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.sql.Database; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.block.Block;
import org.bukkit.block.Chest; import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest; import org.bukkit.block.DoubleChest;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -188,7 +190,10 @@ public class ShopUtils {
plugin.getShopDatabase().connect(); 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()) { for (Shop shop : getShops()) {
removeShop(shop, false); removeShop(shop, false);
@ -215,4 +220,55 @@ public class ShopUtils {
return count; 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);
}
}
}
} }

View File

@ -14,12 +14,12 @@ language-file: "en_US"
# Set whether the floating shop items on top of the chest should be shown # Set whether the floating shop items on top of the chest should be shown
show-shop-items: true show-shop-items: true
# Set whether quality-mode should be enabled. # Set the quality (speed) for hologram and item updating.
# Showing and hiding holograms will look better, # Valid values are (from slowest to fastest):
# but TPS will probably decrease. # 'SLOWEST', 'SLOWER', 'SLOW', 'NORMAL', 'FAST', 'FASTER', 'FASTEST'
# Not recommended on servers with low performance # The faster the value, the more performance will be used, which
# or on servers with a lot of shops! # may lead to TPS loss.
enable-quality-mode: false update-quality: NORMAL
# Set whether interaction with the hologram should be enabled. # Set whether interaction with the hologram should be enabled.
# If set to true, a player can do the exact same thing with the # If set to true, a player can do the exact same thing with the