mirror of
https://github.com/StarWishsama/Slimefun4.git
synced 2024-09-19 19:25:48 +00:00
Added better summaries, ratings + more performance improvements
This commit is contained in:
parent
a4197fe5dd
commit
1016b5fc76
@ -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
|
||||
|
@ -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<Integer, List<Location>> output = mapOutputNodes();
|
||||
|
||||
// Chest Terminal Stuff
|
||||
Set<Location> destinations = new HashSet<>();
|
||||
List<Location> 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<Location> chestTerminalInputs = new HashSet<>();
|
||||
Set<Location> chestTerminalOutputs = new HashSet<>();
|
||||
|
||||
Map<Location, Integer> inputs = mapInputNodes(chestTerminalInputs);
|
||||
Map<Integer, List<Location>> outputs = mapOutputNodes(chestTerminalOutputs);
|
||||
|
||||
SlimefunPlugin.getProfiler().newEntry();
|
||||
Slimefun.runSync(() -> run(b, inputs, outputs, chestTerminalInputs, chestTerminalOutputs));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, List<Location>> mapOutputNodes() {
|
||||
private Map<Location, Integer> mapInputNodes(Set<Location> chestTerminalNodes) {
|
||||
Map<Location, Integer> 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<Integer, List<Location>> mapOutputNodes(Set<Location> chestTerminalOutputs) {
|
||||
Map<Integer, List<Location>> output = new HashMap<>();
|
||||
|
||||
List<Location> 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<Location> destinations, Map<Integer, List<Location>> output) {
|
||||
private void run(Block b, Map<Location, Integer> inputs, Map<Integer, List<Location>> outputs, Set<Location> chestTerminalInputs, Set<Location> 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<Location, Integer> inputs = new HashMap<>();
|
||||
Set<Location> 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<Block> 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<Integer, List<Location>> outputNodes) {
|
||||
|
@ -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<Float> {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<String, Long> chunks;
|
||||
private final Map<String, Long> 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<Map.Entry<String, Long>, String> formatter, Stream<Map.Entry<String, Long>> stream) {
|
||||
List<Entry<String, Long>> 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<String, Long> 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<String, Long> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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<CommandSender> iterator = requests.iterator();
|
||||
if (!requests.isEmpty()) {
|
||||
PerformanceSummary summary = new PerformanceSummary(this, totalElapsedTime, timings.size());
|
||||
Iterator<CommandSender> 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<String, Long> getByItem() {
|
||||
protected Map<String, Long> getByItem() {
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
|
||||
for (Map.Entry<ProfiledBlock, Long> entry : timings.entrySet()) {
|
||||
@ -176,7 +164,7 @@ public class SlimefunProfiler {
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Long> getByChunk() {
|
||||
protected Map<String, Long> getByChunk() {
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
|
||||
for (Map.Entry<ProfiledBlock, Long> 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<String, Long> chunks = getByChunk();
|
||||
Map<String, Long> 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<Map.Entry<String, Long>, String> formatter, Stream<Map.Entry<String, Long>> stream) {
|
||||
List<Entry<String, Long>> 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<String, Long> 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<String, Long> 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() {
|
||||
|
@ -30,14 +30,14 @@ public class EnhancedFurnace extends SimpleSlimefunItem<BlockTicker> {
|
||||
|
||||
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<BlockTicker> {
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -41,7 +41,7 @@ public class CargoManager extends SlimefunItem {
|
||||
|
||||
@Override
|
||||
public boolean isSynchronized() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}, new BlockUseHandler() {
|
||||
|
@ -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<Location, Location> movingQueue = new ConcurrentHashMap<>();
|
||||
private final Map<Location, Boolean> deletionQueue = new ConcurrentHashMap<>();
|
||||
private final Map<Location, Integer> buggedBlocks = new ConcurrentHashMap<>();
|
||||
private final Map<BlockPosition, Integer> 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<Location, Integer> bugs = new HashMap<>(buggedBlocks);
|
||||
buggedBlocks.clear();
|
||||
|
||||
Map<Location, Boolean> removals = new HashMap<>(deletionQueue);
|
||||
|
||||
for (Map.Entry<Location, Boolean> entry : removals.entrySet()) {
|
||||
Iterator<Map.Entry<Location, Boolean>> removals = deletionQueue.entrySet().iterator();
|
||||
while (removals.hasNext()) {
|
||||
Map.Entry<Location, Boolean> 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<Location, Location> entry : movingQueue.entrySet()) {
|
||||
Iterator<Map.Entry<Location, Location>> moves = movingQueue.entrySet().iterator();
|
||||
while (moves.hasNext()) {
|
||||
Map.Entry<Location, Location> entry = moves.next();
|
||||
BlockStorage._integrated_moveLocationInfo(entry.getKey(), entry.getValue());
|
||||
moves.remove();
|
||||
}
|
||||
|
||||
movingQueue.clear();
|
||||
|
||||
Iterator<BlockTicker> 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<Location, Integer> 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<Location, Integer> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user