1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-20 11:45:51 +00:00

Merge branch 'master' into fix/sf-machine-hopper

This commit is contained in:
CURVX 2020-11-21 22:21:34 +05:30 committed by GitHub
commit 5e0e9d5490
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
104 changed files with 1194 additions and 406 deletions

View File

@ -30,6 +30,9 @@
* (API) Added PlayerPreResearchEvent
* Added a config option to disable network visualizations
* (API) Added CoolerFeedPlayerEvent
* Added a config option to delete excess cargo network items
* Added an item setting to configure the Wind Staff velocity
* Added an item setting to the Infused Hopper to toggle it with redstone
#### Changes
* Removed 1.13 support
@ -40,6 +43,10 @@
* Magnets can no longer be placed down
* Electromagnets can no longer be placed down
* 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
@ -59,6 +66,17 @@
* Fixed Magician Talisman sometimes drawing invalid enchantments
* Fixed id conflicts for external Enchantment sources (e.g. plugins) for the Magician Talisman settings
* Fixed network visualizers spawning particles for other player heads
* Fixed #2418
* Fixed #2446
* Fixed CoreProtect not recognizing Slimefun blocks getting broken
* Fixed #2447
* Fixed #2558
* Fixed a duplication bug with the Block Placer
* Fixed Slimefun Guide Settings showing "last activity" as a negative number
* Fixed Armor Stands getting damaged/pushed by Explosive Bow
* Fixed Sword of Beheading dropping Zombie/Skeleton Skulls from Zombie/Skeleton subvariants
* Fixed #2518
* Fixed #2421
## Release Candidate 17 (17 Oct 2020)

View File

@ -315,7 +315,7 @@
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.16</artifactId>
<version>0.15.0</version>
<version>0.15.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
@ -337,7 +337,7 @@
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>CS-CoreLib2</artifactId>
<version>0.26</version>
<version>0.27.2</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -381,7 +381,7 @@
<dependency>
<groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId>
<version>2.1.157</version>
<version>2.1.158</version>
<scope>provided</scope>
<exclusions>
<exclusion>

View File

@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
@ -19,12 +20,25 @@ import io.github.thebusybiscuit.cscorelib2.data.ComputedOptional;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
public class PlayerRightClickEvent extends Event {
/**
* The {@link PlayerRightClickEvent} is our custom version of the {@link PlayerInteractEvent}.
* But it is only triggered on right click.
* The main and (almost) sole purpose of this {@link Event} is to cache the {@link SlimefunItem}
* of the {@link ItemStack} and/or {@link Block} involved.
* This allows us (and addons) to efficiently check the used {@link SlimefunItem} without the need
* to do a heavy lookup or item comparison.
*
* @author TheBusyBiscuit
*
*/
public class PlayerRightClickEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
/**
* The original {@link PlayerInteractEvent}.
*/
private final PlayerInteractEvent event;
private final Player player;
private final Optional<ItemStack> itemStack;
private final Optional<Block> clickedBlock;
@ -35,33 +49,45 @@ public class PlayerRightClickEvent extends Event {
private ComputedOptional<SlimefunItem> slimefunItem = ComputedOptional.createNew();
private ComputedOptional<SlimefunItem> slimefunBlock = ComputedOptional.createNew();
private Result itemResult = Result.DEFAULT;
private Result blockResult = Result.DEFAULT;
private Result itemResult;
private Result blockResult;
public PlayerRightClickEvent(@Nonnull PlayerInteractEvent e) {
event = e;
player = e.getPlayer();
clickedBlock = Optional.ofNullable(e.getClickedBlock());
face = e.getBlockFace();
hand = e.getHand();
/**
* This constructs a new {@link PlayerRightClickEvent} based on the original {@link PlayerInteractEvent}.
* The {@link Result} of the original {@link PlayerInteractEvent} will be copied.
*
* @param originalEvent
* The original {@link PlayerInteractEvent}
*/
public PlayerRightClickEvent(@Nonnull PlayerInteractEvent originalEvent) {
super(originalEvent.getPlayer());
if (e.getItem() == null || e.getItem().getType() == Material.AIR || e.getItem().getAmount() == 0) {
event = originalEvent;
clickedBlock = Optional.ofNullable(originalEvent.getClickedBlock());
face = originalEvent.getBlockFace();
hand = originalEvent.getHand();
itemResult = originalEvent.useItemInHand();
blockResult = originalEvent.useInteractedBlock();
if (originalEvent.getItem() == null || originalEvent.getItem().getType() == Material.AIR || originalEvent.getItem().getAmount() == 0) {
itemStack = Optional.empty();
} else {
itemStack = Optional.of(e.getItem());
itemStack = Optional.of(originalEvent.getItem());
}
}
/**
* This returns the original {@link PlayerInteractEvent} that triggered this
* {@link PlayerRightClickEvent}.
*
* @return The original {@link PlayerInteractEvent}
*/
@Nonnull
public PlayerInteractEvent getInteractEvent() {
return event;
}
@Nonnull
public Player getPlayer() {
return player;
}
/**
* This method returns the {@link ItemStack} that was held in the hand of the {@link Player}.
* It will never return null, should there be no {@link ItemStack} then it will return
@ -121,6 +147,10 @@ public class PlayerRightClickEvent extends Event {
return slimefunBlock.getAsOptional();
}
/**
* This method cancels the {@link PlayerRightClickEvent}.
* This will deny the item and block usage.
*/
public void cancel() {
itemResult = Result.DENY;
blockResult = Result.DENY;

View File

@ -113,6 +113,7 @@ public class ItemSetting<T> {
*
* @param c
* The class of data type you want to compare
*
* @return Whether this {@link ItemSetting} stores the given type
*/
public boolean isType(@Nonnull Class<?> c) {
@ -137,14 +138,19 @@ public class ItemSetting<T> {
* @param item
* The {@link SlimefunItem} who called this method
*/
@SuppressWarnings("unchecked")
public void load(@Nonnull SlimefunItem item) {
Validate.notNull(item, "Cannot apply settings for a non-existing SlimefunItem");
SlimefunPlugin.getItemCfg().setDefaultValue(item.getId() + '.' + getKey(), getDefaultValue());
Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey());
if (defaultValue.getClass().isInstance(configuredValue)) {
if (validateInput((T) configuredValue)) {
this.value = (T) configuredValue;
// We can suppress the warning here, we did an isInstance(...) check before!
@SuppressWarnings("unchecked")
T newValue = (T) configuredValue;
if (validateInput(newValue)) {
this.value = newValue;
} else {
Slimefun.getLogger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!");
Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() });

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

@ -10,6 +10,7 @@ import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponen
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
@ -65,14 +66,36 @@ public interface EnergyNetComponent extends ItemAttribute {
* @return The charge stored at that {@link Location}
*/
default int getCharge(@Nonnull Location l) {
// Emergency fallback, this cannot hold a charge, so we'll just return zero
if (!isChargeable()) {
return 0;
}
return getCharge(l, BlockStorage.getLocationInfo(l));
}
/**
* This returns the currently stored charge at a given {@link Location}.
* This is a more performance saving option if you already have a {@link Config}
* object for this {@link Location}.
*
* @param l
* The target {@link Location}
* @param data
* The data at this {@link Location}
*
* @return The charge stored at that {@link Location}
*/
default int getCharge(@Nonnull Location l, @Nonnull Config data) {
Validate.notNull(l, "Location was null!");
Validate.notNull(data, "data was null!");
// Emergency fallback, this cannot hold a charge, so we'll just return zero
if (!isChargeable()) {
return 0;
}
String charge = BlockStorage.getLocationInfo(l, "energy-charge");
String charge = data.getString("energy-charge");
if (charge != null) {
return Integer.parseInt(charge);

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

@ -46,7 +46,7 @@ final class ContributorsMenu {
});
List<Contributor> contributors = new ArrayList<>(SlimefunPlugin.getGitHubService().getContributors().values());
contributors.sort(Comparator.comparingInt(Contributor::index));
contributors.sort(Comparator.comparingInt(Contributor::getPosition));
for (int i = page * 36; i < contributors.size() && i < (page + 1) * 36; i++) {
Contributor contributor = contributors.get(i);

View File

@ -117,7 +117,7 @@ public abstract class MultiBlockMachine extends SlimefunItem implements NotPlace
protected MultiBlockInteractionHandler getInteractionHandler() {
return (p, mb, b) -> {
if (mb.equals(getMultiBlock())) {
if (!isDisabled() && SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES) && Slimefun.hasUnlocked(p, this, true)) {
if (!isDisabled() && SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK) && Slimefun.hasUnlocked(p, this, true)) {
onInteract(p, b);
}

View File

@ -14,6 +14,7 @@ import org.bukkit.Server;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
/**
@ -30,6 +31,7 @@ public class NetworkManager {
private final int maxNodes;
private final boolean enableVisualizer;
private final boolean deleteExcessItems;
private final List<Network> networks = new LinkedList<>();
/**
@ -37,14 +39,29 @@ public class NetworkManager {
*
* @param maxStepSize
* The maximum amount of nodes a {@link Network} can have
* @param enableVisualizer
* Whether the {@link Network} visualizer is enabled
* @param deleteExcessItems
* Whether excess items from a {@link CargoNet} should be voided
*/
public NetworkManager(int maxStepSize, boolean enableVisualizer) {
public NetworkManager(int maxStepSize, boolean enableVisualizer, boolean deleteExcessItems) {
Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!");
this.enableVisualizer = enableVisualizer;
this.deleteExcessItems = deleteExcessItems;
maxNodes = maxStepSize;
}
/**
* This creates a new {@link NetworkManager} with the given capacity.
*
* @param maxStepSize
* The maximum amount of nodes a {@link Network} can have
*/
public NetworkManager(int maxStepSize) {
this(maxStepSize, true, false);
}
/**
* This method returns the limit of nodes a {@link Network} can have.
* This value is read from the {@link Config} file.
@ -64,6 +81,16 @@ public class NetworkManager {
return enableVisualizer;
}
/**
* This returns whether excess items from a {@link CargoNet} should be voided
* instead of being dropped to the ground.
*
* @return Whether to delete excess items
*/
public boolean isItemDeletionEnabled() {
return deleteExcessItems;
}
/**
* This returns a {@link List} of every {@link Network} on the {@link Server}.
*
@ -81,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));
@ -141,8 +169,11 @@ public class NetworkManager {
public void updateAllNetworks(@Nonnull Location l) {
Validate.notNull(l, "The Location cannot be null");
for (Network network : getNetworksFromLocation(l, Network.class)) {
network.markDirty(l);
// No need to create a sublist and loop through it if there are no Networks
if (!networks.isEmpty()) {
for (Network network : getNetworksFromLocation(l, Network.class)) {
network.markDirty(l);
}
}
}

View File

@ -15,6 +15,7 @@ import org.bukkit.block.Block;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
@ -37,6 +38,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
*/
class CargoNetworkTask implements Runnable {
private final NetworkManager manager;
private final CargoNet network;
private final Map<Location, Inventory> inventories = new HashMap<>();
@ -49,6 +51,7 @@ class CargoNetworkTask implements Runnable {
@ParametersAreNonnullByDefault
CargoNetworkTask(CargoNet network, Map<Location, Integer> inputs, Map<Integer, List<Location>> outputs, Set<Location> chestTerminalInputs, Set<Location> chestTerminalOutputs) {
this.network = network;
this.manager = SlimefunPlugin.getNetworkManager();
this.inputs = inputs;
this.outputs = outputs;
@ -115,7 +118,7 @@ class CargoNetworkTask implements Runnable {
// Try to add the item into another available slot then
ItemStack rest = inv.addItem(stack).get(0);
if (rest != null) {
if (rest != null && !manager.isItemDeletionEnabled()) {
// If the item still couldn't be inserted, simply drop it on the ground
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest);
}
@ -126,7 +129,7 @@ class CargoNetworkTask implements Runnable {
if (menu != null) {
if (menu.getItemInSlot(previousSlot) == null) {
menu.replaceExistingItem(previousSlot, stack);
} else {
} else if (!manager.isItemDeletionEnabled()) {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack);
}
}

View File

@ -210,14 +210,14 @@ public class EnergyNet extends Network {
SlimefunItem item = (SlimefunItem) provider;
try {
Config config = BlockStorage.getLocationInfo(loc);
int energy = provider.getGeneratedOutput(loc, config);
Config data = BlockStorage.getLocationInfo(loc);
int energy = provider.getGeneratedOutput(loc, data);
if (provider.isChargeable()) {
energy += provider.getCharge(loc);
energy += provider.getCharge(loc, data);
}
if (provider.willExplode(loc, config)) {
if (provider.willExplode(loc, data)) {
explodedBlocks.add(loc);
BlockStorage.clearBlockInfo(loc);
@ -228,9 +228,9 @@ public class EnergyNet extends Network {
} else {
supply += energy;
}
} catch (Exception | LinkageError t) {
} catch (Exception | LinkageError throwable) {
explodedBlocks.add(loc);
new ErrorReport<>(t, loc, item);
new ErrorReport<>(throwable, loc, item);
}
long time = SlimefunPlugin.getProfiler().closeEntry(loc, item, timestamp);

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

@ -7,7 +7,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.Material;
@ -20,10 +19,7 @@ import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.papermc.lib.PaperLib;
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
@ -70,20 +66,17 @@ public class BlockDataService implements Keyed {
Validate.notNull(b, "The block cannot be null!");
Validate.notNull(value, "The value cannot be null!");
// Due to a bug on older versions, Persistent Data is nullable for non-snapshots
boolean useSnapshot = SlimefunPlugin.getMinecraftVersion().isBefore(MinecraftVersion.MINECRAFT_1_16);
BlockStateSnapshotResult result = PaperLib.getBlockState(b, useSnapshot);
BlockState state = result.getState();
/**
* Don't use PaperLib here, it seems to be quite buggy in block-placing scenarios
* and it would be too tedious to check for individual build versions to circumvent this.
*/
BlockState state = b.getState();
if (state instanceof TileState) {
try {
PersistentDataContainer container = ((TileState) state).getPersistentDataContainer();
container.set(namespacedKey, PersistentDataType.STRING, value);
if (result.isSnapshot()) {
state.update();
}
state.update();
} catch (Exception x) {
Slimefun.getLogger().log(Level.SEVERE, "Please check if your Server Software is up to date!");
@ -100,21 +93,31 @@ public class BlockDataService implements Keyed {
*
* @param b
* The {@link Block} to retrieve data from
*
* @return The stored value
*/
public Optional<String> getBlockData(@Nonnull Block b) {
Validate.notNull(b, "The block cannot be null!");
BlockState state = PaperLib.getBlockState(b, false).getState();
PersistentDataContainer container = getPersistentDataContainer(state);
if (state instanceof TileState) {
PersistentDataContainer container = ((TileState) state).getPersistentDataContainer();
if (container != null) {
return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING));
} else {
return Optional.empty();
}
}
@Nullable
private PersistentDataContainer getPersistentDataContainer(@Nonnull BlockState state) {
if (state instanceof TileState) {
return ((TileState) state).getPersistentDataContainer();
} else {
return null;
}
}
/**
* This method checks whether the given {@link Material} is a Tile Entity.
* This is used to determine whether the {@link Block} produced by this {@link Material}

View File

@ -23,9 +23,22 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/
public class CustomTextureService {
/**
* The {@link Config} object in which the Server Owner can configure the item models.
*/
private final Config config;
/**
* This nullable {@link StringBuffer} represents the "version" of the used item-models file.
* This version is served with our resource pack.
*/
private String version = null;
/**
* This boolean represents whether the file was modified anyway.
* This is equivalent to at least one value being set to a number which
* is not zero!
*/
private boolean modified = false;
/**
@ -110,6 +123,7 @@ public class CustomTextureService {
*/
public int getModelData(@Nonnull String id) {
Validate.notNull(id, "Cannot get the ModelData for 'null'");
return config.getInt(id);
}

View File

@ -140,7 +140,7 @@ public class MetricsService {
}
/**
* This will close the child classloader and mark all the resources held under this no longer
* This will close the child {@link ClassLoader} and mark all the resources held under this no longer
* in use, they will be cleaned up the next GC run.
*/
public void cleanUp() {

View File

@ -24,8 +24,20 @@ import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
*/
public class UpdaterService {
/**
* Our {@link SlimefunPlugin} instance.
*/
private final SlimefunPlugin plugin;
/**
* Our {@link Updater} implementation.
*/
private final Updater updater;
/**
* The {@link SlimefunBranch} we are currently on.
* If this is an official {@link SlimefunBranch}, auto updates will be enabled.
*/
private final SlimefunBranch branch;
/**

View File

@ -38,7 +38,7 @@ public class Contributor {
private final ComputedOptional<String> headTexture = ComputedOptional.createNew();
private Optional<UUID> uuid = Optional.empty();
private boolean locked = false;
private boolean immutable = false;
/**
* This creates a new {@link Contributor} with the given ingame name and GitHub profile.
@ -76,7 +76,7 @@ public class Contributor {
* specified role.
*
* @param role
* The role
* The role of this {@link Contributor}
* @param commits
* The amount of contributions made as that role
*/
@ -84,7 +84,7 @@ public class Contributor {
Validate.notNull(role, "The role cannot be null!");
Validate.isTrue(commits >= 0, "Contributions cannot be negative");
if (!locked || role.startsWith("translator,")) {
if (!immutable || role.startsWith("translator,")) {
contributions.put(role, commits);
}
}
@ -120,6 +120,13 @@ public class Contributor {
return profileLink;
}
/**
* This returns a {@link List} of contributions for this {@link Contributor}.
* Each entry consists of a {@link String} (for the role) and an {@link Integer}
* (for the amount of commits).
*
* @return A {@link List} of contributions for this {@link Contributor}
*/
@Nonnull
public List<Map.Entry<String, Integer>> getContributions() {
List<Map.Entry<String, Integer>> list = new ArrayList<>(contributions.entrySet());
@ -137,6 +144,8 @@ public class Contributor {
* @return The amount of contributions this {@link Contributor} submitted as the given role
*/
public int getContributions(@Nonnull String role) {
Validate.notNull(role, "The role cannot be null!");
return contributions.getOrDefault(role, 0);
}
@ -214,16 +223,35 @@ public class Contributor {
return contributions.values().stream().mapToInt(Integer::intValue).sum();
}
public int index() {
return -getTotalContributions();
}
/**
* This returns the final display name for this {@link Contributor}.
* The display name is basically the GitHub username but if the Minecraft username differs,
* it will be appended in brackets behind the GitHub username.
*
* @return The final display name of this {@link Contributor}.
*/
@Nonnull
public String getDisplayName() {
return ChatColor.GRAY + githubUsername + (!githubUsername.equals(minecraftUsername) ? ChatColor.DARK_GRAY + " (MC: " + minecraftUsername + ")" : "");
}
public void lock() {
locked = true;
/**
* This returns the position on where to order this {@link Contributor}.
* This is just a convenience method for a {@link Comparator}, it is equivalent to
* {@link #getTotalContributions()} multiplied by minus one.
*
* @return The position of this {@link Contributor} in terms for positioning and ordering.
*/
public int getPosition() {
return -getTotalContributions();
}
/**
* This marks this {@link Contributor} as immutable.
* Immutable {@link Contributor Contributors} will no longer be assigned any contributions.
* This is useful when you want to prevent some commits from counting twice.
*/
public void setImmutable() {
immutable = true;
}
}

View File

@ -8,6 +8,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -37,6 +38,7 @@ public class GitHubService {
private boolean logging = false;
private LocalDateTime lastUpdate = LocalDateTime.now();
private int openIssues = 0;
private int pendingPullRequests = 0;
private int publicForks = 0;
@ -56,9 +58,18 @@ public class GitHubService {
loadConnectors(false);
}
/**
* This will start the {@link GitHubService} and run the asynchronous {@link GitHubTask}
* every so often to update its data.
*
* @param plugin
* Our instance of {@link SlimefunPlugin}
*/
public void start(@Nonnull SlimefunPlugin plugin) {
long period = TimeUnit.HOURS.toMillis(1);
GitHubTask task = new GitHubTask(this);
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 80L, 60 * 60 * 20L);
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 80L, period);
}
/**
@ -215,8 +226,16 @@ public class GitHubService {
texturesCache.save();
}
/**
* This returns the cached skin texture for a given username.
*
* @param username
* The minecraft username
*
* @return The cached skin texture for that user (or null)
*/
@Nullable
protected String getCachedTexture(@Nonnull String name) {
return texturesCache.getString(name);
protected String getCachedTexture(@Nonnull String username) {
return texturesCache.getString(username);
}
}

View File

@ -39,17 +39,23 @@ class GitHubTask implements Runnable {
@Override
public void run() {
gitHubService.getConnectors().forEach(GitHubConnector::download);
connectAndCache();
grabTextures();
}
private void connectAndCache() {
gitHubService.getConnectors().forEach(GitHubConnector::download);
}
/**
* This method will pull the skin textures for every {@link Contributor} and store
* 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;
@ -73,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

@ -183,7 +183,7 @@ public class Translators {
Contributor contributor = github.addContributor(minecraftName, "https://github.com/" + username, "translator," + lang.getLanguageId(), 0);
if (lock) {
contributor.lock();
contributor.setImmutable();
}
}

View File

@ -11,7 +11,14 @@ import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
import me.mrCookieSlime.EmeraldEnchants.EnchantmentGuide;
import me.mrCookieSlime.Slimefun.Objects.Category;
/**
* This is the EmeraldEnchants {@link Category}
*
* @deprecated Support for EmeraldEnchants is being faded out
*
*/
@Deprecated
class EmeraldEnchantsCategory extends FlexCategory {

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

@ -42,11 +42,15 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
*/
public class SlimefunProfiler {
// 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
/**
* 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
*/
private static final int MAX_TICK_DURATION = 100;
private final ExecutorService executor = Executors.newFixedThreadPool(5);
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);
@ -139,8 +143,6 @@ public class SlimefunProfiler {
return;
}
// Since we got more than one Thread in our pool,
// blocking this one is (hopefully) completely fine
executor.execute(this::finishReport);
}
@ -151,6 +153,10 @@ public class SlimefunProfiler {
// Wait for all timing results to come in
while (!running.get() && queued.get() > 0) {
try {
/**
* Since we got more than one Thread in our pool,
* blocking this one is (hopefully) completely fine
*/
Thread.sleep(1);
iterations--;
@ -162,6 +168,7 @@ public class SlimefunProfiler {
iterator.next().sendMessage("Your timings report has timed out, we were still waiting for " + queued.get() + " samples to be collected :/");
iterator.remove();
}
return;
}
} catch (InterruptedException e) {
@ -228,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);
}
@ -243,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++;
@ -284,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);
}
@ -305,6 +314,7 @@ public class SlimefunProfiler {
return PerformanceRating.UNKNOWN;
}
@Nonnull
public String getTime() {
return NumberUtils.getAsMillis(totalElapsedTime);
}
@ -324,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

@ -0,0 +1,49 @@
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}
* and provides a naming convention for our {@link Thread Threads}.
*
* @author TheBusyBiscuit
*
* @see SlimefunProfiler
*
*/
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(@Nonnull Runnable runnable) {
return new Thread(runnable, "Slimefun Profiler");
}
}

View File

@ -18,7 +18,7 @@ import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineTier;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactivity;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.StormStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.LoreBuilder;
@ -403,7 +403,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack ELECTRIC_MOTOR = new SlimefunItemStack("ELECTRIC_MOTOR", HeadTexture.MOTOR, "&cElectric Motor");
public static final SlimefunItemStack CARGO_MOTOR = new SlimefunItemStack("CARGO_MOTOR", HeadTexture.MOTOR, "&3Cargo Motor");
public static final SlimefunItemStack SCROLL_OF_DIMENSIONAL_TELEPOSITION = new SlimefunItemStack("SCROLL_OF_DIMENSIONAL_TELEPOSITION", Material.PAPER, "&6Scroll of Dimensional Teleposition", "", "&cThis Scroll is capable of creating", "&ca temporary black Hole which pulls", "&cnearby Entities into itself and sends", "&cthem into another Dimension where", "&ceverything is turned around", "", "&fIn other words: Makes Entities turn by 180 Degrees");
public static final SlimefunItemStack TOME_OF_KNOWLEDGE_SHARING = new SlimefunItemStack("TOME_OF_KNOWLEDGE_SHARING", Material.BOOK, "&6Tome of Knowledge Sharing", "&7Owner: &bNone", "", "&eRight Click&7 to bind this Tome to yourself", "", "", "&eRight Click&7 to obtain all Researches by", "&7the previously assigned Owner");
public static final SlimefunItemStack TOME_OF_KNOWLEDGE_SHARING = new SlimefunItemStack("TOME_OF_KNOWLEDGE_SHARING", Material.ENCHANTED_BOOK, "&6Tome of Knowledge Sharing", "&7Owner: &bNone", "", "&eRight Click&7 to bind this Tome to yourself", "", "", "&eRight Click&7 to obtain all Researches by", "&7the previously assigned Owner");
public static final SlimefunItemStack HARDENED_GLASS = new SlimefunItemStack("HARDENED_GLASS", Material.LIGHT_GRAY_STAINED_GLASS, "&7Hardened Glass", "", "&fWithstands Explosions");
public static final SlimefunItemStack WITHER_PROOF_OBSIDIAN = new SlimefunItemStack("WITHER_PROOF_OBSIDIAN", Material.OBSIDIAN, "&5Wither-Proof Obsidian", "", "&fWithstands Explosions", "&fWithstands Wither Bosses");
public static final SlimefunItemStack WITHER_PROOF_GLASS = new SlimefunItemStack("WITHER_PROOF_GLASS", Material.PURPLE_STAINED_GLASS, "&5Wither-Proof Glass", "", "&fWithstands Explosions", "&fWithstands Wither Bosses");

View File

@ -12,6 +12,7 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
@ -75,9 +76,11 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.FireworksList
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GadgetsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.IronGolemListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PiglinListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener;
@ -85,7 +88,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBoots
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBowListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemConsumeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemInteractListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VampireBladeListener;
@ -117,7 +120,6 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
/**
* This is the main class of Slimefun.
* This is where all the magic starts, take a look around.
* Feel like home.
*
* @author TheBusyBiscuit
*/
@ -162,15 +164,36 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private final BackpackListener backpackListener = new BackpackListener();
private final SlimefunBowListener bowListener = new SlimefunBowListener();
/**
* Our default constructor for {@link SlimefunPlugin}.
*/
public SlimefunPlugin() {
super();
}
/**
* This constructor is invoked in Unit Test environments only.
*
* @param loader
* Our {@link JavaPluginLoader}
* @param description
* A {@link PluginDescriptionFile}
* @param dataFolder
* The data folder
* @param file
* A {@link File} for this {@link Plugin}
*/
@ParametersAreNonnullByDefault
public SlimefunPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
// This is only invoked during a Unit Test
minecraftVersion = MinecraftVersion.UNIT_TEST;
}
/**
* This is called when the {@link Plugin} has been loaded and enabled on a {@link Server}.
*/
@Override
public void onEnable() {
instance = this;
@ -219,7 +242,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
networkSize = 1;
}
networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"));
networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"), config.getBoolean("networks.delete-excess-items"));
// Setting up bStats
new Thread(metricsService::start, "Slimefun Metrics").start();
@ -303,12 +326,83 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
}
}
/**
* This is our start method for a Unit Test environment.
*/
private void onUnitTestStart() {
local = new LocalizationService(this, "", null);
gpsNetwork = new GPSNetwork();
networkManager = new NetworkManager(200, true);
networkManager = new NetworkManager(200);
command.register();
registry.load(config);
loadTags();
}
/**
* This method gets called when the {@link Plugin} gets disabled.
* Most often it is called when the {@link Server} is shutting down or reloading.
*/
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) {
return;
}
// Cancel all tasks from this plugin immediately
Bukkit.getScheduler().cancelTasks(this);
// Finishes all started movements/removals of block data
ticker.halt();
ticker.run();
// Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) {
profile.save();
}
});
// Save all registered Worlds
for (Map.Entry<String, BlockStorage> entry : getRegistry().getWorlds().entrySet()) {
try {
entry.getValue().saveAndRemove();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion());
}
}
for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) {
menu.save();
}
// Create a new backup zip
backupService.run();
metricsService.cleanUp();
/**
* Prevent Memory Leaks for reloads...
* These static Maps should really be removed at some point...
*/
AContainer.processing = null;
AContainer.progress = null;
AGenerator.processing = null;
AGenerator.progress = null;
Reactor.processing = null;
Reactor.progress = null;
instance = null;
/**
* Close all inventories on the server to prevent item dupes
* (Incase some idiot uses /reload)
*/
for (Player p : Bukkit.getOnlinePlayers()) {
p.closeInventory();
}
}
@Nonnull
@ -368,65 +462,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return list;
}
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) {
return;
}
// Cancel all tasks from this plugin immediately
Bukkit.getScheduler().cancelTasks(this);
// Finishes all started movements/removals of block data
ticker.halt();
ticker.run();
// Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) {
profile.save();
}
});
// Save all registered Worlds
for (Map.Entry<String, BlockStorage> entry : getRegistry().getWorlds().entrySet()) {
try {
entry.getValue().saveAndRemove();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion());
}
}
for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) {
menu.save();
}
// Create a new backup zip
backupService.run();
metricsService.cleanUp();
// Prevent Memory Leaks
// These static Maps should be removed at some point...
AContainer.processing = null;
AContainer.progress = null;
AGenerator.processing = null;
AGenerator.progress = null;
Reactor.processing = null;
Reactor.progress = null;
instance = null;
// Close all inventories on the server to prevent item dupes
// (Incase some idiot uses /reload)
for (Player p : Bukkit.getOnlinePlayers()) {
p.closeInventory();
}
}
private void createDirectories() {
String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" };
String[] pluginFolders = { "scripts", "error-reports", "cache/github", "world-settings" };
@ -450,7 +485,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private void registerListeners() {
new SlimefunBootsListener(this);
new SlimefunItemListener(this);
new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this);
new CargoNodeListener(this);
@ -460,6 +495,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new BlockListener(this);
new EnhancedFurnaceListener(this);
new ItemPickupListener(this);
new ItemDropListener(this);
new DeathpointListener(this);
new ExplosionsListener(this);
new DebugFishListener(this);
@ -477,6 +513,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new GrindstoneListener(this);
new CartographyTableListener(this);
new HopperListener(this);
new NetworkListener(this, networkManager);
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) {
new BeeListener(this);
@ -517,7 +554,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private void loadTags() {
for (SlimefunTag tag : SlimefunTag.valuesCache) {
try {
tag.reload();
// Only reload "empty" (or unloaded) Tags
if (tag.isEmpty()) {
tag.reload();
}
} catch (TagMisconfigurationException e) {
getLogger().log(Level.SEVERE, e, () -> "Failed to load Tag: " + tag.name());
}

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

@ -52,7 +52,7 @@ public class StomperBoots extends SlimefunItem {
n.setVelocity(velocity);
// Check if it's not a Player or if PvP is enabled
if (!(n instanceof Player) || (p.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, n.getLocation(), ProtectableAction.PVP))) {
if (!(n instanceof Player) || (p.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, n.getLocation(), ProtectableAction.ATTACK_PLAYER))) {
EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(p, n, DamageCause.ENTITY_ATTACK, fallDamageEvent.getDamage() / 2);
Bukkit.getPluginManager().callEvent(event);

View File

@ -79,21 +79,25 @@ public class BlockPlacer extends SlimefunItem {
Material material = e.getItem().getType();
if (SlimefunTag.SHULKER_BOXES.isTagged(material)) {
// Since vanilla Dispensers can already place Shulker boxes, we
// simply fallback to the vanilla behaviour.
/**
* Since vanilla Dispensers can already place Shulker boxes,
* we simply fallback to the vanilla behaviour.
*/
return;
}
e.setCancelled(true);
if (!material.isBlock() || SlimefunTag.BLOCK_PLACER_IGNORED_MATERIALS.isTagged(material)) {
// Some materials cannot be reliably placed, like beds, it would look
// kinda wonky, so we just ignore these altogether.
// The event has already been cancelled too, so they won't drop.
/**
* Some materials cannot be reliably placed, like beds,
* it would look kinda wonky, so we just ignore these altogether.
* The event has already been cancelled too, so they won't drop.
*/
return;
}
if (facedBlock.isEmpty() && !isBlacklisted(material)) {
if (facedBlock.isEmpty() && !isBlacklisted(material) && dispenser.getInventory().getViewers().isEmpty()) {
SlimefunItem item = SlimefunItem.getByItem(e.getItem());
if (item != null) {
@ -123,9 +127,11 @@ public class BlockPlacer extends SlimefunItem {
String owner = BlockStorage.getLocationInfo(dispenser.getLocation(), "owner");
if (owner == null) {
// If no owner was set, then we will fallback to the previous behaviour:
// Allowing block placers to bypass protection, newly placed Block placers
// will respect protection plugins.
/**
* If no owner was set, then we will fallback to the previous behaviour:
* Allowing block placers to bypass protection, newly placed Block placers
* will respect protection plugins.
*/
return true;
}

View File

@ -83,7 +83,7 @@ public class Composter extends SimpleSlimefunItem<BlockUseHandler> implements Re
Player p = e.getPlayer();
Block b = block.get();
if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES)) {
if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK)) {
ItemStack input = e.getItem();
ItemStack output = getOutput(p, input);

View File

@ -95,7 +95,7 @@ public class Crucible extends SimpleSlimefunItem<BlockUseHandler> implements Rec
Player p = e.getPlayer();
Block b = optional.get();
if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES)) {
if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK)) {
ItemStack input = e.getItem();
Block block = b.getRelative(BlockFace.UP);

View File

@ -65,7 +65,7 @@ abstract class AbstractCargoNode extends SlimefunItem {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

View File

@ -46,7 +46,7 @@ public class ReactorAccessPort extends SlimefunItem {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

View File

@ -80,7 +80,7 @@ public abstract class AbstractEntityAssembler<T extends Entity> extends SimpleSl
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override
@ -183,7 +183,7 @@ public abstract class AbstractEntityAssembler<T extends Entity> extends SimpleSl
return;
}
if (lifetime % 60 == 0 && getCharge(b.getLocation()) >= getEnergyConsumption()) {
if (lifetime % 60 == 0 && getCharge(b.getLocation(), data) >= getEnergyConsumption()) {
BlockMenu menu = BlockStorage.getInventory(b);
boolean hasBody = findResource(menu, getBody(), bodySlots);

View File

@ -86,7 +86,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

View File

@ -53,7 +53,7 @@ public class ElectricSmeltery extends AbstractVanillaBlockInventory {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

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

@ -37,7 +37,7 @@ public class HeatedPressureChamber extends AContainer {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

View File

@ -98,7 +98,7 @@ public abstract class Reactor extends AbstractEnergyProvider {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override

View File

@ -45,6 +45,6 @@ public class GEOScanner extends SimpleSlimefunItem<BlockUseHandler> {
@ParametersAreNonnullByDefault
private boolean hasAccess(Player p, Location l) {
return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.ACCESS_INVENTORIES));
return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.INTERACT_BLOCK));
}
}

View File

@ -43,7 +43,7 @@ public class OilPump extends AContainer implements RecipeDisplayItem {
@Override
public boolean canOpen(Block b, Player p) {
if (!(p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES))) {
if (!(p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK))) {
return false;
}

View File

@ -36,34 +36,47 @@ import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link ElevatorPlate} is a quick way of teleportation.
* You can place multiple {@link ElevatorPlate ElevatorPlates} along the y axis
* to teleport between them.
*
* @author TheBusyBiscuit
*
*/
public class ElevatorPlate extends SimpleSlimefunItem<BlockUseHandler> {
/**
* This is our key for storing the floor name.
*/
private static final String DATA_KEY = "floor";
/**
* This is our {@link Set} of currently teleporting {@link Player Players}.
* It is used to prevent them from triggering the {@link ElevatorPlate} they land on.
*/
private final Set<UUID> users = new HashSet<>();
@ParametersAreNonnullByDefault
public ElevatorPlate(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput);
addItemHandler(onPlace());
}
@Nonnull
private BlockPlaceHandler onPlace() {
return new BlockPlaceHandler(false) {
@Override
public void onPlayerPlace(BlockPlaceEvent e) {
Block b = e.getBlock();
BlockStorage.addBlockInfo(b, DATA_KEY, "&rFloor #0");
BlockStorage.addBlockInfo(b, DATA_KEY, ChatColor.WHITE + "Floor #0");
BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString());
}
};
}
@Nonnull
public Set<UUID> getUsers() {
return users;
}
@Override
public BlockUseHandler getItemHandler() {
return e -> {
@ -172,7 +185,7 @@ public class ElevatorPlate extends SimpleSlimefunItem<BlockUseHandler> {
public void openEditor(Player p, Block b) {
ChestMenu menu = new ChestMenu("Elevator Settings");
menu.addItem(4, new CustomItem(Material.NAME_TAG, "&7Floor Name &e(Click to edit)", "", "&r" + ChatColors.color(BlockStorage.getLocationInfo(b.getLocation(), DATA_KEY))));
menu.addItem(4, new CustomItem(Material.NAME_TAG, "&7Floor Name &e(Click to edit)", "", ChatColor.WHITE + ChatColors.color(BlockStorage.getLocationInfo(b.getLocation(), DATA_KEY))));
menu.addMenuClickHandler(4, (pl, slot, item, action) -> {
pl.closeInventory();
pl.sendMessage("");

View File

@ -43,6 +43,6 @@ public class GPSControlPanel extends SimpleSlimefunItem<BlockUseHandler> {
@ParametersAreNonnullByDefault
private boolean hasAccess(Player p, Location l) {
return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.ACCESS_INVENTORIES));
return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.INTERACT_BLOCK));
}
}

View File

@ -60,7 +60,7 @@ public abstract class GPSTransmitter extends SimpleSlimefunItem<BlockTicker> imp
@Override
public void tick(Block b, SlimefunItem item, Config data) {
int charge = getCharge(b.getLocation());
int charge = getCharge(b.getLocation(), data);
UUID owner = UUID.fromString(BlockStorage.getLocationInfo(b.getLocation(), "owner"));
if (charge >= getEnergyConsumption()) {

View File

@ -1,10 +1,14 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Hopper;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.inventory.ItemStack;
@ -22,15 +26,27 @@ import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link InfusedHopper} is a special kind of {@link Hopper} which teleports any
* neaby {@link Item} to itself.
* The radius can be configured in the config.
*
* @author TheBusyBiscuit
*
* @see InfusedMagnet
*
*/
public class InfusedHopper extends SimpleSlimefunItem<BlockTicker> {
private final ItemSetting<Boolean> silent = new ItemSetting<>("silent", false);
private final ItemSetting<Boolean> toggleable = new ItemSetting<>("toggleable-with-redstone", false);
private final ItemSetting<Double> radius = new DoubleRangeSetting("radius", 0.1, 3.5, Double.MAX_VALUE);
@ParametersAreNonnullByDefault
public InfusedHopper(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemSetting(silent, radius);
addItemSetting(silent, radius, toggleable);
}
@Override
@ -45,17 +61,35 @@ public class InfusedHopper extends SimpleSlimefunItem<BlockTicker> {
return;
}
Location l = b.getLocation().add(0.5, 1.2, 0.5);
boolean sound = false;
double range = radius.getValue();
// Check if this was enabled in the config
if (toggleable.getValue()) {
Hopper hopper = (Hopper) b.getBlockData();
/**
* If the Hopper was disabled by a redstone signal,
* we just don't do anything.
*/
if (!hopper.isEnabled()) {
return;
}
}
Location l = b.getLocation().add(0.5, 1.2, 0.5);
double range = radius.getValue();
boolean playSound = false;
// Check for any nearby Items that can be picked up
for (Entity item : b.getWorld().getNearbyEntities(l, range, range, range, n -> isValidItem(l, n))) {
item.setVelocity(new Vector(0, 0.1, 0));
item.teleport(l);
sound = true;
playSound = true;
}
if (sound && !silent.getValue().booleanValue()) {
/**
* Play a sound if at least one item was teleported and
* the "silent" setting is set to false.
*/
if (playSound && !silent.getValue().booleanValue()) {
b.getWorld().playSound(b.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.BLOCKS, 1F, 2F);
}
}
@ -67,7 +101,7 @@ public class InfusedHopper extends SimpleSlimefunItem<BlockTicker> {
};
}
private boolean isValidItem(Location l, Entity entity) {
private boolean isValidItem(@Nonnull Location l, @Nonnull Entity entity) {
if (entity instanceof Item && entity.isValid()) {
Item item = (Item) entity;
return !SlimefunUtils.hasNoPickupFlag(item) && item.getLocation().distanceSquared(l) > 0.25;

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.GameMode;
import org.bukkit.Location;
@ -14,6 +15,7 @@ import org.bukkit.entity.ZombieVillager;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable;
@ -40,6 +42,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/
public class MagicalZombiePills extends SimpleSlimefunItem<EntityInteractHandler> implements NotPlaceable {
@ParametersAreNonnullByDefault
public MagicalZombiePills(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput);
@ -50,6 +53,12 @@ public class MagicalZombiePills extends SimpleSlimefunItem<EntityInteractHandler
public EntityInteractHandler getItemHandler() {
return (e, item, offhand) -> {
Entity entity = e.getRightClicked();
if (e.isCancelled() || !SlimefunPlugin.getProtectionManager().hasPermission(e.getPlayer(), entity.getLocation(), ProtectableAction.INTERACT_ENTITY)) {
// They don't have permission to use it in this area
return;
}
Player p = e.getPlayer();
if (entity instanceof ZombieVillager) {

View File

@ -4,6 +4,7 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.SoulboundRune;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes;
import java.util.ArrayList;
import java.util.Collection;

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes;
import java.util.Collection;
import java.util.Optional;
@ -14,6 +14,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundItem;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes;
import java.util.concurrent.ThreadLocalRandom;
@ -10,7 +10,9 @@ import org.bukkit.entity.Villager.Profession;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
@ -33,6 +35,11 @@ public class VillagerRune extends SimpleSlimefunItem<EntityInteractHandler> {
@Override
public EntityInteractHandler getItemHandler() {
return (e, item, offhand) -> {
if (e.isCancelled() || !SlimefunPlugin.getProtectionManager().hasPermission(e.getPlayer(), e.getRightClicked().getLocation(), ProtectableAction.INTERACT_ENTITY)) {
// They don't have permission to use it in this area
return;
}
if (e.getRightClicked() instanceof Villager) {
Villager v = (Villager) e.getRightClicked();

View File

@ -0,0 +1,5 @@
/**
* This package holds any implementation of {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem}
* that is an ancient rune with functionality.
*/
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes;

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves;
import java.util.List;
@ -72,7 +72,7 @@ public class StormStaff extends SimpleSlimefunItem<ItemUseHandler> {
Location loc = p.getTargetBlock(null, 30).getLocation();
if (loc.getWorld() != null && loc.getChunk().isLoaded()) {
if (loc.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, loc, ProtectableAction.PVP)) {
if (loc.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, loc, ProtectableAction.ATTACK_PLAYER)) {
e.cancel();
useItem(p, item, loc);
} else {

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
@ -8,6 +8,8 @@ import org.bukkit.entity.Player;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
@ -15,10 +17,20 @@ import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link WindStaff} is a powerful staff which launches the {@link Player} forward when right clicked.
*
* @author TheBusyBiscuit
*
*/
public class WindStaff extends SimpleSlimefunItem<ItemUseHandler> {
private final ItemSetting<Integer> multiplier = new IntRangeSetting("power", 1, 4, Integer.MAX_VALUE);
public WindStaff(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemSetting(multiplier);
}
@Override
@ -27,6 +39,7 @@ public class WindStaff extends SimpleSlimefunItem<ItemUseHandler> {
Player p = e.getPlayer();
if (p.getFoodLevel() >= 2) {
// The isItem() check is here to prevent the MultiTool from consuming hunger
if (isItem(e.getItem()) && p.getGameMode() != GameMode.CREATIVE) {
FoodLevelChangeEvent event = new FoodLevelChangeEvent(p, p.getFoodLevel() - 2);
Bukkit.getPluginManager().callEvent(event);
@ -36,7 +49,7 @@ public class WindStaff extends SimpleSlimefunItem<ItemUseHandler> {
}
}
p.setVelocity(p.getEyeLocation().getDirection().multiply(4));
p.setVelocity(p.getEyeLocation().getDirection().multiply(multiplier.getValue()));
p.getWorld().playSound(p.getLocation(), Sound.ENTITY_TNT_PRIMED, 1, 1);
p.getWorld().playEffect(p.getLocation(), Effect.SMOKE, 1);
p.setFallDistance(0F);

View File

@ -0,0 +1,5 @@
/**
* This package holds any implementation of {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} that is
* considered a "Magical Staff".
*/
package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves;

View File

@ -57,6 +57,9 @@ public class MagicianTalisman extends Talisman {
*
* @param item
* The {@link ItemStack} to find an {@link Enchantment} for
* @param existingEnchantments
* A {@link Set} containing the {@link Enchantment Enchantments} that currently exist on the
* {@link ItemStack}
*
* @return An applicable {@link TalismanEnchantment} or null
*/

View File

@ -21,7 +21,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.PiglinBarterDrop;
import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.VillagerRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.VillagerRune;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;

View File

@ -2,9 +2,13 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons;
import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
@ -33,6 +37,7 @@ public class ExplosiveBow extends SlimefunBow {
private final ItemSetting<Integer> range = new IntRangeSetting("explosion-range", 1, 3, Integer.MAX_VALUE);
@ParametersAreNonnullByDefault
public ExplosiveBow(Category category, SlimefunItemStack item, ItemStack[] recipe) {
super(category, item, recipe);
@ -44,8 +49,9 @@ public class ExplosiveBow extends SlimefunBow {
return (e, target) -> {
target.getWorld().spawnParticle(Particle.EXPLOSION_LARGE, target.getLocation(), 1);
target.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1, 1);
int radius = range.getValue();
Collection<Entity> entites = target.getWorld().getNearbyEntities(target.getLocation(), range.getValue(), range.getValue(), range.getValue(), entity -> entity instanceof LivingEntity && entity.isValid());
Collection<Entity> entites = target.getWorld().getNearbyEntities(target.getLocation(), radius, radius, radius, this::canDamage);
for (Entity nearby : entites) {
LivingEntity entity = (LivingEntity) nearby;
@ -71,6 +77,10 @@ public class ExplosiveBow extends SlimefunBow {
};
}
private boolean canDamage(@Nonnull Entity n) {
return n instanceof LivingEntity && !(n instanceof ArmorStand) && n.isValid();
}
private double calculateDamage(double distanceSquared, double originalDamage) {
if (distanceSquared <= 0.05) {
return originalDamage;

View File

@ -2,6 +2,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
import org.bukkit.Location;
@ -42,6 +45,7 @@ public class SeismicAxe extends SimpleSlimefunItem<ItemUseHandler> implements No
private static final float DAMAGE = 6;
private static final int RANGE = 10;
@ParametersAreNonnullByDefault
public SeismicAxe(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
}
@ -96,7 +100,8 @@ public class SeismicAxe extends SimpleSlimefunItem<ItemUseHandler> implements No
}
}
private Block findGround(Block b) {
@Nonnull
private Block findGround(@Nonnull Block b) {
if (b.getType() == Material.AIR) {
for (int y = 0; y < b.getY(); y++) {
Block block = b.getRelative(0, -y, 0);

View File

@ -3,8 +3,11 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.entity.Creeper;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.entity.Skeleton;
import org.bukkit.entity.WitherSkeleton;
@ -21,6 +24,17 @@ import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link SwordOfBeheading} is a special kind of sword which allows you to obtain
* {@link Zombie}, {@link Skeleton} and {@link Creeper} skulls when killing the respective {@link Monster}.
* Additionally, you can also obtain the head of a {@link Player} by killing them too.
* This sword also allows you to have a higher chance of getting the skull of a {@link WitherSkeleton} too.
*
* All chances are managed by an {@link ItemSetting} and can be configured.
*
* @author TheBusyBiscuit
*
*/
public class SwordOfBeheading extends SimpleSlimefunItem<EntityKillHandler> {
private final ItemSetting<Integer> chanceZombie = new IntRangeSetting("chance.ZOMBIE", 0, 40, 100);
@ -29,6 +43,7 @@ public class SwordOfBeheading extends SimpleSlimefunItem<EntityKillHandler> {
private final ItemSetting<Integer> chanceCreeper = new IntRangeSetting("chance.CREEPER", 0, 40, 100);
private final ItemSetting<Integer> chancePlayer = new IntRangeSetting("chance.PLAYER", 0, 70, 100);
@ParametersAreNonnullByDefault
public SwordOfBeheading(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
@ -40,29 +55,40 @@ public class SwordOfBeheading extends SimpleSlimefunItem<EntityKillHandler> {
return (e, entity, killer, item) -> {
Random random = ThreadLocalRandom.current();
if (e.getEntity() instanceof Zombie) {
switch (e.getEntityType()) {
case ZOMBIE:
if (random.nextInt(100) < chanceZombie.getValue()) {
e.getDrops().add(new ItemStack(Material.ZOMBIE_HEAD));
}
} else if (e.getEntity() instanceof WitherSkeleton) {
if (random.nextInt(100) < chanceWitherSkeleton.getValue()) {
e.getDrops().add(new ItemStack(Material.WITHER_SKELETON_SKULL));
}
} else if (e.getEntity() instanceof Skeleton) {
break;
case SKELETON:
if (random.nextInt(100) < chanceSkeleton.getValue()) {
e.getDrops().add(new ItemStack(Material.SKELETON_SKULL));
}
} else if (e.getEntity() instanceof Creeper) {
break;
case CREEPER:
if (random.nextInt(100) < chanceCreeper.getValue()) {
e.getDrops().add(new ItemStack(Material.CREEPER_HEAD));
}
} else if (e.getEntity() instanceof Player && random.nextInt(100) < chancePlayer.getValue()) {
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
ItemMeta meta = skull.getItemMeta();
((SkullMeta) meta).setOwningPlayer((Player) e.getEntity());
skull.setItemMeta(meta);
break;
case WITHER_SKELETON:
if (random.nextInt(100) < chanceWitherSkeleton.getValue()) {
e.getDrops().add(new ItemStack(Material.WITHER_SKELETON_SKULL));
}
break;
case PLAYER:
if (random.nextInt(100) < chancePlayer.getValue()) {
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
e.getDrops().add(skull);
ItemMeta meta = skull.getItemMeta();
((SkullMeta) meta).setOwningPlayer((Player) e.getEntity());
skull.setItemMeta(meta);
e.getDrops().add(skull);
}
break;
default:
break;
}
};
}

View File

@ -1,5 +1,8 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.weapons;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Sound;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.LivingEntity;
@ -28,6 +31,7 @@ public class VampireBlade extends SlimefunItem {
private static final double HEALING_AMOUNT = 4.0;
private final ItemSetting<Integer> chance = new IntRangeSetting("chance", 0, 45, 100);
@ParametersAreNonnullByDefault
public VampireBlade(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
@ -43,7 +47,7 @@ public class VampireBlade extends SlimefunItem {
return chance.getValue();
}
public void heal(Player p) {
public void heal(@Nonnull Player p) {
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 0.7F, 0.7F);
double health = p.getHealth() + HEALING_AMOUNT;
double maxHealth = p.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();

View File

@ -128,7 +128,7 @@ public class AncientAltarListener implements Listener {
return;
}
if (!SlimefunPlugin.getProtectionManager().hasPermission(p, pedestal, ProtectableAction.ACCESS_INVENTORIES)) {
if (!SlimefunPlugin.getProtectionManager().hasPermission(p, pedestal, ProtectableAction.INTERACT_BLOCK)) {
SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true);
return;
}
@ -162,7 +162,7 @@ public class AncientAltarListener implements Listener {
}
private void useAltar(@Nonnull Block altar, @Nonnull Player p) {
if (!SlimefunPlugin.getProtectionManager().hasPermission(p, altar, ProtectableAction.ACCESS_INVENTORIES)) {
if (!SlimefunPlugin.getProtectionManager().hasPermission(p, altar, ProtectableAction.INTERACT_BLOCK)) {
SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true);
return;
}

View File

@ -22,6 +22,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
@ -152,6 +153,9 @@ public class BlockListener implements Listener {
if (!drops.isEmpty()) {
e.getBlock().setType(Material.AIR);
// Notify plugins like CoreProtect
SlimefunPlugin.getProtectionManager().logAction(e.getPlayer(), e.getBlock(), ProtectableAction.BREAK_BLOCK);
if (e.isDropItems()) {
for (ItemStack drop : drops) {
if (drop != null && drop.getType() != Material.AIR) {

View File

@ -10,15 +10,17 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.items.ItemState;
import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
* The Listener class responsible for a {@link Player} interacting with an {@link Entity}.
* The {@link Listener} responsible for a {@link Player} interacting with an {@link Entity}.
*
* @author Linox
* @author TheBusyBiscuit
*
* @see EntityInteractHandler
*
@ -45,8 +47,17 @@ public class EntityInteractionListener implements Listener {
SlimefunItem sfItem = SlimefunItem.getByItem(itemStack);
if (sfItem != null && Slimefun.hasUnlocked(e.getPlayer(), sfItem, true)) {
sfItem.callItemHandler(EntityInteractHandler.class, handler -> handler.onInteract(e, itemStack, e.getHand() == EquipmentSlot.OFF_HAND));
if (sfItem != null) {
if (Slimefun.hasUnlocked(e.getPlayer(), sfItem, true)) {
sfItem.callItemHandler(EntityInteractHandler.class, handler -> handler.onInteract(e, itemStack, e.getHand() == EquipmentSlot.OFF_HAND));
} else if (sfItem.getState() != ItemState.VANILLA_FALLBACK) {
/**
* If an Item is disabled, we don't want it to fallback to the vanilla behaviour
* unless it is a Vanilla Item of course.
* Related to Issue #2446
*/
e.setCancelled(true);
}
}
}
}

View File

@ -4,13 +4,22 @@ import javax.annotation.Nonnull;
import org.bukkit.ChatColor;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.meta.FireworkMeta;
import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
/**
* This {@link Listener} makes sure that any {@link Firework} caused by a {@link Player}
* unlocking a {@link Research} does not cause damage to be dealt.
*
* @author TheBusyBiscuit
*
*/
public class FireworksListener implements Listener {
public FireworksListener(@Nonnull SlimefunPlugin plugin) {
@ -23,6 +32,12 @@ public class FireworksListener implements Listener {
Firework firework = (Firework) e.getDamager();
FireworkMeta meta = firework.getFireworkMeta();
/**
* We could use Peristent Data for this in the future, but ItemMeta display names
* work pretty reliably too and they don't cause any memory leaks like metadata.
*
* Entity display names do not work either as Firework cannot be named.
*/
if (meta.hasDisplayName() && meta.getDisplayName().equals(ChatColor.GREEN + "Slimefun Research")) {
e.setCancelled(true);
}

View File

@ -19,7 +19,6 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
* This {@link Listener} makes sure that an {@link IronGolem} cannot be healed with
* a {@link SlimefunItem}.
* This fixes Issue 1332.
*
* @author TheBusyBiscuit
*
@ -42,6 +41,7 @@ public class IronGolemListener implements Listener {
item = inv.getItemInOffHand();
}
// Check if the Golem was clicked using an Iron Ingot
if (item != null && item.getType() == Material.IRON_INGOT) {
SlimefunItem sfItem = SlimefunItem.getByItem(item);
@ -49,8 +49,10 @@ public class IronGolemListener implements Listener {
e.setCancelled(true);
SlimefunPlugin.getLocalization().sendMessage(e.getPlayer(), "messages.no-iron-golem-heal");
// This is just there to update the Inventory...
// Somehow cancelling it isn't enough.
/**
* This is just there to update the Inventory...
* Somehow cancelling it isn't enough.
*/
if (e.getHand() == EquipmentSlot.HAND) {
inv.setItemInMainHand(item);
} else if (e.getHand() == EquipmentSlot.OFF_HAND) {
@ -61,4 +63,11 @@ public class IronGolemListener implements Listener {
}
}
// @EventHandler
// public void onIronGolemSpawn(PLEASE_GIMME_AN_EVENT e) {
// if (e.getBlock().getType() == Material.IRON_BLOCK) {
// e.setCancelled(true);
// }
// }
}

View File

@ -0,0 +1,35 @@
package io.github.thebusybiscuit.slimefun4.implementation.listeners;
import javax.annotation.Nonnull;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerDropItemEvent;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler;
/**
* Listens to the {@link PlayerDropItemEvent} to call any {@link ItemDropHandler}.
*
* @author TheBusyBiscuit
*
* @see ItemDropHandler
*/
public class ItemDropListener implements Listener {
public ItemDropListener(@Nonnull SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onItemDrop(PlayerDropItemEvent e) {
for (ItemHandler handler : SlimefunItem.getPublicItemHandlers(ItemDropHandler.class)) {
if (((ItemDropHandler) handler).onItemDrop(e, e.getPlayer(), e.getItemDrop())) {
return;
}
}
}
}

View File

@ -7,56 +7,78 @@ import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset;
import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
public class SlimefunItemListener implements Listener {
/**
* This {@link Listener} listens to the {@link PlayerInteractEvent}.
* It is also responsible for calling our {@link PlayerRightClickEvent} and triggering any
* {@link ItemUseHandler} or {@link BlockUseHandler} for the clicked {@link ItemStack} or {@link Block}.
*
* @author TheBusyBiscuit
* @author Liruxo
*
* @see PlayerRightClickEvent
* @see ItemUseHandler
* @see BlockUseHandler
*
*/
public class SlimefunItemInteractListener implements Listener {
public SlimefunItemListener(@Nonnull SlimefunPlugin plugin) {
public SlimefunItemInteractListener(@Nonnull SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onRightClick(PlayerInteractEvent e) {
if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {
// Exclude the Debug Fish here because it is handled in a seperate Listener
if (SlimefunUtils.isItemSimilar(e.getItem(), SlimefunItems.DEBUG_FISH, true)) {
return;
}
// Fire our custom Event
PlayerRightClickEvent event = new PlayerRightClickEvent(e);
Bukkit.getPluginManager().callEvent(event);
boolean itemUsed = e.getHand() == EquipmentSlot.OFF_HAND;
// Only handle the Item if it hasn't been denied
if (event.useItem() != Result.DENY) {
rightClickItem(e, event, itemUsed);
}
if (!itemUsed && event.useBlock() != Result.DENY && !rightClickBlock(e, event)) {
if (!itemUsed && event.useBlock() != Result.DENY && !rightClickBlock(event)) {
return;
}
/**
* If the original Event was not denied but the custom one was,
* we also want to deny the original one.
* This only applies for non-denied events because we do not want to
* override any protective checks.
*/
if (e.useInteractedBlock() != Result.DENY) {
e.setUseInteractedBlock(event.useBlock());
}
@ -67,7 +89,6 @@ public class SlimefunItemListener implements Listener {
}
}
@Nonnull
@ParametersAreNonnullByDefault
private boolean rightClickItem(PlayerInteractEvent e, PlayerRightClickEvent event, boolean defaultValue) {
Optional<SlimefunItem> optional = event.getSlimefunItem();
@ -84,12 +105,12 @@ public class SlimefunItemListener implements Listener {
}
@ParametersAreNonnullByDefault
private boolean rightClickBlock(PlayerInteractEvent e, PlayerRightClickEvent event) {
private boolean rightClickBlock(PlayerRightClickEvent event) {
Optional<SlimefunItem> optional = event.getSlimefunBlock();
if (optional.isPresent()) {
if (!Slimefun.hasUnlocked(e.getPlayer(), optional.get(), true)) {
e.setCancelled(true);
if (!Slimefun.hasUnlocked(event.getPlayer(), optional.get(), true)) {
event.getInteractEvent().setCancelled(true);
return false;
}
@ -97,10 +118,10 @@ public class SlimefunItemListener implements Listener {
if (!interactable) {
String id = optional.get().getId();
Player p = e.getPlayer();
Player p = event.getPlayer();
if (BlockMenuPreset.isInventory(id)) {
openInventory(p, id, e, event);
openInventory(p, id, event.getInteractEvent().getClickedBlock(), event);
return false;
}
}
@ -110,22 +131,22 @@ public class SlimefunItemListener implements Listener {
}
@ParametersAreNonnullByDefault
private void openInventory(Player p, String id, PlayerInteractEvent e, PlayerRightClickEvent event) {
if (!p.isSneaking() || Material.AIR == event.getItem().getType()) {
e.setCancelled(true);
private void openInventory(Player p, String id, Block clickedBlock, PlayerRightClickEvent event) {
if (!p.isSneaking() || event.getItem().getType() == Material.AIR) {
event.getInteractEvent().setCancelled(true);
if (BlockStorage.hasUniversalInventory(id)) {
UniversalBlockMenu menu = BlockStorage.getUniversalInventory(id);
if (menu.canOpen(e.getClickedBlock(), p)) {
if (menu.canOpen(clickedBlock, p)) {
menu.open(p);
} else {
SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true);
}
} else if (BlockStorage.getStorage(e.getClickedBlock().getWorld()).hasInventory(e.getClickedBlock().getLocation())) {
BlockMenu menu = BlockStorage.getInventory(e.getClickedBlock().getLocation());
} else if (BlockStorage.getStorage(clickedBlock.getWorld()).hasInventory(clickedBlock.getLocation())) {
BlockMenu menu = BlockStorage.getInventory(clickedBlock.getLocation());
if (menu.canOpen(e.getClickedBlock(), p)) {
if (menu.canOpen(clickedBlock, p)) {
menu.open(p);
} else {
SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true);
@ -134,13 +155,4 @@ public class SlimefunItemListener implements Listener {
}
}
@EventHandler
public void onItemDrop(PlayerDropItemEvent e) {
for (ItemHandler handler : SlimefunItem.getPublicItemHandlers(ItemDropHandler.class)) {
if (((ItemDropHandler) handler).onItemDrop(e, e.getPlayer(), e.getItemDrop())) {
return;
}
}
}
}

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

@ -9,6 +9,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
@ -21,6 +22,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
*
* @author VoidAngel
* @author SoSeDiK
* @author CURVX
*
*/
public class BrewingStandListener implements SlimefunCraftingListener {
@ -52,4 +54,10 @@ public class BrewingStandListener implements SlimefunCraftingListener {
}
}
@EventHandler
public void hopperOnBrew(InventoryMoveItemEvent e) {
if (e.getDestination().getType() == InventoryType.BREWING && isUnallowed(e.getItem())) {
e.setCancelled(true);
}
}
}

View File

@ -17,13 +17,8 @@ interface SlimefunCraftingListener extends Listener {
} else {
SlimefunItem sfItem1 = SlimefunItem.getByItem(item1);
SlimefunItem sfItem2 = SlimefunItem.getByItem(item2);
if (isUnallowed(sfItem1) || isUnallowed(sfItem2)) {
return true;
}
return isUnallowed(sfItem1) || isUnallowed(sfItem2);
}
return false;
}
default boolean isUnallowed(@Nullable ItemStack item) {
@ -32,7 +27,7 @@ interface SlimefunCraftingListener extends Listener {
}
SlimefunItem sfItem = SlimefunItem.getByItem(item);
return !(sfItem instanceof VanillaItem) && !sfItem.isDisabled();
return isUnallowed(sfItem);
}
default boolean isUnallowed(@Nullable SlimefunItem item) {

View File

@ -51,7 +51,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.Crucible;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.EnhancedFurnace;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.HardenedGlass;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.HologramProjector;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.InfusedHopper;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RainbowBlock;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.UnplaceableBlock;
@ -128,20 +127,21 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.gps.GPSTransmitte
import io.github.thebusybiscuit.slimefun4.implementation.items.gps.PersonalActivationPlate;
import io.github.thebusybiscuit.slimefun4.implementation.items.gps.Teleporter;
import io.github.thebusybiscuit.slimefun4.implementation.items.gps.TeleporterPylon;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.EnchantmentRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfernalBonemeal;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedHopper;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.KnowledgeFlask;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.KnowledgeTome;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.MagicEyeOfEnder;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.MagicalZombiePills;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundItem;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.StormStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.TelepositionScroll;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.VillagerRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.WaterStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.WindStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.EnchantmentRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.SoulboundRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.VillagerRune;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.WaterStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.WindStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.MagicianTalisman;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.Talisman;
import io.github.thebusybiscuit.slimefun4.implementation.items.medical.Bandage;

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

@ -26,8 +26,10 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.SolarHelmet;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link ArmorTask} is responsible for handling {@link PotionEffect PotionEffects} for
@ -167,8 +169,16 @@ public class ArmorTask implements Runnable {
return false;
}
for (SlimefunItem radioactiveItem : SlimefunPlugin.getRegistry().getRadioactiveItems()) {
if (radioactiveItem.isItem(item) && Slimefun.isEnabled(p, radioactiveItem, true)) {
Set<SlimefunItem> radioactiveItems = SlimefunPlugin.getRegistry().getRadioactiveItems();
ItemStack subject = item;
if (!(item instanceof SlimefunItemStack) && radioactiveItems.size() > 1) {
// Performance optimization to reduce ItemMeta calls
subject = new ItemStackWrapper(item);
}
for (SlimefunItem radioactiveItem : radioactiveItems) {
if (radioactiveItem.isItem(subject) && Slimefun.isEnabled(p, radioactiveItem, true)) {
// If the item is enabled in the world, then make radioactivity do its job
SlimefunPlugin.getLocalization().sendMessage(p, "messages.radiation");

View File

@ -0,0 +1,71 @@
package io.github.thebusybiscuit.slimefun4.implementation.tasks;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
/**
* This task is run whenever a {@link Capacitor} needs to update their texture.
* <strong>This must be executed on the main {@link Server} {@link Thread}!</strong>
*
* @author TheBusyBiscuit
*
*/
public class CapacitorTextureUpdateTask implements Runnable {
/**
* The {@link Location} of the {@link Capacitor}.
*/
private final Location l;
/**
* The level of how "full" this {@link Capacitor} is.
* From 0.0 to 1.0.
*/
private final double filledPercentage;
/**
* This creates a new {@link CapacitorTextureUpdateTask} with the given parameters.
*
* @param l
* The {@link Location} of the {@link Capacitor}
* @param charge
* The amount of charge in this {@link Capacitor}
* @param capacity
* The capacity of this {@link Capacitor}
*/
public CapacitorTextureUpdateTask(@Nonnull Location l, double charge, double capacity) {
Validate.notNull(l, "The Location cannot be null");
this.l = l;
this.filledPercentage = charge / capacity;
}
@Override
public void run() {
Block b = l.getBlock();
Material type = b.getType();
// Ensure that this Block is still a Player 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) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_50.getTexture());
} else if (filledPercentage <= 0.75) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_75.getTexture());
} else {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_100.getTexture());
}
}
}
}

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

@ -9,7 +9,6 @@ import org.bukkit.World;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TeleporterListener;
import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -69,10 +68,6 @@ public class SlimefunStartupTask implements Runnable {
if (isEnabled("PROGRAMMABLE_ANDROID_BUTCHER", "PROGRAMMABLE_ANDROID_2_BUTCHER", "PROGRAMMABLE_ANDROID_3_BUTCHER")) {
new ButcherAndroidListener(plugin);
}
if (isEnabled("ENERGY_REGULATOR", "CARGO_MANAGER")) {
new NetworkListener(plugin, SlimefunPlugin.getNetworkManager());
}
}
private boolean isEnabled(String... itemIds) {
@ -83,6 +78,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

@ -65,6 +65,7 @@ public final class ChatUtils {
*
* @return A human-friendly version of the given {@link String}
*/
@Nonnull
public static String humanize(@Nonnull String string) {
StringBuilder builder = new StringBuilder();
String[] segments = PatternUtils.UNDERSCORE.split(string.toLowerCase(Locale.ROOT));

View File

@ -159,6 +159,9 @@ public enum ColoredMaterial {
// @formatter:on
/**
* This is our {@link List} of {@link Material Materials}, the backbone of this enum.
*/
private final List<Material> list;
/**
@ -175,11 +178,25 @@ public enum ColoredMaterial {
list = Collections.unmodifiableList(Arrays.asList(materials));
}
/**
* This returns an ordered {@link List} of {@link Material Materials}
* that are part o this {@link ColoredMaterial}.
*
* @return An ordered {@link List} of {@link Material Materials}
*/
@Nonnull
public List<Material> asList() {
return list;
}
/**
* This returns the {@link Material} at the given index.
*
* @param index
* The index
*
* @return The {@link Material} at that index
*/
@Nonnull
public Material get(int index) {
Validate.isTrue(index >= 0 && index < 16, "The index must be between 0 and 15 (inclusive).");
@ -187,6 +204,15 @@ public enum ColoredMaterial {
return list.get(index);
}
/**
* This returns the {@link Material} with the given {@link DyeColor}.
*
* @param color
* The {@link DyeColor}
*
* @return The {@link Material} with that {@link DyeColor}
*/
@Nonnull
public Material get(@Nonnull DyeColor color) {
Validate.notNull(color, "Color cannot be null!");

View File

@ -119,18 +119,19 @@ public final class NumberUtils {
* One hour later it will read {@code "1d 1h"}. For values smaller than an hour {@code "< 1h"}
* will be returned instead.
*
* @param start
* The starting {@link LocalDateTime}.
* @param end
* The ending {@link LocalDateTime}.
* @param current
* The current {@link LocalDateTime}.
* @param priorDate
* The {@link LocalDateTime} in the past.
*
* @return The elapsed time as a {@link String}
*/
@Nonnull
public static String getElapsedTime(@Nonnull LocalDateTime start, @Nonnull LocalDateTime end) {
Validate.notNull(start, "Provided start was null");
Validate.notNull(end, "Provided end was null");
long hours = Duration.between(start, end).toHours();
public static String getElapsedTime(@Nonnull LocalDateTime current, @Nonnull LocalDateTime priorDate) {
Validate.notNull(current, "Provided current date was null");
Validate.notNull(priorDate, "Provided past date was null");
long hours = Duration.between(priorDate, current).toHours();
if (hours == 0) {
return "< 1h";

View File

@ -14,7 +14,6 @@ import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.entity.Item;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
@ -24,7 +23,6 @@ import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.cscorelib2.item.ImmutableItemMeta;
import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock;
import io.github.thebusybiscuit.cscorelib2.skull.SkullItem;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.exceptions.PrematureCodeException;
@ -32,6 +30,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.CapacitorTextureUpdateTask;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants;
import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment;
@ -337,23 +336,7 @@ public final class SlimefunUtils {
Validate.notNull(l, "Cannot update a texture for null");
Validate.isTrue(capacity > 0, "Capacity must be greater than zero!");
SlimefunPlugin.runSync(() -> {
Block b = l.getBlock();
if (b.getType() == Material.PLAYER_HEAD || b.getType() == Material.PLAYER_WALL_HEAD) {
double level = (double) charge / capacity;
if (level <= 0.25) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_25.getTexture());
} else if (level <= 0.5) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_50.getTexture());
} else if (level <= 0.75) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_75.getTexture());
} else {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_100.getTexture());
}
}
});
SlimefunPlugin.runSync(new CapacitorTextureUpdateTask(l, charge, capacity));
}
}

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;

View File

@ -300,6 +300,18 @@ public enum SlimefunTag implements Tag<Material> {
}
}
public boolean isEmpty() {
if (!includedMaterials.isEmpty()) {
/**
* Without even needing to generate a Set we can safely
* return false if there are directly included Materials
*/
return false;
} else {
return getValues().isEmpty();
}
}
/**
* This returns a {@link Set} of {@link Tag Tags} which are children of this {@link SlimefunTag},
* these can be other {@link SlimefunTag SlimefunTags} or regular {@link Tag Tags}.

View File

@ -252,6 +252,8 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
* identify all instances of the same {@link AContainer}.
* This way we can add the recipes to all instances of the same machine.
*
* <strong>This method will be deprecated and replaced in the future</strong>
*
* @return The identifier of this machine
*/
@Nonnull
@ -384,11 +386,13 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
Validate.notNull(l, "Can't attempt to take charge from a null location!");
if (isChargeable()) {
if (getCharge(l) < getEnergyConsumption()) {
int charge = getCharge(l);
if (charge < getEnergyConsumption()) {
return false;
}
removeCharge(l, getEnergyConsumption());
setCharge(l, charge - getEnergyConsumption());
return true;
} else {
return true;

View File

@ -63,7 +63,7 @@ public abstract class AGenerator extends AbstractEnergyProvider {
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override
@ -152,7 +152,7 @@ public abstract class AGenerator extends AbstractEnergyProvider {
ChestMenuUtils.updateProgressbar(inv, 22, timeleft, processing.get(l).getTicks(), getProgressBar());
if (isChargeable()) {
int charge = getCharge(l);
int charge = getCharge(l, data);
if (getCapacity() - charge >= getEnergyProduction()) {
progress.put(l, timeleft - 1);

View File

@ -47,6 +47,7 @@ public interface ItemHandler {
*
* @param item
* The {@link SlimefunItem} to validate
*
* @return An {@link Optional} describing the result, it will contain an {@link IncompatibleItemHandlerException}
* should there be an issue
*/

View File

@ -33,7 +33,6 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonWriter;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
@ -677,19 +676,20 @@ public class BlockStorage {
}
}
public static SlimefunItem check(Block block) {
return check(block.getLocation());
@Nullable
public static SlimefunItem check(@Nonnull Block b) {
String id = checkID(b);
return id == null ? null : SlimefunItem.getByID(id);
}
public static SlimefunItem check(Location l) {
if (!hasBlockInfo(l)) {
return null;
}
return SlimefunItem.getByID(getLocationInfo(l, "id"));
@Nullable
public static SlimefunItem check(@Nonnull Location l) {
String id = checkID(l);
return id == null ? null : SlimefunItem.getByID(id);
}
public static String checkID(Block b) {
@Nullable
public static String checkID(@Nonnull Block b) {
if (SlimefunPlugin.getBlockDataService().isTileEntity(b.getType())) {
Optional<String> blockData = SlimefunPlugin.getBlockDataService().getBlockData(b);
@ -714,18 +714,13 @@ public class BlockStorage {
return getLocationInfo(l, "id");
}
public static boolean check(Location l, String slimefunItem) {
if (slimefunItem == null || !hasBlockInfo(l)) {
public static boolean check(@Nonnull Location l, @Nullable String slimefunItem) {
if (slimefunItem == null) {
return false;
}
try {
String id = getLocationInfo(l, "id");
return id != null && id.equalsIgnoreCase(slimefunItem);
} catch (Exception x) {
Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception occurred while checking " + new BlockPosition(l) + " for: \"" + slimefunItem + "\"");
return false;
}
String id = checkID(l);
return id != null && id.equals(slimefunItem);
}
public static boolean isWorldRegistered(String name) {

View File

@ -91,12 +91,14 @@ public class DirtyChestMenu extends ChestMenu {
}
public boolean fits(@Nonnull ItemStack item, int... slots) {
if (getItemInSlot(slots[0]) == null) {
// Very small optimization
return true;
} else {
return InvUtils.fits(toInventory(), new ItemStackWrapper(item), slots);
for (int slot : slots) {
// A small optimization for empty slots
if (getItemInSlot(slot) == null) {
return true;
}
}
return InvUtils.fits(toInventory(), new ItemStackWrapper(item), slots);
}
@Nullable

View File

@ -38,6 +38,7 @@ networks:
max-size: 200
cargo-ticker-delay: 0
enable-visualizer: true
delete-excess-items: false
items:
talismans: true

View File

@ -86,7 +86,7 @@ class TestCauldronListener {
@Test
@DisplayName("Test Cauldron being cancelled with slimefun leather armor")
void testCauldronWithSlimefunLeatherArmor() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CAULDRON_TEST_MOCK_LEATHER", new CustomItem(Material.LEATHER_BOOTS, "&6Mock"));
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CAULDRON_TEST_MOCK_LEATHER", new CustomItem(Material.LEATHER_BOOTS, "&6Mock Leather Armor"));
item.register(plugin);
PlayerInteractEvent event = mockCauldronEvent(item.getItem());

Some files were not shown because too many files have changed in this diff Show More