1
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:
TheBusyBiscuit 2020-07-01 18:20:34 +02:00
parent a4197fe5dd
commit 1016b5fc76
10 changed files with 319 additions and 175 deletions

View File

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

View File

@ -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) {

View File

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

View File

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

View File

@ -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() {

View File

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

View File

@ -41,7 +41,7 @@ public class CargoManager extends SlimefunItem {
@Override
public boolean isSynchronized() {
return true;
return false;
}
}, new BlockUseHandler() {

View File

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

View File

@ -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) {

View File

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