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

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

This commit is contained in:
TheBusyBiscuit 2020-12-09 18:42:42 +01:00 committed by GitHub
commit 0c1485aa7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 1827 additions and 1655 deletions

View File

@ -2,7 +2,7 @@
name: Bug Report name: Bug Report
about: Report a Bug or an Issue with Slimefun 4. about: Report a Bug or an Issue with Slimefun 4.
title: '' title: ''
labels: Bug Report labels: '🐞 Bug Report'
assignees: '' assignees: ''
--- ---

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

@ -2,9 +2,7 @@
"extends": [ "extends": [
"config:base" "config:base"
], ],
"assignees": [
],
"labels": [ "labels": [
"Dependency Update" "🚨 Dependency Update"
] ]
} }

View File

@ -26,4 +26,4 @@ jobs:
with: with:
token: ${{ secrets.ACCESS_TOKEN }} token: ${{ secrets.ACCESS_TOKEN }}
issue_number: ${{ github.event.pull_request.number }} issue_number: ${{ github.event.pull_request.number }}
labels: 'Translations Update' labels: '📄 Translations Update'

View File

@ -10,7 +10,7 @@ jobs:
name: Invalid Issues name: Invalid Issues
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: contains(github.event.issue.labels.*.name, 'Bug Report') == false && contains(github.event.issue.labels.*.name, 'Hacktoberfest') == false if: contains(github.event.issue.labels.*.name, '🐞 Bug Report') == false && contains(github.event.issue.labels.*.name, 'Hacktoberfest') == false
steps: steps:
- name: Close Issue - name: Close Issue
uses: maxkomarychev/octions/octions/issues/update@master uses: maxkomarychev/octions/octions/issues/update@master

View File

@ -18,4 +18,4 @@ jobs:
with: with:
token: ${{ secrets.ACCESS_TOKEN }} token: ${{ secrets.ACCESS_TOKEN }}
issue_number: ${{ github.event.issue.number }} issue_number: ${{ github.event.issue.number }}
labels: 'Duplicate' labels: '🚩 Duplicate'

View File

@ -14,5 +14,5 @@ jobs:
steps: steps:
- uses: mschilde/auto-label-merge-conflicts@master - uses: mschilde/auto-label-merge-conflicts@master
with: with:
CONFLICT_LABEL_NAME: 'Merge Conflicts' CONFLICT_LABEL_NAME: 'Merge Conflicts'
GITHUB_TOKEN: "${{ secrets.ACCESS_TOKEN }}" GITHUB_TOKEN: "${{ secrets.ACCESS_TOKEN }}"

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,7 +24,13 @@
<!-- 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
@ -33,6 +40,8 @@
* Added a config option to delete excess cargo network items * 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 configure the Wind Staff velocity
* Added an item setting to the Infused Hopper to toggle it with redstone * 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
@ -47,6 +56,8 @@
* Improved performance for radioactive items * Improved performance for radioactive items
* Memory/GC improvements for the profiler * Memory/GC improvements for the profiler
* Performance improvements for the Fluid Pump * Performance improvements for the Fluid Pump
* Removed EmeraldEnchants integration
* Memory and performance improvements for ticking blocks
#### Fixes #### Fixes
* Fixed #2448 * Fixed #2448
@ -77,6 +88,12 @@
* Fixed Sword of Beheading dropping Zombie/Skeleton Skulls from Zombie/Skeleton subvariants * Fixed Sword of Beheading dropping Zombie/Skeleton Skulls from Zombie/Skeleton subvariants
* Fixed #2518 * Fixed #2518
* Fixed #2421 * 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.1</version> <version>0.17.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.27.2</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.158</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

@ -132,10 +132,15 @@ 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) {
return connectedLocations.contains(l); if (regulator.equals(l)) {
return true;
} else {
return connectedLocations.contains(l);
}
} }
@Nullable @Nullable

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

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

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

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

@ -33,7 +33,7 @@ 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 {
@ -68,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();
@ -92,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;
@ -108,30 +111,35 @@ class CargoNetworkTask implements Runnable {
} }
if (stack != null) { if (stack != null) {
Inventory inv = inventories.get(inputTarget.getLocation()); insertItem(inputTarget, previousSlot, stack);
}
}
if (inv != null) { @ParametersAreNonnullByDefault
// Check if the original slot hasn't been occupied in the meantime private void insertItem(Block inputTarget, int previousSlot, ItemStack item) {
if (inv.getItem(previousSlot) == null) { Inventory inv = inventories.get(inputTarget.getLocation());
inv.setItem(previousSlot, stack);
} else {
// Try to add the item into another available slot then
ItemStack rest = inv.addItem(stack).get(0);
if (rest != null && !manager.isItemDeletionEnabled()) { if (inv != null) {
// If the item still couldn't be inserted, simply drop it on the ground // Check if the original slot hasn't been occupied in the meantime
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest); if (inv.getItem(previousSlot) == null) {
} inv.setItem(previousSlot, item);
}
} else { } else {
DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget); // Try to add the item into another available slot then
ItemStack rest = inv.addItem(item).get(0);
if (menu != null) { if (rest != null && !manager.isItemDeletionEnabled()) {
if (menu.getItemInSlot(previousSlot) == null) { // If the item still couldn't be inserted, simply drop it on the ground
menu.replaceExistingItem(previousSlot, stack); inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest);
} else if (!manager.isItemDeletionEnabled()) { }
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack); }
} } else {
DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget);
if (menu != null) {
if (menu.getItemInSlot(previousSlot) == null) {
menu.replaceExistingItem(previousSlot, item);
} else if (!manager.isItemDeletionEnabled()) {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), item);
} }
} }
} }
@ -152,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;
@ -238,7 +238,10 @@ public class EnergyNet extends Network {
} }
// Remove all generators which have exploded // Remove all generators which have exploded
generators.keySet().removeAll(explodedBlocks); if (!explodedBlocks.isEmpty()) {
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

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

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

View File

@ -1,39 +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;
import me.mrCookieSlime.Slimefun.Objects.Category;
/**
* This is the EmeraldEnchants {@link Category}
*
* @deprecated Support for EmeraldEnchants is being faded out
*
*/
@Deprecated
class EmeraldEnchantsCategory extends FlexCategory {
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

@ -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;
@ -48,10 +49,21 @@ public class SlimefunProfiler {
*/ */
private static final int MAX_TICK_DURATION = 100; private static final int MAX_TICK_DURATION = 100;
private final SlimefunThreadFactory threadFactory = new SlimefunThreadFactory(5); /**
* 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 ExecutorService executor = Executors.newFixedThreadPool(threadFactory.getThreadCount(), threadFactory);
private final AtomicBoolean running = new AtomicBoolean(false); 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;
@ -59,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();
} }
@ -74,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;
} }
@ -93,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);
} }
} }
@ -136,7 +157,7 @@ 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
@ -151,7 +172,7 @@ 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, * Since we got more than one Thread in our pool,
@ -177,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;
} }

View File

@ -19,6 +19,7 @@ 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;
@ -63,12 +64,13 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListe
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeListener;
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;
@ -111,7 +113,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;
@ -356,6 +357,9 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
ticker.halt(); ticker.halt();
ticker.run(); ticker.run();
// Kill our Profiler Threads
profiler.kill();
// Save all Player Profiles that are still in memory // Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> { PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) { if (profile.isDirty()) {
@ -379,6 +383,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
// Create a new backup zip // Create a new backup zip
backupService.run(); backupService.run();
// Close and unload any resources from our Metrics Service
metricsService.cleanUp(); metricsService.cleanUp();
/** /**
@ -438,8 +443,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;
@ -454,7 +459,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());
} }
} }
@ -483,6 +488,9 @@ 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 SlimefunItemInteractListener(this); new SlimefunItemInteractListener(this);
@ -505,20 +513,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 HopperListener(this); new ButcherAndroidListener(this);
new NetworkListener(this, networkManager); new NetworkListener(this, networkManager);
new HopperListener(this);
// 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);
} }
// 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);
} }
@ -762,8 +773,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

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

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

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

@ -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
CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState(); */
spawner.setSpawnedType(entity.get()); if (e.getBlock().getType() == Material.SPAWNER) {
spawner.update(true, false); getEntityType(item).ifPresent(entity -> {
CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState();
spawner.setSpawnedType(entity);
spawner.update(true, false);
});
} }
} }
}; };
} }
/**
* This method tries to obtain an {@link EntityType} from a given {@link ItemStack}.
* The provided {@link ItemStack} must be a {@link RepairedSpawner} item.
*
* @param item
* The {@link ItemStack} to extract the {@link EntityType} from
*
* @return An {@link Optional} describing the result
*/
public Optional<EntityType> getEntityType(ItemStack item) {
for (String line : item.getItemMeta().getLore()) {
if (ChatColor.stripColor(line).startsWith("Type: ") && !line.contains("<Type>")) {
EntityType type = EntityType.valueOf(ChatColor.stripColor(line).replace("Type: ", "").replace(' ', '_').toUpperCase(Locale.ROOT));
return Optional.of(type);
}
}
return Optional.empty();
}
@Override @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,28 +33,14 @@ 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) {
@Override
public void onPlayerPlace(BlockPlaceEvent e) {
Block b = e.getBlock();
// The owner and frequency are required by every node
BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString());
BlockStorage.addBlockInfo(b, FREQUENCY, "0");
onPlace(e);
}
});
new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) { new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) {
@Override @Override
@ -60,6 +50,7 @@ abstract class AbstractCargoNode extends SlimefunItem {
@Override @Override
public void newInstance(BlockMenu menu, Block b) { public void newInstance(BlockMenu menu, Block b) {
menu.addMenuCloseHandler(p -> markDirty(b.getLocation()));
updateBlockMenu(menu, b); updateBlockMenu(menu, b);
} }
@ -75,6 +66,25 @@ abstract class AbstractCargoNode extends SlimefunItem {
}; };
} }
@Override
public BlockPlaceHandler getItemHandler() {
return new BlockPlaceHandler(false) {
@Override
public void onPlayerPlace(BlockPlaceEvent e) {
Block b = e.getBlock();
// The owner and frequency are required by every node
BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString());
BlockStorage.addBlockInfo(b, FREQUENCY, "0");
onPlace(e);
}
};
}
@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

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

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

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

View File

@ -2,14 +2,15 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.seasonal;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.block.BlockPlaceEvent;
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.slimefun4.core.attributes.NotPlaceable; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; 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.utils.FireworkUtils; import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
@ -26,10 +27,11 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see EasterEgg * @see EasterEgg
* *
*/ */
public class ChristmasPresent extends SimpleSlimefunItem<BlockPlaceHandler> implements NotPlaceable { public class ChristmasPresent extends SimpleSlimefunItem<ItemUseHandler> implements NotPlaceable {
private final ItemStack[] gifts; private final ItemStack[] gifts;
@ParametersAreNonnullByDefault
public ChristmasPresent(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack... gifts) { public ChristmasPresent(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack... gifts) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
@ -37,24 +39,21 @@ public class ChristmasPresent extends SimpleSlimefunItem<BlockPlaceHandler> impl
} }
@Override @Override
public BlockPlaceHandler getItemHandler() { public ItemUseHandler getItemHandler() {
return new BlockPlaceHandler(false) { return e -> {
e.cancel();
@Override
public void onPlayerPlace(BlockPlaceEvent e) {
e.setCancelled(true);
e.getClickedBlock().ifPresent(block -> {
if (e.getPlayer().getGameMode() != GameMode.CREATIVE) { if (e.getPlayer().getGameMode() != GameMode.CREATIVE) {
ItemUtils.consumeItem(e.getItemInHand(), false); ItemUtils.consumeItem(e.getItem(), false);
} }
FireworkUtils.launchRandom(e.getPlayer(), 3); FireworkUtils.launchRandom(e.getPlayer(), 3);
Block b = e.getBlock(); Block b = block.getRelative(e.getClickedFace());
ItemStack gift = gifts[ThreadLocalRandom.current().nextInt(gifts.length)].clone(); ItemStack gift = gifts[ThreadLocalRandom.current().nextInt(gifts.length)].clone();
b.getWorld().dropItemNaturally(b.getLocation(), gift); b.getWorld().dropItemNaturally(b.getLocation(), gift);
} });
}; };
} }

View File

@ -17,7 +17,6 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.collections.RandomizedSet; import io.github.thebusybiscuit.cscorelib2.collections.RandomizedSet;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem;
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;
@ -26,6 +25,7 @@ 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.electric.machines.ElectricGoldPan; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.ElectricGoldPan;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.AutomatedPanningMachine; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.AutomatedPanningMachine;
import io.github.thebusybiscuit.slimefun4.implementation.settings.GoldPanDrop;
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;
@ -66,13 +66,14 @@ public class GoldPan extends SimpleSlimefunItem<ItemUseHandler> implements Recip
return Material.GRAVEL; return Material.GRAVEL;
} }
@Nonnull
protected Set<GoldPanDrop> getGoldPanDrops() { protected Set<GoldPanDrop> getGoldPanDrops() {
Set<GoldPanDrop> settings = new HashSet<>(); Set<GoldPanDrop> settings = new HashSet<>();
settings.add(new GoldPanDrop("chance.FLINT", 40, new ItemStack(Material.FLINT))); settings.add(new GoldPanDrop(this, "chance.FLINT", 40, new ItemStack(Material.FLINT)));
settings.add(new GoldPanDrop("chance.CLAY", 20, new ItemStack(Material.CLAY_BALL))); settings.add(new GoldPanDrop(this, "chance.CLAY", 20, new ItemStack(Material.CLAY_BALL)));
settings.add(new GoldPanDrop("chance.SIFTED_ORE", 35, SlimefunItems.SIFTED_ORE)); settings.add(new GoldPanDrop(this, "chance.SIFTED_ORE", 35, SlimefunItems.SIFTED_ORE));
settings.add(new GoldPanDrop("chance.IRON_NUGGET", 5, new ItemStack(Material.IRON_NUGGET))); settings.add(new GoldPanDrop(this, "chance.IRON_NUGGET", 5, new ItemStack(Material.IRON_NUGGET)));
return settings; return settings;
} }
@ -83,7 +84,13 @@ public class GoldPan extends SimpleSlimefunItem<ItemUseHandler> implements Recip
updateRandomizer(); updateRandomizer();
} }
protected void updateRandomizer() { /**
* <strong>Do not call this method directly</strong>.
*
* This method is for internal purposes only.
* It will update and re-calculate all weights in our {@link RandomizedSet}.
*/
public void updateRandomizer() {
randomizer.clear(); randomizer.clear();
for (GoldPanDrop setting : drops) { for (GoldPanDrop setting : drops) {
@ -163,31 +170,4 @@ public class GoldPan extends SimpleSlimefunItem<ItemUseHandler> implements Recip
return recipes; return recipes;
} }
public class GoldPanDrop extends ItemSetting<Integer> {
private final ItemStack output;
protected GoldPanDrop(String key, int defaultValue, ItemStack output) {
super(key, defaultValue);
this.output = output;
}
@Override
public boolean validateInput(Integer input) {
return super.validateInput(input) && input >= 0;
}
public ItemStack getOutput() {
return output;
}
@Override
public void update(Integer newValue) {
super.update(newValue);
updateRandomizer();
}
}
} }

View File

@ -3,34 +3,48 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.tools;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.implementation.settings.GoldPanDrop;
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;
/**
* The {@link NetherGoldPan} is a variant of the regular {@link GoldPan}
* which can be used on Soul Sand.
*
* @author TheBusyBiscuit
*
*/
public class NetherGoldPan extends GoldPan { public class NetherGoldPan extends GoldPan {
@ParametersAreNonnullByDefault
public NetherGoldPan(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { public NetherGoldPan(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe); super(category, item, recipeType, recipe);
} }
@Override @Override
@Nonnull
protected Material getTargetMaterial() { protected Material getTargetMaterial() {
return Material.SOUL_SAND; return Material.SOUL_SAND;
} }
@Override @Override
@Nonnull
protected Set<GoldPanDrop> getGoldPanDrops() { protected Set<GoldPanDrop> getGoldPanDrops() {
Set<GoldPanDrop> settings = new HashSet<>(); Set<GoldPanDrop> settings = new HashSet<>();
settings.add(new GoldPanDrop("chance.QUARTZ", 50, new ItemStack(Material.QUARTZ))); settings.add(new GoldPanDrop(this, "chance.QUARTZ", 50, new ItemStack(Material.QUARTZ)));
settings.add(new GoldPanDrop("chance.GOLD_NUGGET", 25, new ItemStack(Material.GOLD_NUGGET))); settings.add(new GoldPanDrop(this, "chance.GOLD_NUGGET", 25, new ItemStack(Material.GOLD_NUGGET)));
settings.add(new GoldPanDrop("chance.NETHER_WART", 10, new ItemStack(Material.NETHER_WART))); settings.add(new GoldPanDrop(this, "chance.NETHER_WART", 10, new ItemStack(Material.NETHER_WART)));
settings.add(new GoldPanDrop("chance.BLAZE_POWDER", 8, new ItemStack(Material.BLAZE_POWDER))); settings.add(new GoldPanDrop(this, "chance.BLAZE_POWDER", 8, new ItemStack(Material.BLAZE_POWDER)));
settings.add(new GoldPanDrop("chance.GLOWSTONE_DUST", 5, new ItemStack(Material.GLOWSTONE_DUST))); settings.add(new GoldPanDrop(this, "chance.GLOWSTONE_DUST", 5, new ItemStack(Material.GLOWSTONE_DUST)));
settings.add(new GoldPanDrop("chance.GHAST_TEAR", 2, new ItemStack(Material.GHAST_TEAR))); settings.add(new GoldPanDrop(this, "chance.GHAST_TEAR", 2, new ItemStack(Material.GHAST_TEAR)));
return settings; return settings;
} }

View File

@ -1,6 +1,6 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.tools; package io.github.thebusybiscuit.slimefun4.implementation.items.tools;
import java.util.List; import javax.annotation.Nonnull;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@ -8,14 +8,13 @@ import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner; import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.AbstractMonsterSpawner;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BrokenSpawner; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BrokenSpawner;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.Category;
@ -54,33 +53,25 @@ public class PickaxeOfContainment extends SimpleSlimefunItem<ToolUseHandler> {
}; };
} }
private ItemStack breakSpawner(Block b) { @Nonnull
// If the spawner's BlockStorage has BlockInfo, then it's not a vanilla spawner and private ItemStack breakSpawner(@Nonnull Block b) {
// should not give a broken spawner. AbstractMonsterSpawner spawner;
ItemStack spawner = SlimefunItems.BROKEN_SPAWNER.clone();
/**
* If the spawner's BlockStorage has BlockInfo, then it's not a vanilla spawner
* and should not give a broken spawner but a repaired one instead.
*/
if (BlockStorage.hasBlockInfo(b)) { if (BlockStorage.hasBlockInfo(b)) {
spawner = SlimefunItems.REPAIRED_SPAWNER.clone(); spawner = (AbstractMonsterSpawner) SlimefunItems.REPAIRED_SPAWNER.getItem();
} else {
spawner = (AbstractMonsterSpawner) SlimefunItems.BROKEN_SPAWNER.getItem();
} }
ItemMeta im = spawner.getItemMeta();
List<String> lore = im.getLore();
BlockState state = PaperLib.getBlockState(b, false).getState(); BlockState state = PaperLib.getBlockState(b, false).getState();
if (state instanceof CreatureSpawner) { if (state instanceof CreatureSpawner) {
EntityType entityType = ((CreatureSpawner) state).getSpawnedType(); EntityType entityType = ((CreatureSpawner) state).getSpawnedType();
return spawner.getItemForEntityType(entityType);
for (int i = 0; i < lore.size(); i++) {
if (lore.get(i).contains("<Type>")) {
lore.set(i, lore.get(i).replace("<Type>", ChatUtils.humanize(entityType.name())));
break;
}
}
im.setLore(lore);
spawner.setItemMeta(im);
return spawner;
} }
return new ItemStack(Material.SPAWNER); return new ItemStack(Material.SPAWNER);

View File

@ -1,7 +1,6 @@
package io.github.thebusybiscuit.slimefun4.implementation.listeners; package io.github.thebusybiscuit.slimefun4.implementation.listeners;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -17,6 +16,7 @@ import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item; import org.bukkit.entity.Item;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result; import org.bukkit.event.Event.Result;
@ -25,7 +25,6 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
@ -36,6 +35,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AltarRecipe; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AltarRecipe;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.AncientAltarTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AncientAltarTask;
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;
@ -312,11 +312,8 @@ public class AncientAltarListener implements Listener {
return Optional.empty(); return Optional.empty();
} }
ItemStack spawner = SlimefunItems.REPAIRED_SPAWNER.clone(); RepairedSpawner spawner = (RepairedSpawner) SlimefunItems.REPAIRED_SPAWNER.getItem();
ItemMeta im = spawner.getItemMeta(); return Optional.of(spawner.getItemForEntityType(spawner.getEntityType(wrapper).orElse(EntityType.PIG)));
im.setLore(Arrays.asList(wrapper.getItemMeta().getLore().get(0)));
spawner.setItemMeta(im);
return Optional.of(spawner);
} }
return checkRecipe(wrapper, items); return checkRecipe(wrapper, items);

View File

@ -53,7 +53,7 @@ public class BlockPhysicsListener implements Listener {
} }
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onPistonExtend(BlockPistonExtendEvent e) { public void onPistonExtend(BlockPistonExtendEvent e) {
if (BlockStorage.hasBlockInfo(e.getBlock())) { if (BlockStorage.hasBlockInfo(e.getBlock())) {
e.setCancelled(true); e.setCancelled(true);
@ -67,7 +67,7 @@ public class BlockPhysicsListener implements Listener {
} }
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onPistonRetract(BlockPistonRetractEvent e) { public void onPistonRetract(BlockPistonRetractEvent e) {
if (BlockStorage.hasBlockInfo(e.getBlock())) { if (BlockStorage.hasBlockInfo(e.getBlock())) {
e.setCancelled(true); e.setCancelled(true);
@ -85,8 +85,19 @@ public class BlockPhysicsListener implements Listener {
public void onLiquidFlow(BlockFromToEvent e) { public void onLiquidFlow(BlockFromToEvent e) {
Block block = e.getToBlock(); Block block = e.getToBlock();
if (SlimefunTag.FLUID_SENSITIVE_MATERIALS.isTagged(block.getType()) && BlockStorage.hasBlockInfo(block)) { // Check if this Material can be destroyed by fluids
e.setCancelled(true); if (SlimefunTag.FLUID_SENSITIVE_MATERIALS.isTagged(block.getType())) {
// Check if this Block holds any data
if (BlockStorage.hasBlockInfo(block)) {
e.setCancelled(true);
} else {
Location loc = block.getLocation();
// Fixes #2496 - Make sure it is not a moving block
if (SlimefunPlugin.getTickerTask().isReserved(loc)) {
e.setCancelled(true);
}
}
} }
} }

View File

@ -50,29 +50,28 @@ public class CoolerListener implements Listener {
@EventHandler @EventHandler
public void onHungerLoss(FoodLevelChangeEvent e) { public void onHungerLoss(FoodLevelChangeEvent e) {
if (cooler == null || cooler.isDisabled() || !(e.getEntity() instanceof Player)) { if (e.getEntity() instanceof Player) {
return; Player p = (Player) e.getEntity();
}
Player p = (Player) e.getEntity(); if (e.getFoodLevel() < p.getFoodLevel()) {
checkAndConsume(p);
if (e.getFoodLevel() < p.getFoodLevel()) { }
checkAndConsume(p);
} }
} }
@EventHandler @EventHandler
public void onHungerDamage(EntityDamageEvent e) { public void onHungerDamage(EntityDamageEvent e) {
if (cooler == null || cooler.isDisabled() || !(e.getEntity() instanceof Player)) { if (e.getEntity() instanceof Player && e.getCause() == DamageCause.STARVATION) {
return;
}
if (e.getCause() == DamageCause.STARVATION) {
checkAndConsume((Player) e.getEntity()); checkAndConsume((Player) e.getEntity());
} }
} }
private void checkAndConsume(@Nonnull Player p) { private void checkAndConsume(@Nonnull Player p) {
if (cooler == null || cooler.isDisabled()) {
// Do not proceed if the Cooler was disabled
return;
}
for (ItemStack item : p.getInventory().getContents()) { for (ItemStack item : p.getInventory().getContents()) {
if (cooler.isItem(item)) { if (cooler.isItem(item)) {
if (Slimefun.hasUnlocked(p, cooler, true)) { if (Slimefun.hasUnlocked(p, cooler, true)) {

View File

@ -27,9 +27,9 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
* *
* @see ElytraCap * @see ElytraCap
*/ */
public class ElytraCrashListener implements Listener { public class ElytraImpactListener implements Listener {
public ElytraCrashListener(@Nonnull SlimefunPlugin plugin) { public ElytraImpactListener(@Nonnull SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin); plugin.getServer().getPluginManager().registerEvents(this, plugin);
} }

View File

@ -16,7 +16,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetBootsTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetBootsTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetpackTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetpackTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.MagnetTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.InfusedMagnetTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.ParachuteTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.ParachuteTask;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -32,7 +32,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
* @see JetpackTask * @see JetpackTask
* @see JetBootsTask * @see JetBootsTask
* @see ParachuteTask * @see ParachuteTask
* @see MagnetTask * @see InfusedMagnetTask
* *
*/ */
public class GadgetsListener implements Listener { public class GadgetsListener implements Listener {
@ -60,7 +60,7 @@ public class GadgetsListener implements Listener {
InfusedMagnet magnet = (InfusedMagnet) SlimefunItems.INFUSED_MAGNET.getItem(); InfusedMagnet magnet = (InfusedMagnet) SlimefunItems.INFUSED_MAGNET.getItem();
if (Slimefun.hasUnlocked(p, magnet, true)) { if (Slimefun.hasUnlocked(p, magnet, true)) {
new MagnetTask(p, magnet.getRadius()).scheduleRepeating(0, 8); new InfusedMagnetTask(p, magnet.getRadius()).scheduleRepeating(0, 8);
} }
} }
} }

View File

@ -63,11 +63,4 @@ public class IronGolemListener implements Listener {
} }
} }
// @EventHandler
// public void onIronGolemSpawn(PLEASE_GIMME_AN_EVENT e) {
// if (e.getBlock().getType() == Material.IRON_BLOCK) {
// e.setCancelled(true);
// }
// }
} }

View File

@ -6,7 +6,6 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -15,9 +14,18 @@ import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
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;
/**
* This {@link Listener} is responsible for handling any {@link Soulbound} items.
* A {@link Soulbound} {@link ItemStack} will not drop upon a {@link Player Player's} death.
* Instead the {@link ItemStack} is saved and given back to the {@link Player} when they respawn.
*
* @author TheBusyBiscuit
*
*/
public class SoulboundListener implements Listener { public class SoulboundListener implements Listener {
private final Map<UUID, Map<Integer, ItemStack>> soulbound = new HashMap<>(); private final Map<UUID, Map<Integer, ItemStack>> soulbound = new HashMap<>();
@ -28,6 +36,7 @@ public class SoulboundListener implements Listener {
@EventHandler @EventHandler
public void onDamage(PlayerDeathEvent e) { public void onDamage(PlayerDeathEvent e) {
Map<Integer, ItemStack> items = new HashMap<>();
Player p = e.getEntity(); Player p = e.getEntity();
for (int slot = 0; slot < p.getInventory().getSize(); slot++) { for (int slot = 0; slot < p.getInventory().getSize(); slot++) {
@ -35,10 +44,19 @@ public class SoulboundListener implements Listener {
// Store soulbound items for later retrieval // Store soulbound items for later retrieval
if (SlimefunUtils.isSoulbound(item)) { if (SlimefunUtils.isSoulbound(item)) {
storeItem(p.getUniqueId(), slot, item); items.put(slot, item);
} }
} }
// There shouldn't even be any items in there, but let's be extra safe!
Map<Integer, ItemStack> existingItems = soulbound.get(p.getUniqueId());
if (existingItems == null) {
soulbound.put(p.getUniqueId(), items);
} else {
existingItems.putAll(items);
}
// Remove soulbound items from our drops // Remove soulbound items from our drops
Iterator<ItemStack> drops = e.getDrops().iterator(); Iterator<ItemStack> drops = e.getDrops().iterator();
while (drops.hasNext()) { while (drops.hasNext()) {
@ -52,16 +70,10 @@ public class SoulboundListener implements Listener {
@EventHandler @EventHandler
public void onRespawn(PlayerRespawnEvent e) { public void onRespawn(PlayerRespawnEvent e) {
retrieveItems(e.getPlayer()); returnSoulboundItems(e.getPlayer());
} }
@ParametersAreNonnullByDefault private void returnSoulboundItems(@Nonnull Player p) {
private void storeItem(UUID uuid, int slot, ItemStack item) {
Map<Integer, ItemStack> items = soulbound.computeIfAbsent(uuid, uid -> new HashMap<>());
items.put(slot, item);
}
private void retrieveItems(@Nonnull Player p) {
Map<Integer, ItemStack> items = soulbound.remove(p.getUniqueId()); Map<Integer, ItemStack> items = soulbound.remove(p.getUniqueId());
if (items != null) { if (items != null) {

View File

@ -34,7 +34,7 @@ public class VampireBladeListener implements Listener {
this.blade = blade; this.blade = blade;
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent e) { public void onDamage(EntityDamageByEntityEvent e) {
if (blade == null || blade.isDisabled()) { if (blade == null || blade.isDisabled()) {
return; return;

View File

@ -22,7 +22,7 @@ public class WorldListener implements Listener {
@EventHandler @EventHandler
public void onWorldLoad(WorldLoadEvent e) { public void onWorldLoad(WorldLoadEvent e) {
SlimefunPlugin.getWorldSettingsService().load(e.getWorld()); SlimefunPlugin.getWorldSettingsService().load(e.getWorld());
BlockStorage.getForcedStorage(e.getWorld()); BlockStorage.getOrCreate(e.getWorld());
} }
@EventHandler @EventHandler

View File

@ -10,7 +10,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.settings.DoubleRangeSetting;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.ClimbingPick; import io.github.thebusybiscuit.slimefun4.implementation.items.tools.ClimbingPick;
/** /**
* This is an {@link ItemSetting} * This is an {@link ItemSetting} that manages the efficiency of climbing
* a certain {@link Material} with the {@link ClimbingPick}.
* *
* @author TheBusyBiscuit * @author TheBusyBiscuit
* *
@ -32,6 +33,7 @@ public class ClimbableSurface extends DoubleRangeSetting {
*/ */
public ClimbableSurface(@Nonnull Material surface, double defaultValue) { public ClimbableSurface(@Nonnull Material surface, double defaultValue) {
super("launch-amounts." + surface.name(), 0, defaultValue, Double.MAX_VALUE); super("launch-amounts." + surface.name(), 0, defaultValue, Double.MAX_VALUE);
this.type = surface; this.type = surface;
} }

View File

@ -0,0 +1,40 @@
package io.github.thebusybiscuit.slimefun4.implementation.settings;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.GoldPan;
public class GoldPanDrop extends ItemSetting<Integer> {
private final GoldPan goldPan;
private final ItemStack output;
@ParametersAreNonnullByDefault
public GoldPanDrop(GoldPan goldPan, String key, int defaultValue, ItemStack output) {
super(key, defaultValue);
this.goldPan = goldPan;
this.output = output;
}
@Override
public boolean validateInput(Integer input) {
return super.validateInput(input) && input >= 0;
}
@Nonnull
public ItemStack getOutput() {
return output;
}
@Override
public void update(Integer newValue) {
super.update(newValue);
goldPan.updateRandomizer();
}
}

View File

@ -28,14 +28,10 @@ import com.google.gson.JsonParser;
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.implementation.items.electric.machines.AutomatedCraftingChamber; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.GrindStone; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.GrindStone;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.MakeshiftSmeltery; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.MakeshiftSmeltery;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.OreCrusher; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.OreCrusher;
import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Smeltery; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Smeltery;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer.ItemFlag;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe;
@ -75,7 +71,11 @@ public final class PostSetup {
Slimefun.getLogger().log(Level.WARNING, "Removed bugged Item ('NULL?')"); Slimefun.getLogger().log(Level.WARNING, "Removed bugged Item ('NULL?')");
iterator.remove(); iterator.remove();
} else { } else {
item.load(); try {
item.load();
} catch (Exception | LinkageError x) {
item.error("Failed to properly load this Item", x);
}
} }
} }
@ -115,33 +115,25 @@ public final class PostSetup {
SlimefunPlugin.getRegistry().setAutoLoadingMode(true); SlimefunPlugin.getRegistry().setAutoLoadingMode(true);
} }
/**
* This method counts the amount of {@link SlimefunItem SlimefunItems} registered
* by Slimefun itself and not by any addons.
*
* @return The amount of {@link SlimefunItem SlimefunItems} added by Slimefun itself
*/
private static int countNonAddonItems() { private static int countNonAddonItems() {
return (int) SlimefunPlugin.getRegistry().getEnabledSlimefunItems().stream().filter(item -> item.getAddon() instanceof SlimefunPlugin).count(); // @formatter:off
return (int) SlimefunPlugin.getRegistry().getEnabledSlimefunItems().stream()
.filter(item -> item.getAddon() instanceof SlimefunPlugin)
.count();
// @formatter:on
} }
private static void loadAutomaticCraftingChamber() { private static void loadAutomaticCraftingChamber() {
AutomatedCraftingChamber crafter = (AutomatedCraftingChamber) SlimefunItems.AUTOMATED_CRAFTING_CHAMBER.getItem(); AutomatedCraftingChamber crafter = (AutomatedCraftingChamber) SlimefunItems.AUTOMATED_CRAFTING_CHAMBER.getItem();
if (crafter != null) { if (crafter != null) {
EnhancedCraftingTable machine = (EnhancedCraftingTable) SlimefunItems.ENHANCED_CRAFTING_TABLE.getItem(); crafter.loadRecipes();
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++;
}
SlimefunPlugin.getRegistry().getAutomatedCraftingChamberRecipes().put(builder.toString(), RecipeType.getRecipeOutputList(machine, inputs));
}
} }
} }

View File

@ -201,7 +201,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/** /**
* This static utility class holds the recipes of all items. * This class holds the recipes of all items.
* This is the place where all items from Slimefun are registered. * This is the place where all items from Slimefun are registered.
* *
*/ */

View File

@ -9,14 +9,14 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
abstract class AbstractPlayerTask implements Runnable { abstract class AbstractPlayerTask implements Runnable {
protected int id; protected final Player p;
protected Player p; private int id;
AbstractPlayerTask(@Nonnull Player p) { AbstractPlayerTask(@Nonnull Player p) {
this.p = p; this.p = p;
} }
public void setID(int id) { private void setID(int id) {
this.id = id; this.id = id;
} }
@ -29,12 +29,19 @@ abstract class AbstractPlayerTask implements Runnable {
} }
@Override @Override
public void run() { public final void run() {
if (isValid()) { if (isValid()) {
executeTask(); executeTask();
} }
} }
/**
* This method cancels this {@link AbstractPlayerTask}.
*/
public final void cancel() {
Bukkit.getScheduler().cancelTask(id);
}
/** /**
* This method checks if this {@link AbstractPlayerTask} should be continued or cancelled. * This method checks if this {@link AbstractPlayerTask} should be continued or cancelled.
* It will also cancel this {@link AbstractPlayerTask} if it became invalid. * It will also cancel this {@link AbstractPlayerTask} if it became invalid.
@ -43,7 +50,7 @@ abstract class AbstractPlayerTask implements Runnable {
*/ */
protected boolean isValid() { protected boolean isValid() {
if (!p.isOnline() || !p.isValid() || p.isDead() || !p.isSneaking()) { if (!p.isOnline() || !p.isValid() || p.isDead() || !p.isSneaking()) {
Bukkit.getScheduler().cancelTask(id); cancel();
return false; return false;
} }

View File

@ -11,6 +11,7 @@ import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock; import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.papermc.lib.PaperLib;
/** /**
* This task is run whenever a {@link Capacitor} needs to update their texture. * This task is run whenever a {@link Capacitor} needs to update their texture.
@ -57,15 +58,24 @@ public class CapacitorTextureUpdateTask implements Runnable {
// Ensure that this Block is still a Player Head // Ensure that this Block is still a Player Head
if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) { if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) {
if (filledPercentage <= 0.25) { if (filledPercentage <= 0.25) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_25.getTexture()); // 0-25% capacity
setTexture(b, HeadTexture.CAPACITOR_25);
} else if (filledPercentage <= 0.5) { } else if (filledPercentage <= 0.5) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_50.getTexture()); // 25-50% capacity
setTexture(b, HeadTexture.CAPACITOR_50);
} else if (filledPercentage <= 0.75) { } else if (filledPercentage <= 0.75) {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_75.getTexture()); // 50-75% capacity
setTexture(b, HeadTexture.CAPACITOR_75);
} else { } else {
SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_100.getTexture()); // 75-100% capacity
setTexture(b, HeadTexture.CAPACITOR_100);
} }
} }
} }
private void setTexture(@Nonnull Block b, @Nonnull HeadTexture texture) {
SkullBlock.setFromHash(b, texture.getUniqueId(), texture.getTexture(), false);
PaperLib.getBlockState(b, false).getState().update(true, false);
}
} }

View File

@ -21,12 +21,15 @@ import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
* @see InfusedMagnet * @see InfusedMagnet
* *
*/ */
public class MagnetTask extends AbstractPlayerTask { public class InfusedMagnetTask extends AbstractPlayerTask {
/**
* The radius in which an {@link Item} is picked up.
*/
private final double radius; private final double radius;
/** /**
* This creates a new {@link MagnetTask} for the given {@link Player} with the given * This creates a new {@link InfusedMagnetTask} for the given {@link Player} with the given
* pickup radius. * pickup radius.
* *
* @param p * @param p
@ -34,7 +37,7 @@ public class MagnetTask extends AbstractPlayerTask {
* @param radius * @param radius
* The radius in which items should be picked up * The radius in which items should be picked up
*/ */
public MagnetTask(@Nonnull Player p, double radius) { public InfusedMagnetTask(@Nonnull Player p, double radius) {
super(p); super(p);
this.radius = radius; this.radius = radius;
@ -55,6 +58,7 @@ public class MagnetTask extends AbstractPlayerTask {
} }
} }
// Only play a sound if an Item was found
if (playSound) { if (playSound) {
p.playSound(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 0.25F, 0.9F); p.playSound(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 0.25F, 0.9F);
} }

View File

@ -4,7 +4,6 @@ import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Effect; import org.bukkit.Effect;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
@ -43,7 +42,7 @@ public class JetBootsTask extends AbstractPlayerTask {
p.setVelocity(vector); p.setVelocity(vector);
} else { } else {
Bukkit.getScheduler().cancelTask(id); cancel();
} }
} }
} }

View File

@ -2,7 +2,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Effect; import org.bukkit.Effect;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
@ -22,11 +21,6 @@ public class JetpackTask extends AbstractPlayerTask {
this.jetpack = jetpack; this.jetpack = jetpack;
} }
@Override
public void setID(int id) {
this.id = id;
}
@Override @Override
protected void executeTask() { protected void executeTask() {
if (p.getInventory().getChestplate() == null || p.getInventory().getChestplate().getType() == Material.AIR) { if (p.getInventory().getChestplate() == null || p.getInventory().getChestplate().getType() == Material.AIR) {
@ -43,7 +37,7 @@ public class JetpackTask extends AbstractPlayerTask {
p.setVelocity(vector); p.setVelocity(vector);
} else { } else {
Bukkit.getScheduler().cancelTask(id); cancel();
} }
} }
} }

View File

@ -2,10 +2,21 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.Parachute;
/**
* The {@link ParachuteTask} adds the entire functionality of the {@link Parachute}.
* It continously sets the velocity of the {@link Player} to make them fall slowly.
* Perhaps it can be changed to use the slow falling effect at some point.
*
* @author TheBusyBiscuit
*
* @see Parachute
*
*/
public class ParachuteTask extends AbstractPlayerTask { public class ParachuteTask extends AbstractPlayerTask {
public ParachuteTask(@Nonnull Player p) { public ParachuteTask(@Nonnull Player p) {
@ -20,7 +31,7 @@ public class ParachuteTask extends AbstractPlayerTask {
p.setFallDistance(0F); p.setFallDistance(0F);
if (!p.isSneaking()) { if (!p.isSneaking()) {
Bukkit.getScheduler().cancelTask(id); cancel();
} }
} }

View File

@ -8,7 +8,6 @@ import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TeleporterListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.TeleporterListener;
import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup; import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -59,15 +58,10 @@ public class SlimefunStartupTask implements Runnable {
} }
} }
// Load all listeners that depend on items to be enabled // Only load this Listener if the corresponding items are enabled
if (isEnabled("ELEVATOR_PLATE", "GPS_ACTIVATION_DEVICE_SHARED", "GPS_ACTIVATION_DEVICE_PERSONAL")) { if (isEnabled("ELEVATOR_PLATE", "GPS_ACTIVATION_DEVICE_SHARED", "GPS_ACTIVATION_DEVICE_PERSONAL")) {
new TeleporterListener(plugin); new TeleporterListener(plugin);
} }
if (isEnabled("PROGRAMMABLE_ANDROID_BUTCHER", "PROGRAMMABLE_ANDROID_2_BUTCHER", "PROGRAMMABLE_ANDROID_3_BUTCHER")) {
new ButcherAndroidListener(plugin);
}
} }
private boolean isEnabled(String... itemIds) { private boolean isEnabled(String... itemIds) {

View File

@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.implementation.tasks; package io.github.thebusybiscuit.slimefun4.implementation.tasks;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@ -10,17 +11,18 @@ import java.util.logging.Level;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition;
import io.github.thebusybiscuit.cscorelib2.blocks.ChunkPosition;
import io.github.thebusybiscuit.slimefun4.api.ErrorReport; import io.github.thebusybiscuit.slimefun4.api.ErrorReport;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
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;
import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
@ -28,8 +30,8 @@ import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.Slimefun;
/** /**
* The {@link TickerTask} is responsible for ticking every {@link BlockTicker}, synchronous * The {@link TickerTask} is responsible for ticking every {@link BlockTicker},
* or not. * synchronous or not.
* *
* @author TheBusyBiscuit * @author TheBusyBiscuit
* *
@ -38,15 +40,19 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
*/ */
public class TickerTask implements Runnable { public class TickerTask implements Runnable {
// This Map holds all currently actively ticking locations /**
private final Map<String, Set<Location>> activeTickers = new ConcurrentHashMap<>(); * This Map holds all currently actively ticking locations.
*/
private final Map<ChunkPosition, Set<Location>> tickingLocations = new ConcurrentHashMap<>();
// These are "Queues" of blocks that need to be removed or moved // These are "Queues" of blocks that need to be removed or moved
private final Map<Location, Location> movingQueue = new ConcurrentHashMap<>(); private final Map<Location, Location> movingQueue = new ConcurrentHashMap<>();
private final Map<Location, Boolean> deletionQueue = new ConcurrentHashMap<>(); private final Map<Location, Boolean> deletionQueue = new ConcurrentHashMap<>();
// This Map tracks how many bugs have occurred in a given Location /**
// If too many bugs happen, we delete that Location * This Map tracks how many bugs have occurred in a given Location .
* If too many bugs happen, we delete that Location.
*/
private final Map<BlockPosition, Integer> bugs = new ConcurrentHashMap<>(); private final Map<BlockPosition, Integer> bugs = new ConcurrentHashMap<>();
private int tickRate; private int tickRate;
@ -69,7 +75,7 @@ public class TickerTask implements Runnable {
/** /**
* This method resets this {@link TickerTask} to run again. * This method resets this {@link TickerTask} to run again.
*/ */
public void reset() { private void reset() {
running = false; running = false;
} }
@ -85,6 +91,7 @@ public class TickerTask implements Runnable {
SlimefunPlugin.getProfiler().start(); SlimefunPlugin.getProfiler().start();
Set<BlockTicker> tickers = new HashSet<>(); Set<BlockTicker> tickers = new HashSet<>();
// Remove any deleted blocks
Iterator<Map.Entry<Location, Boolean>> removals = deletionQueue.entrySet().iterator(); Iterator<Map.Entry<Location, Boolean>> removals = deletionQueue.entrySet().iterator();
while (removals.hasNext()) { while (removals.hasNext()) {
Map.Entry<Location, Boolean> entry = removals.next(); Map.Entry<Location, Boolean> entry = removals.next();
@ -92,12 +99,24 @@ public class TickerTask implements Runnable {
removals.remove(); removals.remove();
} }
if (!halted) { // Fixes #2576 - Remove any deleted instances of BlockStorage
for (Map.Entry<String, Set<Location>> entry : activeTickers.entrySet()) { Iterator<BlockStorage> worlds = SlimefunPlugin.getRegistry().getWorlds().values().iterator();
tickChunk(tickers, entry.getKey(), entry.getValue()); while (worlds.hasNext()) {
BlockStorage storage = worlds.next();
if (storage.isMarkedForRemoval()) {
worlds.remove();
} }
} }
// Run our ticker code
if (!halted) {
for (Map.Entry<ChunkPosition, Set<Location>> entry : tickingLocations.entrySet()) {
tickChunk(entry.getKey(), tickers, entry.getValue());
}
}
// Move any moved block data
Iterator<Map.Entry<Location, Location>> moves = movingQueue.entrySet().iterator(); Iterator<Map.Entry<Location, Location>> moves = movingQueue.entrySet().iterator();
while (moves.hasNext()) { while (moves.hasNext()) {
Map.Entry<Location, Location> entry = moves.next(); Map.Entry<Location, Location> entry = moves.next();
@ -119,21 +138,16 @@ public class TickerTask implements Runnable {
} }
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
private void tickChunk(Set<BlockTicker> tickers, String chunk, Set<Location> locations) { private void tickChunk(ChunkPosition chunk, Set<BlockTicker> tickers, Set<Location> locations) {
try { try {
String[] components = PatternUtils.SEMICOLON.split(chunk); // Only continue if the Chunk is actually loaded
if (chunk.isLoaded()) {
World world = Bukkit.getWorld(components[0]);
int x = Integer.parseInt(components[components.length - 2]);
int z = Integer.parseInt(components[components.length - 1]);
if (world != null && world.isChunkLoaded(x, z)) {
for (Location l : locations) { for (Location l : locations) {
tickLocation(tickers, l); tickLocation(tickers, l);
} }
} }
} catch (ArrayIndexOutOfBoundsException | NumberFormatException x) { } catch (ArrayIndexOutOfBoundsException | NumberFormatException x) {
Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception has occurred while trying to parse Chunk: " + chunk); Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception has occurred while trying to resolve Chunk: " + chunk);
} }
} }
@ -213,14 +227,36 @@ public class TickerTask implements Runnable {
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public void queueMove(Location from, Location to) { public void queueMove(Location from, Location to) {
Validate.notNull(from, "Source Location cannot be null!");
Validate.notNull(to, "Target Location cannot be null!");
movingQueue.put(from, to); movingQueue.put(from, to);
} }
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public void queueDelete(Location l, boolean destroy) { public void queueDelete(Location l, boolean destroy) {
Validate.notNull(l, "Location must not be null!");
deletionQueue.put(l, destroy); deletionQueue.put(l, destroy);
} }
/**
* This method checks if the given {@link Location} has been reserved
* by this {@link TickerTask}.
* A reserved {@link Location} does not currently hold any data but will
* be occupied upon the next tick.
* Checking this ensures that our {@link Location} does not get treated like a normal
* {@link Location} as it is theoretically "moving".
*
* @param l
* The {@link Location} to check
*
* @return Whether this {@link Location} has been reserved and will be filled upon the next tick
*/
public boolean isReserved(@Nonnull Location l) {
return movingQueue.containsValue(l);
}
/** /**
* This returns the delay between ticks * This returns the delay between ticks
* *
@ -231,14 +267,82 @@ public class TickerTask implements Runnable {
} }
/** /**
* This method returns the {@link Map} of actively ticking locations according to * This method returns a <strong>read-only</strong> {@link Map}
* their chunk id. * representation of every {@link ChunkPosition} and its corresponding
* {@link Set} of ticking {@link Location Locations}.
* *
* @return The {@link Map} of active tickers * This does include any {@link Location} from an unloaded {@link Chunk} too!
*
* @return A {@link Map} representation of all ticking {@link Location Locations}
*/ */
@Nonnull @Nonnull
public Map<String, Set<Location>> getActiveTickers() { public Map<ChunkPosition, Set<Location>> getLocations() {
return activeTickers; return Collections.unmodifiableMap(tickingLocations);
}
/**
* This method returns a <strong>read-only</strong> {@link Set}
* of all ticking {@link Location Locations} in a given {@link Chunk}.
* The {@link Chunk} does not have to be loaded.
* If no {@link Location} is present, the returned {@link Set} will be empty.
*
* @param chunk
* The {@link Chunk}
*
* @return A {@link Set} of all ticking {@link Location Locations}
*/
@Nonnull
public Set<Location> getLocations(@Nonnull Chunk chunk) {
Validate.notNull(chunk, "The Chunk cannot be null!");
Set<Location> locations = tickingLocations.getOrDefault(new ChunkPosition(chunk), new HashSet<>());
return Collections.unmodifiableSet(locations);
}
/**
* This enables the ticker at the given {@link Location} and adds it to our "queue".
*
* @param l
* The {@link Location} to activate
*/
public void enableTicker(@Nonnull Location l) {
Validate.notNull(l, "Location cannot be null!");
ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4);
Set<Location> newValue = new HashSet<>();
Set<Location> oldValue = tickingLocations.putIfAbsent(chunk, newValue);
/**
* This is faster than doing computeIfAbsent(...)
* on a ConcurrentHashMap because it won't block the Thread for too long
*/
if (oldValue != null) {
oldValue.add(l);
} else {
newValue.add(l);
}
}
/**
* This method disables the ticker at the given {@link Location} and removes it from our internal
* "queue".
*
* @param l
* The {@link Location} to remove
*/
public void disableTicker(@Nonnull Location l) {
Validate.notNull(l, "Location cannot be null!");
ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4);
Set<Location> locations = tickingLocations.get(chunk);
if (locations != null) {
locations.remove(l);
if (locations.isEmpty()) {
tickingLocations.remove(chunk);
}
}
} }
} }

View File

@ -1,5 +1,8 @@
package io.github.thebusybiscuit.slimefun4.utils; package io.github.thebusybiscuit.slimefun4.utils;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -114,11 +117,14 @@ public enum HeadTexture {
public static final HeadTexture[] valuesCache = values(); public static final HeadTexture[] valuesCache = values();
private final String texture; private final String texture;
private final UUID uuid;
HeadTexture(@Nonnull String texture) { HeadTexture(@Nonnull String texture) {
Validate.notNull(texture, "Texture cannot be null"); Validate.notNull(texture, "Texture cannot be null");
Validate.isTrue(PatternUtils.HEXADECIMAL.matcher(texture).matches(), "Textures must be in hexadecimal."); Validate.isTrue(PatternUtils.HEXADECIMAL.matcher(texture).matches(), "Textures must be in hexadecimal.");
this.texture = texture; this.texture = texture;
this.uuid = UUID.nameUUIDFromBytes(texture.getBytes(StandardCharsets.UTF_8));
} }
/** /**
@ -131,6 +137,18 @@ public enum HeadTexture {
return texture; return texture;
} }
/**
* This returns the {@link UUID} for this {@link HeadTexture}.
* The {@link UUID} is generated from the texture and cached for
* performance reasons.
*
* @return The {@link UUID} for this {@link HeadTexture}
*/
@Nonnull
public UUID getUniqueId() {
return uuid;
}
/** /**
* This method returns an {@link ItemStack} with the given texture assigned to it. * This method returns an {@link ItemStack} with the given texture assigned to it.
* *

View File

@ -24,6 +24,7 @@ public final class NumberUtils {
/** /**
* This is our {@link DecimalFormat} for decimal values. * This is our {@link DecimalFormat} for decimal values.
* This instance is not thread-safe!
*/ */
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.ROOT)); private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.ROOT));
@ -48,6 +49,34 @@ public final class NumberUtils {
return NumberFormat.getNumberInstance(Locale.US).format(number); return NumberFormat.getNumberInstance(Locale.US).format(number);
} }
@Nonnull
public static String getCompactDouble(double value) {
if (value < 0) {
// Negative numbers are a special case
return '-' + getCompactDouble(-value);
}
if (value < 1000.0) {
// Below 1K
return DECIMAL_FORMAT.format(value);
} else if (value < 1000000.0) {
// Thousands
return DECIMAL_FORMAT.format(value / 1000.0) + 'K';
} else if (value < 1000000000.0) {
// Million
return DECIMAL_FORMAT.format(value / 1000000.0) + 'M';
} else if (value < 1000000000000.0) {
// Billion
return DECIMAL_FORMAT.format(value / 1000000000.0) + 'B';
} else if (value < 1000000000000000.0) {
// Trillion
return DECIMAL_FORMAT.format(value / 1000000000000.0) + 'T';
} else {
// Quadrillion
return DECIMAL_FORMAT.format(value / 1000000000000000.0) + 'Q';
}
}
/** /**
* This method transforms a String representation of a {@link LocalDateTime} * This method transforms a String representation of a {@link LocalDateTime}
* from GitHub's API back into a {@link LocalDateTime} object * from GitHub's API back into a {@link LocalDateTime} object

View File

@ -32,8 +32,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.CapacitorTextureUpdateTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.CapacitorTextureUpdateTask;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants;
import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment;
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;
@ -48,7 +46,6 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/ */
public final class SlimefunUtils { public final class SlimefunUtils {
private static final String EMERALDENCHANTS_LORE = ChatColor.YELLOW.toString() + ChatColor.YELLOW.toString() + ChatColor.GRAY.toString();
private static final String NO_PICKUP_METADATA = "no_pickup"; private static final String NO_PICKUP_METADATA = "no_pickup";
private static final NamespacedKey SOULBOUND_KEY = new NamespacedKey(SlimefunPlugin.instance(), "soulbound"); private static final NamespacedKey SOULBOUND_KEY = new NamespacedKey(SlimefunPlugin.instance(), "soulbound");
@ -98,15 +95,6 @@ public final class SlimefunUtils {
return true; return true;
} }
if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) {
// We wanna operate on a copy now
item = item.clone();
for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) {
EmeraldEnchants.getInstance().getRegistry().applyEnchantment(item, enchantment.getEnchantment(), 0);
}
}
SlimefunItem sfItem = SlimefunItem.getByItem(item); SlimefunItem sfItem = SlimefunItem.getByItem(item);
if (sfItem instanceof Soulbound) { if (sfItem instanceof Soulbound) {
@ -313,23 +301,54 @@ public final class SlimefunUtils {
} }
} }
private static boolean equalsLore(@Nonnull List<String> lore, @Nonnull List<String> lore2) { /**
StringBuilder string1 = new StringBuilder(); * This checks if the two provided lores are equal.
StringBuilder string2 = new StringBuilder(); * This method will ignore any lines such as the soulbound one.
*
* @param lore1
* The first lore
* @param lore2
* The second lore
*
* @return Whether the two lores are equal
*/
public static boolean equalsLore(@Nonnull List<String> lore1, @Nonnull List<String> lore2) {
Validate.notNull(lore1, "Cannot compare lore that is null!");
Validate.notNull(lore2, "Cannot compare lore that is null!");
for (String string : lore) { List<String> longerList = lore1.size() > lore2.size() ? lore1 : lore2;
if (!string.equals(SOULBOUND_LORE) && !string.startsWith(EMERALDENCHANTS_LORE)) { List<String> shorterList = lore1.size() > lore2.size() ? lore2 : lore1;
string1.append("-NEW LINE-").append(string);
int a = 0;
int b = 0;
for (; a < longerList.size(); a++) {
if (isLineIgnored(longerList.get(a))) {
continue;
}
while (shorterList.size() > b && isLineIgnored(shorterList.get(b))) {
b++;
}
if (b >= shorterList.size()) {
return false;
} else if (longerList.get(a).equals(shorterList.get(b))) {
b++;
} else {
return false;
} }
} }
for (String string : lore2) { while (shorterList.size() > b && isLineIgnored(shorterList.get(b))) {
if (!string.equals(SOULBOUND_LORE) && !string.startsWith(EMERALDENCHANTS_LORE)) { b++;
string2.append("-NEW LINE-").append(string);
}
} }
return string1.toString().equals(string2.toString()); return b == shorterList.size();
}
private static boolean isLineIgnored(@Nonnull String line) {
return line.equals(SOULBOUND_LORE);
} }
public static void updateCapacitorTexture(@Nonnull Location l, int charge, int capacity) { public static void updateCapacitorTexture(@Nonnull Location l, int charge, int capacity) {

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.utils.holograms; package io.github.thebusybiscuit.slimefun4.utils.holograms;
import java.util.Collection;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -17,9 +19,10 @@ public final class ReactorHologram {
@Nullable @Nullable
public static ArmorStand getArmorStand(@Nonnull Location reactor, boolean createIfNoneExists) { public static ArmorStand getArmorStand(@Nonnull Location reactor, boolean createIfNoneExists) {
Location l = new Location(reactor.getWorld(), reactor.getX() + 0.5, reactor.getY() + 0.7, reactor.getZ() + 0.5); Location l = new Location(reactor.getWorld(), reactor.getX() + 0.5, reactor.getY() + 0.7, reactor.getZ() + 0.5);
Collection<Entity> holograms = l.getWorld().getNearbyEntities(l, 0.2, 0.2, 0.2, ReactorHologram::isPossibleHologram);
for (Entity n : l.getChunk().getEntities()) { for (Entity n : holograms) {
if (n instanceof ArmorStand && l.distanceSquared(n.getLocation()) < 0.4D) { if (n instanceof ArmorStand) {
return (ArmorStand) n; return (ArmorStand) n;
} }
} }
@ -34,14 +37,21 @@ public final class ReactorHologram {
return hologram; return hologram;
} }
private static boolean isPossibleHologram(@Nonnull Entity n) {
if (n instanceof ArmorStand) {
ArmorStand armorstand = (ArmorStand) n;
return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity();
} else {
return false;
}
}
public static void update(@Nonnull Location l, @Nonnull String name) { public static void update(@Nonnull Location l, @Nonnull String name) {
SlimefunPlugin.runSync(() -> { SlimefunPlugin.runSync(() -> {
ArmorStand hologram = getArmorStand(l, true); ArmorStand hologram = getArmorStand(l, true);
if (!hologram.isCustomNameVisible()) { hologram.setCustomNameVisible(true);
hologram.setCustomNameVisible(true);
}
hologram.setCustomName(ChatColors.color(name)); hologram.setCustomName(ChatColors.color(name));
}); });
} }

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.utils.holograms; package io.github.thebusybiscuit.slimefun4.utils.holograms;
import java.util.Collection;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -42,9 +44,10 @@ public final class SimpleHologram {
@Nullable @Nullable
private static ArmorStand getArmorStand(@Nonnull Block b, boolean createIfNoneExists) { private static ArmorStand getArmorStand(@Nonnull Block b, boolean createIfNoneExists) {
Location l = new Location(b.getWorld(), b.getX() + 0.5, b.getY() + 0.7F, b.getZ() + 0.5); Location l = new Location(b.getWorld(), b.getX() + 0.5, b.getY() + 0.7F, b.getZ() + 0.5);
Collection<Entity> holograms = b.getWorld().getNearbyEntities(l, 0.2, 0.2, 0.2, SimpleHologram::isPossibleHologram);
for (Entity n : l.getChunk().getEntities()) { for (Entity n : holograms) {
if (n instanceof ArmorStand && l.distanceSquared(n.getLocation()) < 0.4D && isPossibleHologram((ArmorStand) n)) { if (n instanceof ArmorStand) {
return (ArmorStand) n; return (ArmorStand) n;
} }
} }
@ -56,8 +59,14 @@ public final class SimpleHologram {
} }
} }
private static boolean isPossibleHologram(@Nonnull ArmorStand armorstand) { private static boolean isPossibleHologram(@Nonnull Entity n) {
return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity() && armorstand.isCustomNameVisible(); if (n instanceof ArmorStand) {
ArmorStand armorstand = (ArmorStand) n;
return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity() && armorstand.isCustomNameVisible();
} else {
return false;
}
} }
@Nonnull @Nonnull

View File

@ -62,6 +62,11 @@ public enum SlimefunTag implements Tag<Material> {
* All command block variants * All command block variants
*/ */
COMMAND_BLOCKS, COMMAND_BLOCKS,
/**
* All variants of Spawn Eggs
*/
SPAWN_EGGS,
/** /**
* Every mushroom type, red, brown and nether ones. * Every mushroom type, red, brown and nether ones.
@ -359,6 +364,7 @@ public enum SlimefunTag implements Tag<Material> {
@Nullable @Nullable
public static SlimefunTag getTag(@Nonnull String value) { public static SlimefunTag getTag(@Nonnull String value) {
Validate.notNull(value, "A tag cannot be null!"); Validate.notNull(value, "A tag cannot be null!");
return nameLookup.get(value); return nameLookup.get(value);
} }

View File

@ -106,8 +106,10 @@ public class TagParser implements Keyed {
// Strings will be parsed directly // Strings will be parsed directly
parsePrimitiveValue(element.getAsString(), materials, tags, true); parsePrimitiveValue(element.getAsString(), materials, tags, true);
} else if (element instanceof JsonObject) { } else if (element instanceof JsonObject) {
// JSONObjects can have a "required" property which can make /**
// it optional to resolve the underlying value * JSONObjects can have a "required" property which can
* make it optional to resolve the underlying value
*/
parseComplexValue(element.getAsJsonObject(), materials, tags); parseComplexValue(element.getAsJsonObject(), materials, tags);
} else { } else {
throw new TagMisconfigurationException(key, "Unexpected value format: " + element.getClass().getSimpleName() + " - " + element.toString()); throw new TagMisconfigurationException(key, "Unexpected value format: " + element.getClass().getSimpleName() + " - " + element.toString());
@ -179,8 +181,10 @@ public class TagParser implements Keyed {
if (id instanceof JsonPrimitive && ((JsonPrimitive) id).isString() && required instanceof JsonPrimitive && ((JsonPrimitive) required).isBoolean()) { if (id instanceof JsonPrimitive && ((JsonPrimitive) id).isString() && required instanceof JsonPrimitive && ((JsonPrimitive) required).isBoolean()) {
boolean isRequired = required.getAsBoolean(); boolean isRequired = required.getAsBoolean();
// If the Tag is required, an exception may be thrown. /**
// Otherwise it will just ignore the value * If the Tag is required, an exception may be thrown.
* Otherwise it will just ignore the value
*/
parsePrimitiveValue(id.getAsString(), materials, tags, isRequired); parsePrimitiveValue(id.getAsString(), materials, tags, isRequired);
} else { } else {
throw new TagMisconfigurationException(key, "Found a JSON Object value without an id!"); throw new TagMisconfigurationException(key, "Found a JSON Object value without an id!");

View File

@ -719,15 +719,11 @@ public class SlimefunItem implements Placeable {
* This method is used for internal purposes only. * This method is used for internal purposes only.
*/ */
public void load() { public void load() {
try { if (!hidden) {
if (!hidden) { category.add(this);
category.add(this);
}
recipeType.register(recipe, getRecipeOutput());
} catch (Exception x) {
error("Failed to properly load the Item \"" + id + "\"", x);
} }
recipeType.register(recipe, getRecipeOutput());
} }
/** /**
@ -983,6 +979,20 @@ public class SlimefunItem implements Placeable {
} }
} }
@Override
public final boolean equals(Object obj) {
if (obj instanceof SlimefunItem) {
return ((SlimefunItem) obj).getId().equals(getId());
} else {
return false;
}
}
@Override
public final int hashCode() {
return getId().hashCode();
}
@Nullable @Nullable
public static SlimefunItem getByID(@Nonnull String id) { public static SlimefunItem getByID(@Nonnull String id) {
return SlimefunPlugin.getRegistry().getSlimefunItemIds().get(id); return SlimefunPlugin.getRegistry().getSlimefunItemIds().get(id);
@ -1032,4 +1042,5 @@ public class SlimefunItem implements Placeable {
public static void registerBlockHandler(String id, SlimefunBlockHandler handler) { public static void registerBlockHandler(String id, SlimefunBlockHandler handler) {
SlimefunPlugin.getRegistry().getBlockHandlers().put(id, handler); SlimefunPlugin.getRegistry().getBlockHandlers().put(id, handler);
} }
} }

View File

@ -202,7 +202,7 @@ public abstract class AGenerator extends AbstractEnergyProvider {
} }
ItemStackWrapper wrapper = new ItemStackWrapper(item); ItemStackWrapper wrapper = new ItemStackWrapper(item);
return SlimefunUtils.isItemSimilar(wrapper, new ItemStack(Material.LAVA_BUCKET), true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.FUEL_BUCKET, true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.OIL_BUCKET, true); return item.getType() == Material.LAVA_BUCKET || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.FUEL_BUCKET, true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.OIL_BUCKET, true);
} }
private MachineFuel findRecipe(BlockMenu menu, Map<Integer, Integer> found) { private MachineFuel findRecipe(BlockMenu menu, Map<Integer, Integer> found) {

View File

@ -19,7 +19,6 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
* @deprecated This interface is not designed to be used by addons. * @deprecated This interface is not designed to be used by addons.
* *
*/ */
@Deprecated
public interface InventoryBlock { public interface InventoryBlock {
/** /**

View File

@ -49,8 +49,6 @@ public class BlockInfoConfig extends Config {
throw new UnsupportedOperationException("Can't set \"" + path + "\" to \"" + value + "\" (type: " + value.getClass().getSimpleName() + ") because BlockInfoConfig only supports Strings"); throw new UnsupportedOperationException("Can't set \"" + path + "\" to \"" + value + "\" (type: " + value.getClass().getSimpleName() + ") because BlockInfoConfig only supports Strings");
} }
checkPath(path);
if (value == null) { if (value == null) {
data.remove(path); data.remove(path);
} else { } else {
@ -58,15 +56,8 @@ public class BlockInfoConfig extends Config {
} }
} }
private void checkPath(String path) {
if (path.indexOf('.') != -1) {
throw new UnsupportedOperationException("BlockInfoConfig only supports Map<String,String> (path: " + path + ")");
}
}
@Override @Override
public boolean contains(String path) { public boolean contains(String path) {
checkPath(path);
return data.containsKey(path); return data.containsKey(path);
} }
@ -77,7 +68,6 @@ public class BlockInfoConfig extends Config {
@Override @Override
public String getString(String path) { public String getString(String path) {
checkPath(path);
return data.get(path); return data.get(path);
} }

View File

@ -7,11 +7,10 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -57,24 +56,30 @@ public class BlockStorage {
private final Map<String, Config> blocksCache = new ConcurrentHashMap<>(); private final Map<String, Config> blocksCache = new ConcurrentHashMap<>();
private static int chunkChanges = 0; private static int chunkChanges = 0;
private int changes = 0;
private int changes = 0;
private AtomicBoolean isMarkedForRemoval = new AtomicBoolean(false);
@Nullable
public static BlockStorage getStorage(@Nonnull World world) { public static BlockStorage getStorage(@Nonnull World world) {
return SlimefunPlugin.getRegistry().getWorlds().get(world.getName()); return SlimefunPlugin.getRegistry().getWorlds().get(world.getName());
} }
public static BlockStorage getForcedStorage(@Nonnull World world) { @Nonnull
return isWorldRegistered(world.getName()) ? SlimefunPlugin.getRegistry().getWorlds().get(world.getName()) : new BlockStorage(world); public static BlockStorage getOrCreate(@Nonnull World world) {
BlockStorage storage = SlimefunPlugin.getRegistry().getWorlds().get(world.getName());
if (storage == null) {
return new BlockStorage(world);
} else {
return storage;
}
} }
private static String serializeLocation(Location l) { private static String serializeLocation(Location l) {
return l.getWorld().getName() + ';' + l.getBlockX() + ';' + l.getBlockY() + ';' + l.getBlockZ(); return l.getWorld().getName() + ';' + l.getBlockX() + ';' + l.getBlockY() + ';' + l.getBlockZ();
} }
private static String locationToChunkString(Location l) {
return l.getWorld().getName() + ";Chunk;" + (l.getBlockX() >> 4) + ';' + (l.getBlockZ() >> 4);
}
private static String serializeChunk(World world, int x, int z) { private static String serializeChunk(World world, int x, int z) {
return world.getName() + ";Chunk;" + x + ';' + z; return world.getName() + ";Chunk;" + x + ';' + z;
} }
@ -177,7 +182,6 @@ public class BlockStorage {
} }
try { try {
String chunkString = locationToChunkString(l);
String json = cfg.getString(key); String json = cfg.getString(key);
Config blockInfo = parseBlockInfo(l, json); Config blockInfo = parseBlockInfo(l, json);
@ -195,9 +199,7 @@ public class BlockStorage {
storage.put(l, blockInfo); storage.put(l, blockInfo);
if (SlimefunPlugin.getRegistry().getTickerBlocks().contains(file.getName().replace(".sfb", ""))) { if (SlimefunPlugin.getRegistry().getTickerBlocks().contains(file.getName().replace(".sfb", ""))) {
Map<String, Set<Location>> tickers = SlimefunPlugin.getTickerTask().getActiveTickers(); SlimefunPlugin.getTickerTask().enableTicker(l);
Set<Location> locations = tickers.computeIfAbsent(chunkString, id -> new HashSet<>());
locations.add(l);
} }
} }
} catch (Exception x) { } catch (Exception x) {
@ -330,7 +332,11 @@ public class BlockStorage {
public void saveAndRemove() { public void saveAndRemove() {
save(); save();
SlimefunPlugin.getRegistry().getWorlds().remove(world.getName()); isMarkedForRemoval.set(true);
}
public boolean isMarkedForRemoval() {
return isMarkedForRemoval.get();
} }
public static void saveChunks() { public static void saveChunks() {
@ -495,11 +501,7 @@ public class BlockStorage {
} }
} }
public static void setBlockInfo(Block block, Config cfg, boolean updateTicker) { private static void setBlockInfo(Location l, Config cfg, boolean updateTicker) {
setBlockInfo(block.getLocation(), cfg, updateTicker);
}
public static void setBlockInfo(Location l, Config cfg, boolean updateTicker) {
BlockStorage storage = getStorage(l.getWorld()); BlockStorage storage = getStorage(l.getWorld());
if (storage == null) { if (storage == null) {
@ -509,15 +511,13 @@ public class BlockStorage {
storage.storage.put(l, cfg); storage.storage.put(l, cfg);
String id = cfg.getString("id"); String id = cfg.getString("id");
BlockMenuPreset preset = BlockMenuPreset.getPreset(id);
if (BlockMenuPreset.isInventory(id)) { if (preset != null) {
if (BlockMenuPreset.isUniversalInventory(id)) { if (BlockMenuPreset.isUniversalInventory(id)) {
if (!SlimefunPlugin.getRegistry().getUniversalInventories().containsKey(id)) { SlimefunPlugin.getRegistry().getUniversalInventories().computeIfAbsent(id, key -> new UniversalBlockMenu(preset));
storage.loadUniversalInventory(BlockMenuPreset.getPreset(id));
}
} else if (!storage.hasInventory(l)) { } else if (!storage.hasInventory(l)) {
File file = new File(PATH_INVENTORIES + serializeLocation(l) + ".sfi"); File file = new File(PATH_INVENTORIES + serializeLocation(l) + ".sfi");
BlockMenuPreset preset = BlockMenuPreset.getPreset(id);
if (file.exists()) { if (file.exists()) {
BlockMenu inventory = new BlockMenu(preset, l, new io.github.thebusybiscuit.cscorelib2.config.Config(file)); BlockMenu inventory = new BlockMenu(preset, l, new io.github.thebusybiscuit.cscorelib2.config.Config(file));
@ -573,6 +573,10 @@ public class BlockStorage {
public static void deleteLocationInfoUnsafely(Location l, boolean destroy) { public static void deleteLocationInfoUnsafely(Location l, boolean destroy) {
BlockStorage storage = getStorage(l.getWorld()); BlockStorage storage = getStorage(l.getWorld());
if (storage == null) {
throw new IllegalStateException("World \"" + l.getWorld().getName() + "\" seems to have been deleted. Do not call unsafe methods directly!");
}
if (hasBlockInfo(l)) { if (hasBlockInfo(l)) {
refreshCache(storage, l, getLocationInfo(l).getString("id"), null, destroy); refreshCache(storage, l, getLocationInfo(l).getString("id"), null, destroy);
storage.storage.remove(l); storage.storage.remove(l);
@ -590,17 +594,7 @@ public class BlockStorage {
universalInventory.save(); universalInventory.save();
} }
String chunkString = locationToChunkString(l); SlimefunPlugin.getTickerTask().disableTicker(l);
Map<String, Set<Location>> tickers = SlimefunPlugin.getTickerTask().getActiveTickers();
Set<Location> locations = tickers.get(chunkString);
if (locations != null) {
locations.remove(l);
if (locations.isEmpty()) {
tickers.remove(chunkString);
}
}
} }
} }
@ -638,23 +632,15 @@ public class BlockStorage {
refreshCache(storage, from, previousData.getString("id"), null, true); refreshCache(storage, from, previousData.getString("id"), null, true);
storage.storage.remove(from); storage.storage.remove(from);
String chunkString = locationToChunkString(from); SlimefunPlugin.getTickerTask().disableTicker(from);
Map<String, Set<Location>> tickers = SlimefunPlugin.getTickerTask().getActiveTickers();
Set<Location> locations = tickers.get(chunkString);
if (locations != null) {
locations.remove(from);
if (locations.isEmpty()) {
tickers.remove(chunkString);
}
}
} }
private static void refreshCache(BlockStorage storage, Location l, String key, String value, boolean updateTicker) { private static void refreshCache(BlockStorage storage, Location l, String key, String value, boolean updateTicker) {
if (key == null) { if (key == null) {
// This Block is no longer valid... /**
// Fixes #1577 * This Block is no longer valid...
* Fixes #1577
*/
return; return;
} }
@ -664,14 +650,8 @@ public class BlockStorage {
if (updateTicker) { if (updateTicker) {
SlimefunItem item = SlimefunItem.getByID(key); SlimefunItem item = SlimefunItem.getByID(key);
if (item != null && item.isTicking()) { if (item != null && item.isTicking() && value != null) {
String chunkString = locationToChunkString(l); SlimefunPlugin.getTickerTask().enableTicker(l);
if (value != null) {
Map<String, Set<Location>> tickers = SlimefunPlugin.getTickerTask().getActiveTickers();
Set<Location> locations = tickers.computeIfAbsent(chunkString, id -> new HashSet<>());
locations.add(l);
}
} }
} }
} }
@ -723,8 +703,8 @@ public class BlockStorage {
return id != null && id.equals(slimefunItem); return id != null && id.equals(slimefunItem);
} }
public static boolean isWorldRegistered(String name) { public static boolean isWorldLoaded(World world) {
return SlimefunPlugin.getRegistry().getWorlds().containsKey(name); return SlimefunPlugin.getRegistry().getWorlds().containsKey(world.getName());
} }
public BlockMenu loadInventory(Location l, BlockMenuPreset preset) { public BlockMenu loadInventory(Location l, BlockMenuPreset preset) {
@ -752,11 +732,6 @@ public class BlockStorage {
} }
} }
public void loadUniversalInventory(BlockMenuPreset preset) {
UniversalBlockMenu inventory = new UniversalBlockMenu(preset);
SlimefunPlugin.getRegistry().getUniversalInventories().put(preset.getID(), inventory);
}
public void clearInventory(Location l) { public void clearInventory(Location l) {
BlockMenu menu = getInventory(l); BlockMenu menu = getInventory(l);
@ -825,7 +800,7 @@ public class BlockStorage {
public static Config getChunkInfo(World world, int x, int z) { public static Config getChunkInfo(World world, int x, int z) {
try { try {
if (!isWorldRegistered(world.getName())) { if (!isWorldLoaded(world)) {
return emptyBlockData; return emptyBlockData;
} }

View File

@ -300,7 +300,7 @@ public abstract class BlockMenuPreset extends ChestMenu {
} }
@Nullable @Nullable
public static BlockMenuPreset getPreset(String id) { public static BlockMenuPreset getPreset(@Nullable String id) {
return id == null ? null : SlimefunPlugin.getRegistry().getMenuPresets().get(id); return id == null ? null : SlimefunPlugin.getRegistry().getMenuPresets().get(id);
} }

View File

@ -1,741 +0,0 @@
package me.mrCookieSlime.Slimefun.bstats.bukkit;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*
* @deprecated This class is not guaranteed to be included in future versions of Slimefun! Please shade bStats yourself
* by following the instructions at: https://bstats.org/getting-started/include-metrics
*/
@Deprecated
public class Metrics {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't' });
final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' });
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
// The plugin id
private final int pluginId;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
/**
* Class constructor.
*
* @param plugin
* The plugin which stats should be submitted.
* @param pluginId
* The id of the plugin.
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(Plugin plugin, int pluginId) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
this.pluginId = pluginId;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header("bStats collects some data for plugin authors like how many servers are using their plugins.\n" + "To honor their work, you should not disable it.\n" + "This has nearly no effect on the server performance!\n" + "Check out https://bStats.org/ to learn more :)").copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) {}
}
// Load the data
enabled = config.getBoolean("enabled", true);
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) {}
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Adds a custom chart.
*
* @param chart
* The chart to add.
*/
public void addCustomChart(CustomChart chart) {
if (chart == null) {
throw new IllegalArgumentException("Chart cannot be null!");
}
charts.add(chart);
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit
// scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is
// sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("id", pluginId); // Append the id of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
JsonArray customCharts = new JsonArray();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
JsonObject chart = customChart.getRequestJsonObject();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.add("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) ? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size() : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
}
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {}
}
} catch (NoSuchFieldException ignored) {}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(() -> {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin
* Any plugin. It's just used to get a logger instance.
* @param data
* The data to send.
* @throws Exception
* If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data);
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
}
}
/**
* Gzips the given String.
*
* @param str
* The string to gzip.
* @return The gzipped String.
* @throws IOException
* If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
*/
CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
private JsonObject getRequestJsonObject() {
JsonObject chart = new JsonObject();
chart.addProperty("chartId", chartId);
try {
JsonObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
chart.add("data", data);
} catch (Throwable t) {
if (logFailedRequests) {
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract JsonObject getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom advanced pie.
*/
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom drilldown pie.
*/
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObject value = new JsonObject();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
value.addProperty(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
values.add(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom single line chart.
*/
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom multi line chart.
*/
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom simple bar chart.
*/
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
JsonArray categoryValues = new JsonArray();
categoryValues.add(new JsonPrimitive(entry.getValue()));
values.add(entry.getKey(), categoryValues);
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom advanced bar chart.
*/
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId
* The id of the chart.
* @param callable
* The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
continue; // Skip this invalid
}
allSkipped = false;
JsonArray categoryValues = new JsonArray();
for (int categoryValue : entry.getValue()) {
categoryValues.add(new JsonPrimitive(categoryValue));
}
values.add(entry.getKey(), categoryValues);
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
}

View File

@ -10,7 +10,6 @@ options:
armor-update-interval: 10 armor-update-interval: 10
enable-armor-effects: true enable-armor-effects: true
auto-save-delay-in-minutes: 10 auto-save-delay-in-minutes: 10
emerald-enchantment-limit: 2
legacy-ore-washer: false legacy-ore-washer: false
legacy-dust-washer: false legacy-dust-washer: false
legacy-ore-grinder: true legacy-ore-grinder: true

View File

@ -0,0 +1,26 @@
---
slimefun:
weapons: Armas
tools: Ferramentas
items: Itens úteis
food: Comida
basic_machines: Máquinas básicas
electricity: Energia e Eletricidade
gps: Máquinas baseadas em GPS
armor: Armaduras
magical_items: Itens mágicos
magical_gadgets: Acessórios Mágicos
misc: Itens Variados
technical_gadgets: Dispositivos Técnicos
resources: Recursos
cargo: Gestão de Carga
tech_misc: Componentes Técnicos
magical_armor: Armaduras Mágicas
talismans: Talismãs (Tier I)
ender_talismans: Ender Talismãs (Tier II)
christmas: Natal (Dezembro)
valentines_day: Dia dos Namorados (14 de Fevereiro)
easter: Páscoa (Abril)
birthday: Aniversário do TheBusyBiscuit (26 de Outubro)
halloween: Dia das Bruxas (31 de Outubro)
androids: Androids Programáveis

View File

@ -23,3 +23,4 @@ slimefun:
easter: Lệ Phục Sinh (Tháng Tư) easter: Lệ Phục Sinh (Tháng Tư)
birthday: Sinh Nhật TheBusyBiscuit (26 Tháng 10) birthday: Sinh Nhật TheBusyBiscuit (26 Tháng 10)
halloween: Halloween (31 tháng 10) halloween: Halloween (31 tháng 10)
androids: Programmable Androids

View File

@ -236,3 +236,14 @@ slimefun:
advanced_industrial_miner: Melhor mineração advanced_industrial_miner: Melhor mineração
magical_zombie_pills: Deszombificação magical_zombie_pills: Deszombificação
auto_brewer: Cervejaria Industrial auto_brewer: Cervejaria Industrial
enchantment_rune: Encantador Antigo
lead_clothing: Roupa de Chumbo
tape_measure: Fita métrica
iron_golem_assembler: Golems de Ferro Automatizados
climbing_pick: Bloco atacante
shulker_shell: Shulkers Sintética
villager_rune: Resetador de trocas dos aldeões
caveman_talisman: Talismã do Homem das Cavernas
even_higher_tier_capacitors: Capacitores tier 3
elytra_cap: Engrenagem de colisão
energy_connectors: Fios de conexão

View File

@ -245,4 +245,5 @@ slimefun:
villager_rune: 防奸商神器 villager_rune: 防奸商神器
caveman_talisman: 穴居人护身符 caveman_talisman: 穴居人护身符
even_higher_tier_capacitors: 三级电容器 even_higher_tier_capacitors: 三级电容器
elytra_cap: 无伤落地
energy_connectors: 有线连接 energy_connectors: 有线连接

View File

@ -0,0 +1,89 @@
{
"values" : [
"minecraft:bat_spawn_egg",
{
"id" : "minecraft:bee_spawn_egg",
"required" : false
},
"minecraft:blaze_spawn_egg",
"minecraft:cave_spider_spawn_egg",
"minecraft:cat_spawn_egg",
"minecraft:chicken_spawn_egg",
"minecraft:cod_spawn_egg",
"minecraft:cow_spawn_egg",
"minecraft:creeper_spawn_egg",
"minecraft:dolphin_spawn_egg",
"minecraft:donkey_spawn_egg",
"minecraft:drowned_spawn_egg",
"minecraft:elder_guardian_spawn_egg",
"minecraft:enderman_spawn_egg",
"minecraft:endermite_spawn_egg",
"minecraft:evoker_spawn_egg",
"minecraft:fox_spawn_egg",
"minecraft:ghast_spawn_egg",
"minecraft:guardian_spawn_egg",
{
"id" : "minecraft:hoglin_spawn_egg",
"required" : false
},
"minecraft:horse_spawn_egg",
"minecraft:husk_spawn_egg",
"minecraft:llama_spawn_egg",
"minecraft:magma_cube_spawn_egg",
"minecraft:mooshroom_spawn_egg",
"minecraft:mule_spawn_egg",
"minecraft:ocelot_spawn_egg",
"minecraft:panda_spawn_egg",
"minecraft:parrot_spawn_egg",
"minecraft:phantom_spawn_egg",
"minecraft:pig_spawn_egg",
{
"id" : "minecraft:piglin_spawn_egg",
"required" : false
},
{
"id" : "minecraft:piglin_brute_spawn_egg",
"required" : false
},
"minecraft:pillager_spawn_egg",
"minecraft:polar_bear_spawn_egg",
"minecraft:pufferfish_spawn_egg",
"minecraft:rabbit_spawn_egg",
"minecraft:ravager_spawn_egg",
"minecraft:salmon_spawn_egg",
"minecraft:sheep_spawn_egg",
"minecraft:shulker_spawn_egg",
"minecraft:silverfish_spawn_egg",
"minecraft:skeleton_spawn_egg",
"minecraft:skeleton_horse_spawn_egg",
"minecraft:slime_spawn_egg",
"minecraft:spider_spawn_egg",
"minecraft:squid_spawn_egg",
"minecraft:stray_spawn_egg",
{
"id" : "minecraft:strider_spawn_egg",
"required" : false
},
"minecraft:trader_llama_spawn_egg",
"minecraft:tropical_fish_spawn_egg",
"minecraft:turtle_spawn_egg",
"minecraft:vex_spawn_egg",
"minecraft:villager_spawn_egg",
"minecraft:vindicator_spawn_egg",
"minecraft:wandering_trader_spawn_egg",
"minecraft:witch_spawn_egg",
"minecraft:wither_skeleton_spawn_egg",
"minecraft:wolf_spawn_egg",
{
"id" : "minecraft:zoglin_spawn_egg",
"required" : false
},
"minecraft:zombie_spawn_egg",
"minecraft:zombie_horse_spawn_egg",
"minecraft:zombie_villager_spawn_egg",
{
"id" : "minecraft:zombified_piglin_spawn_egg",
"required" : false
}
]
}

View File

@ -17,6 +17,15 @@ import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
* This is a convenient interface for us to use in unit test classes
* that test the functionality of a particular {@link SlimefunItem}.
*
* @author TheBusyBiscuit
*
* @param <T>
* The class type of {@link SlimefunItem} you want to test
*/
@FunctionalInterface @FunctionalInterface
public interface SlimefunItemTest<T extends SlimefunItem> { public interface SlimefunItemTest<T extends SlimefunItem> {

View File

@ -0,0 +1,179 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.items;
import java.util.Optional;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import be.seeseemelk.mockbukkit.MockBukkit;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.api.exceptions.UnregisteredItemException;
import io.github.thebusybiscuit.slimefun4.api.exceptions.WrongItemStackException;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.testing.TestUtilities;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
class TestSlimefunItem {
private static SlimefunPlugin plugin;
@BeforeAll
public static void load() {
MockBukkit.mock();
plugin = MockBukkit.load(SlimefunPlugin.class);
}
@AfterAll
public static void unload() {
MockBukkit.unmock();
}
@Test
@DisplayName("Test wiki pages getting assigned correctly")
void testWikiPages() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "WIKI_ITEM", new CustomItem(Material.BOOK, "&cTest"));
item.register(plugin);
Assertions.assertFalse(item.getWikipage().isPresent());
// null should not be a valid argument
Assertions.assertThrows(IllegalArgumentException.class, () -> item.addOficialWikipage(null));
item.addOficialWikipage("Test");
Optional<String> wiki = item.getWikipage();
Assertions.assertTrue(wiki.isPresent());
Assertions.assertEquals("https://github.com/Slimefun/Slimefun4/wiki/Test", wiki.get());
}
@Test
@DisplayName("Test SlimefunItem registering Recipes properly")
void testRecipe() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TEST", new CustomItem(Material.DIAMOND, "&dAnother one bites the test"));
ItemStack[] recipe = { null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null };
item.setRecipe(recipe);
item.register(plugin);
Assertions.assertArrayEquals(recipe, item.getRecipe());
Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(null));
Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[3]));
Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[20]));
}
@Test
@DisplayName("Test Recipe outputs being handled correctly")
void testRecipeOutput() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_OUTPUT_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
item.register(plugin);
Assertions.assertEquals(item.getItem(), item.getRecipeOutput());
ItemStack output = new ItemStack(Material.EMERALD, 64);
item.setRecipeOutput(output);
Assertions.assertEquals(output, item.getRecipeOutput());
item.setRecipeOutput(item.getItem());
Assertions.assertEquals(item.getItem(), item.getRecipeOutput());
}
@Test
@DisplayName("Test Recipe Types being handled properly")
void testRecipeType() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TYPE_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
item.register(plugin);
Assertions.assertNotNull(item.getRecipeType());
item.setRecipeType(RecipeType.ENHANCED_CRAFTING_TABLE);
Assertions.assertEquals(RecipeType.ENHANCED_CRAFTING_TABLE, item.getRecipeType());
item.setRecipeType(RecipeType.NULL);
Assertions.assertEquals(RecipeType.NULL, item.getRecipeType());
Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipeType(null));
}
@ParameterizedTest
@DisplayName("Test SlimefunItem#isItem(...)")
@ValueSource(booleans = { true, false })
void testIsItem(boolean compatibility) {
CustomItem item = new CustomItem(Material.BEACON, "&cItem Test");
String id = "IS_ITEM_TEST" + (compatibility ? "_COMPATIBLE" : "");
SlimefunItem sfItem = TestUtilities.mockSlimefunItem(plugin, id, item);
sfItem.register(plugin);
Assertions.assertTrue(sfItem.isItem(sfItem.getItem()));
Assertions.assertFalse(sfItem.isItem(null));
Assertions.assertFalse(sfItem.isItem(new ItemStack(Material.BEACON)));
Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.REDSTONE, "&cTest")));
if (compatibility) {
SlimefunPlugin.getRegistry().setBackwardsCompatible(true);
Assertions.assertEquals(sfItem, SlimefunItem.getByItem(item));
Assertions.assertTrue(sfItem.isItem(item));
Assertions.assertTrue(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test")));
SlimefunPlugin.getRegistry().setBackwardsCompatible(false);
} else {
Assertions.assertFalse(sfItem.isItem(item));
Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test")));
}
Assertions.assertEquals(sfItem, SlimefunItem.getByItem(new SlimefunItemStack(sfItem.getId(), item)));
}
@Test
@DisplayName("Test WrongItemStackException")
void testWrongItemStackException() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "WRONG_ITEMSTACK_EXCEPTION", new CustomItem(Material.NETHER_STAR, "&4Do not modify me"));
item.register(plugin);
item.load();
ItemStack itemStack = item.getItem();
Assertions.assertThrows(WrongItemStackException.class, () -> itemStack.setAmount(40));
}
@Test
@DisplayName("Test UnregisteredItemException")
void testUnregisteredItemException() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "UNREGISTERED_ITEM_EXCEPTION", new CustomItem(Material.NETHER_STAR, "&4Do not modify me"));
Assertions.assertThrows(UnregisteredItemException.class, () -> item.getAddon());
}
@Test
@DisplayName("Test SlimefunItem#equals(...)")
void testEquals() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal"));
SlimefunItem item2 = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal"));
SlimefunItem differentItem = TestUtilities.mockSlimefunItem(plugin, "I_AM_DIFFERENT", new CustomItem(Material.LANTERN, "&6We are equal"));
Assertions.assertEquals(item, item2);
Assertions.assertNotEquals(item, differentItem);
Assertions.assertNotEquals(item2, differentItem);
}
@Test
@DisplayName("Test SlimefunItem#hashCode()")
void testHashCode() {
SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal"));
SlimefunItem item2 = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal"));
SlimefunItem differentItem = TestUtilities.mockSlimefunItem(plugin, "I_AM_DIFFERENT", new CustomItem(Material.LANTERN, "&6We are equal"));
Assertions.assertEquals(item.hashCode(), item2.hashCode());
Assertions.assertNotEquals(item.hashCode(), differentItem.hashCode());
Assertions.assertNotEquals(item2.hashCode(), differentItem.hashCode());
}
}

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