1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-19 19:25:48 +00:00

More performance and memory improvements

This commit is contained in:
TheBusyBiscuit 2020-11-19 22:20:43 +01:00
parent bb764b7584
commit 15f281504f
27 changed files with 237 additions and 74 deletions

View File

@ -44,6 +44,8 @@
* Performance improvements to Cargo network visualizations
* General performance improvements
* Improved performance for radioactive items
* Memory/GC improvements for the profiler
* Performance improvements for the Fluid Pump
#### Fixes
* Fixed #2448

View File

@ -33,7 +33,7 @@ public class PlayerPreResearchEvent extends Event implements Cancellable {
private final Research research;
private final SlimefunItem slimefunItem;
private boolean cancelled;
@ParametersAreNonnullByDefault
public PlayerPreResearchEvent(Player p, Research research, SlimefunItem slimefunItem) {
Validate.notNull(p, "The Player cannot be null");

View File

@ -49,7 +49,9 @@ public interface DamageableItem extends ItemAttribute {
*/
default void damageItem(@Nonnull Player p, @Nullable ItemStack item) {
if (isDamageable() && item != null && item.getType() != Material.AIR && item.getAmount() > 0) {
if (item.getEnchantments().containsKey(Enchantment.DURABILITY) && Math.random() * 100 <= (60 + Math.floorDiv(40, (item.getEnchantmentLevel(Enchantment.DURABILITY) + 1)))) {
int unbreakingLevel = item.getEnchantmentLevel(Enchantment.DURABILITY);
if (unbreakingLevel > 0 && Math.random() * 100 <= (60 + Math.floorDiv(40, (unbreakingLevel + 1)))) {
return;
}

View File

@ -68,8 +68,11 @@ public abstract class FlexCategory extends Category {
@Override
public final boolean isHidden(@Nonnull Player p) {
// We can stop this method right here.
// We provide a custom method with more parameters for this. See isVisible(...)
/**
* We can stop this method right here.
* We provide a custom method with more parameters for this.
* See isVisible(...)
*/
return false;
}

View File

@ -153,9 +153,11 @@ public class LockedCategory extends Category {
public boolean hasUnlocked(@Nonnull Player p, @Nonnull PlayerProfile profile) {
for (Category category : parents) {
for (SlimefunItem item : category.getItems()) {
// Should probably be replaced with Slimefun.hasUnlocked(...)
// However this will result in better performance because we don't
// request the PlayerProfile everytime
/**
* Should probably be replaced with Slimefun.hasUnlocked(...)
* However this will result in better performance because we don't
* request the PlayerProfile everytime
*/
if (Slimefun.isEnabled(p, item, false) && Slimefun.hasPermission(p, item, false) && !profile.hasUnlocked(item.getResearch())) {
return false;
}

View File

@ -27,7 +27,7 @@ public class SeasonalCategory extends Category {
private final Month month;
/**
* The constructor for a SeasonCategory.
* The constructor for a {@link SeasonalCategory}.
*
* @param key
* The {@link NamespacedKey} that is used to identify this {@link Category}

View File

@ -83,9 +83,12 @@ public class SlimefunCommand implements CommandExecutor, Listener {
sendHelp(sender);
// We could just return true here, but if there's no subcommands, then
// something went horribly wrong anyway. This will also stop sonarcloud
// from nagging about this always returning true...
/**
* We could just return true here, but if there's no subcommands,
* then something went horribly wrong anyway.
* This will also stop sonarcloud from nagging about
* this always returning true...
*/
return !commands.isEmpty();
}

View File

@ -77,6 +77,7 @@ public abstract class SubCommand {
*
* @param sender
* The {@link CommandSender} who requested the description
*
* @return A possibly localized description of this {@link SubCommand}
*/
@Nonnull

View File

@ -108,6 +108,7 @@ public class NetworkManager {
}
Validate.notNull(type, "Type must not be null");
for (Network network : networks) {
if (type.isInstance(network) && network.connectsTo(l)) {
return Optional.of(type.cast(network));

View File

@ -117,6 +117,7 @@ public class Research implements Keyed {
*
* @param p
* The {@link Player} to translate this name for.
*
* @return The localized Name of this {@link Research}.
*/
@Nonnull
@ -197,12 +198,18 @@ public class Research implements Keyed {
* Handle what to do when a {@link Player} clicks on an un-researched item in
* a {@link SlimefunGuideImplementation}.
*
* @param guide The {@link SlimefunGuideImplementation} used.
* @param player The {@link Player} who clicked on the item.
* @param profile The {@link PlayerProfile} of that {@link Player}.
* @param sfItem The {@link SlimefunItem} on which the {@link Player} clicked.
* @param category The {@link Category} where the {@link Player} was.
* @param page The page number of where the {@link Player} was in the {@link Category};
* @param guide
* The {@link SlimefunGuideImplementation} used.
* @param player
* The {@link Player} who clicked on the item.
* @param profile
* The {@link PlayerProfile} of that {@link Player}.
* @param sfItem
* The {@link SlimefunItem} on which the {@link Player} clicked.
* @param category
* The {@link Category} where the {@link Player} was.
* @param page
* The page number of where the {@link Player} was in the {@link Category};
*
*/
@ParametersAreNonnullByDefault
@ -230,6 +237,7 @@ public class Research implements Keyed {
*
* @param p
* The {@link Player} to check
*
* @return Whether that {@link Player} can unlock this {@link Research}
*/
public boolean canUnlock(@Nonnull Player p) {

View File

@ -52,8 +52,10 @@ class GitHubTask implements Runnable {
* the {@link UUID} and received skin inside a local cache {@link File}.
*/
private void grabTextures() {
// Store all queried usernames to prevent 429 responses for pinging the
// same URL twice in one run.
/**
* Store all queried usernames to prevent 429 responses for pinging
* the same URL twice in one run.
*/
Map<String, String> skins = new HashMap<>();
int requests = 0;
@ -77,8 +79,11 @@ class GitHubTask implements Runnable {
}
}
// We only wanna save this if all Connectors finished already
// This will run multiple times but thats okay, this way we get as much data as possible stored
/**
* We only wanna save this if all Connectors finished already.
* This will run multiple times but thats okay, this way we get as much
* data as possible stored.
*/
gitHubService.saveCache();
}

View File

@ -45,7 +45,7 @@ public enum PerformanceRating implements Predicate<Float> {
@Override
public boolean test(@Nullable Float value) {
if (value == null) {
// null will only test true for UNKNOWN
// This way null will only test true for UNKNOWN
return threshold < 0;
}

View File

@ -1,8 +1,11 @@
package io.github.thebusybiscuit.slimefun4.core.services.profiler;
import java.util.Objects;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition;
@ -11,44 +14,129 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
* This represents an entry in our {@link SlimefunProfiler}.
* It is a modification of {@link BlockPosition} to be as memory-efficient as possible.
*
* @author TheBusyBiscuit
*
*/
class ProfiledBlock {
final class ProfiledBlock {
private final BlockPosition position;
/**
* The {@link World} this {@link Block} is in.
* It is fine to keep an actual reference here since this is a throwaway object anyway.
*/
private final World world;
/**
* A {@link Long} representation of our {@link Location} (x, y, z).
*/
private final long position;
/**
* The {@link SlimefunItem} whihc is located at this {@link Location}.
*/
private final SlimefunItem item;
/**
* This creates a new {@link ProfiledBlock} for the given {@link Location} and
* the {@link SlimefunItem} found at this {@link Location}.
*
* @param l
* The {@link Location}
* @param item
* The {@link SlimefunItem} found at that {@link Location}
*/
ProfiledBlock(@Nonnull Location l, @Nonnull SlimefunItem item) {
this.position = new BlockPosition(l);
this.item = item;
}
ProfiledBlock(@Nonnull BlockPosition position, @Nonnull SlimefunItem item) {
this.position = position;
this.world = l.getWorld();
this.position = getLocationAsLong((int) l.getX(), (int) l.getY(), (int) l.getZ());
this.item = item;
}
/**
* This is just a <strong>dummy</strong> constructor.
* Please only use this for comparisons or lookups.
*
* @param b
* A {@link Block}
*/
ProfiledBlock(@Nonnull Block b) {
this.position = new BlockPosition(b);
this.world = b.getWorld();
this.position = getLocationAsLong(b.getX(), b.getY(), b.getZ());
this.item = null;
}
public BlockPosition getPosition() {
return position;
/**
* This compresses our {@link Location} into a long for more efficient memory usage
*
* @param x
* The x value
* @param y
* The y value
* @param z
* The z value
*
* @return A {@link Long} representation of this {@link Location}
*/
private static long getLocationAsLong(int x, int y, int z) {
return ((long) (x & 0x3FFFFFF) << 38) | ((long) (z & 0x3FFFFFF) << 12) | (long) (y & 0xFFF);
}
@Nonnull
public World getWorld() {
return world;
}
/**
* Gets the x for this block.
*
* @return This blocks x coordinate.
*/
public int getX() {
return (int) (this.position >> 38);
}
/**
* Gets the y for this block.
*
* @return This blocks y coordinate.
*/
public int getY() {
return (int) (this.position & 0xFFF);
}
/**
* Gets the z for this block.
*
* @return This blocks z coordinate.
*/
public int getZ() {
return (int) (this.position << 26 >> 38);
}
/**
* Gets the chunks x coordinate for this block.
*
* @return The blocks chunks x coordinate.
*/
public int getChunkX() {
return this.getX() >> 4;
}
/**
* Gets the chunks z coordinate for this block.
*
* @return The blocks chunks z coordinate.
*/
public int getChunkZ() {
return this.getZ() >> 4;
}
@Nonnull
public String getId() {
return item.getId();
}
@Nonnull
public SlimefunAddon getAddon() {
return item.getAddon();
}
@ -56,7 +144,8 @@ class ProfiledBlock {
@Override
public boolean equals(Object obj) {
if (obj instanceof ProfiledBlock) {
return position.equals(((ProfiledBlock) obj).position);
ProfiledBlock block = (ProfiledBlock) obj;
return position == block.position && Objects.equals(world, block.world);
}
return false;
@ -64,7 +153,8 @@ class ProfiledBlock {
@Override
public int hashCode() {
return position.hashCode();
long hilo = world.getUID().getMostSignificantBits() ^ world.getUID().getLeastSignificantBits();
return (int) (position ^ (position >> 32) ^ hilo ^ (hilo >> 32));
}
}

View File

@ -50,6 +50,7 @@ public class SlimefunProfiler {
private final SlimefunThreadFactory threadFactory = new SlimefunThreadFactory(5);
private final ExecutorService executor = Executors.newFixedThreadPool(threadFactory.getThreadCount(), threadFactory);
private final AtomicBoolean running = new AtomicBoolean(false);
private final AtomicInteger queued = new AtomicInteger(0);
@ -234,9 +235,10 @@ public class SlimefunProfiler {
Map<String, Long> map = new HashMap<>();
for (Map.Entry<ProfiledBlock, Long> entry : timings.entrySet()) {
String world = entry.getKey().getPosition().getWorld().getName();
int x = entry.getKey().getPosition().getChunkX();
int z = entry.getKey().getPosition().getChunkZ();
ProfiledBlock block = entry.getKey();
String world = block.getWorld().getName();
int x = block.getChunkX();
int z = block.getChunkZ();
map.merge(world + " (" + x + ',' + z + ')', entry.getValue(), Long::sum);
}
@ -249,9 +251,9 @@ public class SlimefunProfiler {
int blocks = 0;
for (ProfiledBlock block : timings.keySet()) {
String world = block.getPosition().getWorld().getName();
int x = block.getPosition().getChunkX();
int z = block.getPosition().getChunkZ();
String world = block.getWorld().getName();
int x = block.getChunkX();
int z = block.getChunkZ();
if (chunk.equals(world + " (" + x + ',' + z + ')')) {
blocks++;
@ -290,6 +292,7 @@ public class SlimefunProfiler {
protected float getPercentageOfTick() {
float millis = totalElapsedTime / 1000000.0F;
float fraction = (millis * 100.0F) / MAX_TICK_DURATION;
return Math.round((fraction * 100.0F) / 100.0F);
}
@ -311,6 +314,7 @@ public class SlimefunProfiler {
return PerformanceRating.UNKNOWN;
}
@Nonnull
public String getTime() {
return NumberUtils.getAsMillis(totalElapsedTime);
}
@ -330,6 +334,7 @@ public class SlimefunProfiler {
*/
public boolean hasTimings(@Nonnull Block b) {
Validate.notNull("Cannot get timings for a null Block");
return timings.containsKey(new ProfiledBlock(b));
}

View File

@ -2,6 +2,8 @@ package io.github.thebusybiscuit.slimefun4.core.services.profiler;
import java.util.concurrent.ThreadFactory;
import javax.annotation.Nonnull;
/**
* This is our {@link ThreadFactory} for the {@link SlimefunProfiler}.
* It holds the amount of {@link Thread Threads} we dedicate towards our {@link SlimefunProfiler}
@ -16,16 +18,31 @@ final class SlimefunThreadFactory implements ThreadFactory {
private final int threadCount;
/**
* This constructs a new {@link SlimefunThreadFactory} with the given {@link Thread} count.
*
* @param threadCount
* The amount of {@link Thread Threads} to provide to the {@link SlimefunProfiler}
*/
SlimefunThreadFactory(int threadCount) {
this.threadCount = threadCount;
}
/**
* This returns the amount of {@link Thread Threads} we dedicate towards
* the {@link SlimefunProfiler}.
*
* @return The {@link Thread} count
*/
int getThreadCount() {
return threadCount;
}
/**
* This creates a new {@link Thread} for the {@link SlimefunProfiler}.
*/
@Override
public Thread newThread(Runnable runnable) {
public Thread newThread(@Nonnull Runnable runnable) {
return new Thread(runnable, "Slimefun Profiler");
}

View File

@ -215,11 +215,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
ChatComponent component = new ChatComponent(ChatUtils.crop(ChatColor.RED, item.getItemName()) + "\n");
component.setHoverEvent(new HoverEvent(ChatColor.RESET + item.getItemName(), ChatColor.DARK_RED.toString() + ChatColor.BOLD + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", ChatColor.GREEN + "> Click to unlock", "", ChatColor.GRAY + "Cost: " + ChatColor.AQUA.toString() + research.getCost() + " Level(s)"));
component.setClickEvent(new ClickEvent(key, player ->
SlimefunPlugin.runSync(() ->
research.unlockFromGuide(this, player, profile, item, category, page)
)
));
component.setClickEvent(new ClickEvent(key, player -> SlimefunPlugin.runSync(() -> research.unlockFromGuide(this, player, profile, item, category, page))));
items.add(component);
} else {

View File

@ -2,6 +2,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
@ -13,12 +17,12 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.blocks.Vein;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction;
@ -49,6 +53,9 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
private final int[] inputBorder = { 9, 10, 11, 12, 18, 21, 27, 28, 29, 30 };
private final int[] outputBorder = { 14, 15, 16, 17, 23, 26, 32, 33, 34, 35 };
private final ItemStack emptyBucket = new ItemStackWrapper(Material.BUCKET);
@ParametersAreNonnullByDefault
public FluidPump(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
@ -66,16 +73,16 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
});
}
private void constructMenu(BlockMenuPreset preset) {
private void constructMenu(@Nonnull BlockMenuPreset preset) {
for (int i : border) {
preset.addItem(i, new CustomItem(Material.GRAY_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler());
preset.addItem(i, ChestMenuUtils.getBackground(), ChestMenuUtils.getEmptyClickHandler());
}
for (int i : inputBorder) {
preset.addItem(i, new CustomItem(Material.CYAN_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler());
preset.addItem(i, ChestMenuUtils.getInputSlotTexture(), ChestMenuUtils.getEmptyClickHandler());
}
for (int i : outputBorder) {
preset.addItem(i, new CustomItem(Material.ORANGE_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler());
preset.addItem(i, ChestMenuUtils.getOutputSlotTexture(), ChestMenuUtils.getEmptyClickHandler());
}
for (int i : getOutputSlots()) {
@ -114,14 +121,14 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
return 512;
}
protected void tick(Block b) {
protected void tick(@Nonnull Block b) {
Block fluid = b.getRelative(BlockFace.DOWN);
if (fluid.isLiquid() && getCharge(b.getLocation()) >= ENERGY_CONSUMPTION) {
BlockMenu menu = BlockStorage.getInventory(b);
for (int slot : getInputSlots()) {
if (SlimefunUtils.isItemSimilar(menu.getItemInSlot(slot), new ItemStack(Material.BUCKET), true, false)) {
if (SlimefunUtils.isItemSimilar(menu.getItemInSlot(slot), emptyBucket, true, false)) {
ItemStack bucket = getFilledBucket(fluid);
if (!menu.fits(bucket, getOutputSlots())) {
@ -143,11 +150,14 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
}
}
private Block findNextFluid(Block fluid) {
@Nullable
private Block findNextFluid(@Nonnull Block fluid) {
if (fluid.getType() == Material.WATER || fluid.getType() == Material.BUBBLE_COLUMN) {
// With water we can be sure to find an infinite source whenever we go
// further than a block, so we can just remove the water here and save
// ourselves all of that computing...
/**
* With water we can be sure to find an infinite source whenever we
* go further than a block, so we can just remove the water here and
* save ourselves all of that computing...
*/
if (isSource(fluid)) {
return fluid;
}
@ -162,10 +172,12 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
}
}
}
return null;
}
private ItemStack getFilledBucket(Block fluid) {
@Nonnull
private ItemStack getFilledBucket(@Nonnull Block fluid) {
if (fluid.getType() == Material.LAVA) {
return new ItemStack(Material.LAVA_BUCKET);
} else if (fluid.getType() == Material.WATER || fluid.getType() == Material.BUBBLE_COLUMN) {
@ -184,7 +196,7 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
*
* @return Whether that {@link Block} is a liquid and a source {@link Block}.
*/
private boolean isSource(Block block) {
private boolean isSource(@Nonnull Block block) {
if (block.isLiquid()) {
BlockData data = block.getBlockData();

View File

@ -35,6 +35,7 @@ public class TeleporterListener implements Listener {
}
String id = BlockStorage.checkID(e.getClickedBlock());
if (id == null) {
return;
}
@ -55,7 +56,13 @@ public class TeleporterListener implements Listener {
@ParametersAreNonnullByDefault
private boolean isTeleporterPad(String id, Block b, UUID uuid) {
return id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_SHARED.getItemId()) || (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_PERSONAL.getItemId()) && BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(uuid.toString()));
if (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_SHARED.getItemId())) {
return true;
} else if (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_PERSONAL.getItemId())) {
return BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(uuid.toString());
} else {
return false;
}
}
private boolean checkForPylons(@Nonnull Block teleporter) {

View File

@ -7,12 +7,12 @@ import org.bukkit.entity.Player;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
abstract class PlayerTask implements Runnable {
abstract class AbstractPlayerTask implements Runnable {
protected int id;
protected Player p;
PlayerTask(@Nonnull Player p) {
AbstractPlayerTask(@Nonnull Player p) {
this.p = p;
}
@ -36,10 +36,10 @@ abstract class PlayerTask implements Runnable {
}
/**
* This method checks if this {@link PlayerTask} should be continued or cancelled.
* It will also cancel this {@link PlayerTask} if it became invalid.
* This method checks if this {@link AbstractPlayerTask} should be continued or cancelled.
* It will also cancel this {@link AbstractPlayerTask} if it became invalid.
*
* @return Whether this {@link PlayerTask} is still valid
* @return Whether this {@link AbstractPlayerTask} is still valid
*/
protected boolean isValid() {
if (!p.isOnline() || !p.isValid() || p.isDead() || !p.isSneaking()) {

View File

@ -34,8 +34,9 @@ public class CapacitorTextureUpdateTask implements Runnable {
@Override
public void run() {
Block b = l.getBlock();
Material type = b.getType();
if (b.getType() == Material.PLAYER_HEAD || b.getType() == Material.PLAYER_WALL_HEAD) {
if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) {
if (filledPercentage <= 0.25) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_25.getTexture());
} else if (filledPercentage <= 0.5) {

View File

@ -14,7 +14,7 @@ import org.bukkit.util.Vector;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.JetBoots;
public class JetBootsTask extends PlayerTask {
public class JetBootsTask extends AbstractPlayerTask {
private static final float COST = 0.075F;

View File

@ -11,7 +11,7 @@ import org.bukkit.util.Vector;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Jetpack;
public class JetpackTask extends PlayerTask {
public class JetpackTask extends AbstractPlayerTask {
private static final float COST = 0.08F;

View File

@ -13,7 +13,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMa
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
/**
* This {@link PlayerTask} is run when a {@link Player} carries an {@link InfusedMagnet}.
* This {@link AbstractPlayerTask} is run when a {@link Player} carries an {@link InfusedMagnet}.
* It manages the automatic pickup of nearby items.
*
* @author TheBusyBiscuit
@ -21,7 +21,7 @@ import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
* @see InfusedMagnet
*
*/
public class MagnetTask extends PlayerTask {
public class MagnetTask extends AbstractPlayerTask {
private final double radius;

View File

@ -6,7 +6,7 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class ParachuteTask extends PlayerTask {
public class ParachuteTask extends AbstractPlayerTask {
public ParachuteTask(@Nonnull Player p) {
super(p);

View File

@ -83,6 +83,7 @@ public class SlimefunStartupTask implements Runnable {
return true;
}
}
return false;
}

View File

@ -146,8 +146,11 @@ public class TickerTask implements Runnable {
if (item.getBlockTicker().isSynchronized()) {
SlimefunPlugin.getProfiler().scheduleEntries(1);
item.getBlockTicker().update();
// We are inserting a new timestamp because synchronized
// actions are always ran with a 50ms delay (1 game tick)
/**
* We are inserting a new timestamp because synchronized actions
* are always ran with a 50ms delay (1 game tick)
*/
SlimefunPlugin.runSync(() -> {
Block b = l.getBlock();
tickBlock(l, b, item, data, System.nanoTime());

View File

@ -42,6 +42,10 @@ public final class ItemStackWrapper extends ItemStack {
}
}
public ItemStackWrapper(@Nonnull Material material) {
this(new ItemStack(material));
}
@Override
public boolean hasItemMeta() {
return hasItemMeta;