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 --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of contents** **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 17 (17 Oct 2020)](#release-candidate-17-17-oct-2020)
- [Release Candidate 16 (07 Sep 2020)](#release-candidate-16-07-sep-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) - [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 --> <!-- 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 #### Additions
* The Smelters Pick now also works on Ancient Debris * The Smelters Pick now also works on Ancient Debris
* (API) Added PlayerPreResearchEvent * (API) Added PlayerPreResearchEvent
* Added a config option to disable network visualizations * Added a config option to disable network visualizations
* (API) Added CoolerFeedPlayerEvent * (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 #### Changes
* Removed 1.13 support * Removed 1.13 support
@ -40,6 +52,12 @@
* Magnets can no longer be placed down * Magnets can no longer be placed down
* Electromagnets can no longer be placed down * Electromagnets can no longer be placed down
* Performance improvements to Cargo network visualizations * 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 #### Fixes
* Fixed #2448 * Fixed #2448
@ -59,6 +77,23 @@
* Fixed Magician Talisman sometimes drawing invalid enchantments * Fixed Magician Talisman sometimes drawing invalid enchantments
* Fixed id conflicts for external Enchantment sources (e.g. plugins) for the Magician Talisman settings * 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 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) ## 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" | | | 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: | | **automatic updates** | :heavy_check_mark: | :heavy_check_mark: |
| **frequent updates** | :heavy_check_mark: | :x: | | **frequent updates** | :heavy_check_mark: | :x: |
| **latest content** | :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.organization>slimefun</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.log.level>DEBUG</sonar.log.level> <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> <sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties> </properties>
@ -315,7 +314,7 @@
<dependency> <dependency>
<groupId>com.github.seeseemelk</groupId> <groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.16</artifactId> <artifactId>MockBukkit-v1.16</artifactId>
<version>0.15.0</version> <version>0.16.0</version>
<scope>test</scope> <scope>test</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -329,7 +328,7 @@
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>3.6.0</version> <version>3.6.28</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -337,13 +336,13 @@
<dependency> <dependency>
<groupId>com.github.TheBusyBiscuit</groupId> <groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>CS-CoreLib2</artifactId> <artifactId>CS-CoreLib2</artifactId>
<version>0.26</version> <version>0.27.4</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.papermc</groupId> <groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId> <artifactId>paperlib</artifactId>
<version>1.0.5</version> <version>1.0.6</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -381,7 +380,7 @@
<dependency> <dependency>
<groupId>com.gmail.nossr50.mcMMO</groupId> <groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId> <artifactId>mcMMO</artifactId>
<version>2.1.154</version> <version>2.1.159</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -414,12 +413,6 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>EmeraldEnchants2</artifactId>
<version>3cd370b5d8</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>me.minebuilders</groupId> <groupId>me.minebuilders</groupId>
<artifactId>clearlag-core</artifactId> <artifactId>clearlag-core</artifactId>

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.api; package io.github.thebusybiscuit.slimefun4.api;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; 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 * This constant represents an exceptional state in which we were unable
* to identify the Minecraft Version we are using * 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 * This is a very special state that represents the environment being a Unit
* Test and not an actual running Minecraft Server. * 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(); public static final MinecraftVersion[] valuesCache = values();
private final String name; private final String name;
private final boolean virtual;
private final String prefix; 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) { 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.name = name;
this.virtual = virtual;
this.prefix = name().replace("MINECRAFT_", "v") + '_'; this.prefix = name().replace("MINECRAFT_", "v") + '_';
} }
@ -63,6 +88,19 @@ public enum MinecraftVersion {
return name; 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 * This method checks whether the given version matches with this
* {@link MinecraftVersion}. * {@link MinecraftVersion}.

View File

@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; 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.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage; 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(); private static final HandlerList handlers = new HandlerList();
/**
* The original {@link PlayerInteractEvent}.
*/
private final PlayerInteractEvent event; private final PlayerInteractEvent event;
private final Player player;
private final Optional<ItemStack> itemStack; private final Optional<ItemStack> itemStack;
private final Optional<Block> clickedBlock; private final Optional<Block> clickedBlock;
@ -35,33 +49,45 @@ public class PlayerRightClickEvent extends Event {
private ComputedOptional<SlimefunItem> slimefunItem = ComputedOptional.createNew(); private ComputedOptional<SlimefunItem> slimefunItem = ComputedOptional.createNew();
private ComputedOptional<SlimefunItem> slimefunBlock = ComputedOptional.createNew(); private ComputedOptional<SlimefunItem> slimefunBlock = ComputedOptional.createNew();
private Result itemResult = Result.DEFAULT; private Result itemResult;
private Result blockResult = Result.DEFAULT; private Result blockResult;
public PlayerRightClickEvent(@Nonnull PlayerInteractEvent e) { /**
event = e; * This constructs a new {@link PlayerRightClickEvent} based on the original {@link PlayerInteractEvent}.
player = e.getPlayer(); * The {@link Result} of the original {@link PlayerInteractEvent} will be copied.
clickedBlock = Optional.ofNullable(e.getClickedBlock()); *
face = e.getBlockFace(); * @param originalEvent
hand = e.getHand(); * 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(); itemStack = Optional.empty();
} else { } 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 @Nonnull
public PlayerInteractEvent getInteractEvent() { public PlayerInteractEvent getInteractEvent() {
return event; return event;
} }
@Nonnull
public Player getPlayer() {
return player;
}
/** /**
* This method returns the {@link ItemStack} that was held in the hand of the {@link 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 * 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(); return slimefunBlock.getAsOptional();
} }
/**
* This method cancels the {@link PlayerRightClickEvent}.
* This will deny the item and block usage.
*/
public void cancel() { public void cancel() {
itemResult = Result.DENY; itemResult = Result.DENY;
blockResult = Result.DENY; blockResult = Result.DENY;

View File

@ -113,6 +113,7 @@ public class ItemSetting<T> {
* *
* @param c * @param c
* The class of data type you want to compare * The class of data type you want to compare
*
* @return Whether this {@link ItemSetting} stores the given type * @return Whether this {@link ItemSetting} stores the given type
*/ */
public boolean isType(@Nonnull Class<?> c) { public boolean isType(@Nonnull Class<?> c) {
@ -137,14 +138,19 @@ public class ItemSetting<T> {
* @param item * @param item
* The {@link SlimefunItem} who called this method * The {@link SlimefunItem} who called this method
*/ */
@SuppressWarnings("unchecked")
public void load(@Nonnull SlimefunItem item) { public void load(@Nonnull SlimefunItem item) {
Validate.notNull(item, "Cannot apply settings for a non-existing SlimefunItem");
SlimefunPlugin.getItemCfg().setDefaultValue(item.getId() + '.' + getKey(), getDefaultValue()); SlimefunPlugin.getItemCfg().setDefaultValue(item.getId() + '.' + getKey(), getDefaultValue());
Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey()); Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey());
if (defaultValue.getClass().isInstance(configuredValue)) { if (defaultValue.getClass().isInstance(configuredValue)) {
if (validateInput((T) configuredValue)) { // We can suppress the warning here, we did an isInstance(...) check before!
this.value = (T) configuredValue; @SuppressWarnings("unchecked")
T newValue = (T) configuredValue;
if (validateInput(newValue)) {
this.value = newValue;
} else { } else {
Slimefun.getLogger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!"); 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() }); 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 * @param l
* The {@link Location} to check for * The {@link Location} to check for
*
* @return Whether the given {@link Location} is part of this {@link Network} * @return Whether the given {@link Location} is part of this {@link Network}
*/ */
public boolean connectsTo(@Nonnull Location l) { public boolean connectsTo(@Nonnull Location l) {
if (regulator.equals(l)) {
return true;
} else {
return connectedLocations.contains(l); return connectedLocations.contains(l);
} }
}
@Nullable @Nullable
private NetworkComponent getCurrentClassification(@Nonnull Location l) { 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.BookSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide; 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.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; 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<Class<? extends ItemHandler>, Set<ItemHandler>> globalItemHandlers = new HashMap<>();
private final Map<String, SlimefunBlockHandler> blockHandlers = new HashMap<>(); private final Map<String, SlimefunBlockHandler> blockHandlers = new HashMap<>();
private final Map<String, ItemStack> automatedCraftingChamberRecipes = new HashMap<>();
public void load(@Nonnull Config cfg) { public void load(@Nonnull Config cfg) {
Validate.notNull(cfg, "The Config cannot be null!"); Validate.notNull(cfg, "The Config cannot be null!");
@ -261,18 +258,6 @@ public final class SlimefunRegistry {
return geoResources; 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() { public boolean logDuplicateBlockEntries() {
return logDuplicateBlockEntries; return logDuplicateBlockEntries;
} }

View File

@ -49,7 +49,9 @@ public interface DamageableItem extends ItemAttribute {
*/ */
default void damageItem(@Nonnull Player p, @Nullable ItemStack item) { default void damageItem(@Nonnull Player p, @Nullable ItemStack item) {
if (isDamageable() && item != null && item.getType() != Material.AIR && item.getAmount() > 0) { 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; 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.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.BlockStorage;
@ -65,14 +66,36 @@ public interface EnergyNetComponent extends ItemAttribute {
* @return The charge stored at that {@link Location} * @return The charge stored at that {@link Location}
*/ */
default int getCharge(@Nonnull Location l) { 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(l, "Location was null!");
Validate.notNull(data, "data was null!");
// Emergency fallback, this cannot hold a charge, so we'll just return zero // Emergency fallback, this cannot hold a charge, so we'll just return zero
if (!isChargeable()) { if (!isChargeable()) {
return 0; return 0;
} }
String charge = BlockStorage.getLocationInfo(l, "energy-charge"); String charge = data.getString("energy-charge");
if (charge != null) { if (charge != null) {
return Integer.parseInt(charge); return Integer.parseInt(charge);

View File

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

View File

@ -68,8 +68,11 @@ public abstract class FlexCategory extends Category {
@Override @Override
public final boolean isHidden(@Nonnull Player p) { 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; return false;
} }

View File

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

View File

@ -27,7 +27,7 @@ public class SeasonalCategory extends Category {
private final Month month; private final Month month;
/** /**
* The constructor for a SeasonCategory. * The constructor for a {@link SeasonalCategory}.
* *
* @param key * @param key
* The {@link NamespacedKey} that is used to identify this {@link Category} * 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); 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 * We could just return true here, but if there's no subcommands,
// from nagging about this always returning true... * then something went horribly wrong anyway.
* This will also stop sonarcloud from nagging about
* this always returning true...
*/
return !commands.isEmpty(); return !commands.isEmpty();
} }

View File

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

View File

@ -46,7 +46,7 @@ final class ContributorsMenu {
}); });
List<Contributor> contributors = new ArrayList<>(SlimefunPlugin.getGitHubService().getContributors().values()); 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++) { for (int i = page * 36; i < contributors.size() && i < (page + 1) * 36; i++) {
Contributor contributor = contributors.get(i); Contributor contributor = contributors.get(i);

View File

@ -75,9 +75,11 @@ public class RainbowTickHandler extends BlockTicker {
} }
for (Material type : materials) { 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 * This BlockData is purely virtual and only created on startup, it should have
// the data but also saves heavy calls for other Materials * 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) { if (type.createBlockData() instanceof GlassPane) {
return true; return true;
} }
@ -89,8 +91,10 @@ public class RainbowTickHandler extends BlockTicker {
@Override @Override
public void tick(Block b, SlimefunItem item, Config data) { public void tick(Block b, SlimefunItem item, Config data) {
if (b.getType() == Material.AIR) { 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; return;
} }

View File

@ -117,7 +117,7 @@ public abstract class MultiBlockMachine extends SlimefunItem implements NotPlace
protected MultiBlockInteractionHandler getInteractionHandler() { protected MultiBlockInteractionHandler getInteractionHandler() {
return (p, mb, b) -> { return (p, mb, b) -> {
if (mb.equals(getMultiBlock())) { 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); onInteract(p, b);
} }

View File

@ -14,6 +14,7 @@ import org.bukkit.Server;
import io.github.thebusybiscuit.cscorelib2.config.Config; import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.network.Network; 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; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
/** /**
@ -30,6 +31,7 @@ public class NetworkManager {
private final int maxNodes; private final int maxNodes;
private final boolean enableVisualizer; private final boolean enableVisualizer;
private final boolean deleteExcessItems;
private final List<Network> networks = new LinkedList<>(); private final List<Network> networks = new LinkedList<>();
/** /**
@ -37,14 +39,29 @@ public class NetworkManager {
* *
* @param maxStepSize * @param maxStepSize
* The maximum amount of nodes a {@link Network} can have * 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!"); Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!");
this.enableVisualizer = enableVisualizer; this.enableVisualizer = enableVisualizer;
this.deleteExcessItems = deleteExcessItems;
maxNodes = maxStepSize; 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 method returns the limit of nodes a {@link Network} can have.
* This value is read from the {@link Config} file. * This value is read from the {@link Config} file.
@ -64,6 +81,16 @@ public class NetworkManager {
return enableVisualizer; 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}. * 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"); Validate.notNull(type, "Type must not be null");
for (Network network : networks) { for (Network network : networks) {
if (type.isInstance(network) && network.connectsTo(l)) { if (type.isInstance(network) && network.connectsTo(l)) {
return Optional.of(type.cast(network)); return Optional.of(type.cast(network));
@ -141,9 +169,12 @@ public class NetworkManager {
public void updateAllNetworks(@Nonnull Location l) { public void updateAllNetworks(@Nonnull Location l) {
Validate.notNull(l, "The Location cannot be null"); 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)) { for (Network network : getNetworksFromLocation(l, Network.class)) {
network.markDirty(l); network.markDirty(l);
} }
} }
}
} }

View File

@ -23,6 +23,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional; import org.bukkit.block.data.Directional;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
@ -47,12 +48,13 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; 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 * @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[] 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 }; 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> imports = new HashSet<>();
protected final Set<Location> exports = 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<>(); 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 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); super(SlimefunPlugin.getNetworkManager(), regulator);
} }
@ -127,7 +138,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(l); Optional<Block> target = getAttachedBlock(l);
if (target.isPresent()) { 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) { if (item == null) {
terminal.replaceExistingItem(request.getSlot(), null); terminal.replaceExistingItem(request.getSlot(), null);
@ -159,7 +170,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(l); Optional<Block> target = getAttachedBlock(l);
if (target.isPresent()) { 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 (is != null) {
if (stack == null) { if (stack == null) {
@ -201,7 +212,7 @@ abstract class ChestTerminalNetwork extends Network {
Optional<Block> target = getAttachedBlock(bus); Optional<Block> target = getAttachedBlock(bus);
if (target.isPresent()) { 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) { if (stack != null) {
menu.replaceExistingItem(17, stack.getItem()); menu.replaceExistingItem(17, stack.getItem());
@ -227,7 +238,7 @@ abstract class ChestTerminalNetwork extends Network {
if (menu.getItemInSlot(17) != null) { if (menu.getItemInSlot(17) != null) {
Optional<Block> target = getAttachedBlock(bus); 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) { if (menu.getItemInSlot(17) == null) {
@ -274,7 +285,7 @@ abstract class ChestTerminalNetwork extends Network {
* found in any provider of the network. * found in any provider of the network.
* *
* @param providers * @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 * @return The time it took to compute this operation
*/ */
@ -326,10 +337,27 @@ abstract class ChestTerminalNetwork extends Network {
@Override @Override
public void markDirty(@Nonnull Location l) { public void markDirty(@Nonnull Location l) {
connectorCache.remove(l); markCargoNodeConfigurationDirty(l);
super.markDirty(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 @ParametersAreNonnullByDefault
private void updateTerminal(Location l, BlockMenu terminal, int slot, int index, List<ItemStackAndInteger> items) { private void updateTerminal(Location l, BlockMenu terminal, int slot, int index, List<ItemStackAndInteger> items) {
if (items.size() > index) { if (items.size() > index) {
@ -430,20 +458,20 @@ abstract class ChestTerminalNetwork extends Network {
int stored = Integer.parseInt(data); int stored = Integer.parseInt(data);
for (int slot : blockMenu.getPreset().getSlotsAccessedByItemTransport((DirtyChestMenu) blockMenu, ItemTransportFlow.WITHDRAW, null)) { 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; boolean add = true;
for (ItemStackAndInteger item : items) { for (ItemStackAndInteger item : items) {
if (SlimefunUtils.isItemSimilar(is, item.getItemStackWrapper(), true, false)) { if (SlimefunUtils.isItemSimilar(stack, item.getItemStackWrapper(), true, false)) {
add = false; add = false;
item.add(is.getAmount() + stored); item.add(stack.getAmount() + stored);
} }
} }
if (add) { 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 @ParametersAreNonnullByDefault
private void filter(@Nullable ItemStack stack, List<ItemStackAndInteger> items, Location node) { 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; boolean add = true;
for (ItemStackAndInteger item : items) { 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. * 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. * with the addon ChestTerminal.
* *
* @author meiamsome * @author meiamsome
@ -37,7 +37,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
* @author DNx5 * @author DNx5
* *
*/ */
public class CargoNet extends ChestTerminalNetwork { public class CargoNet extends AbstractItemNetwork {
private static final int RANGE = 5; private static final int RANGE = 5;
private static final int TICK_DELAY = SlimefunPlugin.getCfg().getInt("networks.cargo-ticker-delay"); 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.Inventory;
import org.bukkit.inventory.ItemStack; 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.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
@ -32,11 +33,12 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
* *
* @see CargoNet * @see CargoNet
* @see CargoUtils * @see CargoUtils
* @see ChestTerminalNetwork * @see AbstractItemNetwork
* *
*/ */
class CargoNetworkTask implements Runnable { class CargoNetworkTask implements Runnable {
private final NetworkManager manager;
private final CargoNet network; private final CargoNet network;
private final Map<Location, Inventory> inventories = new HashMap<>(); private final Map<Location, Inventory> inventories = new HashMap<>();
@ -49,6 +51,7 @@ class CargoNetworkTask implements Runnable {
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
CargoNetworkTask(CargoNet network, Map<Location, Integer> inputs, Map<Integer, List<Location>> outputs, Set<Location> chestTerminalInputs, Set<Location> chestTerminalOutputs) { CargoNetworkTask(CargoNet network, Map<Location, Integer> inputs, Map<Integer, List<Location>> outputs, Set<Location> chestTerminalInputs, Set<Location> chestTerminalOutputs) {
this.network = network; this.network = network;
this.manager = SlimefunPlugin.getNetworkManager();
this.inputs = inputs; this.inputs = inputs;
this.outputs = outputs; this.outputs = outputs;
@ -65,8 +68,10 @@ class CargoNetworkTask implements Runnable {
network.handleItemRequests(inventories, chestTerminalInputs, chestTerminalOutputs); 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(); SlimefunItem inputNode = SlimefunItems.CARGO_INPUT_NODE.getItem();
for (Map.Entry<Location, Integer> entry : inputs.entrySet()) { for (Map.Entry<Location, Integer> entry : inputs.entrySet()) {
long nodeTimestamp = System.nanoTime(); long nodeTimestamp = System.nanoTime();
@ -89,8 +94,9 @@ class CargoNetworkTask implements Runnable {
SlimefunPlugin.getProfiler().closeEntry(network.getRegulator(), SlimefunItems.CARGO_MANAGER.getItem(), timestamp); 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) { 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) { if (slot == null) {
return; return;
@ -105,17 +111,23 @@ class CargoNetworkTask implements Runnable {
} }
if (stack != null) { if (stack != null) {
insertItem(inputTarget, previousSlot, stack);
}
}
@ParametersAreNonnullByDefault
private void insertItem(Block inputTarget, int previousSlot, ItemStack item) {
Inventory inv = inventories.get(inputTarget.getLocation()); Inventory inv = inventories.get(inputTarget.getLocation());
if (inv != null) { if (inv != null) {
// Check if the original slot hasn't been occupied in the meantime // Check if the original slot hasn't been occupied in the meantime
if (inv.getItem(previousSlot) == null) { if (inv.getItem(previousSlot) == null) {
inv.setItem(previousSlot, stack); inv.setItem(previousSlot, item);
} else { } else {
// Try to add the item into another available slot then // 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 // If the item still couldn't be inserted, simply drop it on the ground
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest); inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest);
} }
@ -125,10 +137,9 @@ class CargoNetworkTask implements Runnable {
if (menu != null) { if (menu != null) {
if (menu.getItemInSlot(previousSlot) == null) { if (menu.getItemInSlot(previousSlot) == null) {
menu.replaceExistingItem(previousSlot, stack); menu.replaceExistingItem(previousSlot, item);
} else { } else if (!manager.isItemDeletionEnabled()) {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack); inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), item);
}
} }
} }
} }
@ -149,7 +160,7 @@ class CargoNetworkTask implements Runnable {
Optional<Block> target = network.getAttachedBlock(output); Optional<Block> target = network.getAttachedBlock(output);
if (target.isPresent()) { 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) { if (item == null) {
break; break;

View File

@ -1,7 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.networks.cargo; package io.github.thebusybiscuit.slimefun4.core.networks.cargo;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -17,23 +16,21 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.api.BlockStorage; 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.inventory.DirtyChestMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
final class CargoUtils { 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() {} 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); DirtyChestMenu menu = getChestMenu(target);
if (menu == null) { if (menu == null) {
@ -125,7 +122,7 @@ final class CargoUtils {
Inventory inventory = inventories.get(target.getLocation()); Inventory inventory = inventories.get(target.getLocation());
if (inventory != null) { if (inventory != null) {
return withdrawFromVanillaInventory(node, template, inventory); return withdrawFromVanillaInventory(network, node, template, inventory);
} }
BlockState state = PaperLib.getBlockState(target, false).getState(); BlockState state = PaperLib.getBlockState(target, false).getState();
@ -133,7 +130,7 @@ final class CargoUtils {
if (state instanceof InventoryHolder) { if (state instanceof InventoryHolder) {
inventory = ((InventoryHolder) state).getInventory(); inventory = ((InventoryHolder) state).getInventory();
inventories.put(target.getLocation(), inventory); 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)) { for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) {
ItemStack is = menu.getItemInSlot(slot); 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()) { if (is.getAmount() > template.getAmount()) {
is.setAmount(is.getAmount() - template.getAmount()); is.setAmount(is.getAmount() - template.getAmount());
menu.replaceExistingItem(slot, is.clone()); menu.replaceExistingItem(slot, is.clone());
@ -160,7 +157,7 @@ final class CargoUtils {
return null; 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(); ItemStack[] contents = inv.getContents();
int[] range = getOutputSlotRange(inv); int[] range = getOutputSlotRange(inv);
int minSlot = range[0]; int minSlot = range[0];
@ -172,7 +169,7 @@ final class CargoUtils {
// Changes to these ItemStacks are synchronized with the Item in the Inventory // Changes to these ItemStacks are synchronized with the Item in the Inventory
ItemStack itemInSlot = contents[slot]; 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()) { if (itemInSlot.getAmount() > template.getAmount()) {
itemInSlot.setAmount(itemInSlot.getAmount() - template.getAmount()); itemInSlot.setAmount(itemInSlot.getAmount() - template.getAmount());
return template; return template;
@ -187,14 +184,14 @@ final class CargoUtils {
return null; 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); DirtyChestMenu menu = getChestMenu(target);
if (menu != null) { if (menu != null) {
for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) { for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) {
ItemStack is = menu.getItemInSlot(slot); ItemStack is = menu.getItemInSlot(slot);
if (matchesFilter(node, is)) { if (matchesFilter(network, node, is)) {
menu.replaceExistingItem(slot, null); menu.replaceExistingItem(slot, null);
return new ItemStackAndInteger(is, slot); return new ItemStackAndInteger(is, slot);
} }
@ -203,7 +200,7 @@ final class CargoUtils {
Inventory inventory = inventories.get(target.getLocation()); Inventory inventory = inventories.get(target.getLocation());
if (inventory != null) { if (inventory != null) {
return withdrawFromVanillaInventory(node, inventory); return withdrawFromVanillaInventory(network, node, inventory);
} }
BlockState state = PaperLib.getBlockState(target, false).getState(); BlockState state = PaperLib.getBlockState(target, false).getState();
@ -211,14 +208,14 @@ final class CargoUtils {
if (state instanceof InventoryHolder) { if (state instanceof InventoryHolder) {
inventory = ((InventoryHolder) state).getInventory(); inventory = ((InventoryHolder) state).getInventory();
inventories.put(target.getLocation(), inventory); inventories.put(target.getLocation(), inventory);
return withdrawFromVanillaInventory(node, inventory); return withdrawFromVanillaInventory(network, node, inventory);
} }
} }
return null; return null;
} }
private static ItemStackAndInteger withdrawFromVanillaInventory(Block node, Inventory inv) { private static ItemStackAndInteger withdrawFromVanillaInventory(AbstractItemNetwork network, Block node, Inventory inv) {
ItemStack[] contents = inv.getContents(); ItemStack[] contents = inv.getContents();
int[] range = getOutputSlotRange(inv); int[] range = getOutputSlotRange(inv);
int minSlot = range[0]; int minSlot = range[0];
@ -227,7 +224,7 @@ final class CargoUtils {
for (int slot = minSlot; slot < maxSlot; slot++) { for (int slot = minSlot; slot < maxSlot; slot++) {
ItemStack is = contents[slot]; ItemStack is = contents[slot];
if (matchesFilter(node, is)) { if (matchesFilter(network, node, is)) {
inv.setItem(slot, null); inv.setItem(slot, null);
return new ItemStackAndInteger(is, slot); return new ItemStackAndInteger(is, slot);
} }
@ -236,8 +233,8 @@ final class CargoUtils {
return null; return null;
} }
static ItemStack insert(Map<Location, Inventory> inventories, Block node, Block target, ItemStack stack) { static ItemStack insert(AbstractItemNetwork network, Map<Location, Inventory> inventories, Block node, Block target, ItemStack stack) {
if (!matchesFilter(node, stack)) { if (!matchesFilter(network, node, stack)) {
return stack; return stack;
} }
@ -338,77 +335,12 @@ final class CargoUtils {
return BlockStorage.getUniversalInventory(block); 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) { if (item == null || item.getType() == Material.AIR) {
return false; return false;
} }
// Store the returned Config instance to avoid heavy calls return network.getItemFilter(node).test(item);
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;
} }
/** /**

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.Material;
import org.bukkit.block.Block; 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.ErrorReport;
import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent; 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.core.attributes.EnergyNetProvider;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram; import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -210,14 +210,14 @@ public class EnergyNet extends Network {
SlimefunItem item = (SlimefunItem) provider; SlimefunItem item = (SlimefunItem) provider;
try { try {
Config config = BlockStorage.getLocationInfo(loc); Config data = BlockStorage.getLocationInfo(loc);
int energy = provider.getGeneratedOutput(loc, config); int energy = provider.getGeneratedOutput(loc, data);
if (provider.isChargeable()) { 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); explodedBlocks.add(loc);
BlockStorage.clearBlockInfo(loc); BlockStorage.clearBlockInfo(loc);
@ -228,9 +228,9 @@ public class EnergyNet extends Network {
} else { } else {
supply += energy; supply += energy;
} }
} catch (Exception | LinkageError t) { } catch (Exception | LinkageError throwable) {
explodedBlocks.add(loc); explodedBlocks.add(loc);
new ErrorReport<>(t, loc, item); new ErrorReport<>(throwable, loc, item);
} }
long time = SlimefunPlugin.getProfiler().closeEntry(loc, item, timestamp); long time = SlimefunPlugin.getProfiler().closeEntry(loc, item, timestamp);
@ -238,7 +238,10 @@ public class EnergyNet extends Network {
} }
// Remove all generators which have exploded // Remove all generators which have exploded
if (!explodedBlocks.isEmpty()) {
generators.keySet().removeAll(explodedBlocks); generators.keySet().removeAll(explodedBlocks);
}
return supply; return supply;
} }
@ -254,10 +257,10 @@ public class EnergyNet extends Network {
private void updateHologram(@Nonnull Block b, double supply, double demand) { private void updateHologram(@Nonnull Block b, double supply, double demand) {
if (demand > supply) { 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"); SimpleHologram.update(b, "&4&l- &c" + netLoss + " &7J &e\u26A1");
} else { } else {
String netGain = DoubleHandler.getFancyDouble(supply - demand); String netGain = NumberUtils.getCompactDouble(supply - demand);
SimpleHologram.update(b, "&2&l+ &a" + netGain + " &7J &e\u26A1"); SimpleHologram.update(b, "&2&l+ &a" + netGain + " &7J &e\u26A1");
} }
} }

View File

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

View File

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

View File

@ -7,7 +7,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Keyed; import org.bukkit.Keyed;
import org.bukkit.Material; import org.bukkit.Material;
@ -20,10 +19,7 @@ import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin; 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.PaperLib;
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
import me.mrCookieSlime.Slimefun.api.Slimefun; 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(b, "The block cannot be null!");
Validate.notNull(value, "The value 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); * 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.
BlockStateSnapshotResult result = PaperLib.getBlockState(b, useSnapshot); */
BlockState state = result.getState(); BlockState state = b.getState();
if (state instanceof TileState) { if (state instanceof TileState) {
try { try {
PersistentDataContainer container = ((TileState) state).getPersistentDataContainer(); PersistentDataContainer container = ((TileState) state).getPersistentDataContainer();
container.set(namespacedKey, PersistentDataType.STRING, value); container.set(namespacedKey, PersistentDataType.STRING, value);
if (result.isSnapshot()) {
state.update(); state.update();
}
} catch (Exception x) { } catch (Exception x) {
Slimefun.getLogger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); 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 * @param b
* The {@link Block} to retrieve data from * The {@link Block} to retrieve data from
*
* @return The stored value * @return The stored value
*/ */
public Optional<String> getBlockData(@Nonnull Block b) { public Optional<String> getBlockData(@Nonnull Block b) {
Validate.notNull(b, "The block cannot be null!"); Validate.notNull(b, "The block cannot be null!");
BlockState state = PaperLib.getBlockState(b, false).getState(); BlockState state = PaperLib.getBlockState(b, false).getState();
PersistentDataContainer container = getPersistentDataContainer(state);
if (state instanceof TileState) { if (container != null) {
PersistentDataContainer container = ((TileState) state).getPersistentDataContainer();
return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING)); return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING));
} else { } else {
return Optional.empty(); 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 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} * 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 { public class CustomTextureService {
/**
* The {@link Config} object in which the Server Owner can configure the item models.
*/
private final Config config; 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; 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; private boolean modified = false;
/** /**
@ -110,6 +123,7 @@ public class CustomTextureService {
*/ */
public int getModelData(@Nonnull String id) { public int getModelData(@Nonnull String id) {
Validate.notNull(id, "Cannot get the ModelData for 'null'"); Validate.notNull(id, "Cannot get the ModelData for 'null'");
return config.getInt(id); 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. * in use, they will be cleaned up the next GC run.
*/ */
public void cleanUp() { public void cleanUp() {

View File

@ -24,8 +24,20 @@ import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
*/ */
public class UpdaterService { public class UpdaterService {
/**
* Our {@link SlimefunPlugin} instance.
*/
private final SlimefunPlugin plugin; private final SlimefunPlugin plugin;
/**
* Our {@link Updater} implementation.
*/
private final Updater updater; 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; private final SlimefunBranch branch;
/** /**

View File

@ -38,7 +38,7 @@ public class Contributor {
private final ComputedOptional<String> headTexture = ComputedOptional.createNew(); private final ComputedOptional<String> headTexture = ComputedOptional.createNew();
private Optional<UUID> uuid = Optional.empty(); 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. * This creates a new {@link Contributor} with the given ingame name and GitHub profile.
@ -76,7 +76,7 @@ public class Contributor {
* specified role. * specified role.
* *
* @param role * @param role
* The role * The role of this {@link Contributor}
* @param commits * @param commits
* The amount of contributions made as that role * The amount of contributions made as that role
*/ */
@ -84,7 +84,7 @@ public class Contributor {
Validate.notNull(role, "The role cannot be null!"); Validate.notNull(role, "The role cannot be null!");
Validate.isTrue(commits >= 0, "Contributions cannot be negative"); Validate.isTrue(commits >= 0, "Contributions cannot be negative");
if (!locked || role.startsWith("translator,")) { if (!immutable || role.startsWith("translator,")) {
contributions.put(role, commits); contributions.put(role, commits);
} }
} }
@ -120,6 +120,13 @@ public class Contributor {
return profileLink; 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 @Nonnull
public List<Map.Entry<String, Integer>> getContributions() { public List<Map.Entry<String, Integer>> getContributions() {
List<Map.Entry<String, Integer>> list = new ArrayList<>(contributions.entrySet()); 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 * @return The amount of contributions this {@link Contributor} submitted as the given role
*/ */
public int getContributions(@Nonnull String role) { public int getContributions(@Nonnull String role) {
Validate.notNull(role, "The role cannot be null!");
return contributions.getOrDefault(role, 0); return contributions.getOrDefault(role, 0);
} }
@ -214,16 +223,35 @@ public class Contributor {
return contributions.values().stream().mapToInt(Integer::intValue).sum(); 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 @Nonnull
public String getDisplayName() { public String getDisplayName() {
return ChatColor.GRAY + githubUsername + (!githubUsername.equals(minecraftUsername) ? ChatColor.DARK_GRAY + " (MC: " + minecraftUsername + ")" : ""); 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.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -37,6 +38,7 @@ public class GitHubService {
private boolean logging = false; private boolean logging = false;
private LocalDateTime lastUpdate = LocalDateTime.now(); private LocalDateTime lastUpdate = LocalDateTime.now();
private int openIssues = 0; private int openIssues = 0;
private int pendingPullRequests = 0; private int pendingPullRequests = 0;
private int publicForks = 0; private int publicForks = 0;
@ -56,9 +58,18 @@ public class GitHubService {
loadConnectors(false); 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) { public void start(@Nonnull SlimefunPlugin plugin) {
long period = TimeUnit.HOURS.toMillis(1);
GitHubTask task = new GitHubTask(this); 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(); 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 @Nullable
protected String getCachedTexture(@Nonnull String name) { protected String getCachedTexture(@Nonnull String username) {
return texturesCache.getString(name); return texturesCache.getString(username);
} }
} }

View File

@ -39,17 +39,23 @@ class GitHubTask implements Runnable {
@Override @Override
public void run() { public void run() {
gitHubService.getConnectors().forEach(GitHubConnector::download); connectAndCache();
grabTextures(); grabTextures();
} }
private void connectAndCache() {
gitHubService.getConnectors().forEach(GitHubConnector::download);
}
/** /**
* This method will pull the skin textures for every {@link Contributor} and store * 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}. * the {@link UUID} and received skin inside a local cache {@link File}.
*/ */
private void grabTextures() { 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<>(); Map<String, String> skins = new HashMap<>();
int requests = 0; 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(); gitHubService.saveCache();
} }

View File

@ -165,12 +165,17 @@ public class Translators {
// Translators - Tagalog // Translators - Tagalog
addTranslator("sccooottttie", SupportedLanguage.TAGALOG, true); addTranslator("sccooottttie", SupportedLanguage.TAGALOG, true);
// Translators - Portuguese
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_PORTUGAL, true);
// Translators - Portuguese (Brazil) // Translators - Portuguese (Brazil)
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("yurinogueira", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("yurinogueira", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Sakanas", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("Sakanas", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("krazybeat", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("krazybeat", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("FaolanMalcadh", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("FaolanMalcadh", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_BRAZIL, true);
} }
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
@ -183,7 +188,7 @@ public class Translators {
Contributor contributor = github.addContributor(minecraftName, "https://github.com/" + username, "translator," + lang.getLanguageId(), 0); Contributor contributor = github.addContributor(minecraftName, "https://github.com/" + username, "translator," + lang.getLanguageId(), 0);
if (lock) { 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.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -16,7 +15,6 @@ import org.bukkit.plugin.Plugin;
import com.gmail.nossr50.events.fake.FakeBlockBreakEvent; import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.Slimefun;
@ -38,7 +36,6 @@ public class ThirdPartyPluginService {
private boolean initialized = false; private boolean initialized = false;
private boolean isExoticGardenInstalled = false; private boolean isExoticGardenInstalled = false;
private boolean isChestTerminalInstalled = false; private boolean isChestTerminalInstalled = false;
private boolean isEmeraldEnchantsInstalled = false;
private boolean isMcMMOInstalled = 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 // WorldEdit Hook to clear Slimefun Data upon //set 0 //cut or any other equivalent
if (isPluginInstalled("WorldEdit")) { if (isPluginInstalled("WorldEdit")) {
try { try {
@ -149,10 +139,6 @@ public class ThirdPartyPluginService {
return isChestTerminalInstalled; return isChestTerminalInstalled;
} }
public boolean isEmeraldEnchantsInstalled() {
return isEmeraldEnchantsInstalled;
}
public Optional<ItemStack> harvestExoticGardenPlant(Block block) { public Optional<ItemStack> harvestExoticGardenPlant(Block block) {
return exoticGardenIntegration.apply(block); return exoticGardenIntegration.apply(block);
} }

View File

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

View File

@ -1,8 +1,11 @@
package io.github.thebusybiscuit.slimefun4.core.services.profiler; package io.github.thebusybiscuit.slimefun4.core.services.profiler;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; 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}. * This represents an entry in our {@link SlimefunProfiler}.
* It is a modification of {@link BlockPosition} to be as memory-efficient as possible.
* *
* @author TheBusyBiscuit * @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; 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) { ProfiledBlock(@Nonnull Location l, @Nonnull SlimefunItem item) {
this.position = new BlockPosition(l); this.world = l.getWorld();
this.item = item; this.position = getLocationAsLong((int) l.getX(), (int) l.getY(), (int) l.getZ());
}
ProfiledBlock(@Nonnull BlockPosition position, @Nonnull SlimefunItem item) {
this.position = position;
this.item = item; this.item = item;
} }
/** /**
* This is just a <strong>dummy</strong> constructor. * This is just a <strong>dummy</strong> constructor.
* Please only use this for comparisons or lookups.
* *
* @param b * @param b
* A {@link Block} * A {@link Block}
*/ */
ProfiledBlock(@Nonnull Block b) { 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; 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() { public String getId() {
return item.getId(); return item.getId();
} }
@Nonnull
public SlimefunAddon getAddon() { public SlimefunAddon getAddon() {
return item.getAddon(); return item.getAddon();
} }
@ -56,7 +144,8 @@ class ProfiledBlock {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof ProfiledBlock) { 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; return false;
@ -64,7 +153,8 @@ class ProfiledBlock {
@Override @Override
public int hashCode() { 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.Server;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitScheduler;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
@ -42,12 +43,27 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
*/ */
public class SlimefunProfiler { 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 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 final AtomicInteger queued = new AtomicInteger(0);
private long totalElapsedTime; private long totalElapsedTime;
@ -55,11 +71,20 @@ public class SlimefunProfiler {
private final Map<ProfiledBlock, Long> timings = new ConcurrentHashMap<>(); private final Map<ProfiledBlock, Long> timings = new ConcurrentHashMap<>();
private final Queue<CommandSender> requests = new ConcurrentLinkedQueue<>(); 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. * This method starts the profiling, data from previous runs will be cleared.
*/ */
public void start() { public void start() {
running.set(true); isProfiling.set(true);
queued.set(0); queued.set(0);
timings.clear(); timings.clear();
} }
@ -70,7 +95,7 @@ public class SlimefunProfiler {
* @return A timestamp, best fed back into {@link #closeEntry(Location, SlimefunItem, long)} * @return A timestamp, best fed back into {@link #closeEntry(Location, SlimefunItem, long)}
*/ */
public long newEntry() { public long newEntry() {
if (!running.get()) { if (!isProfiling.get()) {
return 0; return 0;
} }
@ -89,7 +114,7 @@ public class SlimefunProfiler {
* The amount of entries that should be scheduled. Can be negative * The amount of entries that should be scheduled. Can be negative
*/ */
public void scheduleEntries(int amount) { public void scheduleEntries(int amount) {
if (running.get()) { if (isProfiling.get()) {
queued.getAndAdd(amount); queued.getAndAdd(amount);
} }
} }
@ -132,15 +157,13 @@ public class SlimefunProfiler {
* This stops the profiling. * This stops the profiling.
*/ */
public void stop() { public void stop() {
running.set(false); isProfiling.set(false);
if (SlimefunPlugin.instance() == null || !SlimefunPlugin.instance().isEnabled()) { if (SlimefunPlugin.instance() == null || !SlimefunPlugin.instance().isEnabled()) {
// Slimefun has been disabled // Slimefun has been disabled
return; return;
} }
// Since we got more than one Thread in our pool,
// blocking this one is (hopefully) completely fine
executor.execute(this::finishReport); executor.execute(this::finishReport);
} }
@ -149,8 +172,12 @@ public class SlimefunProfiler {
int iterations = 4000; int iterations = 4000;
// Wait for all timing results to come in // Wait for all timing results to come in
while (!running.get() && queued.get() > 0) { while (!isProfiling.get() && queued.get() > 0) {
try { try {
/**
* Since we got more than one Thread in our pool,
* blocking this one is (hopefully) completely fine
*/
Thread.sleep(1); Thread.sleep(1);
iterations--; 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.next().sendMessage("Your timings report has timed out, we were still waiting for " + queued.get() + " samples to be collected :/");
iterator.remove(); iterator.remove();
} }
return; return;
} }
} catch (InterruptedException e) { } 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! // Looks like the next profiling has already started, abort!
return; return;
} }
@ -228,9 +256,10 @@ public class SlimefunProfiler {
Map<String, Long> map = new HashMap<>(); Map<String, Long> map = new HashMap<>();
for (Map.Entry<ProfiledBlock, Long> entry : timings.entrySet()) { for (Map.Entry<ProfiledBlock, Long> entry : timings.entrySet()) {
String world = entry.getKey().getPosition().getWorld().getName(); ProfiledBlock block = entry.getKey();
int x = entry.getKey().getPosition().getChunkX(); String world = block.getWorld().getName();
int z = entry.getKey().getPosition().getChunkZ(); int x = block.getChunkX();
int z = block.getChunkZ();
map.merge(world + " (" + x + ',' + z + ')', entry.getValue(), Long::sum); map.merge(world + " (" + x + ',' + z + ')', entry.getValue(), Long::sum);
} }
@ -243,9 +272,9 @@ public class SlimefunProfiler {
int blocks = 0; int blocks = 0;
for (ProfiledBlock block : timings.keySet()) { for (ProfiledBlock block : timings.keySet()) {
String world = block.getPosition().getWorld().getName(); String world = block.getWorld().getName();
int x = block.getPosition().getChunkX(); int x = block.getChunkX();
int z = block.getPosition().getChunkZ(); int z = block.getChunkZ();
if (chunk.equals(world + " (" + x + ',' + z + ')')) { if (chunk.equals(world + " (" + x + ',' + z + ')')) {
blocks++; blocks++;
@ -284,6 +313,7 @@ public class SlimefunProfiler {
protected float getPercentageOfTick() { protected float getPercentageOfTick() {
float millis = totalElapsedTime / 1000000.0F; float millis = totalElapsedTime / 1000000.0F;
float fraction = (millis * 100.0F) / MAX_TICK_DURATION; float fraction = (millis * 100.0F) / MAX_TICK_DURATION;
return Math.round((fraction * 100.0F) / 100.0F); return Math.round((fraction * 100.0F) / 100.0F);
} }
@ -305,6 +335,7 @@ public class SlimefunProfiler {
return PerformanceRating.UNKNOWN; return PerformanceRating.UNKNOWN;
} }
@Nonnull
public String getTime() { public String getTime() {
return NumberUtils.getAsMillis(totalElapsedTime); return NumberUtils.getAsMillis(totalElapsedTime);
} }
@ -324,6 +355,7 @@ public class SlimefunProfiler {
*/ */
public boolean hasTimings(@Nonnull Block b) { public boolean hasTimings(@Nonnull Block b) {
Validate.notNull("Cannot get timings for a null Block"); Validate.notNull("Cannot get timings for a null Block");
return timings.containsKey(new ProfiledBlock(b)); 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.MachineTier;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactivity; 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.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.LoreBuilder; 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 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 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 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 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_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"); 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.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin; 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.BeeWingsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener; 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.CargoNodeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DispenserListener; 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.EnhancedFurnaceListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EntityInteractionListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.EntityInteractionListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener; 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.GadgetsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.IronGolemListener; 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.ItemPickupListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener; 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.PiglinListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener; 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.SlimefunBowListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemConsumeListener; 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.SoulboundListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VampireBladeListener; 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.implementation.tasks.TickerTask;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib; 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.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
import me.mrCookieSlime.Slimefun.api.BlockStorage; 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 the main class of Slimefun.
* This is where all the magic starts, take a look around. * This is where all the magic starts, take a look around.
* Feel like home.
* *
* @author TheBusyBiscuit * @author TheBusyBiscuit
*/ */
@ -163,15 +166,36 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private final BackpackListener backpackListener = new BackpackListener(); private final BackpackListener backpackListener = new BackpackListener();
private final SlimefunBowListener bowListener = new SlimefunBowListener(); private final SlimefunBowListener bowListener = new SlimefunBowListener();
/**
* Our default constructor for {@link SlimefunPlugin}.
*/
public SlimefunPlugin() { public SlimefunPlugin() {
super(); 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) { public SlimefunPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file); super(loader, description, dataFolder, file);
// This is only invoked during a Unit Test
minecraftVersion = MinecraftVersion.UNIT_TEST; minecraftVersion = MinecraftVersion.UNIT_TEST;
} }
/**
* This is called when the {@link Plugin} has been loaded and enabled on a {@link Server}.
*/
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
@ -220,7 +244,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
networkSize = 1; 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 // Setting up bStats
new Thread(metricsService::start, "Slimefun Metrics").start(); 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() { private void onUnitTestStart() {
local = new LocalizationService(this, "", null); local = new LocalizationService(this, "", null);
gpsNetwork = new GPSNetwork(); gpsNetwork = new GPSNetwork();
networkManager = new NetworkManager(200, true); networkManager = new NetworkManager(200);
command.register(); command.register();
registry.load(config); 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 @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, "### Slimefun was not installed correctly!");
getLogger().log(Level.SEVERE, "### You are using the wrong version of Minecraft!"); getLogger().log(Level.SEVERE, "### You are using the wrong version of Minecraft!");
getLogger().log(Level.SEVERE, "###"); getLogger().log(Level.SEVERE, "###");
getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", ReflectionUtils.getVersion()); getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", currentVersion);
getLogger().log(Level.SEVERE, "### but Slimefun v{0} requires you to be using", getDescription().getVersion()); 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, "### Minecraft {0}", String.join(" / ", getSupportedVersions()));
getLogger().log(Level.SEVERE, "#############################################"); getLogger().log(Level.SEVERE, "#############################################");
return true; return true;
@ -361,7 +460,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
for (MinecraftVersion version : MinecraftVersion.valuesCache) { for (MinecraftVersion version : MinecraftVersion.valuesCache) {
if (version != MinecraftVersion.UNKNOWN) { if (!version.isVirtual()) {
list.add(version.getName()); list.add(version.getName());
} }
} }
@ -369,65 +468,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return list; 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() { private void createDirectories() {
String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" }; String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" };
String[] pluginFolders = { "scripts", "error-reports", "cache/github", "world-settings" }; 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() { private void registerListeners() {
new SlimefunBootsListener(this); new SlimefunBootsListener(this);
new SlimefunItemListener(this); new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this); new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this); new BlockPhysicsListener(this);
new CargoNodeListener(this); new CargoNodeListener(this);
@ -461,6 +504,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new BlockListener(this); new BlockListener(this);
new EnhancedFurnaceListener(this); new EnhancedFurnaceListener(this);
new ItemPickupListener(this); new ItemPickupListener(this);
new ItemDropListener(this);
new DeathpointListener(this); new DeathpointListener(this);
new ExplosionsListener(this); new ExplosionsListener(this);
new DebugFishListener(this); new DebugFishListener(this);
@ -470,19 +514,23 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new EntityInteractionListener(this); new EntityInteractionListener(this);
new MobDropListener(this); new MobDropListener(this);
new VillagerTradingListener(this); new VillagerTradingListener(this);
new ElytraCrashListener(this); new ElytraImpactListener(this);
new CraftingTableListener(this); new CraftingTableListener(this);
new AnvilListener(this); new AnvilListener(this);
new BrewingStandListener(this); new BrewingStandListener(this);
new CauldronListener(this); new CauldronListener(this);
new GrindstoneListener(this); new GrindstoneListener(this);
new CartographyTableListener(this); new CartographyTableListener(this);
new ButcherAndroidListener(this);
new NetworkListener(this, networkManager);
// Bees were added in 1.15
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) { if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) {
new BeeListener(this); new BeeListener(this);
new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem()); new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem());
} }
// Piglins were added in 1.16
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) {
new PiglinListener(this); new PiglinListener(this);
} }
@ -518,7 +566,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private void loadTags() { private void loadTags() {
for (SlimefunTag tag : SlimefunTag.valuesCache) { for (SlimefunTag tag : SlimefunTag.valuesCache) {
try { try {
// Only reload "empty" (or unloaded) Tags
if (tag.isEmpty()) {
tag.reload(); tag.reload();
}
} catch (TagMisconfigurationException e) { } catch (TagMisconfigurationException e) {
getLogger().log(Level.SEVERE, e, () -> "Failed to load Tag: " + tag.name()); 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; return instance.isNewlyInstalled;
} }
@Nonnull
public static String getCSCoreLibVersion() { 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 @Override

View File

@ -215,11 +215,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
ChatComponent component = new ChatComponent(ChatUtils.crop(ChatColor.RED, item.getItemName()) + "\n"); 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.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 -> component.setClickEvent(new ClickEvent(key, player -> SlimefunPlugin.runSync(() -> research.unlockFromGuide(this, player, profile, item, category, page))));
SlimefunPlugin.runSync(() ->
research.unlockFromGuide(this, player, profile, item, category, page)
)
));
items.add(component); items.add(component);
} else { } else {

View File

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

View File

@ -151,7 +151,7 @@ public final class Script {
@Nonnull @Nonnull
private String getScriptRatingPercentage() { private String getScriptRatingPercentage() {
float percentage = getRating(); 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.ProtectionType;
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; 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.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -22,7 +22,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* *
* @author Seggan * @author Seggan
* *
* @see ElytraCrashListener * @see ElytraImpactListener
*/ */
public class ElytraCap extends SlimefunArmorPiece implements DamageableItem, ProtectiveArmor { public class ElytraCap extends SlimefunArmorPiece implements DamageableItem, ProtectiveArmor {

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.armor; package io.github.thebusybiscuit.slimefun4.implementation.items.armor;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Jetpack; 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 { public class Parachute extends SlimefunItem {
@ParametersAreNonnullByDefault
public Parachute(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { public Parachute(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, RecipeType.ENHANCED_CRAFTING_TABLE, recipe); super(category, item, RecipeType.ENHANCED_CRAFTING_TABLE, recipe);
} }

View File

@ -52,7 +52,7 @@ public class StomperBoots extends SlimefunItem {
n.setVelocity(velocity); n.setVelocity(velocity);
// Check if it's not a Player or if PvP is enabled // 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); EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(p, n, DamageCause.ENTITY_ATTACK, fallDamageEvent.getDamage() / 2);
Bukkit.getPluginManager().callEvent(event); 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(); Material material = e.getItem().getType();
if (SlimefunTag.SHULKER_BOXES.isTagged(material)) { 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; return;
} }
e.setCancelled(true); e.setCancelled(true);
if (!material.isBlock() || SlimefunTag.BLOCK_PLACER_IGNORED_MATERIALS.isTagged(material)) { 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. * Some materials cannot be reliably placed, like beds,
// The event has already been cancelled too, so they won't drop. * it would look kinda wonky, so we just ignore these altogether.
* The event has already been cancelled too, so they won't drop.
*/
return; return;
} }
if (facedBlock.isEmpty() && !isBlacklisted(material)) { if (facedBlock.isEmpty() && !isBlacklisted(material) && dispenser.getInventory().getViewers().isEmpty()) {
SlimefunItem item = SlimefunItem.getByItem(e.getItem()); SlimefunItem item = SlimefunItem.getByItem(e.getItem());
if (item != null) { if (item != null) {
@ -123,9 +127,11 @@ public class BlockPlacer extends SlimefunItem {
String owner = BlockStorage.getLocationInfo(dispenser.getLocation(), "owner"); String owner = BlockStorage.getLocationInfo(dispenser.getLocation(), "owner");
if (owner == null) { 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 * If no owner was set, then we will fallback to the previous behaviour:
// will respect protection plugins. * Allowing block placers to bypass protection, newly placed Block placers
* will respect protection plugins.
*/
return true; return true;
} }

View File

@ -1,7 +1,13 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.ItemStack; 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.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -17,10 +23,18 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see RepairedSpawner * @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) { public BrokenSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, 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(); Player p = e.getPlayer();
Block b = block.get(); 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 input = e.getItem();
ItemStack output = getOutput(p, input); ItemStack output = getOutput(p, input);

View File

@ -95,7 +95,7 @@ public class Crucible extends SimpleSlimefunItem<BlockUseHandler> implements Rec
Player p = e.getPlayer(); Player p = e.getPlayer();
Block b = optional.get(); 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(); ItemStack input = e.getItem();
Block block = b.getRelative(BlockFace.UP); 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.ArrayList;
import java.util.Collection; 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.Material;
import org.bukkit.block.CreatureSpawner; import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType;
import org.bukkit.event.block.BlockEvent; import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; 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.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.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -28,14 +29,32 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see BrokenSpawner * @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) { public RepairedSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
addItemSetting(allowSpawnEggs);
addItemHandler(onInteract());
addItemHandler(onPlace());
} }
@Override @Nonnull
public BlockPlaceHandler getItemHandler() { 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) { return new BlockPlaceHandler(true) {
@Override @Override
@ -48,42 +67,29 @@ public class RepairedSpawner extends SimpleSlimefunItem<BlockPlaceHandler> {
onPlace(e.getItemStack(), e); onPlace(e.getItemStack(), e);
} }
@ParametersAreNonnullByDefault
private void onPlace(ItemStack item, BlockEvent e) { private void onPlace(ItemStack item, BlockEvent e) {
Optional<EntityType> entity = getEntityType(item); /**
* This may no longer be needed at some point but for legacy items
if (entity.isPresent() && e.getBlock().getType() == Material.SPAWNER) { * 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(); CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState();
spawner.setSpawnedType(entity.get()); spawner.setSpawnedType(entity);
spawner.update(true, false); 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 @Override
public Collection<ItemStack> getDrops() { 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<>(); 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. * {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} which are blocks.
* Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.InfusedHopper} or the * Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example.
* {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example.
*/ */
package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;

View File

@ -1,5 +1,9 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.cargo; 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.block.Block;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockPlaceEvent; 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.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; 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.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial; 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 io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
@ -29,14 +33,42 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
* @author TheBusyBiscuit * @author TheBusyBiscuit
* *
*/ */
abstract class AbstractCargoNode extends SlimefunItem { abstract class AbstractCargoNode extends SimpleSlimefunItem<BlockPlaceHandler> {
protected static final String FREQUENCY = "frequency"; protected static final String FREQUENCY = "frequency";
@ParametersAreNonnullByDefault
public AbstractCargoNode(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { public AbstractCargoNode(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, 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 @Override
public void onPlayerPlace(BlockPlaceEvent e) { public void onPlayerPlace(BlockPlaceEvent e) {
@ -49,32 +81,10 @@ abstract class AbstractCargoNode extends SlimefunItem {
onPlace(e); 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) { protected void addChannelSelector(Block b, BlockMenu menu, int slotPrev, int slotCurrent, int slotNext) {
boolean isChestTerminalInstalled = SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled(); boolean isChestTerminalInstalled = SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled();
int channel = getSelectedChannel(b); 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)) { if (!BlockStorage.hasBlockInfo(b)) {
return 0; return 0;
} else { } 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; package io.github.thebusybiscuit.slimefun4.implementation.items.cargo;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
@ -65,7 +69,8 @@ abstract class AbstractFilterNode extends AbstractCargoNode {
@Override @Override
protected void updateBlockMenu(BlockMenu menu, Block b) { 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")) { 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")); 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); 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; package io.github.thebusybiscuit.slimefun4.implementation.items.cargo;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
@ -38,4 +39,9 @@ public class CargoOutputNode extends AbstractCargoNode {
addChannelSelector(b, menu, 12, 13, 14); 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 @Override
public boolean canOpen(Block b, Player p) { 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 @Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -80,7 +80,7 @@ public abstract class AbstractEntityAssembler<T extends Entity> extends SimpleSl
@Override @Override
public boolean canOpen(Block b, Player p) { 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 @Override
@ -183,7 +183,7 @@ public abstract class AbstractEntityAssembler<T extends Entity> extends SimpleSl
return; return;
} }
if (lifetime % 60 == 0 && getCharge(b.getLocation()) >= getEnergyConsumption()) { if (lifetime % 60 == 0 && getCharge(b.getLocation(), data) >= getEnergyConsumption()) {
BlockMenu menu = BlockStorage.getInventory(b); BlockMenu menu = BlockStorage.getInventory(b);
boolean hasBody = findResource(menu, getBody(), bodySlots); boolean hasBody = findResource(menu, getBody(), bodySlots);

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack; 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 }; 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) { public AbstractGrowthAccelerator(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);

View File

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

View File

@ -1,9 +1,9 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
@ -11,9 +11,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils; 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.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -24,12 +21,9 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
public class AutoEnchanter extends AContainer { public class AutoEnchanter extends AContainer {
private final int emeraldEnchantsLimit; @ParametersAreNonnullByDefault
public AutoEnchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { public AutoEnchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
emeraldEnchantsLimit = SlimefunPlugin.getCfg().getInt("options.emerald-enchantment-limit");
} }
@Override @Override
@ -51,9 +45,7 @@ public class AutoEnchanter extends AContainer {
if (item != null && item.getType() == Material.ENCHANTED_BOOK && target != null) { if (item != null && item.getType() == Material.ENCHANTED_BOOK && target != null) {
Map<Enchantment, Integer> enchantments = new HashMap<>(); Map<Enchantment, Integer> enchantments = new HashMap<>();
Set<ItemEnchantment> emeraldEnchantments = new HashSet<>();
int amount = 0; int amount = 0;
int specialAmount = 0;
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta();
for (Map.Entry<Enchantment, Integer> e : meta.getStoredEnchants().entrySet()) { for (Map.Entry<Enchantment, Integer> e : meta.getStoredEnchants().entrySet()) {
@ -63,19 +55,7 @@ public class AutoEnchanter extends AContainer {
} }
} }
if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) { if (amount > 0) {
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) {
ItemStack enchantedItem = target.clone(); ItemStack enchantedItem = target.clone();
enchantedItem.setAmount(1); enchantedItem.setAmount(1);
@ -83,10 +63,6 @@ public class AutoEnchanter extends AContainer {
enchantedItem.addUnsafeEnchantment(entry.getKey(), entry.getValue()); 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) }); 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())) { 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; 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.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; 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.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; 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. * This class needs to be rewritten VERY BADLY.
* But we should focus on rewriting the recipe system first. * But we should focus on rewriting the recipe system first.
* *
* @deprecated This is horribly done. Someone needs to rewrite this.
*
* @author TheBusyBiscuit * @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[] 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 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) { public AutomatedCraftingChamber(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
@ -86,7 +94,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
@Override @Override
public boolean canOpen(Block b, Player p) { 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 @Override
@ -267,7 +275,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I
private void testInputAgainstRecipes(Block block, String input) { private void testInputAgainstRecipes(Block block, String input) {
BlockMenu menu = BlockStorage.getInventory(block); BlockMenu menu = BlockStorage.getInventory(block);
ItemStack output = SlimefunPlugin.getRegistry().getAutomatedCraftingChamberRecipes().get(input); ItemStack output = craftingRecipes.get(input);
if (output != null && menu.fits(output, getOutputSlots())) { if (output != null && menu.fits(output, getOutputSlots())) {
menu.pushItem(output.clone(), getOutputSlots()); menu.pushItem(output.clone(), getOutputSlots());
removeCharge(block.getLocation(), getEnergyConsumption()); 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; package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; 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.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; 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 { public class ElectricPress extends AContainer implements RecipeDisplayItem {
@ParametersAreNonnullByDefault
public ElectricPress(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { public ElectricPress(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, 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.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.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.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(6, SlimefunItems.COPPER_INGOT, new CustomItem(SlimefunItems.COPPER_WIRE, 3));
addRecipe(16, new SlimefunItemStack(SlimefunItems.STEEL_INGOT, 8), SlimefunItems.STEEL_PLATE); 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)); addRecipe(8, new ItemStack(Material.DIAMOND, 9), new ItemStack(Material.DIAMOND_BLOCK));
} }
@ParametersAreNonnullByDefault
private void addRecipe(int seconds, ItemStack input, ItemStack output) { private void addRecipe(int seconds, ItemStack input, ItemStack output) {
registerRecipe(seconds, new ItemStack[] { input }, new ItemStack[] { output }); registerRecipe(seconds, new ItemStack[] { input }, new ItemStack[] { output });
} }

View File

@ -53,7 +53,7 @@ public class ElectricSmeltery extends AContainer {
@Override @Override
public boolean canOpen(Block b, Player p) { 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 @Override

View File

@ -2,6 +2,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine
import java.util.List; import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@ -13,12 +17,12 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.blocks.Vein; 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.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; 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.Configuration.Config;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; 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[] 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 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) { public FluidPump(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, 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) { 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) { 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) { 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()) { for (int i : getOutputSlots()) {
@ -114,14 +121,14 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
return 512; return 512;
} }
protected void tick(Block b) { protected void tick(@Nonnull Block b) {
Block fluid = b.getRelative(BlockFace.DOWN); Block fluid = b.getRelative(BlockFace.DOWN);
if (fluid.isLiquid() && getCharge(b.getLocation()) >= ENERGY_CONSUMPTION) { if (fluid.isLiquid() && getCharge(b.getLocation()) >= ENERGY_CONSUMPTION) {
BlockMenu menu = BlockStorage.getInventory(b); BlockMenu menu = BlockStorage.getInventory(b);
for (int slot : getInputSlots()) { 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); ItemStack bucket = getFilledBucket(fluid);
if (!menu.fits(bucket, getOutputSlots())) { 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) { 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 * With water we can be sure to find an infinite source whenever we
// ourselves all of that computing... * go further than a block, so we can just remove the water here and
* save ourselves all of that computing...
*/
if (isSource(fluid)) { if (isSource(fluid)) {
return fluid; return fluid;
} }
@ -162,10 +172,12 @@ public class FluidPump extends SimpleSlimefunItem<BlockTicker> implements Invent
} }
} }
} }
return null; return null;
} }
private ItemStack getFilledBucket(Block fluid) { @Nonnull
private ItemStack getFilledBucket(@Nonnull Block fluid) {
if (fluid.getType() == Material.LAVA) { if (fluid.getType() == Material.LAVA) {
return new ItemStack(Material.LAVA_BUCKET); return new ItemStack(Material.LAVA_BUCKET);
} else if (fluid.getType() == Material.WATER || fluid.getType() == Material.BUBBLE_COLUMN) { } 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}. * @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()) { if (block.isLiquid()) {
BlockData data = block.getBlockData(); BlockData data = block.getBlockData();

View File

@ -37,7 +37,7 @@ public class HeatedPressureChamber extends AContainer {
@Override @Override
public boolean canOpen(Block b, Player p) { 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 @Override

View File

@ -98,7 +98,7 @@ public abstract class Reactor extends AbstractEnergyProvider {
@Override @Override
public boolean canOpen(Block b, Player p) { 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 @Override

View File

@ -45,6 +45,6 @@ public class GEOScanner extends SimpleSlimefunItem<BlockUseHandler> {
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
private boolean hasAccess(Player p, Location l) { 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 @Override
public boolean canOpen(Block b, Player p) { 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; 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.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; 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> { public class ElevatorPlate extends SimpleSlimefunItem<BlockUseHandler> {
/**
* This is our key for storing the floor name.
*/
private static final String DATA_KEY = "floor"; 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<>(); private final Set<UUID> users = new HashSet<>();
@ParametersAreNonnullByDefault
public ElevatorPlate(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { public ElevatorPlate(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput); super(category, item, recipeType, recipe, recipeOutput);
addItemHandler(onPlace()); addItemHandler(onPlace());
} }
@Nonnull
private BlockPlaceHandler onPlace() { private BlockPlaceHandler onPlace() {
return new BlockPlaceHandler(false) { return new BlockPlaceHandler(false) {
@Override @Override
public void onPlayerPlace(BlockPlaceEvent e) { public void onPlayerPlace(BlockPlaceEvent e) {
Block b = e.getBlock(); 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()); BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString());
} }
}; };
} }
@Nonnull
public Set<UUID> getUsers() {
return users;
}
@Override @Override
public BlockUseHandler getItemHandler() { public BlockUseHandler getItemHandler() {
return e -> { return e -> {
@ -172,7 +185,7 @@ public class ElevatorPlate extends SimpleSlimefunItem<BlockUseHandler> {
public void openEditor(Player p, Block b) { public void openEditor(Player p, Block b) {
ChestMenu menu = new ChestMenu("Elevator Settings"); 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) -> { menu.addMenuClickHandler(4, (pl, slot, item, action) -> {
pl.closeInventory(); pl.closeInventory();
pl.sendMessage(""); pl.sendMessage("");

View File

@ -43,6 +43,6 @@ public class GPSControlPanel extends SimpleSlimefunItem<BlockUseHandler> {
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
private boolean hasAccess(Player p, Location l) { 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 @Override
public void tick(Block b, SlimefunItem item, Config data) { 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")); UUID owner = UUID.fromString(BlockStorage.getLocationInfo(b.getLocation(), "owner"));
if (charge >= getEnergyConsumption()) { 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.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.SoundCategory; import org.bukkit.SoundCategory;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.data.type.Hopper;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Item; import org.bukkit.entity.Item;
import org.bukkit.inventory.ItemStack; 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.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; 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> { public class InfusedHopper extends SimpleSlimefunItem<BlockTicker> {
private final ItemSetting<Boolean> silent = new ItemSetting<>("silent", false); 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); 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) { public InfusedHopper(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
addItemSetting(silent, radius); addItemSetting(silent, radius, toggleable);
} }
@Override @Override
@ -45,17 +61,35 @@ public class InfusedHopper extends SimpleSlimefunItem<BlockTicker> {
return; return;
} }
Location l = b.getLocation().add(0.5, 1.2, 0.5); // Check if this was enabled in the config
boolean sound = false; if (toggleable.getValue()) {
double range = radius.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))) { for (Entity item : b.getWorld().getNearbyEntities(l, range, range, range, n -> isValidItem(l, n))) {
item.setVelocity(new Vector(0, 0.1, 0)); item.setVelocity(new Vector(0, 0.1, 0));
item.teleport(l); 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); 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()) { if (entity instanceof Item && entity.isValid()) {
Item item = (Item) entity; Item item = (Item) entity;
return !SlimefunUtils.hasNoPickupFlag(item) && item.getLocation().distanceSquared(l) > 0.25; return !SlimefunUtils.hasNoPickupFlag(item) && item.getLocation().distanceSquared(l) > 0.25;

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.magical; package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
@ -14,6 +15,7 @@ import org.bukkit.entity.ZombieVillager;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; 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.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; 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 { public class MagicalZombiePills extends SimpleSlimefunItem<EntityInteractHandler> implements NotPlaceable {
@ParametersAreNonnullByDefault
public MagicalZombiePills(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { public MagicalZombiePills(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput); super(category, item, recipeType, recipe, recipeOutput);
@ -50,6 +53,12 @@ public class MagicalZombiePills extends SimpleSlimefunItem<EntityInteractHandler
public EntityInteractHandler getItemHandler() { public EntityInteractHandler getItemHandler() {
return (e, item, offhand) -> { return (e, item, offhand) -> {
Entity entity = e.getRightClicked(); 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(); Player p = e.getPlayer();
if (entity instanceof ZombieVillager) { 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.NotPlaceable;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; 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 io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; 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.ArrayList;
import java.util.Collection; 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.Collection;
import java.util.Optional; 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.core.handlers.ItemDropHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; 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 io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; 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; import java.util.concurrent.ThreadLocalRandom;
@ -10,7 +10,9 @@ import org.bukkit.entity.Villager.Profession;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; 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.core.handlers.EntityInteractHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
@ -33,6 +35,11 @@ public class VillagerRune extends SimpleSlimefunItem<EntityInteractHandler> {
@Override @Override
public EntityInteractHandler getItemHandler() { public EntityInteractHandler getItemHandler() {
return (e, item, offhand) -> { 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) { if (e.getRightClicked() instanceof Villager) {
Villager v = (Villager) e.getRightClicked(); 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; import java.util.List;
@ -72,7 +72,7 @@ public class StormStaff extends SimpleSlimefunItem<ItemUseHandler> {
Location loc = p.getTargetBlock(null, 30).getLocation(); Location loc = p.getTargetBlock(null, 30).getLocation();
if (loc.getWorld() != null && loc.getChunk().isLoaded()) { 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(); e.cancel();
useItem(p, item, loc); useItem(p, item, loc);
} else { } 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.entity.Player;
import org.bukkit.inventory.ItemStack; 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.Bukkit;
import org.bukkit.Effect; import org.bukkit.Effect;
@ -8,6 +8,8 @@ import org.bukkit.entity.Player;
import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.inventory.ItemStack; 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.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; 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.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; 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> { 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) { public WindStaff(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
addItemSetting(multiplier);
} }
@Override @Override
@ -27,6 +39,7 @@ public class WindStaff extends SimpleSlimefunItem<ItemUseHandler> {
Player p = e.getPlayer(); Player p = e.getPlayer();
if (p.getFoodLevel() >= 2) { if (p.getFoodLevel() >= 2) {
// The isItem() check is here to prevent the MultiTool from consuming hunger
if (isItem(e.getItem()) && p.getGameMode() != GameMode.CREATIVE) { if (isItem(e.getItem()) && p.getGameMode() != GameMode.CREATIVE) {
FoodLevelChangeEvent event = new FoodLevelChangeEvent(p, p.getFoodLevel() - 2); FoodLevelChangeEvent event = new FoodLevelChangeEvent(p, p.getFoodLevel() - 2);
Bukkit.getPluginManager().callEvent(event); 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().playSound(p.getLocation(), Sound.ENTITY_TNT_PRIMED, 1, 1);
p.getWorld().playEffect(p.getLocation(), Effect.SMOKE, 1); p.getWorld().playEffect(p.getLocation(), Effect.SMOKE, 1);
p.setFallDistance(0F); 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 * @param item
* The {@link ItemStack} to find an {@link Enchantment} for * 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 * @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.EntityInteractHandler;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; 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.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;

View File

@ -4,6 +4,10 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.BlockFace; 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.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack; import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; 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.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -35,20 +40,24 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see ArmorForge * @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); 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"); Inventory fakeInv = Bukkit.createInventory(null, 9, "Fake Inventory");
for (int j = 0; j < inv.getContents().length; j++) { for (int j = 0; j < inv.getContents().length; j++) {
ItemStack stack = inv.getContents()[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) { if (stack != null) {
stack = stack.clone(); stack = stack.clone();
ItemUtils.consumeItem(stack, true); ItemUtils.consumeItem(stack, true);
@ -60,18 +69,24 @@ abstract class BackpackCrafter extends MultiBlockMachine {
return fakeInv; return fakeInv;
} }
@ParametersAreNonnullByDefault
protected void upgradeBackpack(Player p, Inventory inv, SlimefunBackpack backpack, ItemStack output) { protected void upgradeBackpack(Player p, Inventory inv, SlimefunBackpack backpack, ItemStack output) {
ItemStack backpackItem = null; ItemStack input = null;
for (int j = 0; j < 9; j++) { 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) { 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; break;
} }
} }
// Fixes #2574 - Carry over the Soulbound status
if (SlimefunUtils.isSoulbound(input)) {
SlimefunUtils.setSoulbound(output, true);
}
int size = backpack.getSize(); int size = backpack.getSize();
Optional<String> id = retrieveID(backpackItem, size); Optional<String> id = retrieveID(input, size);
if (id.isPresent()) { if (id.isPresent()) {
for (int line = 0; line < output.getItemMeta().getLore().size(); line++) { 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) { if (backpack != null) {
for (String line : backpack.getItemMeta().getLore()) { for (String line : backpack.getItemMeta().getLore()) {
if (line.startsWith(ChatColors.color("&7ID: ")) && line.contains("#")) { 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.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class ArmorForge extends BackpackCrafter { public class ArmorForge extends AbstractCraftingTable {
public ArmorForge(Category category, SlimefunItemStack item) { 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); 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.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class EnhancedCraftingTable extends BackpackCrafter { public class EnhancedCraftingTable extends AbstractCraftingTable {
public EnhancedCraftingTable(Category category, SlimefunItemStack item) { 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); 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.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class MagicWorkbench extends BackpackCrafter { public class MagicWorkbench extends AbstractCraftingTable {
public MagicWorkbench(Category category, SlimefunItemStack item) { 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); 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