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

Merge branch 'master' of https://github.com/Slimefun/Slimefun4.git into performance/cobblestone

This commit is contained in:
TheBusyBiscuit 2020-12-10 12:04:48 +01:00
commit b07a54d515
376 changed files with 8593 additions and 4765 deletions

View File

@ -2,7 +2,7 @@
name: Bug Report
about: Report a Bug or an Issue with Slimefun 4.
title: ''
labels: Bug Report
labels: '🐞 Bug Report'
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

@ -6,10 +6,13 @@ yaml-files:
rules:
## A warning is sufficient here
line-length:
max: 180
level: warning
## Don't warn for line lengths
line-length: disable
truthy:
allowed-values: ['true', 'false']
## We don't want it to trigger for the 'on' in our workflows
check-keys: false
## We don't need indentation warnings
indentation: disable

View File

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

View File

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

View File

@ -10,7 +10,7 @@ jobs:
name: Invalid Issues
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:
- name: Close Issue
uses: maxkomarychev/octions/octions/issues/update@master

View File

@ -4,6 +4,7 @@ on:
push:
paths:
- 'src/**'
- '!src/main/resources/languages/**'
- 'pom.xml'
jobs:
@ -15,11 +16,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v1
- name: Set up JDK 1.8
uses: actions/setup-java@master
uses: actions/checkout@v2.3.4
- name: Set up Java JDK 11
uses: actions/setup-java@v1.4.3
with:
java-version: 1.8
java-version: '11'
java-package: jdk
architecture: x64
- name: Run Discord Webhook
uses: Slimefun/discord-webhook@master
with:

View File

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

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@master
with:

View File

@ -14,5 +14,5 @@ jobs:
steps:
- uses: mschilde/auto-label-merge-conflicts@master
with:
CONFLICT_LABEL_NAME: 'Merge Conflicts'
CONFLICT_LABEL_NAME: 'Merge Conflicts'
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.4.0
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

@ -6,7 +6,7 @@ on:
- master
jobs:
build:
check:
name: URL Checker
runs-on: ubuntu-latest
@ -17,7 +17,7 @@ jobs:
with:
git_path: https://github.com/Slimefun/Slimefun4
file_types: .md,.java,.yml
print_all: False
print_all: false
retry_count: 2
## These URLs will always be correct, even if their services may be offline right now
white_listed_patterns: http://textures.minecraft.net/texture/,https://pastebin.com/,https://www.spigotmc.org/threads/spigot-bungeecord-1-16-1.447405/#post-3852349,https://gitlocalize.com/repo/3841

View File

@ -16,8 +16,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: YAML Linter
uses: ibiqlik/action-yamllint@v1.0.0
uses: ibiqlik/action-yamllint@v3.0.0
with:
config_file: '.github/configs/yaml-linter.yml'

View File

@ -2,7 +2,9 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of contents**
- [Release Candidate 17 (TBD)](#release-candidate-17-tbd)
- [Release Candidate 19 (TBD)](#release-candidate-19-tbd)
- [Release Candidate 18 (03 Dec 2020)](#release-candidate-18-03-dec-2020)
- [Release Candidate 17 (17 Oct 2020)](#release-candidate-17-17-oct-2020)
- [Release Candidate 16 (07 Sep 2020)](#release-candidate-16-07-sep-2020)
- [Release Candidate 15 (01 Aug 2020)](#release-candidate-15-01-aug-2020)
- [Release Candidate 14 (12 Jul 2020)](#release-candidate-14-12-jul-2020)
@ -22,7 +24,78 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Release Candidate 17 (TBD)
## Release Candidate 19 (TBD)
#### Changes
* Performance optimizations for Cargo networks
* Removed an old version of bStats
## Release Candidate 18 (03 Dec 2020)
#### Additions
* The Smelters Pick now also works on Ancient Debris
* (API) Added PlayerPreResearchEvent
* Added a config option to disable network visualizations
* (API) Added CoolerFeedPlayerEvent
* Added a config option to delete excess cargo network items
* Added an item setting to configure the Wind Staff velocity
* Added an item setting to the Infused Hopper to toggle it with redstone
* Added an item setting to prevent Reinforced Spawners from being changed by Spawn Eggs
* Added 4 bricks -> 1 brick block recipe to the Electric Press
#### Changes
* Removed 1.13 support
* Cooling Units can no longer be placed down
* Heating Coils can no longer be placed down
* Electric Motors can no longer be placed down
* Cargo Motors can no longer be placed down
* Magnets can no longer be placed down
* Electromagnets can no longer be placed down
* Performance improvements to Cargo network visualizations
* General performance improvements
* Improved performance for radioactive items
* Memory/GC improvements for the profiler
* Performance improvements for the Fluid Pump
* Removed EmeraldEnchants integration
* Memory and performance improvements for ticking blocks
#### Fixes
* Fixed #2448
* Fixed #2470
* Fixed #2478
* Fixed #2493
* Fixed a missing slot in the contributors menu
* Fixed color codes in script downloading screen
* Fixed #2505
* Fixed contributors not showing correctly
* Fixed #2469
* Fixed #2509
* Fixed #2499
* Fixed #2527
* Fixed #2519
* Fixed #2517
* Fixed Magician Talisman sometimes drawing invalid enchantments
* Fixed id conflicts for external Enchantment sources (e.g. plugins) for the Magician Talisman settings
* Fixed network visualizers spawning particles for other player heads
* Fixed #2418
* Fixed #2446
* Fixed CoreProtect not recognizing Slimefun blocks getting broken
* Fixed #2447
* Fixed #2558
* Fixed a duplication bug with the Block Placer
* Fixed Slimefun Guide Settings showing "last activity" as a negative number
* Fixed Armor Stands getting damaged/pushed by Explosive Bow
* Fixed Sword of Beheading dropping Zombie/Skeleton Skulls from Zombie/Skeleton subvariants
* Fixed #2518
* Fixed #2421
* Fixed #2574
* Fixed color in android script downloading screen
* Fixed #2576
* Fixed #2496
* Fixed #2585
* Fixed #2583
## Release Candidate 17 (17 Oct 2020)
#### Additions
* Added /sf charge
@ -39,6 +112,11 @@
* Added "slimefun.gps.bypass" permission to open GPS devices anywhere
* (API) Added custom tags for developers
* The range of the Seeker Pickaxe is now configurable
* Added Energy Connector
* Blackstone can now be turned into lava using a Crucible
* Basalt can now be turned into lava using a Crucible
* Added "Tainted Sheep" (You can dye a Sheep using Strange Nether Goo)
* Added mcMMO support/integration
#### Changes
* Improved Auto-Updater (Multi-Threading and more)
@ -74,6 +152,22 @@
* Fixed #2405
* Fixed #2412
* Fixed #2238
* Fixed #2439
* Fixed #2420
* Fixed #2422
* Fixed #2433
* Fixed #2455
* Fixed #2450
* Fixed Steel Thrusters being used to milk cows
* Fixed #2424
* Fixed #2468
* Fixed #2414
* Fixed #2454
* Fixed #2457
* Fixed #2411
* Fixed #2423
* Fixed #2452
* Fixed a dupe bug with mcMMO
## Release Candidate 16 (07 Sep 2020)
https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#16

View File

@ -159,7 +159,7 @@ if (something) {
// Actual code...
```
* if/else statements should always include a bracket, please avoid one-line statements. (e.g. Avoid doing: `if (x == 0) return;`)
* We do not enforce any particular width or column limit, but try to prevent your lines from becoming too long.
* We do not enforce any particular width or column limit, just try to prevent your lines from becoming too long. But please avoid line-wrapping.
* Annotations for methods or fields should never go on the same line, place them on the line above.
* Comments should never go on the same line as code! Always above or below.
* Make sure that empty lines are truly empty, they should not contain any whitespace characters.

View File

@ -32,7 +32,7 @@ Here is a full summary of the differences between the two different versions of
| | development (latest) | "stable" |
| ------------------ | -------- | -------- |
| **Minecraft version(s)** | :video_game: **1.13.\* - 1.16.\*** | :video_game: **1.13.\* - 1.16.\*** |
| **Minecraft version(s)** | :video_game: **1.14.\* - 1.16.\*** | :video_game: **1.14.\* - 1.16.\*** |
| **automatic updates** | :heavy_check_mark: | :heavy_check_mark: |
| **frequent updates** | :heavy_check_mark: | :x: |
| **latest content** | :heavy_check_mark: | :x: |
@ -67,7 +67,7 @@ Well, we asked some users on our [Discord server](#discord) to send us some scre
| *Screenshot provided by GalaxyKat11#3816* | *Screenshot provided by TamThan#7987* | *Screenshot provided by Kilaruna#4981* |
## Discord
You can find Slimefun's community on Discord and connect with **over 2500** users of this plugin from all over the world.<br>
You can find Slimefun's community on Discord and connect with **over 3000** users of this plugin from all over the world.<br>
Click the badge down below to join the server for suggestions/questions or other discussions about this plugin.<br>
We are also hosting a community event every so often, join us to find out more.<br>
**Important**: We do **not** accept bug reports on discord, please use our [Issue Tracker](https://github.com/Slimefun/Slimefun4/issues) to submit bug reports!

51
pom.xml
View File

@ -22,7 +22,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<!-- Spigot properties -->
<spigot.version>1.16.3</spigot.version>
<spigot.version>1.16.4</spigot.version>
<spigot.javadocs>https://hub.spigotmc.org/javadocs/spigot/</spigot.javadocs>
<!-- Default settings for sonarcloud.io -->
@ -30,7 +30,6 @@
<sonar.organization>slimefun</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.log.level>DEBUG</sonar.log.level>
<sonar.exclusions>src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java</sonar.exclusions>
<sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties>
@ -64,6 +63,10 @@
<id>worldedit-repo</id>
<url>https://maven.sk89q.com/repo/</url>
</repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public</url>
</repository>
<repository>
<id>placeholderapi-repo</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
@ -311,7 +314,7 @@
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.16</artifactId>
<version>0.10.2</version>
<version>0.17.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
@ -325,7 +328,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.13</version>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
@ -333,19 +336,19 @@
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>CS-CoreLib2</artifactId>
<version>0.26</version>
<version>0.27.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
<version>3.11.01</version>
<version>3.11.05</version>
<scope>compile</scope>
<exclusions>
<exclusion>
@ -359,7 +362,7 @@
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.2.0-SNAPSHOT</version>
<version>7.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@ -368,6 +371,32 @@
<groupId>de.schlichtherle</groupId>
<artifactId>truezip</artifactId>
</exclusion>
<exclusion>
<groupId>net.java.truevfs</groupId>
<artifactId>truevfs-profile-default_2.13</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId>
<version>2.1.161</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<!-- We use javax.annotation instead. Excluding this -->
<!-- prevents us from using inconsistent annotations -->
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-legacy</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -384,12 +413,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>EmeraldEnchants2</artifactId>
<version>3cd370b5d8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.minebuilders</groupId>
<artifactId>clearlag-core</artifactId>

View File

@ -81,7 +81,7 @@ public class ErrorReport<T extends Throwable> {
}
stream.println("Slimefun Data:");
stream.println(" ID: " + item.getID());
stream.println(" ID: " + item.getId());
stream.println(" Inventory: " + BlockStorage.getStorage(l.getWorld()).hasInventory(l));
stream.println(" Data: " + BlockStorage.getBlockInfoAsJson(l));
stream.println();
@ -92,7 +92,7 @@ public class ErrorReport<T extends Throwable> {
public ErrorReport(T throwable, SlimefunItem item) {
this(throwable, item.getAddon(), stream -> {
stream.println("SlimefunItem:");
stream.println(" ID: " + item.getID());
stream.println(" ID: " + item.getId());
stream.println(" Plugin: " + (item.getAddon() == null ? "Unknown" : item.getAddon().getName()));
stream.println();
});

View File

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

View File

@ -0,0 +1,113 @@
package io.github.thebusybiscuit.slimefun4.api.events;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.Cooler;
/**
* This {@link Event} is called whenever a {@link Player} is
* fed through a {@link Cooler}.
*
* @author TheBusyBiscuit
*
* @see Cooler
*
*/
public class CoolerFeedPlayerEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Cooler cooler;
private final ItemStack coolerItem;
private ItemStack consumedItem;
private boolean cancelled;
@ParametersAreNonnullByDefault
public CoolerFeedPlayerEvent(Player player, Cooler cooler, ItemStack coolerItem, ItemStack consumedItem) {
super(player);
this.cooler = cooler;
this.coolerItem = coolerItem;
this.consumedItem = consumedItem;
}
/**
* This returns the {@link Cooler} that was used.
*
* @return The {@link Cooler} that was used
*/
@Nonnull
public Cooler getCooler() {
return cooler;
}
/**
* This returns the {@link Cooler} that was used (as an {@link ItemStack})
*
* @return The {@link Cooler} that was used
*/
@Nonnull
public ItemStack getCoolerItem() {
return coolerItem;
}
/**
* This returns the {@link ItemStack} that was consumed.
* The returned {@link ItemStack} is immutable.
*
* @return The {@link ItemStack} that was consumed
*/
@Nonnull
public ItemStack getConsumedItem() {
return consumedItem.clone();
}
/**
* This sets the {@link ItemStack} that should be "consumed".
* The {@link ItemStack} must be a potion.
* The {@link Player} will receive the {@link PotionEffect PotionEffects} of the
* provided potion upon consumption.
*
* @param item
* The new {@link ItemStack}
*/
public void setConsumedItem(@Nonnull ItemStack item) {
Validate.notNull(item, "The consumed Item cannot be null!");
Validate.isTrue(item.getItemMeta() instanceof PotionMeta, "The item must be a potion!");
this.consumedItem = item;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Nonnull
public static HandlerList getHandlerList() {
return handlers;
}
@Nonnull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
}

View File

@ -0,0 +1,83 @@
package io.github.thebusybiscuit.slimefun4.api.events;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.apache.commons.lang.Validate;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.guide.BookSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide;
/**
* This {@link Event} is called whenever a {@link Player} clicks to unlock a {@link Research}.
* This is called before {@link Research#canUnlock(Player)}.
* The {@link Event} is not called for {@link CheatSheetSlimefunGuide}.
*
* @author uiytt
*
* @see ChestSlimefunGuide
* @see BookSlimefunGuide
*
*/
public class PlayerPreResearchEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Player player;
private final Research research;
private final SlimefunItem slimefunItem;
private boolean cancelled;
@ParametersAreNonnullByDefault
public PlayerPreResearchEvent(Player p, Research research, SlimefunItem slimefunItem) {
Validate.notNull(p, "The Player cannot be null");
Validate.notNull(research, "Research cannot be null");
Validate.notNull(slimefunItem, "SlimefunItem cannot be null");
this.player = p;
this.research = research;
this.slimefunItem = slimefunItem;
}
@Nonnull
public Player getPlayer() {
return player;
}
@Nonnull
public Research getResearch() {
return research;
}
@Nonnull
public SlimefunItem getSlimefunItem() {
return slimefunItem;
}
@Nonnull
public static HandlerList getHandlerList() {
return handlers;
}
@Nonnull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

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

View File

@ -31,4 +31,18 @@ public class TagMisconfigurationException extends Exception {
super("Tag '" + key + "' has been misconfigured: " + message);
}
/**
* This constructs a new {@link TagMisconfigurationException} for the given
* {@link SlimefunTag}'s {@link NamespacedKey} with the provided context.
*
* @param key
* The {@link NamespacedKey} of our {@link SlimefunTag}
* @param cause
* The {@link Throwable} which has caused this to happen
*/
@ParametersAreNonnullByDefault
public TagMisconfigurationException(NamespacedKey key, Throwable cause) {
super("Tag '" + key + "' has been misconfigured (" + cause.getMessage() + ')', cause);
}
}

View File

@ -201,15 +201,19 @@ public class ResourceManager {
menu.addItem(47, ChestMenuUtils.getPreviousButton(p, page + 1, pages));
menu.addMenuClickHandler(47, (pl, slot, item, action) -> {
if (page > 0)
if (page > 0) {
scan(pl, block, page - 1);
}
return false;
});
menu.addItem(51, ChestMenuUtils.getNextButton(p, page + 1, pages));
menu.addMenuClickHandler(51, (pl, slot, item, action) -> {
if (page + 1 < pages)
if (page + 1 < pages) {
scan(pl, block, page + 1);
}
return false;
});

View File

@ -148,10 +148,12 @@ public class GPSNetwork {
int index = 0;
for (Location l : getTransmitters(p.getUniqueId())) {
if (index >= inventory.length)
if (index >= inventory.length) {
break;
}
SlimefunItem sfi = BlockStorage.check(l);
if (sfi instanceof GPSTransmitter) {
int slot = inventory[index];
@ -216,8 +218,10 @@ public class GPSNetwork {
int index = 0;
for (Waypoint waypoint : profile.getWaypoints()) {
if (index >= inventory.length)
if (index >= inventory.length) {
break;
}
int slot = inventory[index];
Location l = waypoint.getLocation();

View File

@ -92,8 +92,9 @@ public final class TeleportationManager {
@ParametersAreNonnullByDefault
public int getTeleportationTime(int complexity, Location source, Location destination) {
if (complexity < 100)
if (complexity < 100) {
return 100;
}
int speed = 50_000 + complexity * complexity;
return 1 + Math.min(4 * distanceSquared(source, destination) / speed, 40);

View File

@ -102,7 +102,7 @@ public final class HashedArmorpiece {
@Override
public String toString() {
return "HashedArmorpiece {hash=" + hash + ",item=" + item.map(SlimefunItem::getID).orElse("null") + '}';
return "HashedArmorpiece {hash=" + hash + ",item=" + item.map(SlimefunItem::getId).orElse("null") + '}';
}
}

View File

@ -113,12 +113,24 @@ public class ItemSetting<T> {
*
* @param c
* The class of data type you want to compare
*
* @return Whether this {@link ItemSetting} stores the given type
*/
public boolean isType(@Nonnull Class<?> c) {
return c.isInstance(defaultValue);
}
/**
* This is an error message which should provide further context on what values
* are allowed.
*
* @return An error message which is displayed when this {@link ItemSetting} is misconfigured.
*/
@Nonnull
protected String getErrorMessage() {
return "Only '" + defaultValue.getClass().getSimpleName() + "' values are allowed!";
}
/**
* This method is called by a {@link SlimefunItem} which wants to load its {@link ItemSetting}
* from the {@link Config} file.
@ -126,18 +138,24 @@ public class ItemSetting<T> {
* @param item
* The {@link SlimefunItem} who called this method
*/
@SuppressWarnings("unchecked")
public void load(@Nonnull SlimefunItem item) {
SlimefunPlugin.getItemCfg().setDefaultValue(item.getID() + '.' + getKey(), getDefaultValue());
Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getID() + '.' + getKey());
Validate.notNull(item, "Cannot apply settings for a non-existing SlimefunItem");
SlimefunPlugin.getItemCfg().setDefaultValue(item.getId() + '.' + getKey(), getDefaultValue());
Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey());
if (defaultValue.getClass().isInstance(configuredValue)) {
if (validateInput((T) configuredValue)) {
this.value = (T) configuredValue;
// We can suppress the warning here, we did an isInstance(...) check before!
@SuppressWarnings("unchecked")
T newValue = (T) configuredValue;
if (validateInput(newValue)) {
this.value = newValue;
} else {
Slimefun.getLogger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!");
Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getID(), getKey() });
Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() });
Slimefun.getLogger().log(Level.WARNING, "{0} is not a valid input!", configuredValue);
Slimefun.getLogger().log(Level.WARNING, getErrorMessage());
}
} else {
this.value = defaultValue;
@ -145,7 +163,7 @@ public class ItemSetting<T> {
Slimefun.getLogger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!");
Slimefun.getLogger().log(Level.WARNING, "Please only use settings that are valid.");
Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getID(), getKey() });
Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() });
Slimefun.getLogger().log(Level.WARNING, "Expected \"{0}\" but found: \"{1}\"", new Object[] { defaultValue.getClass().getSimpleName(), found });
}
}

View File

@ -0,0 +1,63 @@
package io.github.thebusybiscuit.slimefun4.api.items.settings;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
/**
* This variation of {@link ItemSetting} allows you to define an {@link Double} range
* and enforces this range using the {@link #validateInput(Double)} method.
*
* @author TheBusyBiscuit
*
* @see ItemSetting
* @see IntRangeSetting
*
*/
public class DoubleRangeSetting extends ItemSetting<Double> {
private final double min;
private final double max;
@ParametersAreNonnullByDefault
public DoubleRangeSetting(String key, double min, double defaultValue, double max) {
super(key, defaultValue);
Validate.isTrue(defaultValue >= min && defaultValue <= max, "The default value is not in range.");
this.min = min;
this.max = max;
}
@Nonnull
@Override
protected String getErrorMessage() {
return "Only decimal numbers from " + min + '-' + max + "(inclusive) are allowed!";
}
@Override
public boolean validateInput(Double input) {
return super.validateInput(input) && input >= min && input <= max;
}
/**
* This returns the minimum value of this {@link DoubleRangeSetting}.
*
* @return The minimum value
*/
public final double getMinimum() {
return min;
}
/**
* This returns the maximum value of this {@link DoubleRangeSetting}.
*
* @return The maximum value
*/
public final double getMaximum() {
return max;
}
}

View File

@ -0,0 +1,72 @@
package io.github.thebusybiscuit.slimefun4.api.items.settings;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
/**
* This variation of {@link ItemSetting} allows you to allow {@link Enum} constants to be
* used for {@link ItemSetting} validation.
*
* @author TheBusyBiscuit
*
* @see ItemSetting
*
*/
public class EnumSetting<T extends Enum<T>> extends ItemSetting<String> {
private final Class<T> enumClass;
@ParametersAreNonnullByDefault
public EnumSetting(String key, Class<T> enumClass, T defaultValue) {
super(key, defaultValue.name());
this.enumClass = enumClass;
}
@Nonnull
@Override
protected String getErrorMessage() {
String values = Arrays.stream(getAllowedValues()).map(Enum::name).collect(Collectors.joining(", "));
return "The following values are valid: " + values;
}
/**
* This returns an array of valid {@link Enum} values.
* This method may be overridden to further limit the allowed values.
*
* @return An array of allowed {@link Enum} constants
*/
public T[] getAllowedValues() {
return enumClass.getEnumConstants();
}
/**
* This will attempt to get the configured value as a constant of the desired {@link Enum}.
*
* @return The value as an {@link Enum} constant
*/
public T getAsEnumConstant() {
return Enum.valueOf(enumClass, getValue());
}
@Override
public boolean validateInput(String input) {
if (!super.validateInput(input)) {
return false;
} else {
for (Enum<T> value : getAllowedValues()) {
if (value.name().equals(input)) {
return true;
}
}
return false;
}
}
}

View File

@ -1,7 +1,10 @@
package io.github.thebusybiscuit.slimefun4.api.items.settings;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
/**
@ -11,6 +14,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
* @author TheBusyBiscuit
*
* @see ItemSetting
* @see DoubleRangeSetting
*
*/
public class IntRangeSetting extends ItemSetting<Integer> {
@ -21,11 +25,18 @@ public class IntRangeSetting extends ItemSetting<Integer> {
@ParametersAreNonnullByDefault
public IntRangeSetting(String key, int min, int defaultValue, int max) {
super(key, defaultValue);
Validate.isTrue(defaultValue >= min && defaultValue <= max, "The default value is not in range.");
this.min = min;
this.max = max;
}
@Nonnull
@Override
protected String getErrorMessage() {
return "Only whole numbers from " + min + '-' + max + "(inclusive) are allowed!";
}
@Override
public boolean validateInput(Integer input) {
return super.validateInput(input) && input >= min && input <= max;
@ -36,7 +47,7 @@ public class IntRangeSetting extends ItemSetting<Integer> {
*
* @return The minimum value
*/
public int getMinimum() {
public final int getMinimum() {
return min;
}
@ -45,7 +56,7 @@ public class IntRangeSetting extends ItemSetting<Integer> {
*
* @return The maximum value
*/
public int getMaximum() {
public final int getMaximum() {
return max;
}

View File

@ -44,9 +44,15 @@ public class MaterialTagSetting extends ItemSetting<List<String>> {
return defaultTag;
}
@Nonnull
@Override
protected String getErrorMessage() {
return "This List can only contain Materials in the format of e.g. REDSTONE_BLOCK";
}
@Override
public boolean validateInput(List<String> input) {
if (input != null) {
if (super.validateInput(input)) {
for (String value : input) {
Material material = Material.matchMaterial(value);

View File

@ -9,11 +9,8 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.Validate;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
@ -103,13 +100,16 @@ public abstract class Network {
return regulatorNodes.size() + connectorNodes.size() + terminusNodes.size();
}
/**
* This method adds the given {@link Location} to this {@link Network}.
*
* @param l
* The {@link Location} to add
*/
protected void addLocationToNetwork(@Nonnull Location l) {
if (connectedLocations.contains(l)) {
return;
if (connectedLocations.add(l.clone())) {
markDirty(l);
}
connectedLocations.add(l.clone());
markDirty(l);
}
/**
@ -132,10 +132,15 @@ public abstract class Network {
*
* @param l
* The {@link Location} to check for
*
* @return Whether the given {@link Location} is part of this {@link Network}
*/
public boolean connectsTo(@Nonnull Location l) {
return connectedLocations.contains(l);
if (regulator.equals(l)) {
return true;
} else {
return connectedLocations.contains(l);
}
}
@Nullable
@ -211,17 +216,9 @@ public abstract class Network {
* every {@link Location} that this {@link Network} is connected to.
*/
public void display() {
SlimefunPlugin.runSync(() -> {
DustOptions options = new DustOptions(Color.BLUE, 3F);
for (Location l : connectedLocations) {
Material type = l.getBlock().getType();
if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) {
l.getWorld().spawnParticle(Particle.REDSTONE, l.getX() + 0.5, l.getY() + 0.5, l.getZ() + 0.5, 1, 0, 0, 0, 1, options);
}
}
});
if (manager.isVisualizerEnabled()) {
SlimefunPlugin.runSync(new NetworkVisualizer(this));
}
}
/**

View File

@ -0,0 +1,59 @@
package io.github.thebusybiscuit.slimefun4.api.network;
import javax.annotation.Nonnull;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
/**
* This class represents the visualizer task of a given {@link Network}.
*
* @author TheBusyBiscuit
*
*/
class NetworkVisualizer implements Runnable {
/**
* The {@link DustOptions} define the {@link Color} and size of our particles.
*/
private final DustOptions options = new DustOptions(Color.BLUE, 3.5F);
/**
* This is our {@link Network} instance.
*/
private final Network network;
/**
* This creates a new {@link NetworkVisualizer} for the given {@link Network}.
*
* @param network
* The {@link Network} to visualize
*/
NetworkVisualizer(@Nonnull Network network) {
this.network = network;
}
@Override
public void run() {
for (Location l : network.connectorNodes) {
spawnParticles(l);
}
for (Location l : network.terminusNodes) {
spawnParticles(l);
}
}
/**
* This method will spawn the actual particles.
*
* @param l
* The {@link Location} of our node
*/
private void spawnParticles(@Nonnull Location l) {
l.getWorld().spawnParticle(Particle.REDSTONE, l.getX() + 0.5, l.getY() + 0.5, l.getZ() + 0.5, 1, 0, 0, 0, 1, options);
}
}

View File

@ -4,6 +4,8 @@ import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
@ -27,10 +29,11 @@ public class StatusEffect implements Keyed {
private final NamespacedKey key;
public StatusEffect(NamespacedKey key) {
public StatusEffect(@Nonnull NamespacedKey key) {
this.key = key;
}
@Nonnull
@Override
public NamespacedKey getKey() {
return key;
@ -48,7 +51,7 @@ public class StatusEffect implements Keyed {
* @param unit
* The {@link TimeUnit} for the given duration
*/
public void add(Player p, int duration, TimeUnit unit) {
public void add(@Nonnull Player p, int duration, @Nonnull TimeUnit unit) {
add(p, 1, duration, unit);
}
@ -64,7 +67,7 @@ public class StatusEffect implements Keyed {
* @param unit
* The {@link TimeUnit} for the given duration
*/
public void add(Player p, int level, int duration, TimeUnit unit) {
public void add(@Nonnull Player p, int level, int duration, @Nonnull TimeUnit unit) {
PersistentDataAPI.setString(p, getKey(), level + ";" + System.currentTimeMillis() + unit.toMillis(duration));
}
@ -77,7 +80,7 @@ public class StatusEffect implements Keyed {
* @param level
* The level of this effect
*/
public void addPermanent(Player p, int level) {
public void addPermanent(@Nonnull Player p, int level) {
PersistentDataAPI.setString(p, getKey(), level + ";0");
}
@ -91,7 +94,7 @@ public class StatusEffect implements Keyed {
* The {@link Player} to check for
* @return Whether this {@link StatusEffect} is currently applied
*/
public boolean isPresent(Player p) {
public boolean isPresent(@Nonnull Player p) {
Optional<String> optional = PersistentDataAPI.getOptionalString(p, getKey());
if (optional.isPresent()) {
@ -104,8 +107,9 @@ public class StatusEffect implements Keyed {
clear(p);
return false;
}
} else
} else {
return false;
}
}
/**
@ -116,15 +120,16 @@ public class StatusEffect implements Keyed {
* The {@link Player} to check for
* @return An {@link OptionalInt} that describes the result
*/
public OptionalInt getLevel(Player p) {
@Nonnull
public OptionalInt getLevel(@Nonnull Player p) {
Optional<String> optional = PersistentDataAPI.getOptionalString(p, getKey());
if (optional.isPresent()) {
String[] data = PatternUtils.SEMICOLON.split(optional.get());
return OptionalInt.of(Integer.parseInt(data[0]));
} else
} else {
return OptionalInt.empty();
}
}
/**
@ -133,7 +138,7 @@ public class StatusEffect implements Keyed {
* @param p
* The {@link Player} to clear it from
*/
public void clear(Player p) {
public void clear(@Nonnull Player p) {
PersistentDataAPI.remove(p, getKey());
}

View File

@ -11,6 +11,9 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -19,18 +22,15 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.collections.KeyMap;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlock;
import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.guide.BookSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -47,7 +47,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
* @author TheBusyBiscuit
*
*/
public class SlimefunRegistry {
public final class SlimefunRegistry {
private final Map<String, SlimefunItem> slimefunIds = new HashMap<>();
private final List<SlimefunItem> slimefunItems = new ArrayList<>();
@ -84,9 +84,9 @@ public class SlimefunRegistry {
private final Map<Class<? extends ItemHandler>, Set<ItemHandler>> globalItemHandlers = new HashMap<>();
private final Map<String, SlimefunBlockHandler> blockHandlers = new HashMap<>();
private final Map<String, ItemStack> automatedCraftingChamberRecipes = new HashMap<>();
public void load(@Nonnull Config cfg) {
Validate.notNull(cfg, "The Config cannot be null!");
public void load(Config cfg) {
boolean showVanillaRecipes = cfg.getBoolean("guide.show-vanilla-recipes");
layouts.put(SlimefunGuideLayout.CHEST, new ChestSlimefunGuide(showVanillaRecipes));
@ -95,7 +95,7 @@ public class SlimefunRegistry {
researchRanks.addAll(cfg.getStringList("research-ranks"));
backwardsCompatibility = cfg.getBoolean("options.backwards-compatibility") || SlimefunPlugin.getMinecraftVersion().isBefore(MinecraftVersion.MINECRAFT_1_14);
backwardsCompatibility = cfg.getBoolean("options.backwards-compatibility");
freeCreativeResearches = cfg.getBoolean("researches.free-in-creative-mode");
researchFireworks = cfg.getBoolean("researches.enable-fireworks");
logDuplicateBlockEntries = cfg.getBoolean("options.log-duplicate-block-entries");
@ -258,18 +258,6 @@ public class SlimefunRegistry {
return geoResources;
}
/**
* This method returns a list of recipes for the {@link AutomatedCraftingChamber}
*
* @deprecated This just a really bad way to do this. Someone needs to rewrite this.
*
* @return A list of recipes for the {@link AutomatedCraftingChamber}
*/
@Deprecated
public Map<String, ItemStack> getAutomatedCraftingChamberRecipes() {
return automatedCraftingChamberRecipes;
}
public boolean logDuplicateBlockEntries() {
return logDuplicateBlockEntries;
}

View File

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

View File

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

View File

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

View File

@ -13,7 +13,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
import net.md_5.bungee.api.ChatColor;
@ -39,9 +38,7 @@ final class RechargeableHelper {
BigDecimal decimal = BigDecimal.valueOf(charge).setScale(2, RoundingMode.HALF_UP);
float value = decimal.floatValue();
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
meta.getPersistentDataContainer().set(CHARGE_KEY, PersistentDataType.FLOAT, value);
}
meta.getPersistentDataContainer().set(CHARGE_KEY, PersistentDataType.FLOAT, value);
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
for (int i = 0; i < lore.size(); i++) {
@ -59,13 +56,11 @@ final class RechargeableHelper {
}
static float getCharge(@Nonnull ItemMeta meta) {
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
Float value = meta.getPersistentDataContainer().get(CHARGE_KEY, PersistentDataType.FLOAT);
Float value = meta.getPersistentDataContainer().get(CHARGE_KEY, PersistentDataType.FLOAT);
// If persistent data is available, we just return this value
if (value != null) {
return value;
}
// If persistent data is available, we just return this value
if (value != null) {
return value;
}
// If no persistent data exists, we will just fall back to the lore

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,7 @@ class SlimefunTabCompleter implements TabCompleter {
List<String> list = new ArrayList<>(items.size());
for (SlimefunItem item : items) {
list.add(item.getID());
list.add(item.getId());
}
return list;

View File

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

View File

@ -40,7 +40,7 @@ class GiveCommand extends SubCommand {
if (sfItem != null) {
giveItem(sender, p, sfItem, args);
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-valid-item", true, msg -> msg.replace(PLACEHOLDER_ITEM, args[2]));
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.invalid-item", true, msg -> msg.replace(PLACEHOLDER_ITEM, args[2]));
}
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-online", true, msg -> msg.replace(PLACEHOLDER_PLAYER, args[1]));
@ -70,7 +70,7 @@ class GiveCommand extends SubCommand {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.give-item", true, msg -> msg.replace(PLACEHOLDER_PLAYER, args[1]).replace(PLACEHOLDER_ITEM, sfItem.getItemName()).replace(PLACEHOLDER_AMOUNT, String.valueOf(amount)));
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-valid-amount", true, msg -> msg.replace(PLACEHOLDER_AMOUNT, args[3]));
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.invalid-amount", true, msg -> msg.replace(PLACEHOLDER_AMOUNT, args[3]));
}
}
}

View File

@ -49,8 +49,9 @@ class ResearchCommand extends SubCommand {
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-online", true, msg -> msg.replace(PLACEHOLDER_PLAYER, args[1]));
}
} else
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.no-permission", true);
}
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.usage", true, msg -> msg.replace("%usage%", "/sf research <Player> <all/reset/Research>"));
}
@ -65,7 +66,7 @@ class ResearchCommand extends SubCommand {
SlimefunPlugin.getLocalization().sendMessage(player, "messages.give-research", true, variables);
});
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-valid-research", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, input));
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.invalid-research", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, input));
}
}

View File

@ -29,8 +29,9 @@ class StatsCommand extends SubCommand {
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.not-online", true, msg -> msg.replace("%player%", args[1]));
}
} else
} else {
SlimefunPlugin.getLocalization().sendMessage(sender, "messages.no-permission", true);
}
} else if (sender instanceof Player) {
PlayerProfile.get((Player) sender, profile -> profile.sendStats(sender));
} else {

View File

@ -78,15 +78,20 @@ public final class SlimefunGuide {
}
public static void openCategory(PlayerProfile profile, Category category, SlimefunGuideLayout layout, int selectedPage) {
if (category == null)
if (category == null) {
return;
}
SlimefunPlugin.getRegistry().getGuideLayout(layout).openCategory(profile, category, selectedPage);
}
public static void openSearch(PlayerProfile profile, String input, boolean survival, boolean addToHistory) {
SlimefunGuideImplementation layout = SlimefunPlugin.getRegistry().getGuideLayout(SlimefunGuideLayout.CHEST);
if (!survival)
if (!survival) {
layout = SlimefunPlugin.getRegistry().getGuideLayout(SlimefunGuideLayout.CHEAT_SHEET);
}
layout.openSearch(profile, input, addToHistory);
}

View File

@ -37,7 +37,7 @@ final class ContributorsMenu {
menu.setEmptySlotsClickable(false);
menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F));
ChestMenuUtils.drawBackground(menu, 0, 2, 3, 4, 5, 6, 7, 8, 45, 47, 48, 49, 50, 51, 52);
ChestMenuUtils.drawBackground(menu, 0, 2, 3, 4, 5, 6, 7, 8, 45, 47, 48, 49, 50, 51, 53);
menu.addItem(1, new CustomItem(ChestMenuUtils.getBackButton(p, "", "&7" + SlimefunPlugin.getLocalization().getMessage(p, "guide.back.settings"))));
menu.addMenuClickHandler(1, (pl, slot, item, action) -> {
@ -46,7 +46,7 @@ final class ContributorsMenu {
});
List<Contributor> contributors = new ArrayList<>(SlimefunPlugin.getGitHubService().getContributors().values());
contributors.sort(Comparator.comparingInt(Contributor::index));
contributors.sort(Comparator.comparingInt(Contributor::getPosition));
for (int i = page * 36; i < contributors.size() && i < (page + 1) * 36; i++) {
Contributor contributor = contributors.get(i);

View File

@ -12,7 +12,6 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
@ -41,11 +40,8 @@ public final class SlimefunGuideSettings {
static {
options.add(new GuideLayoutOption());
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
options.add(new FireworksOption());
options.add(new PlayerLanguageOption());
}
options.add(new FireworksOption());
options.add(new PlayerLanguageOption());
}
private SlimefunGuideSettings() {}

View File

@ -1,6 +1,9 @@
package io.github.thebusybiscuit.slimefun4.core.handlers;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
@ -10,10 +13,10 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.GlassPane;
import io.github.thebusybiscuit.cscorelib2.collections.LoopIterator;
import io.github.thebusybiscuit.cscorelib2.materials.MaterialCollection;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RainbowBlock;
import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
@ -34,18 +37,26 @@ public class RainbowTickHandler extends BlockTicker {
private final boolean glassPanes;
private Material material;
public RainbowTickHandler(Material... materials) {
public RainbowTickHandler(@Nonnull List<Material> materials) {
Validate.noNullElements(materials, "A RainbowTicker cannot have a Material that is null!");
if (materials.length == 0) {
if (materials.isEmpty()) {
throw new IllegalArgumentException("A RainbowTicker must have at least one Material associated with it!");
}
glassPanes = containsGlassPanes(materials);
iterator = new LoopIterator<>(Arrays.asList(materials));
iterator = new LoopIterator<>(materials);
material = iterator.next();
}
public RainbowTickHandler(@Nonnull Material... materials) {
this(Arrays.asList(materials));
}
public RainbowTickHandler(@Nonnull ColoredMaterial material) {
this(material.asList());
}
/**
* This method checks whether a given {@link Material} array contains any {@link Material}
* that would result in a {@link GlassPane} {@link BlockData}.
@ -57,16 +68,18 @@ public class RainbowTickHandler extends BlockTicker {
*
* @return Whether the array contained any {@link GlassPane} materials
*/
private boolean containsGlassPanes(Material[] materials) {
private boolean containsGlassPanes(@Nonnull List<Material> materials) {
if (SlimefunPlugin.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) {
// BlockData is not available to us during Unit Tests :/
return false;
}
for (Material type : materials) {
// This BlockData is purely virtual and only created on startup, it should have
// no impact on performance, in fact it should save performance as it preloads
// the data but also saves heavy calls for other Materials
/**
* This BlockData is purely virtual and only created on startup, it should have
* no impact on performance, in fact it should save performance as it preloads
* the data but also saves heavy calls for other Materials
*/
if (type.createBlockData() instanceof GlassPane) {
return true;
}
@ -75,15 +88,13 @@ public class RainbowTickHandler extends BlockTicker {
return false;
}
public RainbowTickHandler(MaterialCollection collection) {
this(collection.getAsArray());
}
@Override
public void tick(Block b, SlimefunItem item, Config data) {
if (b.getType() == Material.AIR) {
// The block was broken, setting the Material now would result in a
// duplication glitch
/**
* The block was broken, setting the Material now would result in a
* duplication glitch
*/
return;
}

View File

@ -41,10 +41,8 @@ public class MultiBlock {
SUPPORTED_TAGS.add(Tag.LOGS);
SUPPORTED_TAGS.add(Tag.WOODEN_TRAPDOORS);
SUPPORTED_TAGS.add(Tag.WOODEN_SLABS);
SUPPORTED_TAGS.add(Tag.WOODEN_FENCES);
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
SUPPORTED_TAGS.add(Tag.WOODEN_FENCES);
}
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_16)) {
SUPPORTED_TAGS.add(Tag.FIRE);
}
@ -119,7 +117,7 @@ public class MultiBlock {
@Override
public int hashCode() {
return Objects.hash(item.getID(), blocks, trigger, isSymmetric);
return Objects.hash(item.getId(), blocks, trigger, isSymmetric);
}
private boolean compareBlocks(Material a, @Nullable Material b) {
@ -161,6 +159,6 @@ public class MultiBlock {
@Override
public String toString() {
return "MultiBlock (" + item.getID() + ") {" + Arrays.toString(blocks) + "}";
return "MultiBlock (" + item.getId() + ") {" + Arrays.toString(blocks) + "}";
}
}

View File

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

View File

@ -14,6 +14,7 @@ import org.bukkit.Server;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
/**
@ -29,8 +30,28 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListen
public class NetworkManager {
private final int maxNodes;
private final boolean enableVisualizer;
private final boolean deleteExcessItems;
private final List<Network> networks = new LinkedList<>();
/**
* This creates a new {@link NetworkManager} with the given capacity.
*
* @param maxStepSize
* The maximum amount of nodes a {@link Network} can have
* @param enableVisualizer
* Whether the {@link Network} visualizer is enabled
* @param deleteExcessItems
* Whether excess items from a {@link CargoNet} should be voided
*/
public NetworkManager(int maxStepSize, boolean enableVisualizer, boolean deleteExcessItems) {
Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!");
this.enableVisualizer = enableVisualizer;
this.deleteExcessItems = deleteExcessItems;
maxNodes = maxStepSize;
}
/**
* This creates a new {@link NetworkManager} with the given capacity.
*
@ -38,8 +59,7 @@ public class NetworkManager {
* The maximum amount of nodes a {@link Network} can have
*/
public NetworkManager(int maxStepSize) {
Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!");
maxNodes = maxStepSize;
this(maxStepSize, true, false);
}
/**
@ -52,6 +72,25 @@ public class NetworkManager {
return maxNodes;
}
/**
* This returns whether the {@link Network} visualizer is enabled.
*
* @return Whether the {@link Network} visualizer is enabled
*/
public boolean isVisualizerEnabled() {
return enableVisualizer;
}
/**
* This returns whether excess items from a {@link CargoNet} should be voided
* instead of being dropped to the ground.
*
* @return Whether to delete excess items
*/
public boolean isItemDeletionEnabled() {
return deleteExcessItems;
}
/**
* This returns a {@link List} of every {@link Network} on the {@link Server}.
*
@ -69,6 +108,7 @@ public class NetworkManager {
}
Validate.notNull(type, "Type must not be null");
for (Network network : networks) {
if (type.isInstance(network) && network.connectsTo(l)) {
return Optional.of(type.cast(network));
@ -129,8 +169,11 @@ public class NetworkManager {
public void updateAllNetworks(@Nonnull Location l) {
Validate.notNull(l, "The Location cannot be null");
for (Network network : getNetworksFromLocation(l, Network.class)) {
network.markDirty(l);
// No need to create a sublist and loop through it if there are no Networks
if (!networks.isEmpty()) {
for (Network network : getNetworksFromLocation(l, Network.class)) {
network.markDirty(l);
}
}
}

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import org.bukkit.block.Block;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
@ -32,11 +33,12 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
*
* @see CargoNet
* @see CargoUtils
* @see ChestTerminalNetwork
* @see AbstractItemNetwork
*
*/
class CargoNetworkTask implements Runnable {
private final NetworkManager manager;
private final CargoNet network;
private final Map<Location, Inventory> inventories = new HashMap<>();
@ -49,6 +51,7 @@ class CargoNetworkTask implements Runnable {
@ParametersAreNonnullByDefault
CargoNetworkTask(CargoNet network, Map<Location, Integer> inputs, Map<Integer, List<Location>> outputs, Set<Location> chestTerminalInputs, Set<Location> chestTerminalOutputs) {
this.network = network;
this.manager = SlimefunPlugin.getNetworkManager();
this.inputs = inputs;
this.outputs = outputs;
@ -65,8 +68,10 @@ class CargoNetworkTask implements Runnable {
network.handleItemRequests(inventories, chestTerminalInputs, chestTerminalOutputs);
}
// All operations happen here: Everything gets iterated from the Input Nodes.
// (Apart from ChestTerminal Buses)
/**
* All operations happen here: Everything gets iterated from the Input Nodes.
* (Apart from ChestTerminal Buses)
*/
SlimefunItem inputNode = SlimefunItems.CARGO_INPUT_NODE.getItem();
for (Map.Entry<Location, Integer> entry : inputs.entrySet()) {
long nodeTimestamp = System.nanoTime();
@ -89,8 +94,9 @@ class CargoNetworkTask implements Runnable {
SlimefunPlugin.getProfiler().closeEntry(network.getRegulator(), SlimefunItems.CARGO_MANAGER.getItem(), timestamp);
}
@ParametersAreNonnullByDefault
private void routeItems(Location inputNode, Block inputTarget, int frequency, Map<Integer, List<Location>> outputNodes) {
ItemStackAndInteger slot = CargoUtils.withdraw(inventories, inputNode.getBlock(), inputTarget);
ItemStackAndInteger slot = CargoUtils.withdraw(network, inventories, inputNode.getBlock(), inputTarget);
if (slot == null) {
return;
@ -105,30 +111,35 @@ class CargoNetworkTask implements Runnable {
}
if (stack != null) {
Inventory inv = inventories.get(inputTarget.getLocation());
insertItem(inputTarget, previousSlot, stack);
}
}
if (inv != null) {
// Check if the original slot hasn't been occupied in the meantime
if (inv.getItem(previousSlot) == null) {
inv.setItem(previousSlot, stack);
} else {
// Try to add the item into another available slot then
ItemStack rest = inv.addItem(stack).get(0);
@ParametersAreNonnullByDefault
private void insertItem(Block inputTarget, int previousSlot, ItemStack item) {
Inventory inv = inventories.get(inputTarget.getLocation());
if (rest != null) {
// If the item still couldn't be inserted, simply drop it on the ground
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest);
}
}
if (inv != null) {
// Check if the original slot hasn't been occupied in the meantime
if (inv.getItem(previousSlot) == null) {
inv.setItem(previousSlot, item);
} 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 (menu.getItemInSlot(previousSlot) == null) {
menu.replaceExistingItem(previousSlot, stack);
} else {
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack);
}
if (rest != null && !manager.isItemDeletionEnabled()) {
// If the item still couldn't be inserted, simply drop it on the ground
inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest);
}
}
} 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);
}
}
}
@ -149,7 +160,7 @@ class CargoNetworkTask implements Runnable {
Optional<Block> target = network.getAttachedBlock(output);
if (target.isPresent()) {
item = CargoUtils.insert(inventories, output.getBlock(), target.get(), item);
item = CargoUtils.insert(network, inventories, output.getBlock(), target.get(), item);
if (item == null) {
break;

View File

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

View File

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

View File

@ -15,7 +15,6 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.api.ErrorReport;
import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent;
@ -23,6 +22,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetProvider;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -70,6 +70,7 @@ public class EnergyNet extends Network {
return null;
} else {
switch (component.getEnergyComponentType()) {
case CONNECTOR:
case CAPACITOR:
return NetworkComponent.CONNECTOR;
case CONSUMER:
@ -209,14 +210,14 @@ public class EnergyNet extends Network {
SlimefunItem item = (SlimefunItem) provider;
try {
Config config = BlockStorage.getLocationInfo(loc);
int energy = provider.getGeneratedOutput(loc, config);
Config data = BlockStorage.getLocationInfo(loc);
int energy = provider.getGeneratedOutput(loc, data);
if (provider.isChargeable()) {
energy += provider.getCharge(loc);
energy += provider.getCharge(loc, data);
}
if (provider.willExplode(loc, config)) {
if (provider.willExplode(loc, data)) {
explodedBlocks.add(loc);
BlockStorage.clearBlockInfo(loc);
@ -227,9 +228,9 @@ public class EnergyNet extends Network {
} else {
supply += energy;
}
} catch (Exception | LinkageError t) {
} catch (Exception | LinkageError throwable) {
explodedBlocks.add(loc);
new ErrorReport<>(t, loc, item);
new ErrorReport<>(throwable, loc, item);
}
long time = SlimefunPlugin.getProfiler().closeEntry(loc, item, timestamp);
@ -237,7 +238,10 @@ public class EnergyNet extends Network {
}
// Remove all generators which have exploded
generators.keySet().removeAll(explodedBlocks);
if (!explodedBlocks.isEmpty()) {
generators.keySet().removeAll(explodedBlocks);
}
return supply;
}
@ -253,10 +257,10 @@ public class EnergyNet extends Network {
private void updateHologram(@Nonnull Block b, double supply, double demand) {
if (demand > supply) {
String netLoss = DoubleHandler.getFancyDouble(Math.abs(supply - demand));
String netLoss = NumberUtils.getCompactDouble(demand - supply);
SimpleHologram.update(b, "&4&l- &c" + netLoss + " &7J &e\u26A1");
} else {
String netGain = DoubleHandler.getFancyDouble(supply - demand);
String netGain = NumberUtils.getCompactDouble(supply - demand);
SimpleHologram.update(b, "&2&l+ &a" + netGain + " &7J &e\u26A1");
}
}
@ -272,6 +276,20 @@ public class EnergyNet extends Network {
return null;
}
/**
* This attempts to get an {@link EnergyNet} from a given {@link Location}.
* If no suitable {@link EnergyNet} could be found, {@code null} will be returned.
*
* @param l
* The target {@link Location}
*
* @return The {@link EnergyNet} at that {@link Location}, or {@code null}
*/
@Nullable
public static EnergyNet getNetworkFromLocation(@Nonnull Location l) {
return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, EnergyNet.class).orElse(null);
}
/**
* This attempts to get an {@link EnergyNet} from a given {@link Location}.
* If no suitable {@link EnergyNet} could be found, a new one will be created.

View File

@ -4,6 +4,7 @@ import org.bukkit.block.Block;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.EnergyConnector;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.reactors.Reactor;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
@ -13,6 +14,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
* can have.
*
* @author TheBusyBiscuit
* @author Linox
*
* @see EnergyNetComponent
* @see EnergyNet
@ -38,6 +40,12 @@ public enum EnergyNetComponentType {
*/
CONSUMER,
/**
* A Connector transmits energy through the network.
* Also see: {@link EnergyConnector}
*/
CONNECTOR,
/**
* A fallback value to use when a {@link Block} cannot be classified as any of the
* other options.

View File

@ -8,6 +8,7 @@ import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
@ -25,6 +26,9 @@ import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerPreResearchEvent;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
@ -113,6 +117,7 @@ public class Research implements Keyed {
*
* @param p
* The {@link Player} to translate this name for.
*
* @return The localized Name of this {@link Research}.
*/
@Nonnull
@ -189,11 +194,50 @@ public class Research implements Keyed {
return items;
}
/**
* Handle what to do when a {@link Player} clicks on an un-researched item in
* a {@link SlimefunGuideImplementation}.
*
* @param guide
* The {@link SlimefunGuideImplementation} used.
* @param player
* The {@link Player} who clicked on the item.
* @param profile
* The {@link PlayerProfile} of that {@link Player}.
* @param sfItem
* The {@link SlimefunItem} on which the {@link Player} clicked.
* @param category
* The {@link Category} where the {@link Player} was.
* @param page
* The page number of where the {@link Player} was in the {@link Category};
*
*/
@ParametersAreNonnullByDefault
public void unlockFromGuide(SlimefunGuideImplementation guide, Player player, PlayerProfile profile, SlimefunItem sfItem, Category category, int page) {
if (!SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().contains(player.getUniqueId())) {
if (profile.hasUnlocked(this)) {
guide.openCategory(profile, category, page);
} else {
PlayerPreResearchEvent event = new PlayerPreResearchEvent(player, this, sfItem);
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
if (this.canUnlock(player)) {
guide.unlockItem(player, sfItem, pl -> guide.openCategory(profile, category, page));
} else {
SlimefunPlugin.getLocalization().sendMessage(player, "messages.not-enough-xp", true);
}
}
}
}
}
/**
* Checks if the {@link Player} can unlock this {@link Research}.
*
* @param p
* The {@link Player} to check
*
* @return Whether that {@link Player} can unlock this {@link Research}
*/
public boolean canUnlock(@Nonnull Player p) {

View File

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

View File

@ -1,18 +1,26 @@
package io.github.thebusybiscuit.slimefun4.core.services;
import java.util.Optional;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
* The {@link BlockDataService} is similar to the {@link CustomItemDataService},
@ -23,11 +31,21 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
* @author TheBusyBiscuit
*
*/
public class BlockDataService implements PersistentDataService, Keyed {
public class BlockDataService implements Keyed {
private final NamespacedKey namespacedKey;
public BlockDataService(Plugin plugin, String key) {
/**
* This creates a new {@link BlockDataService} for the given {@link Plugin}.
* The {@link Plugin} and key will together form a {@link NamespacedKey} used to store
* data on a {@link TileState}.
*
* @param plugin
* The {@link Plugin} responsible for this service
* @param key
* The key under which to store data
*/
public BlockDataService(@Nonnull Plugin plugin, @Nonnull String key) {
namespacedKey = new NamespacedKey(plugin, key);
}
@ -44,12 +62,29 @@ public class BlockDataService implements PersistentDataService, Keyed {
* @param value
* The value to store
*/
public void setBlockData(Block b, String value) {
public void setBlockData(@Nonnull Block b, @Nonnull String value) {
Validate.notNull(b, "The block cannot be null!");
Validate.notNull(value, "The value cannot be null!");
/**
* Don't use PaperLib here, it seems to be quite buggy in block-placing scenarios
* and it would be too tedious to check for individual build versions to circumvent this.
*/
BlockState state = b.getState();
if (state instanceof TileState) {
setString((TileState) state, namespacedKey, value);
state.update();
try {
PersistentDataContainer container = ((TileState) state).getPersistentDataContainer();
container.set(namespacedKey, PersistentDataType.STRING, value);
state.update();
} catch (Exception x) {
Slimefun.getLogger().log(Level.SEVERE, "Please check if your Server Software is up to date!");
String serverSoftware = PaperLib.isSpigot() && !PaperLib.isPaper() ? "Spigot" : Bukkit.getName();
Slimefun.getLogger().log(Level.SEVERE, () -> serverSoftware + " | " + Bukkit.getVersion() + " | " + Bukkit.getBukkitVersion());
Slimefun.getLogger().log(Level.SEVERE, "An Exception was thrown while trying to set Persistent Data for a Block", x);
}
}
}
@ -58,18 +93,31 @@ public class BlockDataService implements PersistentDataService, Keyed {
*
* @param b
* The {@link Block} to retrieve data from
*
* @return The stored value
*/
public Optional<String> getBlockData(Block b) {
BlockState state = b.getState();
public Optional<String> getBlockData(@Nonnull Block b) {
Validate.notNull(b, "The block cannot be null!");
if (state instanceof TileState) {
return getString((TileState) state, namespacedKey);
BlockState state = PaperLib.getBlockState(b, false).getState();
PersistentDataContainer container = getPersistentDataContainer(state);
if (container != null) {
return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING));
} else {
return Optional.empty();
}
}
@Nullable
private PersistentDataContainer getPersistentDataContainer(@Nonnull BlockState state) {
if (state instanceof TileState) {
return ((TileState) state).getPersistentDataContainer();
} else {
return null;
}
}
/**
* This method checks whether the given {@link Material} is a Tile Entity.
* This is used to determine whether the {@link Block} produced by this {@link Material}
@ -80,12 +128,12 @@ public class BlockDataService implements PersistentDataService, Keyed {
*
* @param type
* The {@link Material} to check for
*
* @return Whether the given {@link Material} is considered a Tile Entity
*/
public boolean isTileEntity(Material type) {
if (type == null || !SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
// We can only store data on Tile Entities in 1.14+
// So we will just return false here in that case.
public boolean isTileEntity(@Nullable Material type) {
if (type == null || type.isAir()) {
// Cannot store data on air
return false;
}
@ -108,10 +156,12 @@ public class BlockDataService implements PersistentDataService, Keyed {
case BARREL:
case SPAWNER:
case BEACON:
// All of the above Materials are Tile Entities
return true;
default:
// Otherwise we assume they're not Tile Entities
return false;
}
}
}
}

View File

@ -2,11 +2,17 @@ package io.github.thebusybiscuit.slimefun4.core.services;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.Validate;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@ -22,7 +28,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see SlimefunItemStack
*
*/
public class CustomItemDataService implements PersistentDataService, Keyed {
public class CustomItemDataService implements Keyed {
private final NamespacedKey namespacedKey;
@ -35,7 +41,7 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
* @param key
* The key under which to store data
*/
public CustomItemDataService(Plugin plugin, String key) {
public CustomItemDataService(@Nonnull Plugin plugin, @Nonnull String key) {
// Null-Validation is performed in the NamespacedKey constructor
namespacedKey = new NamespacedKey(plugin, key);
}
@ -45,14 +51,39 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
return namespacedKey;
}
public void setItemData(ItemStack item, String id) {
/**
* This method stores the given id on the provided {@link ItemStack} via
* persistent data.
*
* @param item
* The {@link ItemStack} to store data on
* @param id
* The id to store on the {@link ItemStack}
*/
public void setItemData(@Nonnull ItemStack item, @Nonnull String id) {
Validate.notNull(item, "The Item cannot be null!");
Validate.notNull(id, "Cannot store null on an Item!");
ItemMeta im = item.getItemMeta();
setItemData(im, id);
item.setItemMeta(im);
}
public void setItemData(ItemMeta im, String id) {
setString(im, namespacedKey, id);
/**
* This method stores the given id on the provided {@link ItemMeta} via
* persistent data.
*
* @param meta
* The {@link ItemMeta} to store data on
* @param id
* The id to store on the {@link ItemMeta}
*/
public void setItemData(@Nonnull ItemMeta meta, @Nonnull String id) {
Validate.notNull(meta, "The ItemMeta cannot be null!");
Validate.notNull(id, "Cannot store null on an ItemMeta!");
PersistentDataContainer container = meta.getPersistentDataContainer();
container.set(namespacedKey, PersistentDataType.STRING, id);
}
/**
@ -65,7 +96,8 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
*
* @return An {@link Optional} describing the result
*/
public Optional<String> getItemData(ItemStack item) {
@Nonnull
public Optional<String> getItemData(@Nullable ItemStack item) {
if (item == null || item.getType() == Material.AIR || !item.hasItemMeta()) {
return Optional.empty();
}
@ -82,8 +114,12 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
*
* @return An {@link Optional} describing the result
*/
public Optional<String> getItemData(ItemMeta meta) {
return getString(meta, namespacedKey);
@Nonnull
public Optional<String> getItemData(@Nonnull ItemMeta meta) {
Validate.notNull(meta, "Cannot read data from null!");
PersistentDataContainer container = meta.getPersistentDataContainer();
return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING));
}
}

View File

@ -10,8 +10,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@ -25,11 +23,30 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/
public class CustomTextureService {
/**
* The {@link Config} object in which the Server Owner can configure the item models.
*/
private final Config config;
/**
* This nullable {@link StringBuffer} represents the "version" of the used item-models file.
* This version is served with our resource pack.
*/
private String version = null;
/**
* This boolean represents whether the file was modified anyway.
* This is equivalent to at least one value being set to a number which
* is not zero!
*/
private boolean modified = false;
/**
* This creates a new {@link CustomTextureService} for the provided {@link Config}
*
* @param config
* The {@link Config} to read custom model data from
*/
public CustomTextureService(@Nonnull Config config) {
this.config = config;
config.getConfiguration().options().header("This file is used to assign items from Slimefun or any of its addons\n" + "the 'CustomModelData' NBT tag. This can be used in conjunction with a custom resource pack\n" + "to give items custom textures.\n0 means there is no data assigned to that item.\n\n" + "There is no official Slimefun resource pack at the moment.");
@ -51,6 +68,8 @@ public class CustomTextureService {
config.setDefaultValue("SLIMEFUN_GUIDE", 0);
config.setDefaultValue("_UI_BACKGROUND", 0);
config.setDefaultValue("_UI_NO_PERMISSION", 0);
config.setDefaultValue("_UI_NOT_RESEARCHED", 0);
config.setDefaultValue("_UI_INPUT_SLOT", 0);
config.setDefaultValue("_UI_OUTPUT_SLOT", 0);
config.setDefaultValue("_UI_BACK", 0);
@ -64,9 +83,9 @@ public class CustomTextureService {
for (SlimefunItem item : items) {
if (item != null) {
config.setDefaultValue(item.getID(), 0);
config.setDefaultValue(item.getId(), 0);
if (config.getInt(item.getID()) != 0) {
if (config.getInt(item.getId()) != 0) {
modified = true;
}
}
@ -84,27 +103,63 @@ public class CustomTextureService {
return version;
}
/**
* This returns true if any custom model data was configured.
* If every item id has no configured custom model data, it will return false.
*
* @return Whether any custom model data was configured
*/
public boolean isActive() {
return modified;
}
/**
* This returns the configured custom model data for a given id.
*
* @param id
* The id to get the data for
*
* @return The configured custom model data
*/
public int getModelData(@Nonnull String id) {
Validate.notNull(id, "Cannot get the ModelData for 'null'");
return config.getInt(id);
}
/**
* This method sets the custom model data for this {@link ItemStack}
* to the value configured for the provided item id.
*
* @param item
* The {@link ItemStack} to set the custom model data for
* @param id
* The id for which to get the configured model data
*/
public void setTexture(@Nonnull ItemStack item, @Nonnull String id) {
Validate.notNull(item, "The Item cannot be null!");
Validate.notNull(id, "Cannot store null on an Item!");
ItemMeta im = item.getItemMeta();
setTexture(im, id);
item.setItemMeta(im);
}
/**
* This method sets the custom model data for this {@link ItemMeta}
* to the value configured for the provided item id.
*
* @param im
* The {@link ItemMeta} to set the custom model data for
* @param id
* The id for which to get the configured model data
*/
public void setTexture(@Nonnull ItemMeta im, @Nonnull String id) {
int data = getModelData(id);
Validate.notNull(im, "The ItemMeta cannot be null!");
Validate.notNull(id, "Cannot store null on an ItemMeta!");
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
im.setCustomModelData(data == 0 ? null : data);
}
int data = getModelData(id);
im.setCustomModelData(data == 0 ? null : data);
}
}

View File

@ -8,7 +8,6 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
@ -21,6 +20,8 @@ import org.bukkit.Server;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
@ -37,7 +38,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
* @see Language
*
*/
public class LocalizationService extends SlimefunLocalization implements PersistentDataService {
public class LocalizationService extends SlimefunLocalization {
private static final String LANGUAGE_PATH = "language";
@ -151,10 +152,12 @@ public class LocalizationService extends SlimefunLocalization implements Persist
@Override
public Language getLanguage(@Nonnull Player p) {
Validate.notNull("Player cannot be null!");
Optional<String> language = getString(p, languageKey);
if (language.isPresent()) {
Language lang = languages.get(language.get());
PersistentDataContainer container = p.getPersistentDataContainer();
String language = container.get(languageKey, PersistentDataType.STRING);
if (language != null) {
Language lang = languages.get(language);
if (lang != null) {
return lang;
@ -164,7 +167,7 @@ public class LocalizationService extends SlimefunLocalization implements Persist
return getDefaultLanguage();
}
private void setLanguage(String language, boolean reset) {
private void setLanguage(@Nonnull String language, boolean reset) {
// Clearing out the old Language (if necessary)
if (reset) {
getConfig().clear();

View File

@ -34,9 +34,26 @@ import kong.unirest.UnirestException;
*/
public class MetricsService {
/**
* The URL pointing towards the GitHub API.
*/
private static final String API_URL = "https://api.github.com/";
/**
* The Name of our repository
*/
private static final String REPO_NAME = "MetricsModule";
/**
* The URL pointing towards the /releases/ endpoint of our
* Metrics repository
*/
private static final String RELEASES_URL = API_URL + "repos/Slimefun/" + REPO_NAME + "/releases/latest";
/**
* The URL pointing towards the download location for a
* GitHub release of our Metrics repository
*/
private static final String DOWNLOAD_URL = "https://github.com/Slimefun/" + REPO_NAME + "/releases/download";
private final SlimefunPlugin plugin;
@ -48,14 +65,22 @@ public class MetricsService {
private boolean hasDownloadedUpdate = false;
static {
// @formatter:off (We want this to stay this nicely aligned :D )
Unirest.config()
.concurrency(2, 1)
.setDefaultHeader("User-Agent", "MetricsModule Auto-Updater")
.setDefaultHeader("Accept", "application/vnd.github.v3+json")
.enableCookieManagement(false)
.cookieSpec("ignoreCookies");
.concurrency(2, 1)
.setDefaultHeader("User-Agent", "MetricsModule Auto-Updater")
.setDefaultHeader("Accept", "application/vnd.github.v3+json")
.enableCookieManagement(false)
.cookieSpec("ignoreCookies");
// @formatter:on
}
/**
* This constructs a new instance of our {@link MetricsService}.
*
* @param plugin
* Our {@link SlimefunPlugin} instance
*/
public MetricsService(@Nonnull SlimefunPlugin plugin) {
this.plugin = plugin;
this.parentFolder = new File(plugin.getDataFolder(), "cache" + File.separatorChar + "modules");
@ -115,7 +140,7 @@ public class MetricsService {
}
/**
* This will close the child classloader and mark all the resources held under this no longer
* This will close the child {@link ClassLoader} and mark all the resources held under this no longer
* in use, they will be cleaned up the next GC run.
*/
public void cleanUp() {
@ -221,7 +246,7 @@ public class MetricsService {
return true;
}
} catch (UnirestException e) {
plugin.getLogger().log(Level.WARNING, "Failed to fetch the latest jar file from the builds page. Perhaps GitHub is down?");
plugin.getLogger().log(Level.WARNING, "Failed to fetch the latest jar file from the builds page. Perhaps GitHub is down? Response: {0}", e.getMessage());
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "Failed to replace the old metric file with the new one. Please do this manually! Error: {0}", e.getMessage());
}

View File

@ -85,7 +85,7 @@ public class PerWorldSettingsService {
return false;
}
return !items.contains(item.getID());
return !items.contains(item.getId());
}
/**
@ -105,9 +105,9 @@ public class PerWorldSettingsService {
Set<String> items = disabledItems.computeIfAbsent(world.getUID(), id -> loadWorldFromConfig(world));
if (enabled) {
items.remove(item.getID());
items.remove(item.getId());
} else {
items.add(item.getID());
items.add(item.getId());
}
}
@ -178,7 +178,7 @@ public class PerWorldSettingsService {
for (SlimefunItem item : SlimefunPlugin.getRegistry().getEnabledSlimefunItems()) {
if (item != null) {
String addon = item.getAddon().getName().toLowerCase(Locale.ROOT);
config.setValue(addon + '.' + item.getID(), !items.contains(item.getID()));
config.setValue(addon + '.' + item.getId(), !items.contains(item.getId()));
}
}
@ -222,7 +222,7 @@ public class PerWorldSettingsService {
if (item != null) {
String addon = item.getAddon().getName().toLowerCase(Locale.ROOT);
config.setDefaultValue(addon + ".enabled", true);
config.setDefaultValue(addon + '.' + item.getID(), true);
config.setDefaultValue(addon + '.' + item.getId(), true);
// Whether the entire addon has been disabled
boolean isAddonDisabled = config.getBoolean(addon + ".enabled");
@ -232,8 +232,8 @@ public class PerWorldSettingsService {
blacklist.add(worldName);
}
if (!isAddonDisabled || !config.getBoolean(addon + '.' + item.getID())) {
items.add(item.getID());
if (!isAddonDisabled || !config.getBoolean(addon + '.' + item.getId())) {
items.add(item.getId());
}
}
}

View File

@ -41,12 +41,12 @@ public class PermissionsService {
public void register(@Nonnull Iterable<SlimefunItem> items, boolean save) {
for (SlimefunItem item : items) {
if (item != null) {
String path = item.getID() + ".permission";
String path = item.getId() + ".permission";
config.setDefaultValue(path, "none");
config.setDefaultValue(item.getID() + ".lore", new String[] { "&rYou do not have the permission", "&rto access this item." });
config.setDefaultValue(item.getId() + ".lore", new String[] { "&rYou do not have the permission", "&rto access this item." });
permissions.put(item.getID(), config.getString(path));
permissions.put(item.getId(), config.getString(path));
}
}
@ -72,7 +72,7 @@ public class PermissionsService {
return true;
}
String permission = permissions.get(item.getID());
String permission = permissions.get(item.getId());
return permission == null || permission.equals("none") || p.hasPermission(permission);
}
@ -89,7 +89,7 @@ public class PermissionsService {
@Nonnull
public Optional<String> getPermission(@Nonnull SlimefunItem item) {
Validate.notNull(item, "Cannot get permissions for null");
String permission = permissions.get(item.getID());
String permission = permissions.get(item.getId());
if (permission == null || permission.equals("none")) {
return Optional.empty();
@ -108,7 +108,7 @@ public class PermissionsService {
*/
public void setPermission(@Nonnull SlimefunItem item, @Nullable String permission) {
Validate.notNull(item, "You cannot set the permission for null");
permissions.put(item.getID(), permission != null ? permission : "none");
permissions.put(item.getId(), permission != null ? permission : "none");
}
/**
@ -124,7 +124,7 @@ public class PermissionsService {
@Nonnull
public List<String> getLore(@Nonnull SlimefunItem item) {
List<String> lore = config.getStringList(item.getID() + ".lore");
List<String> lore = config.getStringList(item.getId() + ".lore");
return lore == null ? Arrays.asList("LORE NOT FOUND") : lore;
}

View File

@ -1,38 +0,0 @@
package io.github.thebusybiscuit.slimefun4.core.services;
import java.util.Optional;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
/**
* This interface is used to defer calls to Persistent Data and make sure they are only called
* if the {@link MinecraftVersion} supports it.
*
* @author TheBusyBiscuit
*
*/
interface PersistentDataService {
default void setString(Object obj, NamespacedKey key, String value) {
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14) && obj instanceof PersistentDataHolder) {
PersistentDataContainer container = ((PersistentDataHolder) obj).getPersistentDataContainer();
container.set(key, PersistentDataType.STRING, value);
}
}
default Optional<String> getString(Object obj, NamespacedKey key) {
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14) && obj instanceof PersistentDataHolder) {
PersistentDataContainer container = ((PersistentDataHolder) obj).getPersistentDataContainer();
return Optional.ofNullable(container.get(key, PersistentDataType.STRING));
}
return Optional.empty();
}
}

View File

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

View File

@ -16,11 +16,28 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
class ContributionsConnector extends GitHubConnector {
// GitHub Bots that do not count as Contributors
// (includes "invalid-email-address" because it is an invalid contributor)
private static final List<String> blacklist = Arrays.asList("invalid-email-address", "renovate-bot", "TheBusyBot", "ImgBotApp", "imgbot", "imgbot[bot]", "github-actions[bot]", "gitlocalize-app", "gitlocalize-app[bot]", "mt-gitlocalize");
/*
* @formatter:off
* GitHub Bots that do not count as Contributors
* (includes "invalid-email-address" because it is an invalid contributor)
*/
private static final List<String> blacklist = Arrays.asList(
"invalid-email-address",
"renovate-bot",
"TheBusyBot",
"ImgBotApp",
"imgbot",
"imgbot[bot]",
"github-actions[bot]",
"gitlocalize-app",
"gitlocalize-app[bot]",
"mt-gitlocalize"
);
// Matches a GitHub name with a Minecraft name.
/*
* @formatter:on
* Matches a GitHub name with a Minecraft name.
*/
private static final Map<String, String> aliases = new HashMap<>();
// Should probably be switched to UUIDs at some point...
@ -82,8 +99,16 @@ class ContributionsConnector extends GitHubConnector {
}
@Override
public String getURLSuffix() {
return "/contributors?per_page=100&page=" + page;
public String getEndpoint() {
return "/contributors";
}
@Override
public Map<String, Object> getParameters() {
Map<String, Object> parameters = new HashMap<>();
parameters.put("per_page", 100);
parameters.put("page", page);
return parameters;
}
private void computeContributors(@Nonnull JSONArray array) {

View File

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

View File

@ -1,6 +1,8 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
@ -35,8 +37,13 @@ class GitHubActivityConnector extends GitHubConnector {
}
@Override
public String getURLSuffix() {
public String getEndpoint() {
return "";
}
@Override
public Map<String, Object> getParameters() {
return new HashMap<>();
}
}

View File

@ -6,12 +6,13 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
@ -31,22 +32,49 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
abstract class GitHubConnector {
private static final String API_URL = "https://api.github.com/";
private static final String USER_AGENT = "Slimefun4 (https://github.com/Slimefun)";
protected File file;
protected String repository;
protected final GitHubService github;
private final String url;
private File file;
@ParametersAreNonnullByDefault
public GitHubConnector(GitHubService github, String repository) {
/**
* This creates a new {@link GitHubConnector} for the given repository.
*
* @param github
* Our instance of {@link GitHubService}
* @param repository
* The repository we want to connect to
*/
GitHubConnector(@Nonnull GitHubService github, @Nonnull String repository) {
this.github = github;
this.repository = repository;
this.url = API_URL + "repos/" + repository + getEndpoint();
}
/**
* This returns the name of our cache {@link File}.
*
* @return The cache {@link File} name
*/
@Nonnull
public abstract String getFileName();
/**
* This is our {@link URL} endpoint.
* It is the suffix of the {@link URL} we want to connect to.
*
* @return Our endpoint
*/
@Nonnull
public abstract String getURLSuffix();
public abstract String getEndpoint();
/**
* This {@link Map} contains the query parameters for our {@link URL}.
*
* @return A {@link Map} with our query parameters
*/
@Nonnull
public abstract Map<String, Object> getParameters();
/**
* This method is called when the connection finished successfully.
@ -63,7 +91,12 @@ abstract class GitHubConnector {
// Don't do anything by default
}
public void pullFile() {
/**
* This method will connect to GitHub and store the received data inside a local
* cache {@link File}.
* Make sure to call this method asynchronously!
*/
void download() {
file = new File("plugins/Slimefun/cache/github/" + getFileName() + ".json");
if (github.isLoggingEnabled()) {
@ -71,16 +104,19 @@ abstract class GitHubConnector {
}
try {
HttpResponse<JsonNode> resp = Unirest.get(API_URL + "repos/" + repository + getURLSuffix())
.header("User-Agent", "Slimefun4 (https://github.com/Slimefun)")
// @formatter:off
HttpResponse<JsonNode> response = Unirest.get(url)
.queryString(getParameters())
.header("User-Agent", USER_AGENT)
.asJson();
// @formatter:on
if (resp.isSuccess()) {
onSuccess(resp.getBody());
writeCacheFile(resp.getBody());
if (response.isSuccess()) {
onSuccess(response.getBody());
writeCacheFile(response.getBody());
} else {
if (github.isLoggingEnabled()) {
Slimefun.getLogger().log(Level.WARNING, "Failed to fetch {0}: {1} - {2}", new Object[] { repository + getURLSuffix(), resp.getStatus(), resp.getBody() });
Slimefun.getLogger().log(Level.WARNING, "Failed to fetch {0}: {1} - {2}", new Object[] { url, response.getStatus(), response.getBody() });
}
// It has the cached file, let's just read that then
@ -94,7 +130,7 @@ abstract class GitHubConnector {
}
} catch (UnirestException e) {
if (github.isLoggingEnabled()) {
Slimefun.getLogger().log(Level.WARNING, "Could not connect to GitHub in time.");
Slimefun.getLogger().log(Level.WARNING, "Could not connect to GitHub in time.", e);
}
// It has the cached file, let's just read that then

View File

@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull;
@ -50,8 +52,15 @@ class GitHubIssuesConnector extends GitHubConnector {
}
@Override
public String getURLSuffix() {
return "/issues?per_page=100";
public String getEndpoint() {
return "/issues";
}
@Override
public Map<String, Object> getParameters() {
Map<String, Object> parameters = new HashMap<>();
parameters.put("per_page", 100);
return parameters;
}
}

View File

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

View File

@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ -38,13 +39,23 @@ class GitHubTask implements Runnable {
@Override
public void run() {
gitHubService.getConnectors().forEach(GitHubConnector::pullFile);
connectAndCache();
grabTextures();
}
private void connectAndCache() {
gitHubService.getConnectors().forEach(GitHubConnector::download);
}
/**
* This method will pull the skin textures for every {@link Contributor} and store
* the {@link UUID} and received skin inside a local cache {@link File}.
*/
private void grabTextures() {
// Store all queried usernames to prevent 429 responses for pinging the
// same URL twice in one run.
/**
* Store all queried usernames to prevent 429 responses for pinging
* the same URL twice in one run.
*/
Map<String, String> skins = new HashMap<>();
int requests = 0;
@ -68,8 +79,11 @@ class GitHubTask implements Runnable {
}
}
// We only wanna save this if all Connectors finished already
// This will run multiple times but thats okay, this way we get as much data as possible stored
/**
* We only wanna save this if all Connectors finished already.
* This will run multiple times but thats okay, this way we get as much
* data as possible stored.
*/
gitHubService.saveCache();
}
@ -88,7 +102,7 @@ class GitHubTask implements Runnable {
} catch (IOException x) {
// Too many requests
Slimefun.getLogger().log(Level.WARNING, "Attempted to connect to mojang.com, got this response: {0}: {1}", new Object[] { x.getClass().getSimpleName(), x.getMessage() });
Slimefun.getLogger().log(Level.WARNING, "This usually means mojang.com is down or started to rate-limit this connection, this is not an error message!");
Slimefun.getLogger().log(Level.WARNING, "This usually means mojang.com is temporarily down or started to rate-limit this connection, this is not an error message!");
// Retry after 5 minutes if it was rate-limiting
if (x.getMessage().contains("429")) {

View File

@ -31,14 +31,14 @@ import me.mrCookieSlime.Slimefun.Lists.RecipeType;
* This is an abstract parent class of {@link LocalizationService}.
* There is not really much more I can say besides that...
*
* @author TheBusyBiscui
* @author TheBusyBiscuit
*
* @see LocalizationService
*
*/
public abstract class SlimefunLocalization extends Localization implements Keyed {
public SlimefunLocalization(@Nonnull SlimefunPlugin plugin) {
protected SlimefunLocalization(@Nonnull SlimefunPlugin plugin) {
super(plugin);
}
@ -71,6 +71,15 @@ public abstract class SlimefunLocalization extends Localization implements Keyed
*/
public abstract Language getDefaultLanguage();
/**
* This returns whether a {@link Language} with the given id exists within
* the project resources.
*
* @param id
* The {@link Language} id
*
* @return Whether the project contains a {@link Language} with that id
*/
protected abstract boolean hasLanguage(@Nonnull String id);
/**
@ -82,10 +91,23 @@ public abstract class SlimefunLocalization extends Localization implements Keyed
@Nonnull
public abstract Collection<Language> getLanguages();
/**
* This method adds a new {@link Language} with the given id and texture.
*
* @param id
* The {@link Language} id
* @param texture
* The texture of how this {@link Language} should be displayed
*/
protected abstract void addLanguage(@Nonnull String id, @Nonnull String texture);
/**
* This will load every {@link SupportedLanguage} into memory.
* To be precise: It performs {@link #addLanguage(String, String)} for every
* value of {@link SupportedLanguage}.
*/
protected void loadEmbeddedLanguages() {
for (SupportedLanguage lang : SupportedLanguage.valuesCache) {
for (SupportedLanguage lang : SupportedLanguage.values()) {
if (lang.isReadyForRelease() || SlimefunPlugin.getUpdater().getBranch() != SlimefunBranch.STABLE) {
addLanguage(lang.getLanguageId(), lang.getTexture());
}

View File

@ -58,8 +58,6 @@ enum SupportedLanguage {
MACEDONIAN("mk", false, "a0e0b0b5d87a855466980a101a757bcdb5f77d9f7287889f3efa998ee0472fc0"),
TAGALOG("tl", true, "9306c0c1ce6a9c61bb42a572c49e6d0ed20e0e6b3d122cc64c339cbf78e9e937");
public static final SupportedLanguage[] valuesCache = values();
private final String id;
private final boolean releaseReady;
private final String textureHash;
@ -71,11 +69,22 @@ enum SupportedLanguage {
this.textureHash = textureHash;
}
/**
* This returns the id of this {@link Language}.
*
* @return
*/
@Nonnull
public String getLanguageId() {
return id;
}
/**
* This returns whether this {@link SupportedLanguage} is "release-ready".
* A release-ready {@link Language} will be available in RC builds of Slimefun.
*
* @return Whether this {@link Language} is "release-ready"
*/
public boolean isReadyForRelease() {
return releaseReady;
}

View File

@ -99,6 +99,9 @@ public class Translators {
addTranslator("Dr4gonD", "DragonD", SupportedLanguage.DUTCH, true);
addTranslator("svr333", SupportedLanguage.DUTCH, false);
addTranslator("PabloMarcendo", SupportedLanguage.DUTCH, true);
addTranslator("milvantiou", SupportedLanguage.DUTCH, true);
addTranslator("Sven313D", SupportedLanguage.DUTCH, true);
addTranslator("TypischTeun", SupportedLanguage.DUTCH, true);
// Translators - Danish
addTranslator("Mini-kun", SupportedLanguage.DANISH, true);
@ -117,6 +120,7 @@ public class Translators {
// Translators - Chinese (Taiwan)
addTranslator("BrineYT", "HeroBrineKing", SupportedLanguage.CHINESE_TAIWAN, true);
addTranslator("mio9", SupportedLanguage.CHINESE_TAIWAN, true);
addTranslator("ALiangLiang", SupportedLanguage.CHINESE_TAIWAN, true);
// Translators - Arabic
addTranslator("mohkamfer", "AgentBabbie", SupportedLanguage.ARABIC, false);
@ -161,12 +165,17 @@ public class Translators {
// Translators - Tagalog
addTranslator("sccooottttie", SupportedLanguage.TAGALOG, true);
// Translators - Portuguese
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_PORTUGAL, true);
// Translators - Portuguese (Brazil)
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("yurinogueira", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Sakanas", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("krazybeat", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("FaolanMalcadh", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true);
addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_BRAZIL, true);
}
@ParametersAreNonnullByDefault
@ -179,7 +188,7 @@ public class Translators {
Contributor contributor = github.addContributor(minecraftName, "https://github.com/" + username, "translator," + lang.getLanguageId(), 0);
if (lock) {
contributor.lock();
contributor.setImmutable();
}
}

View File

@ -11,11 +11,19 @@ import org.bukkit.event.Listener;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.minebuilders.clearlag.Clearlag;
import me.minebuilders.clearlag.events.EntityRemoveEvent;
class ClearLagHook implements Listener {
/**
* This handles all integrations with {@link Clearlag}.
* We don't want it to clear our altar items.
*
* @author TheBusyBiscuit
*
*/
class ClearLagIntegration implements Listener {
ClearLagHook(@Nonnull SlimefunPlugin plugin) {
ClearLagIntegration(@Nonnull SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}

View File

@ -1,31 +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;
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

@ -0,0 +1,60 @@
package io.github.thebusybiscuit.slimefun4.core.services.plugins;
import javax.annotation.Nonnull;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.events.skills.salvage.McMMOPlayerSalvageCheckEvent;
import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
* This handles all integrations with {@link mcMMO}.
*
* @author TheBusyBiscuit
*
*/
class McMMOIntegration implements Listener {
McMMOIntegration(@Nonnull SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockPlacerPlace(BlockPlacerPlaceEvent e) {
// This registers blocks placed by the BlockPlacer as "player-placed"
mcMMO.getPlaceStore().setTrue(e.getBlock());
}
@EventHandler(ignoreCancelled = true)
public void onItemSalvage(McMMOPlayerSalvageCheckEvent e) {
// Prevent Slimefun items from being salvaged
if (!isSalvageable(e.getSalvageItem())) {
e.setCancelled(true);
SlimefunPlugin.getLocalization().sendMessage(e.getPlayer(), "anvil.mcmmo-salvaging");
}
}
/**
* This method checks if an {@link ItemStack} can be salvaged or not.
* We basically don't want players to salvage any {@link SlimefunItem} unless
* it is a {@link VanillaItem}.
*
* @param item
* The {@link ItemStack} to check
*
* @return Whether this item can be safely salvaged
*/
private boolean isSalvageable(@Nonnull ItemStack item) {
SlimefunItem sfItem = SlimefunItem.getByItem(item);
return sfItem == null || sfItem instanceof VanillaItem;
}
}

View File

@ -13,14 +13,21 @@ import org.bukkit.entity.Player;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
class PlaceholderAPIHook extends PlaceholderExpansion {
/**
* This is our integration for {@link PlaceholderAPI}.
*
* @author TheBusyBiscuit
*
*/
class PlaceholderAPIIntegration extends PlaceholderExpansion {
private final String version;
private final String author;
public PlaceholderAPIHook(@Nonnull SlimefunPlugin plugin) {
public PlaceholderAPIIntegration(@Nonnull SlimefunPlugin plugin) {
this.version = plugin.getDescription().getVersion();
this.author = plugin.getDescription().getAuthors().toString();
}

View File

@ -5,14 +5,16 @@ import java.util.function.Function;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.api.Slimefun;
@ -31,25 +33,40 @@ public class ThirdPartyPluginService {
private final SlimefunPlugin plugin;
private boolean initialized = false;
private boolean isExoticGardenInstalled = false;
private boolean isChestTerminalInstalled = false;
private boolean isEmeraldEnchantsInstalled = false;
private boolean isCoreProtectInstalled = false;
private boolean isPlaceholderAPIInstalled = false;
private boolean isMcMMOInstalled = false;
// Overridden if ExoticGarden is loaded
/**
* This gets overridden if ExoticGarden is loaded
*/
private Function<Block, Optional<ItemStack>> exoticGardenIntegration = b -> Optional.empty();
/**
* This initializes the {@link ThirdPartyPluginService}
*
* @param plugin
* Our instance of {@link SlimefunPlugin}
*/
public ThirdPartyPluginService(@Nonnull SlimefunPlugin plugin) {
this.plugin = plugin;
}
/**
* This method initializes all third party integrations.
*/
public void start() {
if (initialized) {
throw new UnsupportedOperationException("Third Party Integrations have already been initialized!");
}
initialized = true;
if (isPluginInstalled("PlaceholderAPI")) {
try {
PlaceholderAPIHook hook = new PlaceholderAPIHook(plugin);
PlaceholderAPIIntegration hook = new PlaceholderAPIIntegration(plugin);
hook.register();
isPlaceholderAPIInstalled = true;
} catch (Exception | LinkageError x) {
String version = plugin.getServer().getPluginManager().getPlugin("PlaceholderAPI").getDescription().getVersion();
@ -58,18 +75,11 @@ public class ThirdPartyPluginService {
}
}
if (isPluginInstalled("EmeraldEnchants")) {
isEmeraldEnchantsInstalled = true;
Plugin emeraldEnchants = plugin.getServer().getPluginManager().getPlugin("EmeraldEnchants");
FlexCategory category = new EmeraldEnchantsCategory(new NamespacedKey(emeraldEnchants, "enchantment_guide"));
category.register();
}
// WorldEdit Hook to clear Slimefun Data upon //set 0 //cut or any other equivalent
if (isPluginInstalled("WorldEdit")) {
try {
Class.forName("com.sk89q.worldedit.extent.Extent");
new WorldEditHook();
new WorldEditIntegration();
} catch (Exception | LinkageError x) {
String version = plugin.getServer().getPluginManager().getPlugin("WorldEdit").getDescription().getVersion();
@ -78,6 +88,18 @@ public class ThirdPartyPluginService {
}
}
// mcMMO Integration
if (isPluginInstalled("mcMMO")) {
try {
new McMMOIntegration(plugin);
isMcMMOInstalled = true;
} catch (Exception | LinkageError x) {
String version = plugin.getServer().getPluginManager().getPlugin("mcMMO").getDescription().getVersion();
Slimefun.getLogger().log(Level.WARNING, "Maybe consider updating mcMMO or Slimefun?");
Slimefun.getLogger().log(Level.WARNING, x, () -> "Failed to hook into mcMMO v" + version);
}
}
/*
* These Items are not marked as soft-dependencies and
* therefore need to be loaded after the Server has finished
@ -85,7 +107,7 @@ public class ThirdPartyPluginService {
*/
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
if (isPluginInstalled("ClearLag")) {
new ClearLagHook(plugin);
new ClearLagIntegration(plugin);
}
isChestTerminalInstalled = isPluginInstalled("ChestTerminal");
@ -101,6 +123,7 @@ public class ThirdPartyPluginService {
}
}
@ParametersAreNonnullByDefault
public void loadExoticGarden(Plugin plugin, Function<Block, Optional<ItemStack>> method) {
if (plugin.getName().equals("ExoticGarden")) {
isExoticGardenInstalled = true;
@ -116,20 +139,22 @@ public class ThirdPartyPluginService {
return isChestTerminalInstalled;
}
public boolean isEmeraldEnchantsInstalled() {
return isEmeraldEnchantsInstalled;
}
public boolean isCoreProtectInstalled() {
return isCoreProtectInstalled;
}
public boolean isPlaceholderAPIInstalled() {
return isPlaceholderAPIInstalled;
}
public Optional<ItemStack> harvestExoticGardenPlant(Block block) {
return exoticGardenIntegration.apply(block);
}
/**
* This checks if one of our third party integrations faked an {@link Event}.
* Faked {@link Event Events} should be ignored in our logic.
*
* @param event
* The {@link Event} to test
*
* @return Whether this is a fake event
*/
public boolean isEventFaked(@Nonnull Event event) {
// This can be changed to "FakeEvent" in a later version
return isMcMMOInstalled && event instanceof FakeBlockBreakEvent;
}
}

View File

@ -14,9 +14,16 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
class WorldEditHook {
/**
* This handles all integrations with {@link WorldEdit}.
* If an are is cleared, we also wanna clear all Slimefun-related block data.
*
* @author TheBusyBiscuit
*
*/
class WorldEditIntegration {
WorldEditHook() {
WorldEditIntegration() {
WorldEdit.getInstance().getEventBus().register(this);
}

View File

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

View File

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

View File

@ -20,6 +20,7 @@ import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitScheduler;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
@ -42,12 +43,27 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
*/
public class SlimefunProfiler {
// A minecraft server tick is 50ms and Slimefun ticks are stretched across
// two ticks (sync and async blocks), so we use 100ms as a reference here
/**
* A minecraft server tick is 50ms and Slimefun ticks are stretched
* across two ticks (sync and async blocks), so we use 100ms as a reference here
*/
private static final int MAX_TICK_DURATION = 100;
private final ExecutorService executor = Executors.newFixedThreadPool(5);
private final AtomicBoolean running = new AtomicBoolean(false);
/**
* Our internal instance of {@link SlimefunThreadFactory}, it provides the naming
* convention for our {@link Thread} pool and also the count of this pool.
*/
private final SlimefunThreadFactory threadFactory = new SlimefunThreadFactory(2);
/**
* This is our {@link Thread} pool to evaluate timings data.
* We cannot use the {@link BukkitScheduler} here because we need to evaluate
* this data in split seconds.
* So we cannot simply wait until the next server tick for this.
*/
private final ExecutorService executor = Executors.newFixedThreadPool(threadFactory.getThreadCount(), threadFactory);
private final AtomicBoolean isProfiling = new AtomicBoolean(false);
private final AtomicInteger queued = new AtomicInteger(0);
private long totalElapsedTime;
@ -55,11 +71,20 @@ public class SlimefunProfiler {
private final Map<ProfiledBlock, Long> timings = new ConcurrentHashMap<>();
private final Queue<CommandSender> requests = new ConcurrentLinkedQueue<>();
/**
* This method terminates the {@link SlimefunProfiler}.
* We need to call this method when the {@link Server} shuts down to prevent any
* of our {@link Thread Threads} from being kept alive.
*/
public void kill() {
executor.shutdown();
}
/**
* This method starts the profiling, data from previous runs will be cleared.
*/
public void start() {
running.set(true);
isProfiling.set(true);
queued.set(0);
timings.clear();
}
@ -70,7 +95,7 @@ public class SlimefunProfiler {
* @return A timestamp, best fed back into {@link #closeEntry(Location, SlimefunItem, long)}
*/
public long newEntry() {
if (!running.get()) {
if (!isProfiling.get()) {
return 0;
}
@ -89,7 +114,7 @@ public class SlimefunProfiler {
* The amount of entries that should be scheduled. Can be negative
*/
public void scheduleEntries(int amount) {
if (running.get()) {
if (isProfiling.get()) {
queued.getAndAdd(amount);
}
}
@ -132,15 +157,13 @@ public class SlimefunProfiler {
* This stops the profiling.
*/
public void stop() {
running.set(false);
isProfiling.set(false);
if (SlimefunPlugin.instance() == null || !SlimefunPlugin.instance().isEnabled()) {
// Slimefun has been disabled
return;
}
// Since we got more than one Thread in our pool,
// blocking this one is (hopefully) completely fine
executor.execute(this::finishReport);
}
@ -149,8 +172,12 @@ public class SlimefunProfiler {
int iterations = 4000;
// Wait for all timing results to come in
while (!running.get() && queued.get() > 0) {
while (!isProfiling.get() && queued.get() > 0) {
try {
/**
* Since we got more than one Thread in our pool,
* blocking this one is (hopefully) completely fine
*/
Thread.sleep(1);
iterations--;
@ -162,6 +189,7 @@ public class SlimefunProfiler {
iterator.next().sendMessage("Your timings report has timed out, we were still waiting for " + queued.get() + " samples to be collected :/");
iterator.remove();
}
return;
}
} catch (InterruptedException e) {
@ -170,7 +198,7 @@ public class SlimefunProfiler {
}
}
if (running.get() && queued.get() > 0) {
if (isProfiling.get() && queued.get() > 0) {
// Looks like the next profiling has already started, abort!
return;
}
@ -228,9 +256,10 @@ public class SlimefunProfiler {
Map<String, Long> map = new HashMap<>();
for (Map.Entry<ProfiledBlock, Long> entry : timings.entrySet()) {
String world = entry.getKey().getPosition().getWorld().getName();
int x = entry.getKey().getPosition().getChunkX();
int z = entry.getKey().getPosition().getChunkZ();
ProfiledBlock block = entry.getKey();
String world = block.getWorld().getName();
int x = block.getChunkX();
int z = block.getChunkZ();
map.merge(world + " (" + x + ',' + z + ')', entry.getValue(), Long::sum);
}
@ -243,9 +272,9 @@ public class SlimefunProfiler {
int blocks = 0;
for (ProfiledBlock block : timings.keySet()) {
String world = block.getPosition().getWorld().getName();
int x = block.getPosition().getChunkX();
int z = block.getPosition().getChunkZ();
String world = block.getWorld().getName();
int x = block.getChunkX();
int z = block.getChunkZ();
if (chunk.equals(world + " (" + x + ',' + z + ')')) {
blocks++;
@ -284,6 +313,7 @@ public class SlimefunProfiler {
protected float getPercentageOfTick() {
float millis = totalElapsedTime / 1000000.0F;
float fraction = (millis * 100.0F) / MAX_TICK_DURATION;
return Math.round((fraction * 100.0F) / 100.0F);
}
@ -305,6 +335,7 @@ public class SlimefunProfiler {
return PerformanceRating.UNKNOWN;
}
@Nonnull
public String getTime() {
return NumberUtils.getAsMillis(totalElapsedTime);
}
@ -324,6 +355,7 @@ public class SlimefunProfiler {
*/
public boolean hasTimings(@Nonnull Block b) {
Validate.notNull("Cannot get timings for a null Block");
return timings.containsKey(new ProfiledBlock(b));
}
@ -344,7 +376,7 @@ public class SlimefunProfiler {
public String getTime(@Nonnull SlimefunItem item) {
Validate.notNull("Cannot get timings for a null SlimefunItem");
long time = getByItem().getOrDefault(item.getID(), 0L);
long time = getByItem().getOrDefault(item.getId(), 0L);
return NumberUtils.getAsMillis(time);
}

View File

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

View File

@ -18,7 +18,7 @@ import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineTier;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactivity;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.StormStaff;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.LoreBuilder;
@ -394,7 +394,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack PLASTIC_SHEET = new SlimefunItemStack("PLASTIC_SHEET", Material.PAPER, "&fPlastic Sheet");
public static final SlimefunItemStack MAGNET = new SlimefunItemStack("MAGNET", HeadTexture.MAGNET, "&cMagnet");
public static final SlimefunItemStack NECROTIC_SKULL = new SlimefunItemStack("NECROTIC_SKULL", Material.WITHER_SKELETON_SKULL, "&cNecrotic Skull");
public static final SlimefunItemStack NECROTIC_SKULL = new SlimefunItemStack("NECROTIC_SKULL", HeadTexture.NECROTIC_SKULL, "&cNecrotic Skull");
public static final SlimefunItemStack ESSENCE_OF_AFTERLIFE = new SlimefunItemStack("ESSENCE_OF_AFTERLIFE", Material.GUNPOWDER, "&4Essence of Afterlife");
public static final SlimefunItemStack STRANGE_NETHER_GOO = new SlimefunItemStack("STRANGE_NETHER_GOO", Material.PURPLE_DYE, "&5Strange Nether Goo", "", "&fA strange bio matter that", "&fcan be acquired from", "&fbartering with Piglins");
public static final SlimefunItemStack ELECTRO_MAGNET = new SlimefunItemStack("ELECTRO_MAGNET", HeadTexture.MAGNET, "&cElectromagnet");
@ -403,7 +403,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack ELECTRIC_MOTOR = new SlimefunItemStack("ELECTRIC_MOTOR", HeadTexture.MOTOR, "&cElectric Motor");
public static final SlimefunItemStack CARGO_MOTOR = new SlimefunItemStack("CARGO_MOTOR", HeadTexture.MOTOR, "&3Cargo Motor");
public static final SlimefunItemStack SCROLL_OF_DIMENSIONAL_TELEPOSITION = new SlimefunItemStack("SCROLL_OF_DIMENSIONAL_TELEPOSITION", Material.PAPER, "&6Scroll of Dimensional Teleposition", "", "&cThis Scroll is capable of creating", "&ca temporary black Hole which pulls", "&cnearby Entities into itself and sends", "&cthem into another Dimension where", "&ceverything is turned around", "", "&fIn other words: Makes Entities turn by 180 Degrees");
public static final SlimefunItemStack TOME_OF_KNOWLEDGE_SHARING = new SlimefunItemStack("TOME_OF_KNOWLEDGE_SHARING", Material.BOOK, "&6Tome of Knowledge Sharing", "&7Owner: &bNone", "", "&eRight Click&7 to bind this Tome to yourself", "", "", "&eRight Click&7 to obtain all Researches by", "&7the previously assigned Owner");
public static final SlimefunItemStack TOME_OF_KNOWLEDGE_SHARING = new SlimefunItemStack("TOME_OF_KNOWLEDGE_SHARING", Material.ENCHANTED_BOOK, "&6Tome of Knowledge Sharing", "&7Owner: &bNone", "", "&eRight Click&7 to bind this Tome to yourself", "", "", "&eRight Click&7 to obtain all Researches by", "&7the previously assigned Owner");
public static final SlimefunItemStack HARDENED_GLASS = new SlimefunItemStack("HARDENED_GLASS", Material.LIGHT_GRAY_STAINED_GLASS, "&7Hardened Glass", "", "&fWithstands Explosions");
public static final SlimefunItemStack WITHER_PROOF_OBSIDIAN = new SlimefunItemStack("WITHER_PROOF_OBSIDIAN", Material.OBSIDIAN, "&5Wither-Proof Obsidian", "", "&fWithstands Explosions", "&fWithstands Wither Bosses");
public static final SlimefunItemStack WITHER_PROOF_GLASS = new SlimefunItemStack("WITHER_PROOF_GLASS", Material.PURPLE_STAINED_GLASS, "&5Wither-Proof Glass", "", "&fWithstands Explosions", "&fWithstands Wither Bosses");
@ -567,14 +567,14 @@ public final class SlimefunItems {
public static final SlimefunItemStack ENHANCED_CRAFTING_TABLE = new SlimefunItemStack("ENHANCED_CRAFTING_TABLE", Material.CRAFTING_TABLE, "&eEnhanced Crafting Table", "", "&aA regular Crafting Table cannot", "&ahold this massive Amount of Power...");
public static final SlimefunItemStack GRIND_STONE = new SlimefunItemStack("GRIND_STONE", Material.DISPENSER, "&bGrind Stone", "", "&aGrinds items down into other items");
public static final SlimefunItemStack ARMOR_FORGE = new SlimefunItemStack("ARMOR_FORGE", Material.ANVIL, "&6Armor Forge", "", "&aGives you the ability to create powerful armor");
public static final SlimefunItemStack MAKESHIFT_SMELTERY;
public static final SlimefunItemStack MAKESHIFT_SMELTERY = new SlimefunItemStack("MAKESHIFT_SMELTERY", Material.BLAST_FURNACE, "&eMakeshift Smeltery", "", "&fImprovised version of the Smeltery", "&fthat only allows you to", "&fsmelt dusts into ingots");
public static final SlimefunItemStack SMELTERY = new SlimefunItemStack("SMELTERY", Material.FURNACE, "&6Smeltery", "", "&fA high-temperature furnace", "&fthat allows you to smelt dusts", "&finto ingots and create alloys.");
public static final SlimefunItemStack ORE_CRUSHER = new SlimefunItemStack("ORE_CRUSHER", Material.DISPENSER, "&bOre Crusher", "", "&aCrushes ores to double them");
public static final SlimefunItemStack COMPRESSOR = new SlimefunItemStack("COMPRESSOR", Material.PISTON, "&bCompressor", "", "&aCompresses Items");
public static final SlimefunItemStack PRESSURE_CHAMBER = new SlimefunItemStack("PRESSURE_CHAMBER", Material.GLASS, "&bPressure Chamber", "", "&aCompresses Items even further");
public static final SlimefunItemStack MAGIC_WORKBENCH = new SlimefunItemStack("MAGIC_WORKBENCH", Material.CRAFTING_TABLE, "&6Magic Workbench", "", "&dInfuses Items with magical Energy");
public static final SlimefunItemStack ORE_WASHER = new SlimefunItemStack("ORE_WASHER", Material.CAULDRON, "&6Ore Washer", "", "&aWashes Sifted Ore to filter Ores", "&aand gives you small Stone Chunks");
public static final SlimefunItemStack TABLE_SAW;
public static final SlimefunItemStack TABLE_SAW = new SlimefunItemStack("TABLE_SAW", Material.STONECUTTER, "&6Table Saw", "", "&aAllows you to get 8 planks from 1 Log", "&a(Works with all log types)");
public static final SlimefunItemStack JUICER = new SlimefunItemStack("JUICER", Material.GLASS_BOTTLE, "&aJuicer", "", "&aAllows you to create delicious Juice");
public static final SlimefunItemStack AUTOMATED_PANNING_MACHINE = new SlimefunItemStack("AUTOMATED_PANNING_MACHINE", Material.BOWL, "&eAutomated Panning Machine", "", "&fA MultiBlock Version of the Gold Pan", "&fand Nether Gold Pan combined in one machine.");
@ -662,22 +662,22 @@ public final class SlimefunItems {
public static final SlimefunItemStack ELECTRIC_ORE_GRINDER = new SlimefunItemStack("ELECTRIC_ORE_GRINDER", Material.FURNACE, "&cElectric Ore Grinder", "", "&fWorks as an Ore Crusher and Grind Stone", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(12));
public static final SlimefunItemStack ELECTRIC_ORE_GRINDER_2 = new SlimefunItemStack("ELECTRIC_ORE_GRINDER_2", Material.FURNACE, "&cElectric Ore Grinder &7(&eII&7)", "", "&fWorks as an Ore Crusher and Grind Stone", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(4), LoreBuilder.powerPerSecond(30));
public static final SlimefunItemStack ELECTRIC_INGOT_PULVERIZER = new SlimefunItemStack("ELECTRIC_INGOT_PULVERIZER", Material.FURNACE, "&cElectric Ingot Pulverizer", "", "&fPulverizes Ingots into Dust", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(14));
public static final SlimefunItemStack AUTO_DRIER;
public static final SlimefunItemStack AUTO_DRIER = new SlimefunItemStack("AUTO_DRIER", Material.SMOKER, "&6Auto Drier", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(10));
public static final SlimefunItemStack AUTO_ENCHANTER = new SlimefunItemStack("AUTO_ENCHANTER", Material.ENCHANTING_TABLE, "&5Auto Enchanter", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(18));
public static final SlimefunItemStack AUTO_DISENCHANTER = new SlimefunItemStack("AUTO_DISENCHANTER", Material.ENCHANTING_TABLE, "&5Auto Disenchanter", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(18));
public static final SlimefunItemStack AUTO_ANVIL = new SlimefunItemStack("AUTO_ANVIL", Material.IRON_BLOCK, "&7Auto Anvil", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), "&8\u21E8 &7Repair Factor: 10%", LoreBuilder.powerPerSecond(24));
public static final SlimefunItemStack AUTO_ANVIL_2 = new SlimefunItemStack("AUTO_ANVIL_2", Material.IRON_BLOCK, "&7Auto Anvil Mk.II", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), "&8\u21E8 &7Repair Factor: 25%", LoreBuilder.powerPerSecond(32));
public static final SlimefunItemStack AUTO_BREWER;
public static final SlimefunItemStack AUTO_BREWER = new SlimefunItemStack("AUTO_BREWER", Material.SMOKER, "&6Auto Brewer", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(12));
public static final SlimefunItemStack BIO_REACTOR = new SlimefunItemStack("BIO_REACTOR", Material.LIME_TERRACOTTA, "&2Bio Reactor", "", LoreBuilder.machine(MachineTier.AVERAGE, MachineType.GENERATOR), LoreBuilder.powerBuffer(128), LoreBuilder.powerPerSecond(8));
public static final SlimefunItemStack MULTIMETER = new SlimefunItemStack("MULTIMETER", Material.CLOCK, "&eMultimeter", "", "&fMeasures the Amount of stored", "&fEnergy in a Block");
public static final SlimefunItemStack SMALL_CAPACITOR = new SlimefunItemStack("SMALL_CAPACITOR", HeadTexture.CAPACITOR_25, "&aSmall Energy Capacitor", "", LoreBuilder.machine(MachineTier.BASIC, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7128 J Capacity");
public static final SlimefunItemStack MEDIUM_CAPACITOR = new SlimefunItemStack("MEDIUM_CAPACITOR", HeadTexture.CAPACITOR_25, "&aMedium Energy Capacitor", "", LoreBuilder.machine(MachineTier.AVERAGE, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7512 J Capacity");
public static final SlimefunItemStack BIG_CAPACITOR = new SlimefunItemStack("BIG_CAPACITOR", HeadTexture.CAPACITOR_25, "&aBig Energy Capacitor", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &71024 J Capacity");
public static final SlimefunItemStack LARGE_CAPACITOR = new SlimefunItemStack("LARGE_CAPACITOR", HeadTexture.CAPACITOR_25, "&aLarge Energy Capacitor", "", LoreBuilder.machine(MachineTier.GOOD, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &78192 J Capacity");
public static final SlimefunItemStack CARBONADO_EDGED_CAPACITOR = new SlimefunItemStack("CARBONADO_EDGED_CAPACITOR", HeadTexture.CAPACITOR_25, "&aCarbonado Edged Energy Capacitor", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &765536 J Capacity");
public static final SlimefunItemStack ENERGIZED_CAPACITOR = new SlimefunItemStack("ENERGIZED_CAPACITOR", HeadTexture.CAPACITOR_25, "&aEnergized Energy Capacitor", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7524288 J Capacity");
public static final SlimefunItemStack SMALL_CAPACITOR = new SlimefunItemStack("SMALL_CAPACITOR", HeadTexture.CAPACITOR_25, "&aSmall Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.BASIC, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7128 J Capacity");
public static final SlimefunItemStack MEDIUM_CAPACITOR = new SlimefunItemStack("MEDIUM_CAPACITOR", HeadTexture.CAPACITOR_25, "&aMedium Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.AVERAGE, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7512 J Capacity");
public static final SlimefunItemStack BIG_CAPACITOR = new SlimefunItemStack("BIG_CAPACITOR", HeadTexture.CAPACITOR_25, "&aBig Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &71024 J Capacity");
public static final SlimefunItemStack LARGE_CAPACITOR = new SlimefunItemStack("LARGE_CAPACITOR", HeadTexture.CAPACITOR_25, "&aLarge Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.GOOD, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &78192 J Capacity");
public static final SlimefunItemStack CARBONADO_EDGED_CAPACITOR = new SlimefunItemStack("CARBONADO_EDGED_CAPACITOR", HeadTexture.CAPACITOR_25, "&aCarbonado Edged Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &765536 J Capacity");
public static final SlimefunItemStack ENERGIZED_CAPACITOR = new SlimefunItemStack("ENERGIZED_CAPACITOR", HeadTexture.CAPACITOR_25, "&aEnergized Energy Capacitor", LoreBuilder.range(6), "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.CAPACITOR), "&8\u21E8 &e\u26A1 &7524288 J Capacity");
/* Robots */
public static final SlimefunItemStack PROGRAMMABLE_ANDROID = new SlimefunItemStack("PROGRAMMABLE_ANDROID", HeadTexture.PROGRAMMABLE_ANDROID, "&cProgrammable Android &7(Normal)", "", "&8\u21E8 &7Function: None", "&8\u21E8 &7Fuel Efficiency: 1.0x");
@ -750,6 +750,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack BLISTERING_INGOT_3 = new SlimefunItemStack("BLISTERING_INGOT_3", Material.GOLD_INGOT, "&6Blistering Ingot", "", LoreBuilder.radioactive(Radioactivity.VERY_HIGH), LoreBuilder.HAZMAT_SUIT_REQUIRED);
public static final SlimefunItemStack ENERGY_REGULATOR = new SlimefunItemStack("ENERGY_REGULATOR", HeadTexture.ENERGY_REGULATOR, "&6Energy Regulator", "", "&fCore Component of an Energy Network");
public static final SlimefunItemStack ENERGY_CONNECTOR = new SlimefunItemStack("ENERGY_CONNECTOR", HeadTexture.ENERGY_CONNECTOR, "&eEnergy Connector", LoreBuilder.range(6), "", "&fPlace this between machines", "&fand generators to connect them", "&fto your regulator.");
public static final SlimefunItemStack DEBUG_FISH = new SlimefunItemStack("DEBUG_FISH", Material.SALMON, "&3How much is the Fish?", "", "&eRight Click &fany Block to view it's BlockData", "&eLeft Click &fto break a Block", "&eShift + Left Click &fany Block to erase it's BlockData", "&eShift + Right Click &fto place a Placeholder Block");
public static final SlimefunItemStack NETHER_ICE = new SlimefunItemStack("NETHER_ICE", HeadTexture.NETHER_ICE, "&eNether Ice", "", LoreBuilder.radioactive(Radioactivity.MODERATE), LoreBuilder.HAZMAT_SUIT_REQUIRED);
@ -840,17 +841,5 @@ public final class SlimefunItems {
static {
INFUSED_ELYTRA.addUnsafeEnchantment(Enchantment.MENDING, 1);
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
TABLE_SAW = new SlimefunItemStack("TABLE_SAW", Material.STONECUTTER, "&6Table Saw", "", "&aAllows you to get 8 planks from 1 Log", "&a(Works with all log types)");
MAKESHIFT_SMELTERY = new SlimefunItemStack("MAKESHIFT_SMELTERY", Material.BLAST_FURNACE, "&eMakeshift Smeltery", "", "&fImprovised version of the Smeltery", "&fthat only allows you to", "&fsmelt dusts into ingots");
AUTO_DRIER = new SlimefunItemStack("AUTO_DRIER", Material.SMOKER, "&6Auto Drier", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(10));
AUTO_BREWER = new SlimefunItemStack("AUTO_BREWER", Material.SMOKER, "&6Auto Brewer", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(12));
} else {
TABLE_SAW = null;
MAKESHIFT_SMELTERY = new SlimefunItemStack("MAKESHIFT_SMELTERY", Material.FURNACE, "&eMakeshift Smeltery", "", "&fImprovised version of the Smeltery", "&fthat only allows you to", "&fsmelt dusts into ingots");
AUTO_DRIER = new SlimefunItemStack("AUTO_DRIER", Material.FURNACE, "&6Auto Drier", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(10));
AUTO_BREWER = new SlimefunItemStack("AUTO_BREWER", Material.BREWING_STAND, "&6Auto Brewer", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(12));
}
}
}

View File

@ -12,12 +12,14 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
@ -62,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.BlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CargoNodeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DispenserListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraCrashListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EnhancedFurnaceListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EntityInteractionListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener;
@ -75,9 +78,11 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.FireworksList
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GadgetsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.IronGolemListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PiglinListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener;
@ -85,14 +90,19 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBoots
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBowListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemConsumeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemInteractListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VampireBladeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VanillaMachinesListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VillagerTradingListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.WitherListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.WorldListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.AnvilListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.BrewingStandListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CartographyTableListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CauldronListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CraftingTableListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.GrindstoneListener;
import io.github.thebusybiscuit.slimefun4.implementation.resources.GEOResourcesSetup;
import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup;
import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
@ -102,7 +112,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.tasks.SlimefunStartupTa
import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.CSCoreLib;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
@ -111,7 +120,6 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
/**
* This is the main class of Slimefun.
* This is where all the magic starts, take a look around.
* Feel like home.
*
* @author TheBusyBiscuit
*/
@ -156,29 +164,51 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private final BackpackListener backpackListener = new BackpackListener();
private final SlimefunBowListener bowListener = new SlimefunBowListener();
/**
* Our default constructor for {@link SlimefunPlugin}.
*/
public SlimefunPlugin() {
super();
}
/**
* This constructor is invoked in Unit Test environments only.
*
* @param loader
* Our {@link JavaPluginLoader}
* @param description
* A {@link PluginDescriptionFile}
* @param dataFolder
* The data folder
* @param file
* A {@link File} for this {@link Plugin}
*/
@ParametersAreNonnullByDefault
public SlimefunPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
// This is only invoked during a Unit Test
minecraftVersion = MinecraftVersion.UNIT_TEST;
}
/**
* This is called when the {@link Plugin} has been loaded and enabled on a {@link Server}.
*/
@Override
public void onEnable() {
instance = this;
if (minecraftVersion == MinecraftVersion.UNIT_TEST) {
local = new LocalizationService(this, "", null);
gpsNetwork = new GPSNetwork();
networkManager = new NetworkManager(200);
command.register();
registry.load(config);
onUnitTestStart();
} else if (getServer().getPluginManager().isPluginEnabled("CS-CoreLib")) {
getLogger().log(Level.INFO, "CS-CoreLib was detected!");
long timestamp = System.nanoTime();
PaperLib.suggestPaper(this);
if (PaperLib.isPaper()) {
getLogger().log(Level.INFO, "Paper was detected! Performance optimizations have been applied.");
}
// We wanna ensure that the Server uses a compatible version of Minecraft
if (isVersionUnsupported()) {
getServer().getPluginManager().disablePlugin(this);
@ -212,7 +242,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
networkSize = 1;
}
networkManager = new NetworkManager(networkSize);
networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"), config.getBoolean("networks.delete-excess-items"));
// Setting up bStats
new Thread(metricsService::start, "Slimefun Metrics").start();
@ -270,7 +300,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
autoSavingService.start(this, config.getInt("options.auto-save-delay-in-minutes"));
ticker.start(this);
getLogger().log(Level.INFO, "Loading Third-Party plugin integrations...");
thirdPartySupportService.start();
gitHubService.start(this);
// Hooray!
@ -293,6 +326,89 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
}
}
/**
* This is our start method for a Unit Test environment.
*/
private void onUnitTestStart() {
local = new LocalizationService(this, "", null);
gpsNetwork = new GPSNetwork();
networkManager = new NetworkManager(200);
command.register();
registry.load(config);
loadTags();
}
/**
* This method gets called when the {@link Plugin} gets disabled.
* Most often it is called when the {@link Server} is shutting down or reloading.
*/
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) {
return;
}
// Cancel all tasks from this plugin immediately
Bukkit.getScheduler().cancelTasks(this);
// Finishes all started movements/removals of block data
ticker.halt();
ticker.run();
// Kill our Profiler Threads
profiler.kill();
// Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) {
profile.save();
}
});
// Save all registered Worlds
for (Map.Entry<String, BlockStorage> entry : getRegistry().getWorlds().entrySet()) {
try {
entry.getValue().saveAndRemove();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion());
}
}
for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) {
menu.save();
}
// Create a new backup zip
backupService.run();
// Close and unload any resources from our Metrics Service
metricsService.cleanUp();
/**
* Prevent Memory Leaks for reloads...
* These static Maps should really be removed at some point...
*/
AContainer.processing = null;
AContainer.progress = null;
AGenerator.processing = null;
AGenerator.progress = null;
Reactor.processing = null;
Reactor.progress = null;
instance = null;
/**
* Close all inventories on the server to prevent item dupes
* (Incase some idiot uses /reload)
*/
for (Player p : Bukkit.getOnlinePlayers()) {
p.closeInventory();
}
}
@Nonnull
private String getStartupTime(long timestamp) {
long ms = (System.nanoTime() - timestamp) / 1000000;
@ -326,8 +442,8 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
getLogger().log(Level.SEVERE, "### Slimefun was not installed correctly!");
getLogger().log(Level.SEVERE, "### You are using the wrong version of Minecraft!");
getLogger().log(Level.SEVERE, "###");
getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", ReflectionUtils.getVersion());
getLogger().log(Level.SEVERE, "### but Slimefun v{0} requires you to be using", getDescription().getVersion());
getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", currentVersion);
getLogger().log(Level.SEVERE, "### but Slimefun {0} requires you to be using", getDescription().getVersion());
getLogger().log(Level.SEVERE, "### Minecraft {0}", String.join(" / ", getSupportedVersions()));
getLogger().log(Level.SEVERE, "#############################################");
return true;
@ -342,7 +458,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
List<String> list = new ArrayList<>();
for (MinecraftVersion version : MinecraftVersion.valuesCache) {
if (version != MinecraftVersion.UNKNOWN) {
if (!version.isVirtual()) {
list.add(version.getName());
}
}
@ -350,65 +466,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return list;
}
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) {
return;
}
// Cancel all tasks from this plugin immediately
Bukkit.getScheduler().cancelTasks(this);
// Finishes all started movements/removals of block data
ticker.halt();
ticker.run();
// Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) {
profile.save();
}
});
// Save all registered Worlds
for (Map.Entry<String, BlockStorage> entry : getRegistry().getWorlds().entrySet()) {
try {
entry.getValue().saveAndRemove();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion());
}
}
for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) {
menu.save();
}
// Create a new backup zip
backupService.run();
metricsService.cleanUp();
// Prevent Memory Leaks
// These static Maps should be removed at some point...
AContainer.processing = null;
AContainer.progress = null;
AGenerator.processing = null;
AGenerator.progress = null;
Reactor.processing = null;
Reactor.progress = null;
instance = null;
// Close all inventories on the server to prevent item dupes
// (Incase some idiot uses /reload)
for (Player p : Bukkit.getOnlinePlayers()) {
p.closeInventory();
}
}
private void createDirectories() {
String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" };
String[] pluginFolders = { "scripts", "error-reports", "cache/github", "world-settings" };
@ -430,9 +487,12 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
}
}
/**
* This method registers all of our {@link Listener Listeners}.
*/
private void registerListeners() {
new SlimefunBootsListener(this);
new SlimefunItemListener(this);
new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this);
new CargoNodeListener(this);
@ -442,22 +502,32 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new BlockListener(this);
new EnhancedFurnaceListener(this);
new ItemPickupListener(this);
new ItemDropListener(this);
new DeathpointListener(this);
new ExplosionsListener(this);
new DebugFishListener(this);
new VanillaMachinesListener(this);
new FireworksListener(this);
new WitherListener(this);
new IronGolemListener(this);
new EntityInteractionListener(this);
new MobDropListener(this);
new VillagerTradingListener(this);
new ElytraCrashListener(this);
new ElytraImpactListener(this);
new CraftingTableListener(this);
new AnvilListener(this);
new BrewingStandListener(this);
new CauldronListener(this);
new GrindstoneListener(this);
new CartographyTableListener(this);
new ButcherAndroidListener(this);
new NetworkListener(this, networkManager);
// Bees were added in 1.15
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) {
new BeeListener(this);
}
// Piglins were added in 1.16
if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) {
new PiglinListener(this);
}
@ -469,6 +539,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new AncientAltarListener(this, (AncientAltar) SlimefunItems.ANCIENT_ALTAR.getItem(), (AncientPedestal) SlimefunItems.ANCIENT_PEDESTAL.getItem());
grapplingHookListener.register(this, (GrapplingHook) SlimefunItems.GRAPPLING_HOOK.getItem());
bowListener.register(this);
backpackListener.register(this);
// Toggleable Listeners for performance reasons
if (config.getBoolean("items.talismans")) {
@ -479,10 +550,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new SoulboundListener(this);
}
if (config.getBoolean("items.backpacks")) {
backpackListener.register(this);
}
// Handle Slimefun Guide being given on Join
new SlimefunGuideListener(this, config.getBoolean("guide.receive-on-first-join"));
@ -496,7 +563,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private void loadTags() {
for (SlimefunTag tag : SlimefunTag.valuesCache) {
try {
tag.reload();
// Only reload "empty" (or unloaded) Tags
if (tag.isEmpty()) {
tag.reload();
}
} catch (TagMisconfigurationException e) {
getLogger().log(Level.SEVERE, e, () -> "Failed to load Tag: " + tag.name());
}
@ -701,8 +771,15 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return instance.isNewlyInstalled;
}
@Nonnull
public static String getCSCoreLibVersion() {
return CSCoreLib.getLib().getDescription().getVersion();
Plugin cscorelib = instance.getServer().getPluginManager().getPlugin("CS-CoreLib");
if (cscorelib == null) {
throw new IllegalStateException("CS-CoreLib is not installed.");
} else {
return cscorelib.getDescription().getVersion();
}
}
@Override

View File

@ -208,14 +208,14 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
}
private void addSlimefunItem(Category category, int page, Player p, PlayerProfile profile, SlimefunItem item, List<ChatComponent> items) {
NamespacedKey key = new NamespacedKey(SlimefunPlugin.instance(), item.getID().toLowerCase(Locale.ROOT));
NamespacedKey key = new NamespacedKey(SlimefunPlugin.instance(), item.getId().toLowerCase(Locale.ROOT));
if (!Slimefun.hasUnlocked(p, item, false) && item.getResearch() != null) {
Research research = item.getResearch();
ChatComponent component = new ChatComponent(ChatUtils.crop(ChatColor.RED, item.getItemName()) + "\n");
component.setHoverEvent(new HoverEvent(ChatColor.RESET + item.getItemName(), ChatColor.DARK_RED.toString() + ChatColor.BOLD + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", ChatColor.GREEN + "> Click to unlock", "", ChatColor.GRAY + "Cost: " + ChatColor.AQUA.toString() + research.getCost() + " Level(s)"));
component.setClickEvent(new ClickEvent(key, player -> research(player, profile, item, research, category, page)));
component.setClickEvent(new ClickEvent(key, player -> SlimefunPlugin.runSync(() -> research.unlockFromGuide(this, player, profile, item, category, page))));
items.add(component);
} else {
@ -234,22 +234,6 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
}
}
private void research(Player p, PlayerProfile profile, SlimefunItem item, Research research, Category category, int page) {
SlimefunPlugin.runSync(() -> {
if (!SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().contains(p.getUniqueId())) {
if (research.canUnlock(p)) {
if (profile.hasUnlocked(research)) {
openCategory(profile, category, page);
} else {
unlockItem(p, item, pl -> openCategory(profile, category, page));
}
} else {
SlimefunPlugin.getLocalization().sendMessage(p, "messages.not-enough-xp", true);
}
}
});
}
@Override
public void openSearch(PlayerProfile profile, String input, boolean addToHistory) {
// We need to write a book implementation for this at some point

View File

@ -1,7 +1,5 @@
package io.github.thebusybiscuit.slimefun4.implementation.guide;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
@ -10,6 +8,9 @@ import java.util.Locale;
import java.util.Optional;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
@ -26,7 +27,6 @@ import io.github.thebusybiscuit.cscorelib2.chat.ChatInput;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.recipes.MinecraftRecipe;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
@ -65,21 +65,15 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
public class ChestSlimefunGuide implements SlimefunGuideImplementation {
private static final int CATEGORY_SIZE = 36;
private static final Sound sound = Sound.ITEM_BOOK_PAGE_TURN;
private final ItemStack item;
private final int[] recipeSlots = { 3, 4, 5, 12, 13, 14, 21, 22, 23 };
private final Sound sound;
private final ItemStack item;
private final boolean showVanillaRecipes;
public ChestSlimefunGuide(boolean vanillaRecipes) {
showVanillaRecipes = vanillaRecipes;
public ChestSlimefunGuide(boolean showVanillaRecipes) {
this.showVanillaRecipes = showVanillaRecipes;
item = new SlimefunGuideItem(this, "&aSlimefun Guide &7(Chest GUI)");
if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
sound = Sound.ITEM_BOOK_PAGE_TURN;
} else {
sound = Sound.ENTITY_BAT_TAKEOFF;
}
}
@Override
@ -132,7 +126,6 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
}
ChestMenu menu = create(p);
List<Category> categories = getVisibleCategories(p, profile);
int index = 9;
@ -233,16 +226,22 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
menu.addItem(46, ChestMenuUtils.getPreviousButton(p, page, pages));
menu.addMenuClickHandler(46, (pl, slot, item, action) -> {
int next = page - 1;
if (next != page && next > 0)
if (next != page && next > 0) {
openCategory(profile, category, next);
}
return false;
});
menu.addItem(52, ChestMenuUtils.getNextButton(p, page, pages));
menu.addMenuClickHandler(52, (pl, slot, item, action) -> {
int next = page + 1;
if (next != page && next <= pages)
if (next != page && next <= pages) {
openCategory(profile, category, next);
}
return false;
});
@ -272,23 +271,12 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
if (isSurvivalMode() && !Slimefun.hasPermission(p, sfitem, false)) {
List<String> message = SlimefunPlugin.getPermissionsService().getLore(sfitem);
menu.addItem(index, new CustomItem(Material.BARRIER, sfitem.getItemName(), message.toArray(new String[0])));
menu.addItem(index, new CustomItem(ChestMenuUtils.getNoPermissionItem(), sfitem.getItemName(), message.toArray(new String[0])));
menu.addMenuClickHandler(index, ChestMenuUtils.getEmptyClickHandler());
} else if (isSurvivalMode() && research != null && !profile.hasUnlocked(research)) {
menu.addItem(index, new CustomItem(Material.BARRIER, ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " Level(s)"));
menu.addItem(index, new CustomItem(ChestMenuUtils.getNotResearchedItem(), ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " Level(s)"));
menu.addMenuClickHandler(index, (pl, slot, item, action) -> {
if (!SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().contains(pl.getUniqueId())) {
if (research.canUnlock(pl)) {
if (profile.hasUnlocked(research)) {
openCategory(profile, category, page);
} else {
unlockItem(pl, sfitem, player -> openCategory(profile, category, page));
}
} else {
SlimefunPlugin.getLocalization().sendMessage(pl, "messages.not-enough-xp", true);
}
}
research.unlockFromGuide(this, p, profile, sfitem, category, page);
return false;
});
} else {
@ -335,26 +323,14 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
int index = 9;
// Find items and add them
for (SlimefunItem slimefunItem : SlimefunPlugin.getRegistry().getEnabledSlimefunItems()) {
String itemName = ChatColor.stripColor(slimefunItem.getItemName()).toLowerCase(Locale.ROOT);
if (index == 44) {
break;
}
if (!itemName.isEmpty() && (itemName.equals(searchTerm) || itemName.contains(searchTerm))) {
if (isSearchFilterApplicable(slimefunItem, searchTerm)) {
ItemStack itemstack = new CustomItem(slimefunItem.getItem(), meta -> {
List<String> lore = null;
Category category = slimefunItem.getCategory();
if (category != null) {
ItemStack categoryItem = category.getItem(p);
if (categoryItem != null && categoryItem.hasItemMeta() && categoryItem.getItemMeta().hasDisplayName()) {
lore = Arrays.asList("", ChatColor.DARK_GRAY + "\u21E8 " + ChatColor.WHITE + categoryItem.getItemMeta().getDisplayName());
}
}
meta.setLore(lore);
meta.setLore(Arrays.asList("", ChatColor.DARK_GRAY + "\u21E8 " + ChatColor.WHITE + category.getDisplayName(p)));
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_POTION_EFFECTS);
});
@ -380,6 +356,12 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
menu.open(p);
}
@ParametersAreNonnullByDefault
private boolean isSearchFilterApplicable(SlimefunItem slimefunItem, String searchTerm) {
String itemName = ChatColor.stripColor(slimefunItem.getItemName()).toLowerCase(Locale.ROOT);
return !itemName.isEmpty() && (itemName.equals(searchTerm) || itemName.contains(searchTerm));
}
@Override
public void displayItem(PlayerProfile profile, ItemStack item, int index, boolean addToHistory) {
Player p = profile.getPlayer();

View File

@ -31,6 +31,9 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*/
public class RadioactiveItem extends SlimefunItem implements Radioactive, NotPlaceable {
/**
* This is the level of {@link Radioactivity} for this {@link SlimefunItem}
*/
private final Radioactivity radioactivity;
/**

View File

@ -2,6 +2,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.altar;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
@ -17,7 +21,9 @@ import org.bukkit.util.Vector;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.core.handlers.BlockDispenseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.AncientAltarListener;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.AncientAltarTask;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
@ -39,14 +45,15 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
* @see AncientAltarTask
*
*/
public class AncientPedestal extends SlimefunItem {
public class AncientPedestal extends SimpleSlimefunItem<BlockDispenseHandler> {
public static final String ITEM_PREFIX = ChatColors.color("&dALTAR &3Probe - &e");
@ParametersAreNonnullByDefault
public AncientPedestal(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) {
super(category, item, recipeType, recipe, recipeOutput);
SlimefunItem.registerBlockHandler(getID(), (p, b, tool, reason) -> {
SlimefunItem.registerBlockHandler(getId(), (p, b, tool, reason) -> {
Optional<Item> entity = getPlacedItem(b);
if (entity.isPresent()) {
@ -63,7 +70,13 @@ public class AncientPedestal extends SlimefunItem {
});
}
public Optional<Item> getPlacedItem(Block pedestal) {
@Override
public BlockDispenseHandler getItemHandler() {
return (e, d, block, machine) -> e.setCancelled(true);
}
@Nonnull
public Optional<Item> getPlacedItem(@Nonnull Block pedestal) {
Location l = pedestal.getLocation().add(0.5, 1.2, 0.5);
for (Entity n : l.getWorld().getNearbyEntities(l, 0.5, 0.5, 0.5, this::testItem)) {
@ -75,7 +88,7 @@ public class AncientPedestal extends SlimefunItem {
return Optional.empty();
}
private boolean testItem(Entity n) {
private boolean testItem(@Nullable Entity n) {
if (n instanceof Item && n.isValid()) {
Item item = (Item) n;
ItemMeta meta = item.getItemStack().getItemMeta();
@ -86,7 +99,8 @@ public class AncientPedestal extends SlimefunItem {
}
}
public ItemStack getOriginalItemStack(Item item) {
@Nonnull
public ItemStack getOriginalItemStack(@Nonnull Item item) {
ItemStack stack = item.getItemStack().clone();
String customName = item.getCustomName();
@ -108,7 +122,7 @@ public class AncientPedestal extends SlimefunItem {
return stack;
}
public void placeItem(Player p, Block b) {
public void placeItem(@Nonnull Player p, @Nonnull Block b) {
ItemStack hand = p.getInventory().getItemInMainHand();
ItemStack displayItem = new CustomItem(hand, ITEM_PREFIX + System.nanoTime());
displayItem.setAmount(1);

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