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

Merge branch 'master' into feature/bee-armor

This commit is contained in:
TheBusyBiscuit 2020-12-05 21:43:40 +01:00 committed by GitHub
commit 9504e523f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
174 changed files with 2996 additions and 2035 deletions

View File

@ -1,57 +0,0 @@
---
name: Hacktoberfest Issue
about: "- DO NOT USE - Please post your suggestions on discord and we will create
an issue for you if applicable."
title: ''
labels: Hacktoberfest
assignees: ''
---
<!-- READ ME FIRST -->
<!-- If you wanna submit an idea for Hacktoberfest, please go to our discord server -->
<!-- And post your ideas there, this issue format is for internal purposes only. -->
<!-- If we like your idea, we will happily convert it into an actual issue. -->
This Issue is part of [Hacktoberfest](https://hacktoberfest.digitalocean.com/) - A yearly event which encourages participation in the open-source community. [Sign up on their website](https://hacktoberfest.digitalocean.com/) and submit **four pull requests during october (Oct 1st - Oct 31st)** to any public open-source project on GitHub and earn a limited-edition T-shirt or plant a tree!
## :mag_right: Scope
The following bullet points explain what the scope of this feature should be.
It should give a general idea of what we want, you can of course deviate from this as needed.
<!-- Please list the scope using bullet points below -->
## :anchor: Difficulty
Here is our honest estimate on how difficult (on a scale of 1-5) the implementation may be:
<!-- Please rate on a scale of 1-5 -->
<!-- white_cirlce = empty; large_blue_circle = filled out -->
:white_circle::white_circle::white_circle::white_circle::white_circle:
## :construction: Technical Challenges
These are some challenges which may need to be overcome, we wanna be as transparent as possible, so here is some guidance on what may present itself as an obstacle.
<!-- Please list possible obstacles as bullet points below -->
## :memo: Relevant Classes or Snippets
Here are some classes or code snippets which we think might help you get a better understanding where to look for in this gigantic codebase
<!-- Please list relevant classes or snippets below and include a link! -->
## :book: Useful Resources
If you need help on how to get started, maybe try looking into the following resources!
<!-- List helpful resources below -->
* Hacktoberfest
* [Getting started with Hacktoberfest](https://hacktoberfest.digitalocean.com/details#get-started)
* [Hacktoberfest FAQ](https://hacktoberfest.digitalocean.com/faq)
* GitHub/Open-Source
* [How to contribute to Open-Source](https://opensource.guide/how-to-contribute/)
* [Working with forks](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/working-with-forks)
* [Creating a Pull Request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
* Slimefun
* [Contributing to Slimefun](https://github.com/Slimefun/Slimefun4/blob/master/CONTRIBUTING.md)
* [Code of Conduct](https://github.com/Slimefun/Slimefun4/blob/master/.github/CODE_OF_CONDUCT.md)
<hr>
If you want to work on this, simply comment down below and state your interests! Please also comment again if you have changed your mind. Anyone is allowed to discuss in the comments below, feel free to collaborate and work together :heart: <br>
You can always ask for help here if you get stuck.

View File

@ -1,28 +0,0 @@
name: Changelog Populator
on:
push:
branches:
- master
paths:
- 'CHANGELOG.md'
jobs:
populate:
name: Changelog Generator
runs-on: ubuntu-latest
if: github.repository == 'Slimefun/Slimefun4'
steps:
- name: TOC Generator
uses: technote-space/toc-generator@v2.6.1
with:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
TARGET_PATHS: 'CHANGELOG.md'
TOC_TITLE: '**Table of contents**'
COMMIT_MESSAGE: '[CI skip] Updated Changelog'
COMMIT_NAME: 'TheBusyBot'
CREATE_PR: false
MAX_HEADER_LEVEL: 2
FOLDING: false

View File

@ -2,7 +2,8 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of contents**
- [Release Candidate 18 (TBD)](#release-candidate-18-tbd)
- [Release Candidate 19 (TBD)](#release-candidate-19-tbd)
- [Release Candidate 18 (03 Dec 2020)](#release-candidate-18-03-dec-2020)
- [Release Candidate 17 (17 Oct 2020)](#release-candidate-17-17-oct-2020)
- [Release Candidate 16 (07 Sep 2020)](#release-candidate-16-07-sep-2020)
- [Release Candidate 15 (01 Aug 2020)](#release-candidate-15-01-aug-2020)
@ -23,13 +24,24 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Release Candidate 18 (TBD)
## Release Candidate 19 (TBD)
#### Changes
* Performance optimizations for Cargo networks
* Removed an old version of bStats
## Release Candidate 18 (03 Dec 2020)
#### Additions
* The Smelters Pick now also works on Ancient Debris
* (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
* Added an item setting to prevent Reinforced Spawners from being changed by Spawn Eggs
* Added 4 bricks -> 1 brick block recipe to the Electric Press
#### Changes
* Removed 1.13 support
@ -40,6 +52,12 @@
* 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
* Removed EmeraldEnchants integration
* Memory and performance improvements for ticking blocks
#### Fixes
* Fixed #2448
@ -59,6 +77,23 @@
* 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
* Fixed #2574
* Fixed color in android script downloading screen
* Fixed #2576
* Fixed #2496
* Fixed #2585
* Fixed #2583
## Release Candidate 17 (17 Oct 2020)

View File

@ -32,7 +32,7 @@ Here is a full summary of the differences between the two different versions of
| | development (latest) | "stable" |
| ------------------ | -------- | -------- |
| **Minecraft version(s)** | :video_game: **1.14.\* - 1.16.\*** | :video_game: **1.13.\* - 1.16.\*** |
| **Minecraft version(s)** | :video_game: **1.14.\* - 1.16.\*** | :video_game: **1.14.\* - 1.16.\*** |
| **automatic updates** | :heavy_check_mark: | :heavy_check_mark: |
| **frequent updates** | :heavy_check_mark: | :x: |
| **latest content** | :heavy_check_mark: | :x: |

17
pom.xml
View File

@ -30,7 +30,6 @@
<sonar.organization>slimefun</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.log.level>DEBUG</sonar.log.level>
<sonar.exclusions>src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java</sonar.exclusions>
<sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties>
@ -315,7 +314,7 @@
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.16</artifactId>
<version>0.15.0</version>
<version>0.16.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
@ -329,7 +328,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.0</version>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
@ -337,13 +336,13 @@
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>CS-CoreLib2</artifactId>
<version>0.26</version>
<version>0.27.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -381,7 +380,7 @@
<dependency>
<groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId>
<version>2.1.154</version>
<version>2.1.159</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@ -414,12 +413,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>EmeraldEnchants2</artifactId>
<version>3cd370b5d8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.minebuilders</groupId>
<artifactId>clearlag-core</artifactId>

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.api;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
@ -36,21 +37,45 @@ public enum MinecraftVersion {
* This constant represents an exceptional state in which we were unable
* to identify the Minecraft Version we are using
*/
UNKNOWN("Unknown"),
UNKNOWN("Unknown", true),
/**
* This is a very special state that represents the environment being a Unit
* Test and not an actual running Minecraft Server.
*/
UNIT_TEST("Unit Test Environment");
UNIT_TEST("Unit Test Environment", true);
public static final MinecraftVersion[] valuesCache = values();
private final String name;
private final boolean virtual;
private final String prefix;
/**
* This constructs a new {@link MinecraftVersion} with the given name.
* This constructor forces the {@link MinecraftVersion} to be real.
* It must be a real version of Minecraft.
*
* @param name
* The display name of this {@link MinecraftVersion}
*/
MinecraftVersion(String name) {
this(name, false);
}
/**
* This constructs a new {@link MinecraftVersion} with the given name.
* A virtual {@link MinecraftVersion} (unknown or unit test) is not an actual
* version of Minecraft but rather a state of the {@link Server} software.
*
* @param name
* The display name of this {@link MinecraftVersion}
* @param virtual
* Whether this {@link MinecraftVersion} is virtual
*/
MinecraftVersion(String name, boolean virtual) {
this.name = name;
this.virtual = virtual;
this.prefix = name().replace("MINECRAFT_", "v") + '_';
}
@ -63,6 +88,19 @@ public enum MinecraftVersion {
return name;
}
/**
* This returns whether this {@link MinecraftVersion} is virtual or not.
* A virtual {@link MinecraftVersion} does not actually exist but is rather
* a state of the {@link Server} software used.
* Virtual {@link MinecraftVersion MinecraftVersions} include "UNKNOWN" and
* "UNIT TEST".
*
* @return Whether this {@link MinecraftVersion} is virtual or not
*/
public boolean isVirtual() {
return virtual;
}
/**
* This method checks whether the given version matches with this
* {@link MinecraftVersion}.

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

@ -132,11 +132,16 @@ public abstract class Network {
*
* @param l
* The {@link Location} to check for
*
* @return Whether the given {@link Location} is part of this {@link Network}
*/
public boolean connectsTo(@Nonnull Location l) {
if (regulator.equals(l)) {
return true;
} else {
return connectedLocations.contains(l);
}
}
@Nullable
private NetworkComponent getCurrentClassification(@Nonnull Location l) {

View File

@ -31,7 +31,6 @@ import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.guide.BookSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -85,8 +84,6 @@ public final class SlimefunRegistry {
private final Map<Class<? extends ItemHandler>, Set<ItemHandler>> globalItemHandlers = new HashMap<>();
private final Map<String, SlimefunBlockHandler> blockHandlers = new HashMap<>();
private final Map<String, ItemStack> automatedCraftingChamberRecipes = new HashMap<>();
public void load(@Nonnull Config cfg) {
Validate.notNull(cfg, "The Config cannot be null!");
@ -261,18 +258,6 @@ public final class SlimefunRegistry {
return geoResources;
}
/**
* This method returns a list of recipes for the {@link AutomatedCraftingChamber}
*
* @deprecated This just a really bad way to do this. Someone needs to rewrite this.
*
* @return A list of recipes for the {@link AutomatedCraftingChamber}
*/
@Deprecated
public Map<String, ItemStack> getAutomatedCraftingChamberRecipes() {
return automatedCraftingChamberRecipes;
}
public boolean logDuplicateBlockEntries() {
return logDuplicateBlockEntries;
}

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

@ -7,6 +7,7 @@ import org.bukkit.entity.Bee;
* prevents the damage from.
*
* @author Linox
* @author Seggan
*
* @see ProtectiveArmor
*

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

@ -75,9 +75,11 @@ public class RainbowTickHandler extends BlockTicker {
}
for (Material type : materials) {
// This BlockData is purely virtual and only created on startup, it should have
// no impact on performance, in fact it should save performance as it preloads
// the data but also saves heavy calls for other Materials
/**
* This BlockData is purely virtual and only created on startup, it should have
* no impact on performance, in fact it should save performance as it preloads
* the data but also saves heavy calls for other Materials
*/
if (type.createBlockData() instanceof GlassPane) {
return true;
}
@ -89,8 +91,10 @@ public class RainbowTickHandler extends BlockTicker {
@Override
public void tick(Block b, SlimefunItem item, Config data) {
if (b.getType() == Material.AIR) {
// The block was broken, setting the Material now would result in a
// duplication glitch
/**
* The block was broken, setting the Material now would result in a
* duplication glitch
*/
return;
}

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,9 +169,12 @@ public class NetworkManager {
public void updateAllNetworks(@Nonnull Location l) {
Validate.notNull(l, "The Location cannot be null");
// 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

@ -23,6 +23,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
@ -47,12 +48,13 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
/**
* An abstract super class of {@link CargoNet} that handles interactions with ChestTerminal.
* An abstract super class of {@link CargoNet} that handles
* interactions with ChestTerminal.
*
* @author TheBusyBiscuit
*
*/
abstract class ChestTerminalNetwork extends Network {
abstract class AbstractItemNetwork extends Network {
private static final int[] slots = { 19, 20, 21, 28, 29, 30, 37, 38, 39 };
private static final int[] TERMINAL_SLOTS = { 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 30, 31, 32, 33, 36, 37, 38, 39, 40, 41, 42 };
@ -64,14 +66,23 @@ abstract class ChestTerminalNetwork extends Network {
protected final Set<Location> imports = new HashSet<>();
protected final Set<Location> exports = new HashSet<>();
// This represents a Queue of requests to handle
/**
* This represents a {@link Queue} of requests to handle
*/
private final Queue<ItemRequest> itemRequests = new LinkedList<>();
// This is a cache for the BlockFace a node is facing, so we don't need to request the
// BlockData each time we visit a node
/**
* This is a cache for the {@link BlockFace} a node is facing, so we don't need to
* request the {@link BlockData} each time we visit a node
*/
protected Map<Location, BlockFace> connectorCache = new HashMap<>();
protected ChestTerminalNetwork(Location regulator) {
/**
* This is our cache for the {@link ItemFilter} for each node.
*/
protected Map<Location, ItemFilter> filterCache = new HashMap<>();
protected AbstractItemNetwork(Location regulator) {
super(SlimefunPlugin.getNetworkManager(), regulator);
}
@ -127,7 +138,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(l);
if (target.isPresent()) {
item = CargoUtils.insert(inventories, l.getBlock(), target.get(), item);
item = CargoUtils.insert(this, inventories, l.getBlock(), target.get(), item);
if (item == null) {
terminal.replaceExistingItem(request.getSlot(), null);
@ -159,7 +170,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(l);
if (target.isPresent()) {
ItemStack is = CargoUtils.withdraw(inventories, l.getBlock(), target.get(), item);
ItemStack is = CargoUtils.withdraw(this, inventories, l.getBlock(), target.get(), item);
if (is != null) {
if (stack == null) {
@ -201,7 +212,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(bus);
if (target.isPresent()) {
ItemStackAndInteger stack = CargoUtils.withdraw(inventories, bus.getBlock(), target.get());
ItemStackAndInteger stack = CargoUtils.withdraw(this, inventories, bus.getBlock(), target.get());
if (stack != null) {
menu.replaceExistingItem(17, stack.getItem());
@ -227,7 +238,7 @@ abstract class ChestTerminalNetwork extends Network {
if (menu.getItemInSlot(17) != null) {
Optional<Block> target = getAttachedBlock(bus);
target.ifPresent(block -> menu.replaceExistingItem(17, CargoUtils.insert(inventories, bus.getBlock(), block, menu.getItemInSlot(17))));
target.ifPresent(block -> menu.replaceExistingItem(17, CargoUtils.insert(this, inventories, bus.getBlock(), block, menu.getItemInSlot(17))));
}
if (menu.getItemInSlot(17) == null) {
@ -274,7 +285,7 @@ abstract class ChestTerminalNetwork extends Network {
* found in any provider of the network.
*
* @param providers
* A {@link Set} of providers to this {@link ChestTerminalNetwork}
* A {@link Set} of providers to this {@link AbstractItemNetwork}
*
* @return The time it took to compute this operation
*/
@ -326,10 +337,27 @@ abstract class ChestTerminalNetwork extends Network {
@Override
public void markDirty(@Nonnull Location l) {
connectorCache.remove(l);
markCargoNodeConfigurationDirty(l);
super.markDirty(l);
}
/**
* This will mark the {@link ItemFilter} of the given node dirty.
* It will also invalidate the cached rotation.
*
* @param node
* The {@link Location} of the cargo node
*/
public void markCargoNodeConfigurationDirty(@Nonnull Location node) {
ItemFilter filter = filterCache.get(node);
if (filter != null) {
filter.markDirty();
}
connectorCache.remove(node);
}
@ParametersAreNonnullByDefault
private void updateTerminal(Location l, BlockMenu terminal, int slot, int index, List<ItemStackAndInteger> items) {
if (items.size() > index) {
@ -430,20 +458,20 @@ abstract class ChestTerminalNetwork extends Network {
int stored = Integer.parseInt(data);
for (int slot : blockMenu.getPreset().getSlotsAccessedByItemTransport((DirtyChestMenu) blockMenu, ItemTransportFlow.WITHDRAW, null)) {
ItemStack is = blockMenu.getItemInSlot(slot);
ItemStack stack = blockMenu.getItemInSlot(slot);
if (is != null && CargoUtils.matchesFilter(l.getBlock(), is)) {
if (stack != null && CargoUtils.matchesFilter(this, l.getBlock(), stack)) {
boolean add = true;
for (ItemStackAndInteger item : items) {
if (SlimefunUtils.isItemSimilar(is, item.getItemStackWrapper(), true, false)) {
if (SlimefunUtils.isItemSimilar(stack, item.getItemStackWrapper(), true, false)) {
add = false;
item.add(is.getAmount() + stored);
item.add(stack.getAmount() + stored);
}
}
if (add) {
items.add(new ItemStackAndInteger(is, is.getAmount() + stored));
items.add(new ItemStackAndInteger(stack, stack.getAmount() + stored));
}
}
}
@ -461,7 +489,7 @@ abstract class ChestTerminalNetwork extends Network {
@ParametersAreNonnullByDefault
private void filter(@Nullable ItemStack stack, List<ItemStackAndInteger> items, Location node) {
if (stack != null && CargoUtils.matchesFilter(node.getBlock(), stack)) {
if (stack != null && CargoUtils.matchesFilter(this, node.getBlock(), stack)) {
boolean add = true;
for (ItemStackAndInteger item : items) {
@ -477,4 +505,21 @@ abstract class ChestTerminalNetwork extends Network {
}
}
@Nonnull
protected ItemFilter getItemFilter(@Nonnull Block node) {
Location loc = node.getLocation();
ItemFilter filter = filterCache.get(loc);
if (filter == null) {
ItemFilter newFilter = new ItemFilter(node);
filterCache.put(loc, newFilter);
return newFilter;
} else if (filter.isDirty()) {
filter.update(node);
return filter;
} else {
return filter;
}
}
}

View File

@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
* The {@link CargoNet} is a type of {@link Network} which deals with {@link ItemStack} transportation.
* It is also an extension of {@link ChestTerminalNetwork} which provides methods to deal
* It is also an extension of {@link AbstractItemNetwork} which provides methods to deal
* with the addon ChestTerminal.
*
* @author meiamsome
@ -37,7 +37,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
* @author DNx5
*
*/
public class CargoNet extends ChestTerminalNetwork {
public class CargoNet extends AbstractItemNetwork {
private static final int RANGE = 5;
private static final int TICK_DELAY = SlimefunPlugin.getCfg().getInt("networks.cargo-ticker-delay");

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;
@ -32,11 +33,12 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
*
* @see CargoNet
* @see CargoUtils
* @see ChestTerminalNetwork
* @see AbstractItemNetwork
*
*/
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;
@ -65,8 +68,10 @@ class CargoNetworkTask implements Runnable {
network.handleItemRequests(inventories, chestTerminalInputs, chestTerminalOutputs);
}
// All operations happen here: Everything gets iterated from the Input Nodes.
// (Apart from ChestTerminal Buses)
/**
* All operations happen here: Everything gets iterated from the Input Nodes.
* (Apart from ChestTerminal Buses)
*/
SlimefunItem inputNode = SlimefunItems.CARGO_INPUT_NODE.getItem();
for (Map.Entry<Location, Integer> entry : inputs.entrySet()) {
long nodeTimestamp = System.nanoTime();
@ -89,8 +94,9 @@ class CargoNetworkTask implements Runnable {
SlimefunPlugin.getProfiler().closeEntry(network.getRegulator(), SlimefunItems.CARGO_MANAGER.getItem(), timestamp);
}
@ParametersAreNonnullByDefault
private void routeItems(Location inputNode, Block inputTarget, int frequency, Map<Integer, List<Location>> outputNodes) {
ItemStackAndInteger slot = CargoUtils.withdraw(inventories, inputNode.getBlock(), inputTarget);
ItemStackAndInteger slot = CargoUtils.withdraw(network, inventories, inputNode.getBlock(), inputTarget);
if (slot == null) {
return;
@ -105,17 +111,23 @@ class CargoNetworkTask implements Runnable {
}
if (stack != null) {
insertItem(inputTarget, previousSlot, stack);
}
}
@ParametersAreNonnullByDefault
private void insertItem(Block inputTarget, int previousSlot, ItemStack item) {
Inventory inv = inventories.get(inputTarget.getLocation());
if (inv != null) {
// Check if the original slot hasn't been occupied in the meantime
if (inv.getItem(previousSlot) == null) {
inv.setItem(previousSlot, stack);
inv.setItem(previousSlot, item);
} else {
// Try to add the item into another available slot then
ItemStack rest = inv.addItem(stack).get(0);
ItemStack rest = inv.addItem(item).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);
}
@ -125,10 +137,9 @@ class CargoNetworkTask implements Runnable {
if (menu != null) {
if (menu.getItemInSlot(previousSlot) == null) {
menu.replaceExistingItem(previousSlot, stack);
} else {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack);
}
menu.replaceExistingItem(previousSlot, item);
} else if (!manager.isItemDeletionEnabled()) {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), item);
}
}
}
@ -149,7 +160,7 @@ class CargoNetworkTask implements Runnable {
Optional<Block> target = network.getAttachedBlock(output);
if (target.isPresent()) {
item = CargoUtils.insert(inventories, output.getBlock(), target.get(), item);
item = CargoUtils.insert(network, inventories, output.getBlock(), target.get(), item);
if (item == null) {
break;

View File

@ -1,7 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.networks.cargo;
import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -17,23 +16,21 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
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.DirtyChestMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
final class CargoUtils {
// Whitelist or blacklist slots
private static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 };
/**
* These are the slots where our filter items sit.
*/
static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 };
private CargoUtils() {}
@ -117,7 +114,7 @@ final class CargoUtils {
}
}
static ItemStack withdraw(Map<Location, Inventory> inventories, Block node, Block target, ItemStack template) {
static ItemStack withdraw(AbstractItemNetwork network, Map<Location, Inventory> inventories, Block node, Block target, ItemStack template) {
DirtyChestMenu menu = getChestMenu(target);
if (menu == null) {
@ -125,7 +122,7 @@ final class CargoUtils {
Inventory inventory = inventories.get(target.getLocation());
if (inventory != null) {
return withdrawFromVanillaInventory(node, template, inventory);
return withdrawFromVanillaInventory(network, node, template, inventory);
}
BlockState state = PaperLib.getBlockState(target, false).getState();
@ -133,7 +130,7 @@ final class CargoUtils {
if (state instanceof InventoryHolder) {
inventory = ((InventoryHolder) state).getInventory();
inventories.put(target.getLocation(), inventory);
return withdrawFromVanillaInventory(node, template, inventory);
return withdrawFromVanillaInventory(network, node, template, inventory);
}
}
@ -145,7 +142,7 @@ final class CargoUtils {
for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) {
ItemStack is = menu.getItemInSlot(slot);
if (SlimefunUtils.isItemSimilar(is, wrapper, true) && matchesFilter(node, is)) {
if (SlimefunUtils.isItemSimilar(is, wrapper, true) && matchesFilter(network, node, is)) {
if (is.getAmount() > template.getAmount()) {
is.setAmount(is.getAmount() - template.getAmount());
menu.replaceExistingItem(slot, is.clone());
@ -160,7 +157,7 @@ final class CargoUtils {
return null;
}
static ItemStack withdrawFromVanillaInventory(Block node, ItemStack template, Inventory inv) {
static ItemStack withdrawFromVanillaInventory(AbstractItemNetwork network, Block node, ItemStack template, Inventory inv) {
ItemStack[] contents = inv.getContents();
int[] range = getOutputSlotRange(inv);
int minSlot = range[0];
@ -172,7 +169,7 @@ final class CargoUtils {
// Changes to these ItemStacks are synchronized with the Item in the Inventory
ItemStack itemInSlot = contents[slot];
if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false) && matchesFilter(node, itemInSlot)) {
if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false) && matchesFilter(network, node, itemInSlot)) {
if (itemInSlot.getAmount() > template.getAmount()) {
itemInSlot.setAmount(itemInSlot.getAmount() - template.getAmount());
return template;
@ -187,14 +184,14 @@ final class CargoUtils {
return null;
}
static ItemStackAndInteger withdraw(Map<Location, Inventory> inventories, Block node, Block target) {
static ItemStackAndInteger withdraw(AbstractItemNetwork network, Map<Location, Inventory> inventories, Block node, Block target) {
DirtyChestMenu menu = getChestMenu(target);
if (menu != null) {
for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) {
ItemStack is = menu.getItemInSlot(slot);
if (matchesFilter(node, is)) {
if (matchesFilter(network, node, is)) {
menu.replaceExistingItem(slot, null);
return new ItemStackAndInteger(is, slot);
}
@ -203,7 +200,7 @@ final class CargoUtils {
Inventory inventory = inventories.get(target.getLocation());
if (inventory != null) {
return withdrawFromVanillaInventory(node, inventory);
return withdrawFromVanillaInventory(network, node, inventory);
}
BlockState state = PaperLib.getBlockState(target, false).getState();
@ -211,14 +208,14 @@ final class CargoUtils {
if (state instanceof InventoryHolder) {
inventory = ((InventoryHolder) state).getInventory();
inventories.put(target.getLocation(), inventory);
return withdrawFromVanillaInventory(node, inventory);
return withdrawFromVanillaInventory(network, node, inventory);
}
}
return null;
}
private static ItemStackAndInteger withdrawFromVanillaInventory(Block node, Inventory inv) {
private static ItemStackAndInteger withdrawFromVanillaInventory(AbstractItemNetwork network, Block node, Inventory inv) {
ItemStack[] contents = inv.getContents();
int[] range = getOutputSlotRange(inv);
int minSlot = range[0];
@ -227,7 +224,7 @@ final class CargoUtils {
for (int slot = minSlot; slot < maxSlot; slot++) {
ItemStack is = contents[slot];
if (matchesFilter(node, is)) {
if (matchesFilter(network, node, is)) {
inv.setItem(slot, null);
return new ItemStackAndInteger(is, slot);
}
@ -236,8 +233,8 @@ final class CargoUtils {
return null;
}
static ItemStack insert(Map<Location, Inventory> inventories, Block node, Block target, ItemStack stack) {
if (!matchesFilter(node, stack)) {
static ItemStack insert(AbstractItemNetwork network, Map<Location, Inventory> inventories, Block node, Block target, ItemStack stack) {
if (!matchesFilter(network, node, stack)) {
return stack;
}
@ -338,77 +335,12 @@ final class CargoUtils {
return BlockStorage.getUniversalInventory(block);
}
static boolean matchesFilter(@Nonnull Block block, @Nullable ItemStack item) {
static boolean matchesFilter(@Nonnull AbstractItemNetwork network, @Nonnull Block node, @Nullable ItemStack item) {
if (item == null || item.getType() == Material.AIR) {
return false;
}
// Store the returned Config instance to avoid heavy calls
Config blockData = BlockStorage.getLocationInfo(block.getLocation());
String id = blockData.getString("id");
if (id == null) {
// This should normally not happen but if it does...
// Don't accept any items.
return false;
} else if (id.equals("CARGO_NODE_OUTPUT")) {
// Cargo Output nodes have no filter actually
return true;
}
try {
BlockMenu menu = BlockStorage.getInventory(block.getLocation());
if (menu == null) {
return false;
}
boolean lore = "true".equals(blockData.getString("filter-lore"));
boolean allowByDefault = !"whitelist".equals(blockData.getString("filter-type"));
return matchesFilterList(item, menu, lore, allowByDefault);
} catch (Exception x) {
Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception occurred while trying to filter items for a Cargo Node (" + id + ") at " + new BlockPosition(block));
return false;
}
}
private static boolean matchesFilterList(ItemStack item, BlockMenu menu, boolean respectLore, boolean defaultValue) {
// Little performance optimization:
// First check if there is more than one item to compare, if so
// then we know we should create an ItemStackWrapper, otherwise it would
// be of no benefit to us and just be redundant
int itemsToCompare = 0;
for (int slot : FILTER_SLOTS) {
ItemStack stack = menu.getItemInSlot(slot);
if (stack != null && stack.getType() != Material.AIR) {
itemsToCompare++;
if (itemsToCompare > 1) {
break;
}
}
}
// Check if there are event non-air items
if (itemsToCompare > 0) {
// Only create the Wrapper if its worth it
if (itemsToCompare > 1) {
// Create an itemStackWrapper to save performance
item = new ItemStackWrapper(item);
}
for (int slot : FILTER_SLOTS) {
ItemStack stack = menu.getItemInSlot(slot);
if (SlimefunUtils.isItemSimilar(stack, item, respectLore, false)) {
return !defaultValue;
}
}
}
return defaultValue;
return network.getItemFilter(node).test(item);
}
/**

View File

@ -0,0 +1,191 @@
package io.github.thebusybiscuit.slimefun4.core.networks.cargo;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
/**
* The {@link ItemFilter} is a performance-optimization for our {@link CargoNet}.
* It is a snapshot of a cargo node's configuration.
*
* @author TheBusyBiscuit
*
* @see CargoNet
* @see CargoNetworkTask
*
*/
class ItemFilter implements Predicate<ItemStack> {
/**
* Our {@link List} of items to check against, might be empty.
* This has a maximum capacity of 9.
*/
private final List<ItemStackWrapper> items = new ArrayList<>(9);
/**
* Our default value for this {@link ItemFilter}.
* A default value of {@literal true} will mean that it returns true if no
* match was found. It will deny any items that match.
* A default value of {@literal false} means that it will return false if no
* match was found. Only items that match will make it past this {@link ItemFilter}.
*/
private boolean rejectOnMatch;
/**
* Whether we should also compare the lore.
*/
private boolean checkLore;
/**
* If an {@link ItemFilter} is marked as dirty / outdated, then it will be updated
* on the next tick.
*/
private boolean dirty = false;
/**
* This creates a new {@link ItemFilter} for the given {@link Block}.
* This will copy all settings from that {@link Block} to this filter.
*
* @param b
* The {@link Block}
*/
public ItemFilter(@Nonnull Block b) {
update(b);
}
/**
* This updates or refreshes the {@link ItemFilter} to copy the settings
* from the given {@link Block}. It takes a new snapshot.
*
* @param b
* The {@link Block}
*/
public void update(@Nonnull Block b) {
// Store the returned Config instance to avoid heavy calls
Config blockData = BlockStorage.getLocationInfo(b.getLocation());
String id = blockData.getString("id");
SlimefunItem item = SlimefunItem.getByID(id);
BlockMenu menu = BlockStorage.getInventory(b.getLocation());
if (item == null || menu == null) {
// Don't filter for a non-existing item (safety check)
clear(false);
} else if (id.equals("CARGO_NODE_OUTPUT")) {
// Output Nodes have no filter, allow everything
clear(true);
} else {
this.items.clear();
this.checkLore = Objects.equals(blockData.getString("filter-lore"), "true");
this.rejectOnMatch = !Objects.equals(blockData.getString("filter-type"), "whitelist");
for (int slot : CargoUtils.FILTER_SLOTS) {
ItemStack stack = menu.getItemInSlot(slot);
if (stack != null && stack.getType() != Material.AIR) {
this.items.add(new ItemStackWrapper(stack));
}
}
}
this.dirty = false;
}
/**
* This will clear the {@link ItemFilter} and reject <strong>any</strong>
* {@link ItemStack}.
*
* @param rejectOnMatch
* Whether the item should be rejected on matches
*/
private void clear(boolean rejectOnMatch) {
this.items.clear();
this.checkLore = false;
this.rejectOnMatch = rejectOnMatch;
}
/**
* Whether this {@link ItemFilter} is outdated and needs to be refreshed.
*
* @return Whether the filter is outdated.
*/
public boolean isDirty() {
return this.dirty;
}
/**
* This marks this {@link ItemFilter} as dirty / outdated.
*/
public void markDirty() {
this.dirty = true;
}
@Override
public boolean test(@Nonnull ItemStack item) {
/**
* An empty Filter does not need to be iterated over.
* We can just return our default value in this scenario.
*/
if (items.isEmpty()) {
return rejectOnMatch;
}
// The amount of potential matches with that item.
int potentialMatches = 0;
/*
* This is a first check for materials to see if we might even have any match.
* If there is no potential match then we won't need to perform the quite
* intense operation .getItemMeta()
*/
for (ItemStackWrapper stack : items) {
if (stack.getType() == item.getType()) {
// We found a potential match based on the Material
potentialMatches++;
}
}
if (potentialMatches == 0) {
// If there is no match, we can safely assume the default value
return rejectOnMatch;
} else {
/*
* If there is more than one potential match, create a wrapper to save
* performance on the ItemMeta otherwise just use the item directly.
*/
ItemStack subject = potentialMatches == 1 ? item : new ItemStackWrapper(item);
/*
* If there is only one match, we won't need to create a Wrapper
* and thus only perform .getItemMeta() once
*/
for (ItemStackWrapper stack : items) {
if (SlimefunUtils.isItemSimilar(subject, stack, checkLore, false)) {
/*
* The filter has found a match, we can return the opposite
* of our default value. If we exclude items, this is where we
* would return false. Otherwise we return true.
*/
return !rejectOnMatch;
}
}
// If no particular item was matched, we fallback to our default value.
return rejectOnMatch;
}
}
}

View File

@ -15,7 +15,6 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.api.ErrorReport;
import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent;
@ -23,6 +22,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetProvider;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -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);
@ -238,7 +238,10 @@ public class EnergyNet extends Network {
}
// Remove all generators which have exploded
if (!explodedBlocks.isEmpty()) {
generators.keySet().removeAll(explodedBlocks);
}
return supply;
}
@ -254,10 +257,10 @@ public class EnergyNet extends Network {
private void updateHologram(@Nonnull Block b, double supply, double demand) {
if (demand > supply) {
String netLoss = DoubleHandler.getFancyDouble(Math.abs(supply - demand));
String netLoss = NumberUtils.getCompactDouble(demand - supply);
SimpleHologram.update(b, "&4&l- &c" + netLoss + " &7J &e\u26A1");
} else {
String netGain = DoubleHandler.getFancyDouble(supply - demand);
String netGain = NumberUtils.getCompactDouble(supply - demand);
SimpleHologram.update(b, "&2&l+ &a" + netGain + " &7J &e\u26A1");
}
}

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

@ -77,8 +77,9 @@ public class AutoSavingService {
Set<BlockStorage> worlds = new HashSet<>();
for (World world : Bukkit.getWorlds()) {
if (BlockStorage.isWorldRegistered(world.getName())) {
BlockStorage storage = BlockStorage.getStorage(world);
if (storage != null) {
storage.computeChanges();
if (storage.getChanges() > 0) {

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

@ -165,12 +165,17 @@ public class Translators {
// Translators - Tagalog
addTranslator("sccooottttie", SupportedLanguage.TAGALOG, true);
// Translators - Portuguese
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_PORTUGAL, true);
// Translators - Portuguese (Brazil)
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("yurinogueira", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Sakanas", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("krazybeat", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("FaolanMalcadh", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_BRAZIL, true);
}
@ParametersAreNonnullByDefault
@ -183,7 +188,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

@ -1,32 +0,0 @@
package io.github.thebusybiscuit.slimefun4.core.services.plugins;
import javax.annotation.Nonnull;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
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;
@Deprecated
class EmeraldEnchantsCategory extends FlexCategory {
public EmeraldEnchantsCategory(@Nonnull NamespacedKey key) {
super(key, new CustomItem(Material.ENCHANTED_BOOK, "&2EmeraldEnchants &a(Enchantment Guide)"), 2);
}
@Override
public void open(Player p, PlayerProfile profile, SlimefunGuideLayout layout) {
EnchantmentGuide.open(p);
}
@Override
public boolean isVisible(Player p, PlayerProfile profile, SlimefunGuideLayout layout) {
return true;
}
}

View File

@ -7,7 +7,6 @@ import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
@ -16,7 +15,6 @@ import org.bukkit.plugin.Plugin;
import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.api.Slimefun;
@ -38,7 +36,6 @@ public class ThirdPartyPluginService {
private boolean initialized = false;
private boolean isExoticGardenInstalled = false;
private boolean isChestTerminalInstalled = false;
private boolean isEmeraldEnchantsInstalled = false;
private boolean isMcMMOInstalled = false;
/**
@ -78,13 +75,6 @@ public class ThirdPartyPluginService {
}
}
if (isPluginInstalled("EmeraldEnchants")) {
isEmeraldEnchantsInstalled = true;
Plugin emeraldEnchants = plugin.getServer().getPluginManager().getPlugin("EmeraldEnchants");
FlexCategory category = new EmeraldEnchantsCategory(new NamespacedKey(emeraldEnchants, "enchantment_guide"));
category.register();
}
// WorldEdit Hook to clear Slimefun Data upon //set 0 //cut or any other equivalent
if (isPluginInstalled("WorldEdit")) {
try {
@ -149,10 +139,6 @@ public class ThirdPartyPluginService {
return isChestTerminalInstalled;
}
public boolean isEmeraldEnchantsInstalled() {
return isEmeraldEnchantsInstalled;
}
public Optional<ItemStack> harvestExoticGardenPlant(Block block) {
return exoticGardenIntegration.apply(block);
}

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

@ -20,6 +20,7 @@ import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitScheduler;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
@ -42,12 +43,27 @@ 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 AtomicBoolean running = new AtomicBoolean(false);
/**
* Our internal instance of {@link SlimefunThreadFactory}, it provides the naming
* convention for our {@link Thread} pool and also the count of this pool.
*/
private final SlimefunThreadFactory threadFactory = new SlimefunThreadFactory(2);
/**
* This is our {@link Thread} pool to evaluate timings data.
* We cannot use the {@link BukkitScheduler} here because we need to evaluate
* this data in split seconds.
* So we cannot simply wait until the next server tick for this.
*/
private final ExecutorService executor = Executors.newFixedThreadPool(threadFactory.getThreadCount(), threadFactory);
private final AtomicBoolean isProfiling = new AtomicBoolean(false);
private final AtomicInteger queued = new AtomicInteger(0);
private long totalElapsedTime;
@ -55,11 +71,20 @@ public class SlimefunProfiler {
private final Map<ProfiledBlock, Long> timings = new ConcurrentHashMap<>();
private final Queue<CommandSender> requests = new ConcurrentLinkedQueue<>();
/**
* This method terminates the {@link SlimefunProfiler}.
* We need to call this method when the {@link Server} shuts down to prevent any
* of our {@link Thread Threads} from being kept alive.
*/
public void kill() {
executor.shutdown();
}
/**
* This method starts the profiling, data from previous runs will be cleared.
*/
public void start() {
running.set(true);
isProfiling.set(true);
queued.set(0);
timings.clear();
}
@ -70,7 +95,7 @@ public class SlimefunProfiler {
* @return A timestamp, best fed back into {@link #closeEntry(Location, SlimefunItem, long)}
*/
public long newEntry() {
if (!running.get()) {
if (!isProfiling.get()) {
return 0;
}
@ -89,7 +114,7 @@ public class SlimefunProfiler {
* The amount of entries that should be scheduled. Can be negative
*/
public void scheduleEntries(int amount) {
if (running.get()) {
if (isProfiling.get()) {
queued.getAndAdd(amount);
}
}
@ -132,15 +157,13 @@ public class SlimefunProfiler {
* This stops the profiling.
*/
public void stop() {
running.set(false);
isProfiling.set(false);
if (SlimefunPlugin.instance() == null || !SlimefunPlugin.instance().isEnabled()) {
// Slimefun has been disabled
return;
}
// Since we got more than one Thread in our pool,
// blocking this one is (hopefully) completely fine
executor.execute(this::finishReport);
}
@ -149,8 +172,12 @@ public class SlimefunProfiler {
int iterations = 4000;
// Wait for all timing results to come in
while (!running.get() && queued.get() > 0) {
while (!isProfiling.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 +189,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) {
@ -170,7 +198,7 @@ public class SlimefunProfiler {
}
}
if (running.get() && queued.get() > 0) {
if (isProfiling.get() && queued.get() > 0) {
// Looks like the next profiling has already started, abort!
return;
}
@ -228,9 +256,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 +272,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 +313,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 +335,7 @@ public class SlimefunProfiler {
return PerformanceRating.UNKNOWN;
}
@Nonnull
public String getTime() {
return NumberUtils.getAsMillis(totalElapsedTime);
}
@ -324,6 +355,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;
@ -417,7 +417,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,12 +12,14 @@ 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;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
@ -64,12 +66,13 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeWingsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CargoNodeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DispenserListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraCrashListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EnhancedFurnaceListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EntityInteractionListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener;
@ -77,9 +80,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;
@ -87,7 +92,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;
@ -109,7 +114,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.tasks.SlimefunStartupTa
import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.CSCoreLib;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
@ -118,7 +122,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
*/
@ -163,15 +166,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;
@ -220,7 +244,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();
@ -304,12 +328,87 @@ 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();
// Kill our Profiler Threads
profiler.kill();
// 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();
// Close and unload any resources from our Metrics Service
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
@ -345,8 +444,8 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
getLogger().log(Level.SEVERE, "### Slimefun was not installed correctly!");
getLogger().log(Level.SEVERE, "### You are using the wrong version of Minecraft!");
getLogger().log(Level.SEVERE, "###");
getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", ReflectionUtils.getVersion());
getLogger().log(Level.SEVERE, "### but Slimefun v{0} requires you to be using", getDescription().getVersion());
getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", currentVersion);
getLogger().log(Level.SEVERE, "### but Slimefun {0} requires you to be using", getDescription().getVersion());
getLogger().log(Level.SEVERE, "### Minecraft {0}", String.join(" / ", getSupportedVersions()));
getLogger().log(Level.SEVERE, "#############################################");
return true;
@ -361,7 +460,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
List<String> list = new ArrayList<>();
for (MinecraftVersion version : MinecraftVersion.valuesCache) {
if (version != MinecraftVersion.UNKNOWN) {
if (!version.isVirtual()) {
list.add(version.getName());
}
}
@ -369,65 +468,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" };
@ -449,9 +489,12 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
}
}
/**
* This method registers all of our {@link Listener Listeners}.
*/
private void registerListeners() {
new SlimefunBootsListener(this);
new SlimefunItemListener(this);
new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this);
new CargoNodeListener(this);
@ -461,6 +504,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);
@ -470,19 +514,23 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new EntityInteractionListener(this);
new MobDropListener(this);
new VillagerTradingListener(this);
new ElytraCrashListener(this);
new ElytraImpactListener(this);
new CraftingTableListener(this);
new AnvilListener(this);
new BrewingStandListener(this);
new CauldronListener(this);
new GrindstoneListener(this);
new CartographyTableListener(this);
new ButcherAndroidListener(this);
new NetworkListener(this, networkManager);
// Bees were added in 1.15
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) {
new BeeListener(this);
new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem());
}
// Piglins were added in 1.16
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) {
new PiglinListener(this);
}
@ -518,7 +566,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private void loadTags() {
for (SlimefunTag tag : SlimefunTag.valuesCache) {
try {
// 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());
}
@ -723,8 +774,15 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return instance.isNewlyInstalled;
}
@Nonnull
public static String getCSCoreLibVersion() {
return CSCoreLib.getLib().getDescription().getVersion();
Plugin cscorelib = instance.getServer().getPluginManager().getPlugin("CS-CoreLib");
if (cscorelib == null) {
throw new IllegalStateException("CS-CoreLib is not installed.");
} else {
return cscorelib.getDescription().getVersion();
}
}
@Override

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

@ -496,6 +496,7 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock,
menu.open(p);
}
@Nonnull
protected List<Instruction> getValidScriptInstructions() {
List<Instruction> list = new ArrayList<>();
@ -593,8 +594,9 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock,
}
}
public void registerFuelType(MachineFuel fuel) {
public void registerFuelType(@Nonnull MachineFuel fuel) {
Validate.notNull(fuel, "Cannot register null as a Fuel type");
fuelTypes.add(fuel);
}

View File

@ -151,7 +151,7 @@ public final class Script {
@Nonnull
private String getScriptRatingPercentage() {
float percentage = getRating();
return NumberUtils.getColorFromPercentage(percentage) + String.valueOf(percentage) + ChatColor.RESET + "% ";
return NumberUtils.getColorFromPercentage(percentage) + String.valueOf(percentage) + ChatColor.WHITE + "% ";
}
/**

View File

@ -12,7 +12,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem;
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType;
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraCrashListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -22,7 +22,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*
* @author Seggan
*
* @see ElytraCrashListener
* @see ElytraImpactListener
*/
public class ElytraCap extends SlimefunArmorPiece implements DamageableItem, ProtectiveArmor {

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.armor;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Jetpack;
@ -24,6 +26,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/
public class Parachute extends SlimefunItem {
@ParametersAreNonnullByDefault
public Parachute(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, RecipeType.ENHANCED_CRAFTING_TABLE, recipe);
}

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

@ -0,0 +1,113 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* This is a parent class for the {@link BrokenSpawner} and {@link RepairedSpawner}
* to provide some utility methods.
*
* @author TheBusyBiscuit
*
* @see BrokenSpawner
* @see RepairedSpawner
*
*/
public abstract class AbstractMonsterSpawner extends SlimefunItem {
@ParametersAreNonnullByDefault
AbstractMonsterSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
}
/**
* This method tries to obtain an {@link EntityType} from a given {@link ItemStack}.
* The provided {@link ItemStack} must be a {@link RepairedSpawner} item.
*
* @param item
* The {@link ItemStack} to extract the {@link EntityType} from
*
* @return An {@link Optional} describing the result
*/
@Nonnull
public Optional<EntityType> getEntityType(@Nonnull ItemStack item) {
Validate.notNull(item, "The Item cannot be null");
ItemMeta meta = item.getItemMeta();
// We may want to update this in the future to also make use of the BlockStateMeta
for (String line : meta.getLore()) {
if (ChatColor.stripColor(line).startsWith("Type: ") && !line.contains("<Type>")) {
EntityType type = EntityType.valueOf(ChatColor.stripColor(line).replace("Type: ", "").replace(' ', '_').toUpperCase(Locale.ROOT));
return Optional.of(type);
}
}
return Optional.empty();
}
/**
* This method returns a finished {@link ItemStack} of this {@link SlimefunItem}, modified
* to hold and represent the given {@link EntityType}.
* It updates the lore and {@link BlockStateMeta} to reflect the specified {@link EntityType}.
*
* @param type
* The {@link EntityType} to apply
*
* @return An {@link ItemStack} for this {@link SlimefunItem} holding that {@link EntityType}
*/
@Nonnull
public ItemStack getItemForEntityType(@Nonnull EntityType type) {
Validate.notNull(type, "The EntityType cannot be null");
ItemStack item = getItem().clone();
ItemMeta meta = item.getItemMeta();
// Fixes #2583 - Proper NBT handling of Spawners
if (meta instanceof BlockStateMeta) {
BlockStateMeta stateMeta = (BlockStateMeta) meta;
BlockState state = stateMeta.getBlockState();
if (state instanceof CreatureSpawner) {
((CreatureSpawner) state).setSpawnedType(type);
}
stateMeta.setBlockState(state);
}
// Setting the lore to indicate the Type visually
List<String> lore = meta.getLore();
for (int i = 0; i < lore.size(); i++) {
if (lore.get(i).contains("<Type>")) {
lore.set(i, lore.get(i).replace("<Type>", ChatUtils.humanize(type.name())));
break;
}
}
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
}

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

@ -1,7 +1,13 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -17,10 +23,18 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see RepairedSpawner
*
*/
public class BrokenSpawner extends UnplaceableBlock {
public class BrokenSpawner extends AbstractMonsterSpawner implements NotPlaceable {
@ParametersAreNonnullByDefault
public BrokenSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemHandler(onRightClick());
}
@Nonnull
private ItemUseHandler onRightClick() {
return PlayerRightClickEvent::cancel;
}
}

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

@ -2,20 +2,21 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import org.bukkit.ChatColor;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -28,14 +29,32 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see BrokenSpawner
*
*/
public class RepairedSpawner extends SimpleSlimefunItem<BlockPlaceHandler> {
public class RepairedSpawner extends AbstractMonsterSpawner {
private final ItemSetting<Boolean> allowSpawnEggs = new ItemSetting<>("allow-spawn-eggs", true);
@ParametersAreNonnullByDefault
public RepairedSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemSetting(allowSpawnEggs);
addItemHandler(onInteract());
addItemHandler(onPlace());
}
@Override
public BlockPlaceHandler getItemHandler() {
@Nonnull
private BlockUseHandler onInteract() {
return e -> {
if (!allowSpawnEggs.getValue() && SlimefunTag.SPAWN_EGGS.isTagged(e.getItem().getType())) {
// Disallow spawn eggs from being used on Reinforced Spawners if disabled
e.cancel();
}
};
}
@Nonnull
private BlockPlaceHandler onPlace() {
return new BlockPlaceHandler(true) {
@Override
@ -48,42 +67,29 @@ public class RepairedSpawner extends SimpleSlimefunItem<BlockPlaceHandler> {
onPlace(e.getItemStack(), e);
}
@ParametersAreNonnullByDefault
private void onPlace(ItemStack item, BlockEvent e) {
Optional<EntityType> entity = getEntityType(item);
if (entity.isPresent() && e.getBlock().getType() == Material.SPAWNER) {
/**
* This may no longer be needed at some point but for legacy items
* we still need to set the spawned EntityType manually
*/
if (e.getBlock().getType() == Material.SPAWNER) {
getEntityType(item).ifPresent(entity -> {
CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState();
spawner.setSpawnedType(entity.get());
spawner.setSpawnedType(entity);
spawner.update(true, false);
});
}
}
};
}
/**
* This method tries to obtain an {@link EntityType} from a given {@link ItemStack}.
* The provided {@link ItemStack} must be a {@link RepairedSpawner} item.
*
* @param item
* The {@link ItemStack} to extract the {@link EntityType} from
*
* @return An {@link Optional} describing the result
*/
public Optional<EntityType> getEntityType(ItemStack item) {
for (String line : item.getItemMeta().getLore()) {
if (ChatColor.stripColor(line).startsWith("Type: ") && !line.contains("<Type>")) {
EntityType type = EntityType.valueOf(ChatColor.stripColor(line).replace("Type: ", "").replace(' ', '_').toUpperCase(Locale.ROOT));
return Optional.of(type);
}
}
return Optional.empty();
}
@Override
public Collection<ItemStack> getDrops() {
// There should be no drops by default since drops are handled by the
// Pickaxe of Containment exclusively.
/**
* There should be no drops by default since drops are handled
* by the Pickaxe of Containment exclusively.
*/
return new ArrayList<>();
}

View File

@ -1,7 +1,6 @@
/**
* This package contains different implementations of
* This package contains a few miscellaneous implementations of
* {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} which are blocks.
* Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.InfusedHopper} or the
* {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example.
* Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example.
*/
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;

View File

@ -1,5 +1,9 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.cargo;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockPlaceEvent;
@ -9,6 +13,7 @@ import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial;
@ -16,7 +21,6 @@ import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
@ -29,14 +33,42 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
* @author TheBusyBiscuit
*
*/
abstract class AbstractCargoNode extends SlimefunItem {
abstract class AbstractCargoNode extends SimpleSlimefunItem<BlockPlaceHandler> {
protected static final String FREQUENCY = "frequency";
@ParametersAreNonnullByDefault
public AbstractCargoNode(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput);
addItemHandler(new BlockPlaceHandler(false) {
new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) {
@Override
public void init() {
createBorder(this);
}
@Override
public void newInstance(BlockMenu menu, Block b) {
menu.addMenuCloseHandler(p -> markDirty(b.getLocation()));
updateBlockMenu(menu, b);
}
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK);
}
@Override
public int[] getSlotsAccessedByItemTransport(ItemTransportFlow flow) {
return new int[0];
}
};
}
@Override
public BlockPlaceHandler getItemHandler() {
return new BlockPlaceHandler(false) {
@Override
public void onPlayerPlace(BlockPlaceEvent e) {
@ -49,32 +81,10 @@ abstract class AbstractCargoNode extends SlimefunItem {
onPlace(e);
}
});
new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) {
@Override
public void init() {
createBorder(this);
}
@Override
public void newInstance(BlockMenu menu, Block b) {
updateBlockMenu(menu, b);
}
@Override
public boolean canOpen(Block b, Player p) {
return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES);
}
@Override
public int[] getSlotsAccessedByItemTransport(ItemTransportFlow flow) {
return new int[0];
}
};
}
@ParametersAreNonnullByDefault
protected void addChannelSelector(Block b, BlockMenu menu, int slotPrev, int slotCurrent, int slotNext) {
boolean isChestTerminalInstalled = SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled();
int channel = getSelectedChannel(b);
@ -122,7 +132,7 @@ abstract class AbstractCargoNode extends SlimefunItem {
});
}
private int getSelectedChannel(Block b) {
private int getSelectedChannel(@Nonnull Block b) {
if (!BlockStorage.hasBlockInfo(b)) {
return 0;
} else {
@ -137,10 +147,12 @@ abstract class AbstractCargoNode extends SlimefunItem {
}
}
protected abstract void onPlace(BlockPlaceEvent e);
protected abstract void onPlace(@Nonnull BlockPlaceEvent e);
protected abstract void createBorder(BlockMenuPreset preset);
protected abstract void createBorder(@Nonnull BlockMenuPreset preset);
protected abstract void updateBlockMenu(BlockMenu menu, Block b);
protected abstract void updateBlockMenu(@Nonnull BlockMenu menu, @Nonnull Block b);
protected abstract void markDirty(@Nonnull Location loc);
}

View File

@ -1,11 +1,15 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.cargo;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
@ -65,7 +69,8 @@ abstract class AbstractFilterNode extends AbstractCargoNode {
@Override
protected void updateBlockMenu(BlockMenu menu, Block b) {
String filterType = BlockStorage.getLocationInfo(b.getLocation(), FILTER_TYPE);
Location loc = b.getLocation();
String filterType = BlockStorage.getLocationInfo(loc, FILTER_TYPE);
if (!BlockStorage.hasBlockInfo(b) || filterType == null || filterType.equals("whitelist")) {
menu.replaceExistingItem(15, new CustomItem(Material.WHITE_WOOL, "&7Type: &rWhitelist", "", "&e> Click to change it to Blacklist"));
@ -102,6 +107,16 @@ abstract class AbstractFilterNode extends AbstractCargoNode {
}
addChannelSelector(b, menu, 41, 42, 43);
markDirty(loc);
}
@Override
protected void markDirty(@Nonnull Location loc) {
CargoNet network = CargoNet.getNetworkFromLocation(loc);
if (network != null) {
network.markCargoNodeConfigurationDirty(loc);
}
}
}

View File

@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.cargo;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent;
@ -38,4 +39,9 @@ public class CargoOutputNode extends AbstractCargoNode {
addChannelSelector(b, menu, 12, 13, 14);
}
@Override
protected void markDirty(Location loc) {
// No need to mark anything as dirty, there is no item filter.
}
}

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

@ -85,7 +85,7 @@ public abstract class AbstractEnergyProvider extends SlimefunItem implements Inv
@Override
@Nonnull
public EnergyNetComponentType getEnergyComponentType() {
public final EnergyNetComponentType getEnergyComponentType() {
return EnergyNetComponentType.GENERATOR;
}
@ -95,7 +95,7 @@ public abstract class AbstractEnergyProvider extends SlimefunItem implements Inv
}
@Nonnull
public Set<MachineFuel> getFuelTypes2() {
public Set<MachineFuel> getFuelTypes() {
return fuelTypes;
}

View File

@ -38,7 +38,7 @@ public class Capacitor extends SlimefunItem implements EnergyNetComponent {
@Override
@Nonnull
public EnergyNetComponentType getEnergyComponentType() {
public final EnergyNetComponentType getEnergyComponentType() {
return EnergyNetComponentType.CAPACITOR;
}

View File

@ -53,7 +53,7 @@ public class EnergyConnector extends SimpleSlimefunItem<BlockUseHandler> impleme
@Nonnull
@Override
public EnergyNetComponentType getEnergyComponentType() {
public final EnergyNetComponentType getEnergyComponentType() {
return EnergyNetComponentType.CONNECTOR;
}

View File

@ -8,12 +8,12 @@ import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNet;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -50,8 +50,8 @@ public class Multimeter extends SimpleSlimefunItem<ItemUseHandler> {
e.cancel();
Location l = e.getClickedBlock().get().getLocation();
String stored = DoubleHandler.getFancyDouble(component.getCharge(l)) + " J";
String capacity = DoubleHandler.getFancyDouble(component.getCapacity()) + " J";
String stored = NumberUtils.getCompactDouble(component.getCharge(l)) + " J";
String capacity = NumberUtils.getCompactDouble(component.getCapacity()) + " J";
Player p = e.getPlayer();
p.sendMessage("");

View File

@ -61,10 +61,15 @@ public class SolarGenerator extends SlimefunItem implements EnergyNetProvider {
}
@Override
public int getCapacity() {
public final int getCapacity() {
return 0;
}
@Override
public final boolean isChargeable() {
return false;
}
@Override
public int getGeneratedOutput(Location l, Config data) {
World world = l.getWorld();

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

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
@ -23,6 +25,7 @@ public abstract class AbstractGrowthAccelerator extends SlimefunItem implements
private static final int[] BORDER = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
@ParametersAreNonnullByDefault
public AbstractGrowthAccelerator(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);

View File

@ -1,11 +1,10 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -17,9 +16,6 @@ import org.bukkit.inventory.meta.Repairable;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.slimefun4.api.events.AutoDisenchantEvent;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants;
import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -42,6 +38,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
*/
public class AutoDisenchanter extends AContainer {
@ParametersAreNonnullByDefault
public AutoDisenchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
}
@ -54,7 +51,6 @@ public class AutoDisenchanter extends AContainer {
@Override
protected MachineRecipe findNextRecipe(BlockMenu menu) {
Map<Enchantment, Integer> enchantments = new HashMap<>();
Set<ItemEnchantment> emeraldEnchantments = new HashSet<>();
for (int slot : getInputSlots()) {
ItemStack item = menu.getItemInSlot(slot);
@ -81,13 +77,6 @@ public class AutoDisenchanter extends AContainer {
amount++;
}
if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) {
for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) {
amount++;
emeraldEnchantments.add(enchantment);
}
}
if (amount > 0) {
ItemStack disenchantedItem = item.clone();
disenchantedItem.setAmount(1);
@ -95,11 +84,6 @@ public class AutoDisenchanter extends AContainer {
ItemStack book = new ItemStack(Material.ENCHANTED_BOOK);
transferEnchantments(disenchantedItem, book, enchantments);
for (ItemEnchantment ench : emeraldEnchantments) {
EmeraldEnchants.getInstance().getRegistry().applyEnchantment(book, ench.getEnchantment(), ench.getLevel());
EmeraldEnchants.getInstance().getRegistry().applyEnchantment(disenchantedItem, ench.getEnchantment(), 0);
}
MachineRecipe recipe = new MachineRecipe(90 * amount / this.getSpeed(), new ItemStack[] { target, item }, new ItemStack[] { disenchantedItem, book });
if (!InvUtils.fitAll(menu.toInventory(), recipe.getOutput(), getOutputSlots())) {

View File

@ -1,9 +1,9 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
@ -11,9 +11,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants;
import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -24,12 +21,9 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
public class AutoEnchanter extends AContainer {
private final int emeraldEnchantsLimit;
@ParametersAreNonnullByDefault
public AutoEnchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
emeraldEnchantsLimit = SlimefunPlugin.getCfg().getInt("options.emerald-enchantment-limit");
}
@Override
@ -51,9 +45,7 @@ public class AutoEnchanter extends AContainer {
if (item != null && item.getType() == Material.ENCHANTED_BOOK && target != null) {
Map<Enchantment, Integer> enchantments = new HashMap<>();
Set<ItemEnchantment> emeraldEnchantments = new HashSet<>();
int amount = 0;
int specialAmount = 0;
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta();
for (Map.Entry<Enchantment, Integer> e : meta.getStoredEnchants().entrySet()) {
@ -63,19 +55,7 @@ public class AutoEnchanter extends AContainer {
}
}
if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) {
for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) {
if (EmeraldEnchants.getInstance().getRegistry().isApplicable(target, enchantment.getEnchantment()) && EmeraldEnchants.getInstance().getRegistry().getEnchantmentLevel(target, enchantment.getEnchantment().getName()) < enchantment.getLevel()) {
amount++;
specialAmount++;
emeraldEnchantments.add(enchantment);
}
}
specialAmount += EmeraldEnchants.getInstance().getRegistry().getEnchantments(target).size();
}
if (amount > 0 && specialAmount <= emeraldEnchantsLimit) {
if (amount > 0) {
ItemStack enchantedItem = target.clone();
enchantedItem.setAmount(1);
@ -83,10 +63,6 @@ public class AutoEnchanter extends AContainer {
enchantedItem.addUnsafeEnchantment(entry.getKey(), entry.getValue());
}
for (ItemEnchantment ench : emeraldEnchantments) {
EmeraldEnchants.getInstance().getRegistry().applyEnchantment(enchantedItem, ench.getEnchantment(), ench.getLevel());
}
MachineRecipe recipe = new MachineRecipe(75 * amount / this.getSpeed(), new ItemStack[] { target, item }, new ItemStack[] { enchantedItem, new ItemStack(Material.BOOK) });
if (!InvUtils.fitAll(menu.toInventory(), recipe.getOutput(), getOutputSlots())) {

View File

@ -3,7 +3,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.block.Block;
@ -18,7 +20,9 @@ import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction;
@ -40,6 +44,8 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
* This class needs to be rewritten VERY BADLY.
* But we should focus on rewriting the recipe system first.
*
* @deprecated This is horribly done. Someone needs to rewrite this.
*
* @author TheBusyBiscuit
*
*/
@ -49,6 +55,8 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
private final int[] inputBorder = { 9, 10, 11, 12, 13, 18, 22, 27, 31, 36, 40, 45, 46, 47, 48, 49 };
private final int[] outputBorder = { 23, 24, 25, 26, 32, 35, 41, 42, 43, 44 };
private final Map<String, ItemStack> craftingRecipes = new HashMap<>();
public AutomatedCraftingChamber(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
@ -86,7 +94,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
@ -267,7 +275,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
private void testInputAgainstRecipes(Block block, String input) {
BlockMenu menu = BlockStorage.getInventory(block);
ItemStack output = SlimefunPlugin.getRegistry().getAutomatedCraftingChamberRecipes().get(input);
ItemStack output = craftingRecipes.get(input);
if (output != null && menu.fits(output, getOutputSlots())) {
menu.pushItem(output.clone(), getOutputSlots());
removeCharge(block.getLocation(), getEnergyConsumption());
@ -279,4 +287,25 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
}
}
}
public void loadRecipes() {
EnhancedCraftingTable machine = (EnhancedCraftingTable) SlimefunItems.ENHANCED_CRAFTING_TABLE.getItem();
for (ItemStack[] inputs : RecipeType.getRecipeInputList(machine)) {
StringBuilder builder = new StringBuilder();
int i = 0;
for (ItemStack item : inputs) {
if (i > 0) {
builder.append(" </slot> ");
}
builder.append(CustomItemSerializer.serialize(item, ItemFlag.MATERIAL, ItemFlag.ITEMMETA_DISPLAY_NAME, ItemFlag.ITEMMETA_LORE));
i++;
}
craftingRecipes.put(builder.toString(), RecipeType.getRecipeOutputList(machine, inputs));
}
}
}

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
@ -11,8 +13,16 @@ import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
* The {@link ElectricPress} is a pretty simple electrical machine.
* It allows you to compact items into their block variant, e.g. 9 diamonds into a diamond block.
*
* @author TheBusyBiscuit
*
*/
public class ElectricPress extends AContainer implements RecipeDisplayItem {
@ParametersAreNonnullByDefault
public ElectricPress(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
}
@ -28,6 +38,7 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem {
addRecipe(3, new ItemStack(Material.DRIED_KELP, 9), new ItemStack(Material.DRIED_KELP_BLOCK));
addRecipe(3, new ItemStack(Material.BONE_MEAL, 9), new ItemStack(Material.BONE_BLOCK));
addRecipe(3, new ItemStack(Material.CLAY_BALL, 4), new ItemStack(Material.CLAY));
addRecipe(3, new ItemStack(Material.BRICK, 4), new ItemStack(Material.BRICKS));
addRecipe(6, SlimefunItems.COPPER_INGOT, new CustomItem(SlimefunItems.COPPER_WIRE, 3));
addRecipe(16, new SlimefunItemStack(SlimefunItems.STEEL_INGOT, 8), SlimefunItems.STEEL_PLATE);
@ -61,6 +72,7 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem {
addRecipe(8, new ItemStack(Material.DIAMOND, 9), new ItemStack(Material.DIAMOND_BLOCK));
}
@ParametersAreNonnullByDefault
private void addRecipe(int seconds, ItemStack input, ItemStack output) {
registerRecipe(seconds, new ItemStack[] { input }, new ItemStack[] { output });
}

View File

@ -53,7 +53,7 @@ public class ElectricSmeltery 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

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

@ -4,6 +4,10 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
@ -20,6 +24,7 @@ import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -35,20 +40,24 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see ArmorForge
*
*/
abstract class BackpackCrafter extends MultiBlockMachine {
abstract class AbstractCraftingTable extends MultiBlockMachine {
BackpackCrafter(Category category, SlimefunItemStack item, ItemStack[] recipe, BlockFace trigger) {
@ParametersAreNonnullByDefault
AbstractCraftingTable(Category category, SlimefunItemStack item, ItemStack[] recipe, BlockFace trigger) {
super(category, item, recipe, trigger);
}
protected Inventory createVirtualInventory(Inventory inv) {
@Nonnull
protected Inventory createVirtualInventory(@Nonnull Inventory inv) {
Inventory fakeInv = Bukkit.createInventory(null, 9, "Fake Inventory");
for (int j = 0; j < inv.getContents().length; j++) {
ItemStack stack = inv.getContents()[j];
// Fixes #2103 - Properly simulating the consumption
// (which may leave behind empty buckets or glass bottles)
/**
* Fixes #2103 - Properly simulating the consumption
* (which may leave behind empty buckets or glass bottles)
*/
if (stack != null) {
stack = stack.clone();
ItemUtils.consumeItem(stack, true);
@ -60,18 +69,24 @@ abstract class BackpackCrafter extends MultiBlockMachine {
return fakeInv;
}
@ParametersAreNonnullByDefault
protected void upgradeBackpack(Player p, Inventory inv, SlimefunBackpack backpack, ItemStack output) {
ItemStack backpackItem = null;
ItemStack input = null;
for (int j = 0; j < 9; j++) {
if (inv.getContents()[j] != null && inv.getContents()[j].getType() != Material.AIR && SlimefunItem.getByItem(inv.getContents()[j]) instanceof SlimefunBackpack) {
backpackItem = inv.getContents()[j];
input = inv.getContents()[j];
break;
}
}
// Fixes #2574 - Carry over the Soulbound status
if (SlimefunUtils.isSoulbound(input)) {
SlimefunUtils.setSoulbound(output, true);
}
int size = backpack.getSize();
Optional<String> id = retrieveID(backpackItem, size);
Optional<String> id = retrieveID(input, size);
if (id.isPresent()) {
for (int line = 0; line < output.getItemMeta().getLore().size(); line++) {
@ -100,7 +115,8 @@ abstract class BackpackCrafter extends MultiBlockMachine {
}
}
private Optional<String> retrieveID(ItemStack backpack, int size) {
@Nonnull
private Optional<String> retrieveID(@Nullable ItemStack backpack, int size) {
if (backpack != null) {
for (String line : backpack.getItemMeta().getLore()) {
if (line.startsWith(ChatColors.color("&7ID: ")) && line.contains("#")) {

View File

@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class ArmorForge extends BackpackCrafter {
public class ArmorForge extends AbstractCraftingTable {
public ArmorForge(Category category, SlimefunItemStack item) {
super(category, item, new ItemStack[] { null, null, null, null, new ItemStack(Material.ANVIL), null, null, new CustomItem(Material.DISPENSER, "Dispenser (Facing up)"), null }, BlockFace.SELF);

View File

@ -23,7 +23,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class EnhancedCraftingTable extends BackpackCrafter {
public class EnhancedCraftingTable extends AbstractCraftingTable {
public EnhancedCraftingTable(Category category, SlimefunItemStack item) {
super(category, item, new ItemStack[] { null, null, null, null, new ItemStack(Material.CRAFTING_TABLE), null, null, new ItemStack(Material.DISPENSER), null }, BlockFace.SELF);

View File

@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class MagicWorkbench extends BackpackCrafter {
public class MagicWorkbench extends AbstractCraftingTable {
public MagicWorkbench(Category category, SlimefunItemStack item) {
super(category, item, new ItemStack[] { null, null, null, null, null, null, new ItemStack(Material.BOOKSHELF), new ItemStack(Material.CRAFTING_TABLE), new ItemStack(Material.DISPENSER) }, BlockFace.UP);

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