From 1016b5fc76d30bf0733f7fb09c0ba6925825dce8 Mon Sep 17 00:00:00 2001 From: TheBusyBiscuit Date: Wed, 1 Jul 2020 18:20:34 +0200 Subject: [PATCH] Added better summaries, ratings + more performance improvements --- CHANGELOG.md | 2 +- .../core/networks/cargo/CargoNet.java | 93 ++++++----- .../services/profiler/PerformanceRating.java | 46 +++++ .../services/profiler/PerformanceSummary.java | 157 ++++++++++++++++++ .../services/profiler/SlimefunProfiler.java | 110 +++--------- .../items/blocks/EnhancedFurnace.java | 11 +- .../items/cargo/CargoManager.java | 2 +- .../implementation/tasks/TickerTask.java | 48 +++--- .../slimefun4/utils/ChestMenuUtils.java | 19 +-- .../slimefun4/utils/NumberUtils.java | 6 +- 10 files changed, 319 insertions(+), 175 deletions(-) create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 146e6b4d7..8008e0d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,10 +52,10 @@ * Crafting Tin cans now produces 8 items instead of 4 * Multi Tool lore now says "Crouch" instead of "Hold Shift" * items which cannot be distributed by a Cargo Net will be dropped on the ground now instead of getting deleted -* Small performance improvements to the Cargo Net * Slimefun no longer supports CraftBukkit * Item Energy is now also stored persistently via NBT * General performance improvements for ticking blocks +* Performance improvements to the Cargo Net #### Fixes * Fixed #2005 diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java index 5d5c11918..c98c6e9f7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java @@ -19,6 +19,7 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; @@ -156,28 +157,59 @@ public class CargoNet extends ChestTerminalNetwork { } else { SimpleHologram.update(b, "&7Status: &a&lONLINE"); - Map> output = mapOutputNodes(); - // Chest Terminal Stuff - Set destinations = new HashSet<>(); - List output16 = output.get(16); - - if (output16 != null) { - destinations.addAll(output16); + // Skip ticking if the threshold is not reached. The delay is not same as minecraft tick, + // but it's based on 'custom-ticker-delay' config. + if (tickDelayThreshold < TICK_DELAY) { + tickDelayThreshold++; + return; } - run(b, destinations, output); + // Reset the internal threshold, so we can start skipping again + tickDelayThreshold = 0; + + // Chest Terminal Stuff + Set chestTerminalInputs = new HashSet<>(); + Set chestTerminalOutputs = new HashSet<>(); + + Map inputs = mapInputNodes(chestTerminalInputs); + Map> outputs = mapOutputNodes(chestTerminalOutputs); + + SlimefunPlugin.getProfiler().newEntry(); + Slimefun.runSync(() -> run(b, inputs, outputs, chestTerminalInputs, chestTerminalOutputs)); } } - private Map> mapOutputNodes() { + private Map mapInputNodes(Set chestTerminalNodes) { + Map inputs = new HashMap<>(); + + for (Location node : inputNodes) { + int frequency = getFrequency(node); + + if (frequency == 16) { + chestTerminalNodes.add(node); + } + else if (frequency >= 0 && frequency < 16) { + inputs.put(node, frequency); + } + } + + return inputs; + } + + private Map> mapOutputNodes(Set chestTerminalOutputs) { Map> output = new HashMap<>(); List list = new LinkedList<>(); int lastFrequency = -1; - for (Location outputNode : outputNodes) { - int frequency = getFrequency(outputNode); + for (Location node : outputNodes) { + int frequency = getFrequency(node); + + if (frequency == 16) { + chestTerminalOutputs.add(node); + continue; + } if (frequency != lastFrequency && lastFrequency != -1) { output.merge(lastFrequency, list, (prev, next) -> { @@ -188,7 +220,7 @@ public class CargoNet extends ChestTerminalNetwork { list = new LinkedList<>(); } - list.add(outputNode); + list.add(node); lastFrequency = frequency; } @@ -202,38 +234,16 @@ public class CargoNet extends ChestTerminalNetwork { return output; } - private void run(Block b, Set destinations, Map> output) { + private void run(Block b, Map inputs, Map> outputs, Set chestTerminalInputs, Set chestTerminalOutputs) { + long timestamp = System.nanoTime(); + if (BlockStorage.getLocationInfo(b.getLocation(), "visualizer") == null) { display(); } - // Skip ticking if the threshold is not reached. The delay is not same as minecraft tick, - // but it's based on 'custom-ticker-delay' config. - if (tickDelayThreshold < TICK_DELAY) { - tickDelayThreshold++; - return; - } - - // Reset the internal threshold, so we can start skipping again - tickDelayThreshold = 0; - - Map inputs = new HashMap<>(); - Set providers = new HashSet<>(); - - for (Location node : inputNodes) { - int frequency = getFrequency(node); - - if (frequency == 16) { - providers.add(node); - } - else if (frequency >= 0 && frequency < 16) { - inputs.put(node, frequency); - } - } - // Chest Terminal Code if (SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled()) { - handleItemRequests(providers, destinations); + handleItemRequests(chestTerminalInputs, chestTerminalOutputs); } // All operations happen here: Everything gets iterated from the Input Nodes. @@ -243,14 +253,17 @@ public class CargoNet extends ChestTerminalNetwork { Optional attachedBlock = getAttachedBlock(input.getBlock()); if (attachedBlock.isPresent()) { - routeItems(input, attachedBlock.get(), entry.getValue(), output); + routeItems(input, attachedBlock.get(), entry.getValue(), outputs); } } // Chest Terminal Code if (SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled()) { - updateTerminals(providers); + updateTerminals(chestTerminalInputs); } + + // Submit a timings report + SlimefunPlugin.getProfiler().closeEntry(regulator, SlimefunItems.CARGO_MANAGER.getItem(), timestamp); } private void routeItems(Location inputNode, Block inputTarget, int frequency, Map> outputNodes) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java new file mode 100644 index 000000000..0ebfbc0fe --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java @@ -0,0 +1,46 @@ +package io.github.thebusybiscuit.slimefun4.core.services.profiler; + +import java.util.function.Predicate; + +import org.bukkit.ChatColor; + +/** + * This enum is used to quantify Slimefun's performance impact. This way we can assign a + * "grade" to each timings report and also use this for metrics collection. + * + * @author TheBusyBiscuit + * + * @see SlimefunProfiler + * + */ +public enum PerformanceRating implements Predicate { + + // Thresholds might change in the future! + + UNKNOWN(ChatColor.WHITE, -1), + + GOOD(ChatColor.DARK_GREEN, 10), + FINE(ChatColor.DARK_GREEN, 20), + OKAY(ChatColor.GREEN, 30), + MODERATE(ChatColor.YELLOW, 55), + SEVERE(ChatColor.RED, 85), + HURTFUL(ChatColor.DARK_RED, Float.MAX_VALUE); + + private final ChatColor color; + private final float threshold; + + PerformanceRating(ChatColor color, float threshold) { + this.color = color; + this.threshold = threshold; + } + + @Override + public boolean test(Float value) { + return value <= threshold; + } + + public ChatColor getColor() { + return color; + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java new file mode 100644 index 000000000..b87d04973 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java @@ -0,0 +1,157 @@ +package io.github.thebusybiscuit.slimefun4.core.services.profiler; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import io.github.thebusybiscuit.cscorelib2.chat.ChatColors; +import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; +import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; + +class PerformanceSummary { + + // The threshold at which a Block or Chunk is significant enough to appear in /sf timings + private static final int VISIBILITY_THRESHOLD = 275_000; + + // A minecraft server tick is 50ms and Slimefun ticks are stretched across + // two ticks (sync and async blocks), so we use 100ms as a reference here + static final int MAX_TICK_DURATION = 100; + + private final SlimefunProfiler profiler; + private final PerformanceRating rating; + private final long totalElapsedTime; + private final int totalTickedBlocks; + + private final Map chunks; + private final Map items; + + PerformanceSummary(SlimefunProfiler profiler, long totalElapsedTime, int totalTickedBlocks) { + this.profiler = profiler; + this.rating = profiler.getPerformance(); + this.totalElapsedTime = totalElapsedTime; + this.totalTickedBlocks = totalTickedBlocks; + + chunks = profiler.getByChunk(); + items = profiler.getByItem(); + } + + public void send(CommandSender sender) { + sender.sendMessage(""); + sender.sendMessage(ChatColor.GREEN + "===== Slimefun Lag Profiler ====="); + sender.sendMessage(ChatColors.color("&6Total: &e" + NumberUtils.getAsMillis(totalElapsedTime))); + sender.sendMessage(ChatColors.color("&6Performance: " + getPerformanceRating())); + sender.sendMessage(ChatColors.color("&6Active Chunks: &e" + chunks.size())); + sender.sendMessage(ChatColors.color("&6Active Blocks: &e" + totalTickedBlocks)); + sender.sendMessage(""); + + summarizeTimings("Chunks", sender, entry -> { + int count = profiler.getBlocksOfId(entry.getKey()); + String time = NumberUtils.getAsMillis(entry.getValue()); + + if (count > 1) { + String average = NumberUtils.getAsMillis(entry.getValue() / count); + + return entry.getKey() + " - " + count + "x (" + time + ", " + average + " avg/block)"; + } + else { + return entry.getKey() + " - " + count + "x (" + time + ')'; + } + }, items.entrySet().stream()); + + sender.sendMessage(""); + + summarizeTimings("Blocks", sender, entry -> { + int count = profiler.getBlocksInChunk(entry.getKey()); + String time = NumberUtils.getAsMillis(entry.getValue()); + + return entry.getKey() + " - " + count + "x (" + time + ")"; + }, chunks.entrySet().stream()); + } + + private void summarizeTimings(String prefix, CommandSender sender, Function, String> formatter, Stream> stream) { + List> results = stream.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()); + + if (sender instanceof Player) { + TextComponent component = new TextComponent(prefix); + component.setColor(ChatColor.GOLD); + + TextComponent hoverComponent = new TextComponent("\n Hover for more details..."); + hoverComponent.setColor(ChatColor.GRAY); + hoverComponent.setItalic(true); + StringBuilder builder = new StringBuilder(); + int hidden = 0; + + for (Map.Entry entry : results) { + if (entry.getValue() > VISIBILITY_THRESHOLD) { + builder.append("\n&e").append(formatter.apply(entry)); + } + else { + hidden++; + } + } + + builder.append("\n\n&c+ &6").append(hidden).append(" more"); + hoverComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(ChatColors.color(builder.toString())))); + + component.addExtra(hoverComponent); + sender.spigot().sendMessage(component); + } + else { + int hidden = 0; + + sender.sendMessage(ChatColor.GOLD + prefix); + + for (Map.Entry entry : results) { + if (entry.getValue() > VISIBILITY_THRESHOLD) { + sender.sendMessage(" " + ChatColor.stripColor(formatter.apply(entry))); + } + else { + hidden++; + } + } + + sender.sendMessage("+ " + hidden + " more"); + } + } + + private String getPerformanceRating() { + StringBuilder builder = new StringBuilder(); + + float percentage = Math.round(((((totalElapsedTime / 1000000.0) * 100.0F) / MAX_TICK_DURATION) * 100.0F) / 100.0F); + builder.append(NumberUtils.getColorFromPercentage(100 - Math.min(percentage, 100))); + + int rest = 20; + for (int i = (int) Math.min(percentage, 100); i >= 5; i = i - 5) { + builder.append(':'); + rest--; + } + + builder.append(ChatColor.DARK_GRAY); + + for (int i = 0; i < rest; i++) { + builder.append(':'); + } + + builder.append(" - "); + + builder.append(rating.getColor() + ChatUtils.humanize(rating.name())); + + builder.append(ChatColor.GRAY); + builder.append(" ("); + builder.append(NumberUtils.roundDecimalNumber(percentage)); + builder.append("%)"); + + return builder.toString(); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java index 5fbe2e270..614885e65 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java @@ -1,12 +1,8 @@ package io.github.thebusybiscuit.slimefun4.core.services.profiler; -import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; -import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -14,30 +10,22 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang.Validate; -import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; -import io.github.thebusybiscuit.cscorelib2.chat.ChatColors; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.Slimefun; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; /** * The {@link SlimefunProfiler} works closely to the {@link TickerTask} and is responsible for @@ -53,9 +41,6 @@ import net.md_5.bungee.api.chat.TextComponent; */ public class SlimefunProfiler { - // The threshold at which a Block or Chunk is significant enough to appear in /sf timings - private static final int VISIBILITY_THRESHOLD = 275_000; - private final ExecutorService executor = Executors.newFixedThreadPool(3); private final AtomicBoolean running = new AtomicBoolean(false); private final AtomicInteger queued = new AtomicInteger(0); @@ -145,11 +130,14 @@ public class SlimefunProfiler { totalElapsedTime = timings.values().stream().mapToLong(Long::longValue).sum(); - Iterator iterator = requests.iterator(); + if (!requests.isEmpty()) { + PerformanceSummary summary = new PerformanceSummary(this, totalElapsedTime, timings.size()); + Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - sendSummary(iterator.next()); - iterator.remove(); + while (iterator.hasNext()) { + summary.send(iterator.next()); + iterator.remove(); + } } }); @@ -166,7 +154,7 @@ public class SlimefunProfiler { requests.add(sender); } - private Map getByItem() { + protected Map getByItem() { Map map = new HashMap<>(); for (Map.Entry entry : timings.entrySet()) { @@ -176,7 +164,7 @@ public class SlimefunProfiler { return map; } - private Map getByChunk() { + protected Map getByChunk() { Map map = new HashMap<>(); for (Map.Entry entry : timings.entrySet()) { @@ -190,7 +178,7 @@ public class SlimefunProfiler { return map; } - private int getBlocksInChunk(String chunk) { + protected int getBlocksInChunk(String chunk) { int blocks = 0; for (ProfiledBlock block : timings.keySet()) { @@ -206,7 +194,7 @@ public class SlimefunProfiler { return blocks; } - private int getBlocks(String id) { + protected int getBlocksOfId(String id) { int blocks = 0; for (ProfiledBlock block : timings.keySet()) { @@ -218,75 +206,21 @@ public class SlimefunProfiler { return blocks; } - private void sendSummary(CommandSender sender) { - Map chunks = getByChunk(); - Map machines = getByItem(); + /** + * This method returns the current {@link PerformanceRating}. + * + * @return The current performance grade + */ + public PerformanceRating getPerformance() { + float percentage = Math.round(((((totalElapsedTime / 1000000.0) * 100.0F) / PerformanceSummary.MAX_TICK_DURATION) * 100.0F) / 100.0F); - sender.sendMessage(ChatColors.color("&2== &aSlimefun Lag Profiler &2==")); - sender.sendMessage(ChatColors.color("&6Running: &e&l" + String.valueOf(!SlimefunPlugin.getTickerTask().isHalted()).toUpperCase(Locale.ROOT))); - sender.sendMessage(""); - sender.sendMessage(ChatColors.color("&6Impact: &e" + NumberUtils.getAsMillis(totalElapsedTime))); - sender.sendMessage(ChatColors.color("&6Ticked Chunks: &e" + chunks.size())); - sender.sendMessage(ChatColors.color("&6Ticked Blocks: &e" + timings.size())); - sender.sendMessage(""); - sender.sendMessage(ChatColors.color("&6Ticking Machines:")); - - summarizeTimings(sender, entry -> { - int count = getBlocks(entry.getKey()); - String time = NumberUtils.getAsMillis(entry.getValue()); - String average = NumberUtils.getAsMillis(entry.getValue() / count); - - return entry.getKey() + " - " + count + "x (" + time + ", " + average + " avg/block)"; - }, machines.entrySet().stream()); - - sender.sendMessage(""); - sender.sendMessage(ChatColors.color("&6Ticking Chunks:")); - - summarizeTimings(sender, entry -> { - int count = getBlocksInChunk(entry.getKey()); - String time = NumberUtils.getAsMillis(entry.getValue()); - - return entry.getKey() + " - " + count + "x (" + time + ")"; - }, chunks.entrySet().stream()); - } - - private void summarizeTimings(CommandSender sender, Function, String> formatter, Stream> stream) { - List> results = stream.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()); - - if (sender instanceof Player) { - TextComponent component = new TextComponent(" Hover for more details..."); - component.setColor(net.md_5.bungee.api.ChatColor.GRAY); - component.setItalic(true); - StringBuilder builder = new StringBuilder(); - int hidden = 0; - - for (Map.Entry entry : results) { - if (entry.getValue() > VISIBILITY_THRESHOLD) { - builder.append("\n&e").append(formatter.apply(entry)); - } - else { - hidden++; - } + for (PerformanceRating rating : PerformanceRating.values()) { + if (rating.test(percentage)) { + return rating; } - - builder.append("\n\n&c+ &6").append(hidden).append(" more"); - component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(ChatColors.color(builder.toString())))); - sender.spigot().sendMessage(component); } - else { - int hidden = 0; - for (Map.Entry entry : results) { - if (entry.getValue() > VISIBILITY_THRESHOLD) { - sender.sendMessage(" " + ChatColor.stripColor(formatter.apply(entry))); - } - else { - hidden++; - } - } - - sender.sendMessage("+ " + hidden + " more"); - } + return PerformanceRating.UNKNOWN; } public String getTime() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java index 3f7432f69..24012fc56 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java @@ -30,14 +30,14 @@ public class EnhancedFurnace extends SimpleSlimefunItem { private final int speed; private final int efficiency; - private final int fortune; + private final int fortuneLevel; public EnhancedFurnace(Category category, int speed, int efficiency, int fortune, SlimefunItemStack item, ItemStack[] recipe) { super(category, item, RecipeType.ENHANCED_CRAFTING_TABLE, recipe); this.speed = speed - 1; this.efficiency = efficiency - 1; - this.fortune = fortune - 1; + this.fortuneLevel = fortune - 1; } public int getSpeed() { @@ -49,11 +49,8 @@ public class EnhancedFurnace extends SimpleSlimefunItem { } public int getOutput() { - int bonus = this.fortune; - bonus = ThreadLocalRandom.current().nextInt(bonus + 2) - 1; - if (bonus <= 0) bonus = 0; - bonus++; - return bonus; + int bonus = ThreadLocalRandom.current().nextInt(fortuneLevel + 2); + return 1 + bonus; } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java index 66b8bc768..29ff47e5b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java @@ -41,7 +41,7 @@ public class CargoManager extends SlimefunItem { @Override public boolean isSynchronized() { - return true; + return false; } }, new BlockUseHandler() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java index 518845fbb..e13d9c236 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java @@ -1,6 +1,5 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -14,6 +13,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; import io.github.thebusybiscuit.slimefun4.api.ErrorReport; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; @@ -30,7 +30,7 @@ public class TickerTask implements Runnable { // These are "Queues" of blocks that need to be removed or moved private final Map movingQueue = new ConcurrentHashMap<>(); private final Map deletionQueue = new ConcurrentHashMap<>(); - private final Map buggedBlocks = new ConcurrentHashMap<>(); + private final Map bugs = new ConcurrentHashMap<>(); private boolean halted = false; private boolean running = false; @@ -48,14 +48,11 @@ public class TickerTask implements Runnable { running = true; SlimefunPlugin.getProfiler().start(); - Map bugs = new HashMap<>(buggedBlocks); - buggedBlocks.clear(); - - Map removals = new HashMap<>(deletionQueue); - - for (Map.Entry entry : removals.entrySet()) { + Iterator> removals = deletionQueue.entrySet().iterator(); + while (removals.hasNext()) { + Map.Entry entry = removals.next(); BlockStorage._integrated_removeBlockInfo(entry.getKey(), entry.getValue()); - deletionQueue.remove(entry.getKey()); + removals.remove(); } if (!halted) { @@ -70,7 +67,7 @@ public class TickerTask implements Runnable { if (world != null && world.isChunkLoaded(x, z)) { for (Location l : locations) { - tick(l, bugs); + tick(l); } } } @@ -80,12 +77,13 @@ public class TickerTask implements Runnable { } } - for (Map.Entry entry : movingQueue.entrySet()) { + Iterator> moves = movingQueue.entrySet().iterator(); + while (moves.hasNext()) { + Map.Entry entry = moves.next(); BlockStorage._integrated_moveLocationInfo(entry.getKey(), entry.getValue()); + moves.remove(); } - movingQueue.clear(); - Iterator iterator = tickers.iterator(); while (iterator.hasNext()) { iterator.next().startNewTick(); @@ -96,7 +94,7 @@ public class TickerTask implements Runnable { SlimefunPlugin.getProfiler().stop(); } - private void tick(Location l, Map bugs) { + private void tick(Location l) { Config data = BlockStorage.getLocationInfo(l); SlimefunItem item = SlimefunItem.getByID(data.getString("id")); @@ -109,53 +107,53 @@ public class TickerTask implements Runnable { if (item.getBlockTicker().isSynchronized()) { // We are ignoring the timestamp from above because synchronized actions // are always ran with a 50ms delay (1 game tick) - Slimefun.runSync(() -> tickBlock(bugs, l, b, item, data, System.nanoTime())); + Slimefun.runSync(() -> tickBlock(l, b, item, data, System.nanoTime())); } else { - tickBlock(bugs, l, b, item, data, timestamp); + tickBlock(l, b, item, data, timestamp); } tickers.add(item.getBlockTicker()); } catch (Exception x) { - int errors = bugs.getOrDefault(l, 0); - reportErrors(l, item, x, errors); + reportErrors(l, item, x); } } } - private void tickBlock(Map bugs, Location l, Block b, SlimefunItem item, Config data, long timestamp) { + private void tickBlock(Location l, Block b, SlimefunItem item, Config data, long timestamp) { try { item.getBlockTicker().tick(b, item, data); } catch (Exception | LinkageError x) { - int errors = bugs.getOrDefault(l, 0); - reportErrors(l, item, x, errors); + reportErrors(l, item, x); } finally { SlimefunPlugin.getProfiler().closeEntry(l, item, timestamp); } } - private void reportErrors(Location l, SlimefunItem item, Throwable x, int errors) { - errors++; + private void reportErrors(Location l, SlimefunItem item, Throwable x) { + BlockPosition position = new BlockPosition(l); + int errors = bugs.getOrDefault(position, 0) + 1; if (errors == 1) { // Generate a new Error-Report new ErrorReport(x, l, item); - buggedBlocks.put(l, errors); + bugs.put(position, errors); } else if (errors == 4) { Slimefun.getLogger().log(Level.SEVERE, "X: {0} Y: {1} Z: {2} ({3})", new Object[] { l.getBlockX(), l.getBlockY(), l.getBlockZ(), item.getID() }); Slimefun.getLogger().log(Level.SEVERE, "has thrown 4 error messages in the last 4 Ticks, the Block has been terminated."); Slimefun.getLogger().log(Level.SEVERE, "Check your /plugins/Slimefun/error-reports/ folder for details."); Slimefun.getLogger().log(Level.SEVERE, " "); + bugs.remove(position); BlockStorage._integrated_removeBlockInfo(l, true); Bukkit.getScheduler().scheduleSyncDelayedTask(SlimefunPlugin.instance, () -> l.getBlock().setType(Material.AIR)); } else { - buggedBlocks.put(l, errors); + bugs.put(position, errors); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java index 0c861495f..bebdaf7ab 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java @@ -116,30 +116,25 @@ public final class ChestMenuUtils { } public static String getProgressBar(int time, int total) { - StringBuilder progress = new StringBuilder(); + StringBuilder builder = new StringBuilder(); float percentage = Math.round(((((total - time) * 100.0F) / total) * 100.0F) / 100.0F); - if (percentage < 16.0F) progress.append("&4"); - else if (percentage < 32.0F) progress.append("&c"); - else if (percentage < 48.0F) progress.append("&6"); - else if (percentage < 64.0F) progress.append("&e"); - else if (percentage < 80.0F) progress.append("&2"); - else progress.append("&a"); + builder.append(NumberUtils.getColorFromPercentage(percentage)); int rest = 20; for (int i = (int) percentage; i >= 5; i = i - 5) { - progress.append(':'); + builder.append(':'); rest--; } - progress.append("&7"); + builder.append("&7"); for (int i = 0; i < rest; i++) { - progress.append(':'); + builder.append(':'); } - progress.append(" - ").append(percentage).append('%'); - return ChatColors.color(progress.toString()); + builder.append(" - ").append(percentage).append('%'); + return ChatColors.color(builder.toString()); } private static short getDurability(ItemStack item, int timeLeft, int max) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java index 2619f02ec..15e9f15fc 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java @@ -73,7 +73,7 @@ public final class NumberUtils { return "0ms"; } - String number = DECIMAL_FORMAT.format(nanoseconds / 1000000.0); + String number = roundDecimalNumber(nanoseconds / 1000000.0); String[] parts = PatternUtils.NUMBER_SEPERATOR.split(number); if (parts.length == 1) { @@ -84,6 +84,10 @@ public final class NumberUtils { } } + public static String roundDecimalNumber(double number) { + return DECIMAL_FORMAT.format(number); + } + public static long getLong(Long value, long defaultValue) { return value == null ? defaultValue : value; }