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

Merge branch 'master' into fixes/blocks

This commit is contained in:
TheBusyBiscuit 2021-01-28 13:41:27 +01:00 committed by GitHub
commit cadf66a13c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 2206 additions and 337 deletions

View File

@ -42,7 +42,7 @@ assignees: ''
## :compass: Environment (REQUIRED)
<!-- Any info without the exact version numbers will be closed! -->
<!-- Any issue without the exact version numbers will be closed! -->
<!-- "latest" IS NOT A VERSION NUMBER. -->
<!-- We recommend running "/sf versions" and showing us a screenshot of that. -->
<!-- Make sure that the screenshot covers the entire output of that command. -->
@ -51,4 +51,3 @@ assignees: ''
- Server Software:
- Minecraft Version:
- Slimefun Version:
- CS-CoreLib Version:

53
.github/workflows/pr-labels.yml vendored Normal file
View File

@ -0,0 +1,53 @@
name: Pull Request Labels
on:
pull_request:
types:
- opened
jobs:
pr-labeler:
name: Pull Request Labels
runs-on: ubuntu-latest
if: github.repository == 'Slimefun/Slimefun4' && github.actor != 'gitlocalize-app[bot]' && github.actor != 'renovate[bot]'
steps:
- uses: WalshyDev/pr-labels@v1.1
id: labeller
name: Apply labels based on branch
with:
token: "${{ secrets.ACCESS_TOKEN }}"
feature: '🎈 Feature'
fix: '✨ Fix'
chore: '🧹 Chores'
performance: '💡 Performance Optimization'
api: '🔧 API'
- uses: thollander/actions-comment-pull-request@1.0.1
name: Comment the applied label
if: ${{ steps.labeller.outputs.applied != 0 }}
with:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
message: |
Your Pull Request was automatically labelled as: ${{ steps.labeller.outputs.applied }}
Thank you for contributing to this project! ❤️
- uses: thollander/actions-comment-pull-request@1.0.1
name: Comment the applied label
if: ${{ steps.labeller.outputs.applied == 0 }}
with:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
message: |
**Pro Tip!**
You can help us label your Pull Requests by using the following branch naming convention next time you create a pull request. ❤️
Branch naming convention | Label
------------------------ | ------
`feature/**` | 🎈 Feature
`fix/**` | ✨ Fix
`chore/**` | 🧹 Chores
`api/**` | 🔧 API
`performance/**` | 💡 Performance Optimization
<hr>
If your changes do not fall into any of these categories, don't worry.
You can just ignore this message in that case! 👀

2
.gitignore vendored
View File

@ -4,10 +4,12 @@
/sonar/
/.settings/
/.idea/
/.vscode/
dependency-reduced-pom.xml
.classpath
.factorypath
.project
*.iml
.DS_Store

View File

@ -28,10 +28,16 @@
#### Additions
* Added a new language: Bulgarian
* Added a new language: Hebrew
* (API) Added AsyncProfileLoadEvent
* Added Talisman of the Wise
* Added Book Binder
* Added Tier 3 Electric Ore Grinder
#### Changes
* (API) Improvements to the BlockBreakHandler
* Massive performance improvements to holograms/armorstands
* Slimefun no longer requires CS-CoreLib to be installed
#### Fixes
* Fixed elevator floor order
@ -48,6 +54,12 @@
* Fixed some backpack opening issues
* Fixed Infused Hopper picking up items with a max pickup delay
* Fixed duplication issues related to holograms/armorstands
* Fixed #2754
* Fixed machines not respecting max size from inventories
* Fixed #2761
* Fixed #2460
* Fixed #2760
* Fixed #2771
## Release Candidate 19 (11 Jan 2021)

10
pom.xml
View File

@ -23,7 +23,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<!-- Spigot properties -->
<spigot.version>1.16.4</spigot.version>
<spigot.version>1.16.5</spigot.version>
<spigot.javadocs>https://hub.spigotmc.org/javadocs/spigot/</spigot.javadocs>
<!-- Default settings for sonarcloud.io -->
@ -302,12 +302,6 @@
<version>${spigot.version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
<artifactId>CS-CoreLib</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
<!-- Development dependencies -->
<dependency>
@ -393,7 +387,7 @@
<dependency>
<groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId>
<version>2.1.171</version>
<version>2.1.173</version>
<scope>provided</scope>
<exclusions>
<exclusion>

View File

@ -149,7 +149,6 @@ public class ErrorReport<T extends Throwable> {
stream.println();
stream.println("Slimefun Environment:");
stream.println(" CS-CoreLib v" + SlimefunPlugin.getCSCoreLibVersion());
stream.println(" Slimefun v" + SlimefunPlugin.getVersion());
stream.println(" Caused by: " + addon.getName() + " v" + addon.getPluginVersion());
stream.println();

View File

@ -0,0 +1,75 @@
package io.github.thebusybiscuit.slimefun4.api.events;
import java.util.UUID;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
/**
* This {@link Event} is called when the {@link PlayerProfile} of a {@link Player}
* is loaded into memory.
* The {@link AsyncProfileLoadEvent} is called asynchronously and can be used to "inject"
* a custom {@link PlayerProfile} if necessary.
*
* @author TheBusyBiscuit
*
* @see PlayerProfile
*
*/
public class AsyncProfileLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final UUID uniqueId;
private PlayerProfile profile;
public AsyncProfileLoadEvent(@Nonnull PlayerProfile profile) {
super(true);
Validate.notNull(profile, "The Profile cannot be null");
this.uniqueId = profile.getUUID();
this.profile = profile;
}
@Nonnull
public UUID getPlayerUUID() {
return uniqueId;
}
@Nonnull
public PlayerProfile getProfile() {
return profile;
}
/**
* This method can be used to inject your custom {@link PlayerProfile} implementations.
* However, the passed {@link PlayerProfile} must have the same {@link UUID} as the original one!
*
* @param profile
* The {@link PlayerProfile}
*/
public void setProfile(@Nonnull PlayerProfile profile) {
Validate.notNull(profile, "The PlayerProfile cannot be null!");
Validate.isTrue(profile.getUUID().equals(uniqueId), "Cannot inject a PlayerProfile with a different UUID");
this.profile = profile;
}
@Nonnull
public static HandlerList getHandlerList() {
return handlers;
}
@Nonnull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
}

View File

@ -3,6 +3,7 @@ package io.github.thebusybiscuit.slimefun4.api.events;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
@ -27,8 +28,11 @@ public class ResearchUnlockEvent extends Event implements Cancellable {
private boolean cancelled;
public ResearchUnlockEvent(@Nonnull Player p, @Nonnull Research research) {
super(!Bukkit.isPrimaryThread());
Validate.notNull(p, "The Player cannot be null");
Validate.notNull(research, "Research cannot be null");
this.player = p;
this.research = research;
}

View File

@ -1,7 +1,5 @@
package io.github.thebusybiscuit.slimefun4.api.items;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
@ -92,7 +90,7 @@ public class ItemSetting<T> {
*/
@Nonnull
public T getValue() {
Validate.notNull(value, "An ItemSetting was invoked but was not initialized yet.");
Validate.notNull(value, "ItemSetting '" + key + "' was invoked but was not initialized yet.");
return value;
}
@ -137,6 +135,7 @@ public class ItemSetting<T> {
* @param item
* The {@link SlimefunItem} who called this method
*/
@SuppressWarnings("unchecked")
public void load(@Nonnull SlimefunItem item) {
Validate.notNull(item, "Cannot apply settings for a non-existing SlimefunItem");
@ -144,26 +143,33 @@ public class ItemSetting<T> {
Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey());
if (defaultValue.getClass().isInstance(configuredValue)) {
// We can suppress the warning here, we did an isInstance(...) check before!
@SuppressWarnings("unchecked")
// We can unsafe cast here, we did an isInstance(...) check before!
T newValue = (T) configuredValue;
if (validateInput(newValue)) {
this.value = newValue;
} else {
SlimefunPlugin.logger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!");
SlimefunPlugin.logger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() });
SlimefunPlugin.logger().log(Level.WARNING, "{0} is not a valid input!", configuredValue);
SlimefunPlugin.logger().log(Level.WARNING, getErrorMessage());
// @formatter:off
item.warn(
"We have found an invalid config setting in your Items.yml!" +
"\n at \"" + item.getId() + "." + getKey() + "\"" +
"\n " + configuredValue + " is not a valid input!" +
"\n" + getErrorMessage()
);
// @formatter:on
}
} else {
this.value = defaultValue;
String found = configuredValue == null ? "null" : configuredValue.getClass().getSimpleName();
SlimefunPlugin.logger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!");
SlimefunPlugin.logger().log(Level.WARNING, "Please only use settings that are valid.");
SlimefunPlugin.logger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() });
SlimefunPlugin.logger().log(Level.WARNING, "Expected \"{0}\" but found: \"{1}\"", new Object[] { defaultValue.getClass().getSimpleName(), found });
// @formatter:off
item.warn(
"We have found an invalid config setting in your Items.yml!" +
"\nPlease only use settings that are valid." +
"\n at \"" + item.getId() + "." + getKey() + "\"" +
"\n Expected \"" + defaultValue.getClass().getSimpleName() + "\" but found: \"" + found + "\""
);
// @formatter:on
}
}

View File

@ -1,6 +1,5 @@
package io.github.thebusybiscuit.slimefun4.api.player;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -33,6 +32,7 @@ import com.google.common.collect.ImmutableSet;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.events.AsyncProfileLoadEvent;
import io.github.thebusybiscuit.slimefun4.api.gps.Waypoint;
import io.github.thebusybiscuit.slimefun4.api.items.HashedArmorpiece;
import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType;
@ -56,7 +56,7 @@ import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
* @see HashedArmorpiece
*
*/
public final class PlayerProfile {
public class PlayerProfile {
private final UUID uuid;
private final String name;
@ -74,13 +74,17 @@ public final class PlayerProfile {
private final HashedArmorpiece[] armor = { new HashedArmorpiece(), new HashedArmorpiece(), new HashedArmorpiece(), new HashedArmorpiece() };
private PlayerProfile(@Nonnull OfflinePlayer p) {
protected PlayerProfile(@Nonnull OfflinePlayer p) {
this.uuid = p.getUniqueId();
this.name = p.getName();
configFile = new Config(new File("data-storage/Slimefun/Players/" + uuid.toString() + ".yml"));
configFile = new Config("data-storage/Slimefun/Players/" + uuid.toString() + ".yml");
waypointsFile = new Config("data-storage/Slimefun/waypoints/" + uuid.toString() + ".yml");
loadProfileData();
}
private void loadProfileData() {
for (Research research : SlimefunPlugin.getRegistry().getResearches()) {
if (configFile.contains("researches." + research.getID())) {
researches.add(research);
@ -95,7 +99,7 @@ public final class PlayerProfile {
waypoints.add(new Waypoint(this, key, loc, waypointName));
}
} catch (Exception x) {
SlimefunPlugin.logger().log(Level.WARNING, x, () -> "Could not load Waypoint \"" + key + "\" for Player \"" + p.getName() + '"');
SlimefunPlugin.logger().log(Level.WARNING, x, () -> "Could not load Waypoint \"" + key + "\" for Player \"" + name + '"');
}
}
}
@ -268,14 +272,14 @@ public final class PlayerProfile {
* Call this method if the Player has left.
* The profile can then be removed from RAM.
*/
public void markForDeletion() {
public final void markForDeletion() {
markedForDeletion = true;
}
/**
* Call this method if this Profile has unsaved changes.
*/
public void markDirty() {
public final void markDirty() {
dirty = true;
}
@ -382,9 +386,11 @@ public final class PlayerProfile {
}
Bukkit.getScheduler().runTaskAsynchronously(SlimefunPlugin.instance(), () -> {
PlayerProfile pp = new PlayerProfile(p);
SlimefunPlugin.getRegistry().getPlayerProfiles().put(uuid, pp);
callback.accept(pp);
AsyncProfileLoadEvent event = new AsyncProfileLoadEvent(new PlayerProfile(p));
Bukkit.getPluginManager().callEvent(event);
SlimefunPlugin.getRegistry().getPlayerProfiles().put(uuid, event.getProfile());
callback.accept(event.getProfile());
});
return false;

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
@ -61,7 +62,7 @@ public final class SlimefunRegistry {
private final List<Research> researches = new LinkedList<>();
private final List<String> researchRanks = new ArrayList<>();
private final Set<UUID> researchingPlayers = new HashSet<>();
private final Set<UUID> researchingPlayers = Collections.synchronizedSet(new HashSet<>());
private boolean backwardsCompatibility;
private boolean automaticallyLoadItems;

View File

@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.attributes;
import io.github.thebusybiscuit.slimefun4.utils.ChargeUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
@ -57,7 +58,7 @@ public interface Rechargeable extends ItemAttribute {
}
ItemMeta meta = item.getItemMeta();
RechargeableHelper.setCharge(meta, charge, maximum);
ChargeUtils.setCharge(meta, charge, maximum);
item.setItemMeta(meta);
}
@ -74,7 +75,7 @@ public interface Rechargeable extends ItemAttribute {
throw new IllegalArgumentException("Cannot get Item charge for null or AIR");
}
return RechargeableHelper.getCharge(item.getItemMeta());
return ChargeUtils.getCharge(item.getItemMeta());
}
/**
@ -97,7 +98,7 @@ public interface Rechargeable extends ItemAttribute {
}
ItemMeta meta = item.getItemMeta();
float currentCharge = RechargeableHelper.getCharge(meta);
float currentCharge = ChargeUtils.getCharge(meta);
float maximum = getMaxItemCharge(item);
// If the item is already fully charged, we abort.
@ -106,7 +107,7 @@ public interface Rechargeable extends ItemAttribute {
}
float newCharge = Math.min(currentCharge + charge, maximum);
RechargeableHelper.setCharge(meta, newCharge, maximum);
ChargeUtils.setCharge(meta, newCharge, maximum);
item.setItemMeta(meta);
return true;
@ -132,7 +133,7 @@ public interface Rechargeable extends ItemAttribute {
}
ItemMeta meta = item.getItemMeta();
float currentCharge = RechargeableHelper.getCharge(meta);
float currentCharge = ChargeUtils.getCharge(meta);
// If the item does not have enough charge, we abort
if (currentCharge < charge) {
@ -140,7 +141,7 @@ public interface Rechargeable extends ItemAttribute {
}
float newCharge = Math.max(currentCharge - charge, 0);
RechargeableHelper.setCharge(meta, newCharge, getMaxItemCharge(item));
ChargeUtils.setCharge(meta, newCharge, getMaxItemCharge(item));
item.setItemMeta(meta);
return true;

View File

@ -30,7 +30,6 @@ class VersionsCommand extends SubCommand {
sender.sendMessage(ChatColor.GRAY + "This Server uses the following setup of Slimefun:");
sender.sendMessage(ChatColors.color("&a" + serverSoftware + " &2" + Bukkit.getVersion()));
sender.sendMessage(ChatColors.color("&aCS-CoreLib &2v" + SlimefunPlugin.getCSCoreLibVersion()));
sender.sendMessage(ChatColors.color("&aSlimefun &2v" + SlimefunPlugin.getVersion()));
if (SlimefunPlugin.getMetricsService().getVersion() != null) {

View File

@ -4,6 +4,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@ -14,6 +17,7 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideMode;
import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
@ -46,10 +50,11 @@ public final class SlimefunGuideSettings {
private SlimefunGuideSettings() {}
public static <T> void addOption(SlimefunGuideOption<T> option) {
public static <T> void addOption(@Nonnull SlimefunGuideOption<T> option) {
options.add(option);
}
@ParametersAreNonnullByDefault
public static void openSettings(Player p, ItemStack guide) {
ChestMenu menu = new ChestMenu(SlimefunPlugin.getLocalization().getMessage(p, "guide.title.settings"));
@ -64,6 +69,7 @@ public final class SlimefunGuideSettings {
menu.open(p);
}
@ParametersAreNonnullByDefault
private static void addHeader(Player p, ChestMenu menu, ItemStack guide) {
menu.addItem(0, new CustomItem(SlimefunGuide.getItem(SlimefunGuideMode.SURVIVAL_MODE), "&e\u21E6 " + SlimefunPlugin.getLocalization().getMessage(p, "guide.back.title"), "", "&7" + SlimefunPlugin.getLocalization().getMessage(p, "guide.back.guide")), (pl, slot, item, action) -> {
SlimefunGuide.openGuide(pl, guide);
@ -81,7 +87,7 @@ public final class SlimefunGuideSettings {
return false;
});
menu.addItem(4, new CustomItem(Material.WRITABLE_BOOK, ChatColor.GREEN + SlimefunPlugin.getLocalization().getMessage(p, "guide.title.versions"), "&7&o" + SlimefunPlugin.getLocalization().getMessage(p, "guide.tooltips.versions-notice"), "", "&fMinecraft: &a" + Bukkit.getBukkitVersion(), "&fSlimefun: &a" + SlimefunPlugin.getVersion(), "&fCS-CoreLib: &a" + SlimefunPlugin.getCSCoreLibVersion()), ChestMenuUtils.getEmptyClickHandler());
menu.addItem(4, new CustomItem(Material.WRITABLE_BOOK, ChatColor.GREEN + SlimefunPlugin.getLocalization().getMessage(p, "guide.title.versions"), "&7&o" + SlimefunPlugin.getLocalization().getMessage(p, "guide.tooltips.versions-notice"), "", "&fMinecraft: &a" + Bukkit.getBukkitVersion(), "&fSlimefun: &a" + SlimefunPlugin.getVersion()), ChestMenuUtils.getEmptyClickHandler());
menu.addItem(6,
new CustomItem(Material.COMPARATOR, "&e" + SlimefunPlugin.getLocalization().getMessage(p, "guide.title.source"), "", "&7Last Activity: &a" + NumberUtils.getElapsedTime(SlimefunPlugin.getGitHubService().getLastUpdate()) + " ago", "&7Forks: &e" + SlimefunPlugin.getGitHubService().getForks(), "&7Stars: &e" + SlimefunPlugin.getGitHubService().getStars(), "", "&7&oSlimefun 4 is a community project,", "&7&othe source code is available on GitHub", "&7&oand if you want to keep this Plugin alive,", "&7&othen please consider contributing to it", "", "&7\u21E8 &eClick to go to GitHub"));
@ -119,6 +125,7 @@ public final class SlimefunGuideSettings {
});
}
@ParametersAreNonnullByDefault
private static void addConfigurableOptions(Player p, ChestMenu menu, ItemStack guide) {
int i = 19;
@ -137,7 +144,17 @@ public final class SlimefunGuideSettings {
}
}
public static boolean hasFireworksEnabled(Player p) {
/**
* This method checks if the given {@link Player} has enabled the {@link FireworksOption}
* in their {@link SlimefunGuide}.
* If they enabled this setting, they will see fireworks when they unlock a {@link Research}.
*
* @param p
* The {@link Player}
*
* @return Whether this {@link Player} wants to see fireworks when unlocking a {@link Research}
*/
public static boolean hasFireworksEnabled(@Nonnull Player p) {
for (SlimefunGuideOption<?> option : options) {
if (option instanceof FireworksOption) {
FireworksOption fireworks = (FireworksOption) option;

View File

@ -0,0 +1,129 @@
package io.github.thebusybiscuit.slimefun4.core.researching;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;
/**
* A {@link PlayerResearchTask} is run when a {@link Player} unlocks a {@link Research}.
*
* @author TheBusyBiscuit
*
* @see Research
* @see ResearchUnlockEvent
* @see PlayerProfile
*
*/
public class PlayerResearchTask implements Consumer<PlayerProfile> {
private static final int[] RESEARCH_PROGRESS = { 23, 44, 57, 92 };
private static final String PLACEHOLDER = "%research%";
private final Research research;
private final boolean isInstant;
private final Consumer<Player> callback;
/**
* This constructs a new {@link PlayerResearchTask}.
*
* @param research
* The {@link Research} to unlock
* @param isInstant
* Whether to unlock this {@link Research} instantaneously
* @param callback
* The callback to run when the task has completed
*/
PlayerResearchTask(@Nonnull Research research, boolean isInstant, @Nullable Consumer<Player> callback) {
Validate.notNull(research, "The Research must not be null");
this.research = research;
this.isInstant = isInstant;
this.callback = callback;
}
@Override
public void accept(PlayerProfile profile) {
if (!profile.hasUnlocked(research)) {
Player p = profile.getPlayer();
if (p == null) {
return;
}
if (!isInstant) {
SlimefunPlugin.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER, research.getName(p)).replace("%progress%", "0%"));
}, 5L);
}
ResearchUnlockEvent event = new ResearchUnlockEvent(p, research);
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
if (isInstant) {
SlimefunPlugin.runSync(() -> unlockResearch(p, profile));
} else if (SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().add(p.getUniqueId())) {
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.start", true, msg -> msg.replace(PLACEHOLDER, research.getName(p)));
sendUpdateMessage(p);
SlimefunPlugin.runSync(() -> {
unlockResearch(p, profile);
SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().remove(p.getUniqueId());
}, (RESEARCH_PROGRESS.length + 1) * 20L);
}
}
}
}
private void sendUpdateMessage(@Nonnull Player p) {
for (int i = 1; i < RESEARCH_PROGRESS.length + 1; i++) {
int index = i;
SlimefunPlugin.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> {
String progress = RESEARCH_PROGRESS[index - 1] + "%";
return msg.replace(PLACEHOLDER, research.getName(p)).replace("%progress%", progress);
});
}, i * 20L);
}
}
private void unlockResearch(@Nonnull Player p, @Nonnull PlayerProfile profile) {
profile.setResearched(research, true);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.unlocked", true, msg -> msg.replace(PLACEHOLDER, research.getName(p)));
onFinish(p);
// Check if the Server and the Player have enabled fireworks for researches
if (SlimefunPlugin.getRegistry().isResearchFireworkEnabled() && SlimefunGuideSettings.hasFireworksEnabled(p)) {
FireworkUtils.launchRandom(p, 1);
}
}
/**
* This method is called when the {@link Research} successfully finished to unlock.
*
* @param p
* The {@link Player} who has unlocked this {@link Research}
*/
private void onFinish(@Nonnull Player p) {
if (callback != null) {
callback.accept(p);
}
}
}

View File

@ -15,19 +15,16 @@ import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.events.PlayerPreResearchEvent;
import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
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;
@ -43,9 +40,6 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
*/
public class Research implements Keyed {
private static final int[] RESEARCH_PROGRESS = { 23, 44, 57, 92 };
private static final String PLACEHOLDER_RESEARCH = "%research%";
private final NamespacedKey key;
private final int id;
private final String name;
@ -258,7 +252,7 @@ public class Research implements Keyed {
* Whether to unlock it instantly
*/
public void unlock(@Nonnull Player p, boolean instant) {
unlock(p, instant, pl -> {});
unlock(p, instant, null);
}
/**
@ -266,62 +260,13 @@ public class Research implements Keyed {
*
* @param p
* The {@link Player} for which to unlock this {@link Research}
* @param instant
* @param isInstant
* Whether to unlock this {@link Research} instantly
* @param callback
* A callback which will be run when the {@link Research} animation completed
*/
public void unlock(@Nonnull Player p, boolean instant, @Nonnull Consumer<Player> callback) {
if (!instant) {
SlimefunPlugin.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)).replace("%progress%", "0%"));
}, 10L);
}
PlayerProfile.get(p, profile -> {
if (!profile.hasUnlocked(this)) {
SlimefunPlugin.runSync(() -> {
ResearchUnlockEvent event = new ResearchUnlockEvent(p, this);
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
if (instant) {
finishResearch(p, profile, callback);
} else if (SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().add(p.getUniqueId())) {
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.start", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)));
playResearchAnimation(p);
SlimefunPlugin.runSync(() -> {
finishResearch(p, profile, callback);
SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().remove(p.getUniqueId());
}, (RESEARCH_PROGRESS.length + 1) * 20L);
}
}
});
}
});
}
private void finishResearch(@Nonnull Player p, @Nonnull PlayerProfile profile, @Nonnull Consumer<Player> callback) {
profile.setResearched(this, true);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.unlocked", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)));
callback.accept(p);
if (SlimefunPlugin.getRegistry().isResearchFireworkEnabled() && SlimefunGuideSettings.hasFireworksEnabled(p)) {
FireworkUtils.launchRandom(p, 1);
}
}
private void playResearchAnimation(@Nonnull Player p) {
for (int i = 1; i < RESEARCH_PROGRESS.length + 1; i++) {
int j = i;
SlimefunPlugin.runSync(() -> {
p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
SlimefunPlugin.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)).replace("%progress%", RESEARCH_PROGRESS[j - 1] + "%"));
}, i * 20L);
}
public void unlock(@Nonnull Player p, boolean isInstant, @Nullable Consumer<Player> callback) {
PlayerProfile.get(p, new PlayerResearchTask(this, isInstant, callback));
}
/**
@ -329,7 +274,6 @@ public class Research implements Keyed {
*/
public void register() {
SlimefunPlugin.getResearchCfg().setDefaultValue("enable-researching", true);
String path = key.getNamespace() + '.' + key.getKey();
if (SlimefunPlugin.getResearchCfg().contains(path + ".enabled") && !SlimefunPlugin.getResearchCfg().getBoolean(path + ".enabled")) {

View File

@ -47,7 +47,7 @@ enum SupportedLanguage {
CHINESE_TAIWAN("zh-TW", true, "702a4afb2e1e2e3a1894a8b74272f95cfa994ce53907f9ac140bd3c932f9f"),
JAPANESE("ja", true, "d640ae466162a47d3ee33c4076df1cab96f11860f07edb1f0832c525a9e33323"),
KOREAN("ko", true, "fc1be5f12f45e413eda56f3de94e08d90ede8e339c7b1e8f32797390e9a5f"),
HEBREW("he", false, "1ba086a2cc7272cf5ba49c80248546c22e5ef1bab54120e8a8e5d9e75b6a"),
HEBREW("he", true, "1ba086a2cc7272cf5ba49c80248546c22e5ef1bab54120e8a8e5d9e75b6a"),
ARABIC("ar", true, "a4be759a9cf7f0a19a7e8e62f23789ad1d21cebae38af9d9541676a3db001572"),
TURKISH("tr", true, "9852b9aba3482348514c1034d0affe73545c9de679ae4647f99562b5e5f47d09"),
PERSIAN("fa", false, "5cd9badf1972583b663b44b1e027255de8f275aa1e89defcf77782ba6fcc652"),

View File

@ -47,6 +47,14 @@ public class Translators {
addTranslator("dracrus", SupportedLanguage.ITALIAN, true);
addTranslator("prolletto64", SupportedLanguage.ITALIAN, true);
// Translators - Spanish
addTranslator("Luu7", "_Luu", SupportedLanguage.SPANISH, true);
addTranslator("Vravinite", SupportedLanguage.SPANISH, true);
addTranslator("NotUmBr4", SupportedLanguage.SPANISH, true);
addTranslator("dbzjjoe", SupportedLanguage.SPANISH, true);
addTranslator("DaHolyCheese", SupportedLanguage.SPANISH, true);
addTranslator("d-l-n", SupportedLanguage.SPANISH, true);
// Translators - Latvian
addTranslator("AgnisT", "NIKNAIZ", SupportedLanguage.LATVIAN, true);
@ -91,13 +99,6 @@ public class Translators {
// Translators - Ukrainian
addTranslator("SoSeDiK", SupportedLanguage.UKRAINIAN, false);
// Translators - Spanish
addTranslator("Luu7", "_Luu", SupportedLanguage.SPANISH, true);
addTranslator("Vravinite", SupportedLanguage.SPANISH, true);
addTranslator("NotUmBr4", SupportedLanguage.SPANISH, true);
addTranslator("dbzjjoe", SupportedLanguage.SPANISH, true);
addTranslator("DaHolyCheese", SupportedLanguage.SPANISH, true);
// Translators - Swedish
addTranslator("NihilistBrew", "ma1yang2", SupportedLanguage.SWEDISH, false);
addTranslator("Tra-sh", "TurretTrash", SupportedLanguage.SWEDISH, true);
@ -137,7 +138,9 @@ public class Translators {
addTranslator("Eylonnn", SupportedLanguage.HEBREW, true);
addTranslator("sarhatabaot", SupportedLanguage.HEBREW, false);
addTranslator("Timotiyadeyhakesem", SupportedLanguage.HEBREW, true);
addTranslator("PaladinBear", SupportedLanguage.HEBREW, true);
addTranslator("Molioron", SupportedLanguage.HEBREW, true);
addTranslator("McAlmog", SupportedLanguage.HEBREW, true);
// Translators - Japanese
addTranslator("bito-blosh", "Bloshop", SupportedLanguage.JAPANESE, false);

View File

@ -562,6 +562,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack TALISMAN_WHIRLWIND = new SlimefunItemStack("WHIRLWIND_TALISMAN", Material.EMERALD, "&aTalisman of the Whirlwind", "", "&fHaving this Talisman", "&fin your Inventory will reflect", "&f60% of any projectiles fired at you.", "&e&oOnly a thrown Trident can pierce", "&e&othrough this layer of protection");
public static final SlimefunItemStack TALISMAN_WIZARD = new SlimefunItemStack("WIZARD_TALISMAN", Material.EMERALD, "&aTalisman of the Wizard", "", "&fWhile you have this Talisman", "&fin your Inventory it allows you to", "&fobtain Fortune Level 4/5 however", "&fit also has a chance to lower the", "&fLevel of some Enchantments on your Item");
public static final SlimefunItemStack TALISMAN_CAVEMAN = new SlimefunItemStack("CAVEMAN_TALISMAN", Material.EMERALD, "&aTalisman of the Caveman", "", "&fWhile you have this Talisman", "&fin your inventory it gives", "&fyou a 50% chance for a decent", "&fHaste buff when you mine any ore");
public static final SlimefunItemStack TALISMAN_WISE = new SlimefunItemStack("WISE_TALISMAN", Material.EMERALD, "&aTalisman of the Wise", "", "&fWhile you have this Talisman", "&fin your inventory it gives", "&fyou a 20% chance of doubling", "&fany experience you obtain");
/* Staves */
public static final SlimefunItemStack STAFF_ELEMENTAL = new SlimefunItemStack("STAFF_ELEMENTAL", Material.STICK, "&6Elemental Staff");
@ -675,6 +676,7 @@ 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_ORE_GRINDER_3 = new SlimefunItemStack("ELECTRIC_ORE_GRINDER_3", Material.FURNACE, "&cElectric Ore Grinder &7(&eIII&7)", "", "&fWorks as an Ore Crusher and Grind Stone", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(10), LoreBuilder.powerPerSecond(90));
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 = 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));
@ -683,6 +685,8 @@ public final class SlimefunItems {
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 = new SlimefunItemStack("AUTO_BREWER", Material.SMOKER, "&6Auto Brewer", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(12));
public static final SlimefunItemStack BOOK_BINDER = new SlimefunItemStack("BOOK_BINDER", Material.BOOKSHELF, "&6Book Binder", "", "&fBinds multiple enchanted books into one.", "", LoreBuilder.machine(MachineTier.MEDIUM, MachineType.MACHINE), LoreBuilder.powerPerSecond(16));
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");

View File

@ -118,6 +118,7 @@ import io.github.thebusybiscuit.slimefun4.integrations.IntegrationsManager;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.MenuListener;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
@ -203,38 +204,17 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
*/
@Override
public void onEnable() {
if (minecraftVersion == MinecraftVersion.UNIT_TEST) {
setInstance(this);
if (isUnitTest()) {
// We handle Unit Tests seperately.
setInstance(this);
onUnitTestStart();
} else if (isVersionUnsupported()) {
// We wanna ensure that the Server uses a compatible version of Minecraft.
setInstance(this);
getLogger().log(Level.WARNING, "Slimefun was not installed properly! Disabling...");
getServer().getPluginManager().disablePlugin(this);
} else if (getServer().getPluginManager().isPluginEnabled("CS-CoreLib")) {
// The Environment and dependencies have been validated.
setInstance(this);
getLogger().log(Level.INFO, "CS-CoreLib was detected!");
onPluginStart();
} else {
// Terminate our Plugin instance
setInstance(null);
// CS-CoreLib has not been installed!
getLogger().log(Level.INFO, "#################### - INFO - ####################");
getLogger().log(Level.INFO, " ");
getLogger().log(Level.INFO, "Slimefun could not be loaded (yet).");
getLogger().log(Level.INFO, "It appears that you have not installed CS-CoreLib.");
getLogger().log(Level.INFO, "Please download and install CS-CoreLib manually:");
getLogger().log(Level.INFO, "https://thebusybiscuit.github.io/builds/TheBusyBiscuit/CS-CoreLib/master/");
// Send a message upon doing /slimefun
getCommand("slimefun").setExecutor((sender, cmd, label, args) -> {
sender.sendMessage("You have forgotten to install CS-CoreLib! Slimefun is disabled.");
sender.sendMessage("https://thebusybiscuit.github.io/builds/TheBusyBiscuit/CS-CoreLib/master/");
return true;
});
// The Environment has been validated.
onPluginStart();
}
}
@ -457,7 +437,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
long ms = (System.nanoTime() - timestamp) / 1000000;
if (ms > 1000) {
return NumberUtils.roundDecimalNumber(ms / 1000.0) + "s";
return NumberUtils.roundDecimalNumber(ms / 1000.0) + 's';
} else {
return NumberUtils.roundDecimalNumber(ms) + "ms";
}
@ -480,6 +460,16 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
Reactor.progress = null;
}
/**
* This method checks if this is currently running in a unit test
* environment.
*
* @return Whether we are inside a unit test
*/
public boolean isUnitTest() {
return minecraftVersion == MinecraftVersion.UNIT_TEST;
}
/**
* This method checks for the {@link MinecraftVersion} of the {@link Server}.
* If the version is unsupported, a warning will be printed to the console.
@ -596,6 +586,9 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
* This method registers all of our {@link Listener Listeners}.
*/
private void registerListeners() {
// Old deprecated CS-CoreLib Listener
new MenuListener(this);
new SlimefunBootsListener(this);
new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this);
@ -984,18 +977,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
return instance.isNewlyInstalled;
}
@Nonnull
public static String getCSCoreLibVersion() {
validateInstance();
Plugin cscorelib = instance.getServer().getPluginManager().getPlugin("CS-CoreLib");
if (cscorelib == null) {
throw new IllegalStateException("CS-CoreLib is not installed.");
} else {
return cscorelib.getDescription().getVersion();
}
}
/**
* This method returns a {@link Set} of every {@link Plugin} that lists Slimefun
* as a required or optional dependency.

View File

@ -0,0 +1,142 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
/**
* Represents Book Binder, a machine that binds multiple enchantments books into one.
*
* @author ProfElements
*/
public class BookBinder extends AContainer {
private final ItemSetting<Boolean> bypassVanillaMaxLevel = new ItemSetting<>("bypass-vanilla-max-level", false);
private final ItemSetting<Boolean> hasCustomMaxLevel = new ItemSetting<>("has-custom-max-level", false);
private final ItemSetting<Integer> customMaxLevel = new IntRangeSetting("custom-max-level", 0, 15, Integer.MAX_VALUE);
@ParametersAreNonnullByDefault
public BookBinder(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
addItemSetting(bypassVanillaMaxLevel, hasCustomMaxLevel, customMaxLevel);
}
@Override
protected MachineRecipe findNextRecipe(BlockMenu menu) {
for (int slot : getInputSlots()) {
ItemStack target = menu.getItemInSlot(slot == getInputSlots()[0] ? getInputSlots()[1] : getInputSlots()[0]);
ItemStack item = menu.getItemInSlot(slot);
if (isCompatible(item) && isCompatible(target)) {
EnchantmentStorageMeta itemMeta = (EnchantmentStorageMeta) item.getItemMeta();
EnchantmentStorageMeta targetMeta = (EnchantmentStorageMeta) target.getItemMeta();
Map<Enchantment, Integer> storedItemEnchantments = itemMeta.getStoredEnchants();
Map<Enchantment, Integer> storedTargetEnchantments = targetMeta.getStoredEnchants();
Map<Enchantment, Integer> enchantments = combineEnchantments(storedItemEnchantments, storedTargetEnchantments);
if (enchantments.size() > 0) {
ItemStack book = new ItemStack(Material.ENCHANTED_BOOK);
EnchantmentStorageMeta enchantMeta = (EnchantmentStorageMeta) book.getItemMeta();
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
enchantMeta.addStoredEnchant(entry.getKey(), entry.getValue(), bypassVanillaMaxLevel.getValue());
}
book.setItemMeta(enchantMeta);
MachineRecipe recipe = new MachineRecipe(25 * (enchantments.size() / this.getSpeed()), new ItemStack[] { target, item }, new ItemStack[] { book });
if (!InvUtils.fitAll(menu.toInventory(), recipe.getOutput(), getOutputSlots())) {
return null;
}
for (int inputSlot : getInputSlots()) {
menu.consumeItem(inputSlot);
}
return recipe;
}
return null;
}
}
return null;
}
private boolean isCompatible(@Nullable ItemStack item) {
return item != null && item.getType() == Material.ENCHANTED_BOOK;
}
@Override
public ItemStack getProgressBar() {
return new ItemStack(Material.IRON_CHESTPLATE);
}
@Override
public String getMachineIdentifier() {
return "BOOK_BINDER";
}
@Nonnull
@ParametersAreNonnullByDefault
private Map<Enchantment, Integer> combineEnchantments(Map<Enchantment, Integer> ech1, Map<Enchantment, Integer> ech2) {
Map<Enchantment, Integer> enchantments = new HashMap<>();
boolean conflicts = false;
enchantments.putAll(ech1);
for (Map.Entry<Enchantment, Integer> entry : ech2.entrySet()) {
for (Map.Entry<Enchantment, Integer> conflictsWith : enchantments.entrySet()) {
if (entry.getKey().conflictsWith(conflictsWith.getKey())) {
conflicts = true;
}
}
if (!conflicts) {
enchantments.merge(entry.getKey(), entry.getValue(), (a, b) -> {
if (a.intValue() == b.intValue()) {
if (hasCustomMaxLevel.getValue()) {
return a + 1 > customMaxLevel.getValue() ? customMaxLevel.getValue() : a + 1;
} else {
return a + 1;
}
} else {
int highestLevel = Math.max(a, b);
if (hasCustomMaxLevel.getValue()) {
return highestLevel > customMaxLevel.getValue() ? customMaxLevel.getValue() : highestLevel;
} else {
return highestLevel;
}
}
});
}
}
return enchantments;
}
}

View File

@ -1,16 +1,5 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.core.attributes.NotHopperable;
@ -22,13 +11,23 @@ import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenu
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset;
import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
* The {@link ElectricSmeltery} is an electric version of the standard {@link Smeltery}.
@ -112,12 +111,10 @@ public class ElectricSmeltery extends AContainer implements NotHopperable {
processing.remove(b);
return true;
});
this.registerDefaultRecipes();
}
private Comparator<Integer> compareSlots(DirtyChestMenu menu) {
return (slot1, slot2) -> menu.getItemInSlot(slot1).getAmount() - menu.getItemInSlot(slot2).getAmount();
return Comparator.comparingInt(slot -> menu.getItemInSlot(slot).getAmount());
}
@Override

View File

@ -1,15 +1,5 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
@ -22,6 +12,15 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset;
import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class HeatedPressureChamber extends AContainer {
@ -73,12 +72,10 @@ public class HeatedPressureChamber extends AContainer {
}
}
};
this.registerDefaultRecipes();
}
private Comparator<Integer> compareSlots(DirtyChestMenu menu) {
return (slot1, slot2) -> menu.getItemInSlot(slot1).getAmount() - menu.getItemInSlot(slot2).getAmount();
return Comparator.comparingInt(slot -> menu.getItemInSlot(slot).getAmount());
}
@Override

View File

@ -3,6 +3,8 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.tools;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
import org.bukkit.Material;
@ -12,7 +14,6 @@ import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem;
@ -43,6 +44,7 @@ class ExplosiveTool extends SimpleSlimefunItem<ToolUseHandler> implements NotPla
private final ItemSetting<Boolean> damageOnUse = new ItemSetting<>("damage-on-use", true);
private final ItemSetting<Boolean> callExplosionEvent = new ItemSetting<>("call-explosion-event", false);
@ParametersAreNonnullByDefault
public ExplosiveTool(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
super(category, item, recipeType, recipe);
@ -59,11 +61,12 @@ class ExplosiveTool extends SimpleSlimefunItem<ToolUseHandler> implements NotPla
b.getWorld().playSound(b.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 0.2F, 1F);
List<Block> blocks = findBlocks(b);
breakBlocks(p, tool, b, blocks, fortune, drops);
breakBlocks(p, tool, b, blocks, drops);
};
}
private void breakBlocks(Player p, ItemStack item, Block b, List<Block> blocks, int fortune, List<ItemStack> drops) {
@ParametersAreNonnullByDefault
private void breakBlocks(Player p, ItemStack item, Block b, List<Block> blocks, List<ItemStack> drops) {
if (callExplosionEvent.getValue().booleanValue()) {
BlockExplodeEvent blockExplodeEvent = new BlockExplodeEvent(b, blocks, 0);
Bukkit.getServer().getPluginManager().callEvent(blockExplodeEvent);
@ -71,14 +74,14 @@ class ExplosiveTool extends SimpleSlimefunItem<ToolUseHandler> implements NotPla
if (!blockExplodeEvent.isCancelled()) {
for (Block block : blockExplodeEvent.blockList()) {
if (canBreak(p, block)) {
breakBlock(p, item, block, fortune, drops);
breakBlock(p, item, block, drops);
}
}
}
} else {
for (Block block : blocks) {
if (canBreak(p, block)) {
breakBlock(p, item, block, fortune, drops);
breakBlock(p, item, block, drops);
}
}
}
@ -122,7 +125,8 @@ class ExplosiveTool extends SimpleSlimefunItem<ToolUseHandler> implements NotPla
}
}
private void breakBlock(Player p, ItemStack item, Block b, int fortune, List<ItemStack> drops) {
@ParametersAreNonnullByDefault
private void breakBlock(Player p, ItemStack item, Block b, List<ItemStack> drops) {
SlimefunPlugin.getProtectionManager().logAction(p, b, ProtectableAction.BREAK_BLOCK);
Material material = b.getType();
@ -135,19 +139,8 @@ class ExplosiveTool extends SimpleSlimefunItem<ToolUseHandler> implements NotPla
if (handler != null && !handler.onBreak(p, b, sfItem, UnregisterReason.PLAYER_BREAK)) {
drops.add(BlockStorage.retrieve(b));
}
} else if (material == Material.PLAYER_HEAD || SlimefunTag.SHULKER_BOXES.isTagged(material)) {
b.breakNaturally(item);
} else {
boolean applyFortune = SlimefunTag.FORTUNE_COMPATIBLE_ORES.isTagged(material);
for (ItemStack drop : b.getDrops(getItem())) {
// For some reason this check is necessary with Paper
if (drop != null && drop.getType() != Material.AIR) {
b.getWorld().dropItemNaturally(b.getLocation(), applyFortune ? new CustomItem(drop, fortune) : drop);
}
}
b.setType(Material.AIR);
b.breakNaturally(item);
}
damageItem(p, item);

View File

@ -10,7 +10,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@ -51,10 +50,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
*/
public class BlockListener implements Listener {
private final SlimefunPlugin plugin;
public BlockListener(@Nonnull SlimefunPlugin plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@ -66,22 +62,14 @@ public class BlockListener implements Listener {
if (e.getBlockReplacedState().getType().isAir()) {
SlimefunItem sfItem = BlockStorage.check(block);
if (sfItem != null) {
/*
* We can move the TickerTask synchronization to an async task to
* avoid blocking the main Thread here.
*/
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
if (!SlimefunPlugin.getTickerTask().isDeletedSoon(block.getLocation())) {
for (ItemStack item : sfItem.getDrops()) {
if (item != null && !item.getType().isAir()) {
SlimefunPlugin.runSync(() -> block.getWorld().dropItemNaturally(block.getLocation(), item));
}
}
BlockStorage.clearBlockInfo(block);
if (sfItem != null && !SlimefunPlugin.getTickerTask().isDeletedSoon(block.getLocation())) {
for (ItemStack item : sfItem.getDrops()) {
if (item != null && !item.getType().isAir()) {
block.getWorld().dropItemNaturally(block.getLocation(), item);
}
});
}
BlockStorage.clearBlockInfo(block);
}
} else if (BlockStorage.hasBlockInfo(e.getBlock())) {
// If there is no air (e.g. grass) then don't let the block be placed
@ -120,6 +108,11 @@ public class BlockListener implements Listener {
return;
}
// Ignore blocks which we have marked as deleted (Fixes #2771)
if (SlimefunPlugin.getTickerTask().isDeletedSoon(e.getBlock().getLocation())) {
return;
}
ItemStack item = e.getPlayer().getInventory().getItemInMainHand();
checkForSensitiveBlockAbove(e, item);

View File

@ -2,15 +2,14 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners;
import javax.annotation.Nonnull;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
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.implementation.items.cargo.CargoNode;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
@ -28,25 +27,15 @@ public class CargoNodeListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onCargoNodePlace(BlockPlaceEvent e) {
if (e.getBlock().getY() != e.getBlockAgainst().getY() && isCargoNode(e.getItemInHand())) {
Block b = e.getBlock();
if ((b.getY() != e.getBlockAgainst().getY() || !e.getBlockReplacedState().getType().isAir()) && isCargoNode(e.getItemInHand())) {
SlimefunPlugin.getLocalization().sendMessage(e.getPlayer(), "machines.CARGO_NODES.must-be-placed", true);
e.setCancelled(true);
}
}
private boolean isCargoNode(@Nonnull ItemStack item) {
if (SlimefunPlugin.getRegistry().isBackwardsCompatible()) {
ItemStackWrapper wrapper = new ItemStackWrapper(item);
return SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.CARGO_INPUT_NODE, false) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.CARGO_OUTPUT_NODE, false) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.CARGO_OUTPUT_NODE_2, false);
}
SlimefunItem sfItem = SlimefunItem.getByItem(item);
if (sfItem == null) {
return false;
}
return sfItem.getId().equals(SlimefunItems.CARGO_INPUT_NODE.getItemId()) || sfItem.getId().equals(SlimefunItems.CARGO_OUTPUT_NODE.getItemId()) || sfItem.getId().equals(SlimefunItems.CARGO_OUTPUT_NODE_2.getItemId());
return SlimefunItem.getByItem(item) instanceof CargoNode;
}
}

View File

@ -31,6 +31,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerExpChangeEvent;
import org.bukkit.event.player.PlayerItemBreakEvent;
import org.bukkit.event.player.PlayerToggleSprintEvent;
import org.bukkit.inventory.EntityEquipment;
@ -245,17 +246,26 @@ public class TalismanListener implements Listener {
// Wizard Talisman
if (!enchantments.containsKey(Enchantment.SILK_TOUCH) && Enchantment.LOOT_BONUS_BLOCKS.canEnchantItem(e.getItem()) && Talisman.checkFor(e, SlimefunItems.TALISMAN_WIZARD)) {
for (Enchantment enchantment : enchantments.keySet()) {
if (random.nextInt(100) < 40) {
e.getEnchantsToAdd().put(enchantment, random.nextInt(3) + 1);
// Randomly lower some enchantments
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
if (entry.getValue() > 1 && random.nextInt(100) < 40) {
enchantments.put(entry.getKey(), entry.getValue() - 1);
}
}
// Give an extra Fortune boost (Lvl 3 - 5)
enchantments.put(Enchantment.LOOT_BONUS_BLOCKS, random.nextInt(3) + 3);
}
}
@EventHandler(ignoreCancelled = true)
public void onExperienceReceive(PlayerExpChangeEvent e) {
if (e.getAmount() > 0 && Talisman.checkFor(e, SlimefunItems.TALISMAN_WISE)) {
// Double-XP
e.setAmount(e.getAmount() * 2);
}
}
@EventHandler(ignoreCancelled = true)
public void onBlockDropItems(BlockDropItemEvent e) {
// We only want to double ores

View File

@ -217,7 +217,7 @@ public final class ResearchSetup {
register("cargo_basics", 205, "Cargo Basics", 30, SlimefunItems.CARGO_MOTOR, SlimefunItems.CARGO_MANAGER, SlimefunItems.CARGO_CONNECTOR_NODE);
register("cargo_nodes", 206, "Cargo Setup", 30, SlimefunItems.CARGO_INPUT_NODE, SlimefunItems.CARGO_OUTPUT_NODE);
register("electric_ingot_machines", 207, "Electric Ingot Fabrication", 18, SlimefunItems.ELECTRIC_GOLD_PAN, SlimefunItems.ELECTRIC_DUST_WASHER, SlimefunItems.ELECTRIC_INGOT_FACTORY);
register("high_tier_electric_ingot_machines", 209, "Super Fast Ingot Fabrication", 32, SlimefunItems.ELECTRIC_GOLD_PAN_3, SlimefunItems.ELECTRIC_DUST_WASHER_3, SlimefunItems.ELECTRIC_INGOT_FACTORY_3, SlimefunItems.ELECTRIC_ORE_GRINDER_2);
register("high_tier_electric_ingot_machines", 209, "Super Fast Ingot Fabrication", 32, SlimefunItems.ELECTRIC_GOLD_PAN_3, SlimefunItems.ELECTRIC_DUST_WASHER_3, SlimefunItems.ELECTRIC_INGOT_FACTORY_3, SlimefunItems.ELECTRIC_ORE_GRINDER_2, SlimefunItems.ELECTRIC_ORE_GRINDER_3);
register("automated_crafting_chamber", 210, "Automated Crafting", 20, SlimefunItems.AUTOMATED_CRAFTING_CHAMBER);
register("better_food_fabricator", 211, "Upgraded Food Fabrication", 28, SlimefunItems.FOOD_FABRICATOR_2, SlimefunItems.FOOD_COMPOSTER_2);
register("reactor_access_port", 212, "Reactor Interaction", 18, SlimefunItems.REACTOR_ACCESS_PORT);
@ -280,6 +280,8 @@ public final class ResearchSetup {
register("elytra_cap", 268, "Crash Gear", 20, SlimefunItems.ELYTRA_CAP);
register("energy_connectors", 269, "Wired Connections", 12, SlimefunItems.ENERGY_CONNECTOR);
register("bee_armor", 270, "Bee Armor", 24, SlimefunItems.BEE_HELMET, SlimefunItems.BEE_WINGS, SlimefunItems.BEE_LEGGINGS, SlimefunItems.BEE_BOOTS);
register("wise_talisman", 271, "Talisman of the Wise", 20, SlimefunItems.TALISMAN_WISE);
register("book_binder", 272, "Enchantment Book Binding", 26, SlimefunItems.BOOK_BINDER);
}
@ParametersAreNonnullByDefault

View File

@ -85,6 +85,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoDrier;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoEnchanter;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.BookBinder;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.CarbonPress;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.ChargingBench;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.CropGrowthAccelerator;
@ -861,6 +862,11 @@ public final class SlimefunItemSetup {
false, false, "caveman", 50, new PotionEffect(PotionEffectType.FAST_DIGGING, 800, 2))
.register(plugin);
new Talisman(SlimefunItems.TALISMAN_WISE,
new ItemStack[] { SlimefunItems.MAGIC_LUMP_3, SlimefunItems.MAGICAL_GLASS, SlimefunItems.MAGIC_LUMP_3, SlimefunItems.FILLED_FLASK_OF_KNOWLEDGE, SlimefunItems.TALISMAN_MAGICIAN, SlimefunItems.FILLED_FLASK_OF_KNOWLEDGE, SlimefunItems.MAGIC_LUMP_3, SlimefunItems.MAGICAL_GLASS, SlimefunItems.MAGIC_LUMP_3},
false, false, "wise", 20)
.register(plugin);
new SlimefunItem(categories.resources, SlimefunItems.GILDED_IRON, RecipeType.SMELTERY,
new ItemStack[] {SlimefunItems.GOLD_24K, SlimefunItems.IRON_DUST, null, null, null, null, null, null, null})
.register(plugin);
@ -1660,6 +1666,13 @@ public final class SlimefunItemSetup {
.setProcessingSpeed(4)
.register(plugin);
new ElectricOreGrinder(categories.electricity, SlimefunItems.ELECTRIC_ORE_GRINDER_3, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.REINFORCED_PLATE, SlimefunItems.HEATING_COIL, SlimefunItems.REINFORCED_PLATE, null, SlimefunItems.ELECTRIC_ORE_GRINDER_2, null, SlimefunItems.REINFORCED_PLATE, SlimefunItems.BLISTERING_INGOT_3, SlimefunItems.REINFORCED_PLATE})
.setCapacity(1024)
.setEnergyConsumption(45)
.setProcessingSpeed(10)
.register(plugin);
new HeatedPressureChamber(categories.electricity, SlimefunItems.HEATED_PRESSURE_CHAMBER, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.LEAD_INGOT, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.LEAD_INGOT, SlimefunItems.LEAD_INGOT, new ItemStack(Material.GLASS), SlimefunItems.LEAD_INGOT, SlimefunItems.LEAD_INGOT, SlimefunItems.HEATING_COIL, SlimefunItems.LEAD_INGOT})
.setCapacity(128)
@ -1765,6 +1778,13 @@ public final class SlimefunItemSetup {
.setProcessingSpeed(1)
.register(plugin);
new BookBinder(categories.electricity, SlimefunItems.BOOK_BINDER, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {null, new ItemStack(Material.ENCHANTING_TABLE), null, new ItemStack(Material.BOOKSHELF), SlimefunItems.HARDENED_METAL_INGOT, new ItemStack(Material.BOOKSHELF), SlimefunItems.SYNTHETIC_SAPPHIRE, SlimefunItems.SMALL_CAPACITOR, SlimefunItems.SYNTHETIC_SAPPHIRE})
.setCapacity(256)
.setEnergyConsumption(16)
.setProcessingSpeed(1)
.register(plugin);
new Multimeter(categories.technicalGadgets, SlimefunItems.MULTIMETER, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.COPPER_INGOT, null, SlimefunItems.COPPER_INGOT, null, SlimefunItems.REDSTONE_ALLOY, null, null, SlimefunItems.GOLD_6K, null})
.register(plugin);

View File

@ -1,4 +1,4 @@
package io.github.thebusybiscuit.slimefun4.core.attributes;
package io.github.thebusybiscuit.slimefun4.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ -8,32 +8,40 @@ import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import io.github.thebusybiscuit.slimefun4.core.attributes.Rechargeable;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
import net.md_5.bungee.api.ChatColor;
/**
* This is just a simple helper class to provide static methods to the {@link Rechargeable}
* interface.
*
* @author TheBusyBiscuit
* @author WalshyDev
*
* @see Rechargeable
*
*/
final class RechargeableHelper {
public final class ChargeUtils {
private static final String LORE_PREFIX = ChatColors.color("&8\u21E8 &e\u26A1 &7");
private static final Pattern REGEX = Pattern.compile(ChatColors.color("(&c&o)?" + LORE_PREFIX) + "[0-9.]+ / [0-9.]+ J");
private RechargeableHelper() {}
private ChargeUtils() {}
public static void setCharge(@Nonnull ItemMeta meta, float charge, float capacity) {
Validate.notNull(meta, "Meta cannot be null!");
Validate.isTrue(charge >= 0, "Charge has to be equal to or greater than 0!");
Validate.isTrue(capacity > 0, "Capacity has to be greater than 0!");
Validate.isTrue(charge <= capacity, "Charge may not be bigger than the capacity!");
static void setCharge(@Nonnull ItemMeta meta, float charge, float capacity) {
BigDecimal decimal = BigDecimal.valueOf(charge).setScale(2, RoundingMode.HALF_UP);
float value = decimal.floatValue();
@ -55,9 +63,12 @@ final class RechargeableHelper {
meta.setLore(lore);
}
static float getCharge(@Nonnull ItemMeta meta) {
public static float getCharge(@Nonnull ItemMeta meta) {
Validate.notNull(meta, "Meta cannot be null!");
NamespacedKey key = SlimefunPlugin.getRegistry().getItemChargeDataKey();
Float value = meta.getPersistentDataContainer().get(key, PersistentDataType.FLOAT);
PersistentDataContainer container = meta.getPersistentDataContainer();
Float value = container.get(key, PersistentDataType.FLOAT);
// If persistent data is available, we just return this value
if (value != null) {
@ -69,12 +80,14 @@ final class RechargeableHelper {
for (String line : meta.getLore()) {
if (REGEX.matcher(line).matches()) {
String data = ChatColor.stripColor(PatternUtils.SLASH_SEPARATOR.split(line)[0].replace(LORE_PREFIX, ""));
return Float.parseFloat(data);
float loreValue = Float.parseFloat(data);
container.set(key, PersistentDataType.FLOAT, loreValue);
return loreValue;
}
}
}
return 0;
}
}

View File

@ -53,10 +53,12 @@ public final class ItemStackWrapper extends ItemStack {
@Override
public ItemMeta getItemMeta() {
// This method normally always does a .clone() operation which can be very slow.
// Since this class is immutable, we can simply let the super class create one copy
// and then store that instead of creating a clone everytime.
// This will significantly speed up any loop comparisons if used correctly.
/*
* This method normally always does a .clone() operation which can be very slow.
* Since this class is immutable, we can simply let the super class create one copy
* and then store that instead of creating a clone everytime.
* This will significantly speed up any loop comparisons if used correctly.
*/
if (meta == null) {
throw new UnsupportedOperationException("This ItemStack has no ItemMeta! Make sure to check ItemStack#hasItemMeta() before accessing this method!");
} else {

View File

@ -0,0 +1,190 @@
package me.mrCookieSlime.CSCoreLibPlugin.Configuration;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
/**
* An old remnant of CS-CoreLib.
* This will be removed once we updated everything.
* Don't look at the code, it will be gone soon, don't worry.
*
*/
public class Config {
private final File file;
private FileConfiguration config;
/**
* Creates a new Config Object for the specified File
*
* @param file
* The File for which the Config object is created for
*/
public Config(File file) {
this(file, YamlConfiguration.loadConfiguration(file));
}
/**
* Creates a new Config Object for the specified File and FileConfiguration
*
* @param file
* The File to save to
* @param config
* The FileConfiguration
*/
public Config(File file, FileConfiguration config) {
this.file = file;
this.config = config;
}
/**
* Creates a new Config Object for the File with in
* the specified Location
*
* @param path
* The Path of the File which the Config object is created for
*/
public Config(String path) {
this.file = new File(path);
this.config = YamlConfiguration.loadConfiguration(this.file);
}
/**
* Returns the File the Config is handling
*
* @return The File this Config is handling
*/
public File getFile() {
return this.file;
}
/**
* Converts this Config Object into a plain FileConfiguration Object
*
* @return The converted FileConfiguration Object
*/
public FileConfiguration getConfiguration() {
return this.config;
}
/**
* Sets the Value for the specified Path
*
* @param path
* The path in the Config File
* @param value
* The Value for that Path
*/
public void setValue(String path, Object value) {
this.config.set(path, value);
}
/**
* Saves the Config Object to its File
*/
public void save() {
try {
config.save(file);
} catch (IOException e) {}
}
/**
* Saves the Config Object to a File
*
* @param file
* The File you are saving this Config to
*/
public void save(File file) {
try {
config.save(file);
} catch (IOException e) {}
}
/**
* Sets the Value for the specified Path
* (IF the Path does not yet exist)
*
* @param path
* The path in the Config File
* @param value
* The Value for that Path
*/
public void setDefaultValue(String path, Object value) {
if (!contains(path)) {
setValue(path, value);
}
}
/**
* Checks whether the Config contains the specified Path
*
* @param path
* The path in the Config File
* @return True/false
*/
public boolean contains(String path) {
return config.contains(path);
}
/**
* Returns the Object at the specified Path
*
* @param path
* The path in the Config File
* @return The Value at that Path
*/
public Object getValue(String path) {
return config.get(path);
}
/**
* Returns the String at the specified Path
*
* @param path
* The path in the Config File
* @return The String at that Path
*/
public String getString(String path) {
return config.getString(path);
}
/**
* Recreates the File of this Config
*/
public void createFile() {
try {
this.file.createNewFile();
} catch (IOException e) {}
}
/**
* Returns all Paths in this Config
*
* @return All Paths in this Config
*/
public Set<String> getKeys() {
return config.getKeys(false);
}
/**
* Returns all Sub-Paths in this Config
*
* @param path
* The path in the Config File
* @return All Sub-Paths of the specified Path
*/
public Set<String> getKeys(String path) {
return config.getConfigurationSection(path).getKeys(false);
}
/**
* Reloads the Configuration File
*/
public void reload() {
this.config = YamlConfiguration.loadConfiguration(this.file);
}
}

View File

@ -0,0 +1,344 @@
package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
/**
* An old remnant of CS-CoreLib.
* This will be removed once we updated everything.
* Don't look at the code, it will be gone soon, don't worry.
*
*/
public class ChestMenu {
private boolean clickable;
private boolean emptyClickable;
private String title;
private Inventory inv;
private List<ItemStack> items;
private Map<Integer, MenuClickHandler> handlers;
private MenuOpeningHandler open;
private MenuCloseHandler close;
private MenuClickHandler playerclick;
/**
* Creates a new ChestMenu with the specified
* Title
*
* @param title
* The title of the Menu
*/
public ChestMenu(String title) {
this.title = ChatColor.translateAlternateColorCodes('&', title);
this.clickable = false;
this.emptyClickable = true;
this.items = new ArrayList<>();
this.handlers = new HashMap<>();
this.open = p -> {};
this.close = p -> {};
this.playerclick = (p, slot, item, action) -> isPlayerInventoryClickable();
}
/**
* Toggles whether Players can access there
* Inventory while viewing this Menu
*
* @param clickable
* Whether the Player can access his Inventory
* @return The ChestMenu Instance
*/
public ChestMenu setPlayerInventoryClickable(boolean clickable) {
this.clickable = clickable;
return this;
}
/**
* Returns whether the Player's Inventory is
* accessible while viewing this Menu
*
* @return Whether the Player Inventory is clickable
*/
public boolean isPlayerInventoryClickable() {
return clickable;
}
/**
* Toggles whether Players can click the
* empty menu slots while viewing this Menu
*
* @param emptyClickable
* Whether the Player can click empty slots
* @return The ChestMenu Instance
*/
public ChestMenu setEmptySlotsClickable(boolean emptyClickable) {
this.emptyClickable = emptyClickable;
return this;
}
/**
* Returns whether the empty menu slots are
* clickable while viewing this Menu
*
* @return Whether the empty menu slots are clickable
*/
public boolean isEmptySlotsClickable() {
return emptyClickable;
}
/**
* Adds a ClickHandler to ALL Slots of the
* Player's Inventory
*
* @param handler
* The MenuClickHandler
* @return The ChestMenu Instance
*/
public ChestMenu addPlayerInventoryClickHandler(MenuClickHandler handler) {
this.playerclick = handler;
return this;
}
/**
* Adds an Item to the Inventory in that Slot
*
* @param slot
* The Slot in the Inventory
* @param item
* The Item for that Slot
* @return The ChestMenu Instance
*/
public ChestMenu addItem(int slot, ItemStack item) {
final int size = this.items.size();
if (size > slot)
this.items.set(slot, item);
else {
for (int i = 0; i < slot - size; i++) {
this.items.add(null);
}
this.items.add(item);
}
return this;
}
/**
* Adds an Item to the Inventory in that Slot
* as well as a Click Handler
*
* @param slot
* The Slot in the Inventory
* @param item
* The Item for that Slot
* @param clickHandler
* The MenuClickHandler for that Slot
* @return The ChestMenu Instance
*/
public ChestMenu addItem(int slot, ItemStack item, MenuClickHandler clickHandler) {
addItem(slot, item);
addMenuClickHandler(slot, clickHandler);
return this;
}
/**
* Returns the ItemStack in that Slot
*
* @param slot
* The Slot in the Inventory
* @return The ItemStack in that Slot
*/
public ItemStack getItemInSlot(int slot) {
setup();
return this.inv.getItem(slot);
}
/**
* Executes a certain Action upon clicking an
* Item in the Menu
*
* @param slot
* The Slot in the Inventory
* @param handler
* The MenuClickHandler
* @return The ChestMenu Instance
*/
public ChestMenu addMenuClickHandler(int slot, MenuClickHandler handler) {
this.handlers.put(slot, handler);
return this;
}
/**
* Executes a certain Action upon opening
* this Menu
*
* @param handler
* The MenuOpeningHandler
* @return The ChestMenu Instance
*/
public ChestMenu addMenuOpeningHandler(MenuOpeningHandler handler) {
this.open = handler;
return this;
}
/**
* Executes a certain Action upon closing
* this Menu
*
* @param handler
* The MenuCloseHandler
* @return The ChestMenu Instance
*/
public ChestMenu addMenuCloseHandler(MenuCloseHandler handler) {
this.close = handler;
return this;
}
/**
* Finishes the Creation of the Menu
*
* @return The ChestMenu Instance
*/
@Deprecated
public ChestMenu build() {
return this;
}
/**
* Returns an Array containing the Contents
* of this Inventory
*
* @return The Contents of this Inventory
*/
public ItemStack[] getContents() {
setup();
return this.inv.getContents();
}
private void setup() {
if (this.inv != null)
return;
this.inv = Bukkit.createInventory(null, ((int) Math.ceil(this.items.size() / 9F)) * 9, title);
for (int i = 0; i < this.items.size(); i++) {
this.inv.setItem(i, this.items.get(i));
}
}
public void reset(boolean update) {
if (update)
this.inv.clear();
else
this.inv = Bukkit.createInventory(null, ((int) Math.ceil(this.items.size() / 9F)) * 9, title);
for (int i = 0; i < this.items.size(); i++) {
this.inv.setItem(i, this.items.get(i));
}
}
/**
* Modifies an ItemStack in an ALREADY OPENED ChestMenu
*
* @param slot
* The Slot of the Item which will be replaced
* @param item
* The new Item
*/
public void replaceExistingItem(int slot, ItemStack item) {
setup();
this.inv.setItem(slot, item);
}
/**
* Opens this Menu for the specified Player/s
*
* @param players
* The Players who will see this Menu
*/
public void open(Player... players) {
setup();
for (Player p : players) {
p.openInventory(this.inv);
MenuListener.menus.put(p.getUniqueId(), this);
if (open != null)
open.onOpen(p);
}
}
/**
* Returns the MenuClickHandler which was registered for the specified Slot
*
* @param slot
* The Slot in the Inventory
* @return The MenuClickHandler registered for the specified Slot
*/
public MenuClickHandler getMenuClickHandler(int slot) {
return handlers.get(slot);
}
/**
* Returns the registered MenuCloseHandler
*
* @return The registered MenuCloseHandler
*/
public MenuCloseHandler getMenuCloseHandler() {
return close;
}
/**
* Returns the registered MenuOpeningHandler
*
* @return The registered MenuOpeningHandler
*/
public MenuOpeningHandler getMenuOpeningHandler() {
return open;
}
/**
* Returns the registered MenuClickHandler
* for Player Inventories
*
* @return The registered MenuClickHandler
*/
public MenuClickHandler getPlayerInventoryClickHandler() {
return playerclick;
}
/**
* Converts this ChestMenu Instance into a
* normal Inventory
*
* @return The converted Inventory
*/
public Inventory toInventory() {
return this.inv;
}
@FunctionalInterface
public interface MenuClickHandler {
public boolean onClick(Player p, int slot, ItemStack item, ClickAction action);
}
public interface AdvancedMenuClickHandler extends MenuClickHandler {
public boolean onClick(InventoryClickEvent e, Player p, int slot, ItemStack cursor, ClickAction action);
}
@FunctionalInterface
public interface MenuOpeningHandler {
public void onOpen(Player p);
}
@FunctionalInterface
public interface MenuCloseHandler {
public void onClose(Player p);
}
}

View File

@ -0,0 +1,27 @@
package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory;
/**
* An old remnant of CS-CoreLib.
* This will be removed once we updated everything.
* Don't look at the code, it will be gone soon, don't worry.
*
*/
public class ClickAction {
private boolean right;
private boolean shift;
public ClickAction(boolean rightClicked, boolean shiftClicked) {
this.right = rightClicked;
this.shift = shiftClicked;
}
public boolean isRightClicked() {
return right;
}
public boolean isShiftClicked() {
return shift;
}
}

View File

@ -0,0 +1,117 @@
package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
/**
* An old remnant of CS-CoreLib.
* This will be removed once we updated everything.
* Don't look at the code, it will be gone soon, don't worry.
*
* @deprecated This was a horrible idea. Don't use it.
*
*/
@Deprecated
public class CustomItemSerializer {
public enum ItemFlag {
MATERIAL(0),
DATA(1),
AMOUNT(2),
DURABILITY(3),
ENCHANTMENTS(4),
ITEMMETA_DISPLAY_NAME(5),
ITEMMETA_LORE(6);
private int weight;
ItemFlag(int weight) {
this.weight = weight;
}
public int getWeight() {
return this.weight;
}
}
private static ItemFlagComparator comparator = new ItemFlagComparator();
public static String serialize(ItemStack item, ItemFlag... flags) {
if (item == null)
return "NULL";
List<ItemFlag> flaglist = Arrays.asList(flags);
Collections.sort(flaglist, comparator);
StringBuilder builder = new StringBuilder();
int i = 0;
for (ItemFlag flag : flags) {
if (i > 0)
builder.append(" </sep> ");
builder.append(flag.toString() + "=");
switch (flag) {
case AMOUNT: {
builder.append(item.getAmount());
break;
}
case DATA: {
builder.append((int) item.getData().getData());
break;
}
case DURABILITY: {
builder.append((int) item.getDurability());
break;
}
case ENCHANTMENTS:
for (Enchantment enchantment : Enchantment.values()) {
if (item.getEnchantments().containsKey(enchantment)) {
builder.append(enchantment.getName() + ":" + item.getEnchantmentLevel(enchantment));
} else {
builder.append(enchantment.getName() + ":0");
}
}
break;
case ITEMMETA_DISPLAY_NAME: {
if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) {
builder.append(item.getItemMeta().getDisplayName().replaceAll("\\u00a7", "&"));
} else {
builder.append("NONE");
}
break;
}
case ITEMMETA_LORE: {
if (item.hasItemMeta() && item.getItemMeta().hasLore()) {
builder.append(item.getItemMeta().getLore().toString().replaceAll("\\u00a7", "&"));
} else {
builder.append("NONE");
}
break;
}
case MATERIAL: {
builder.append(item.getType().toString());
break;
}
default:
break;
}
i++;
}
return builder.toString();
}
public static boolean equals(ItemStack stack1, ItemStack stack2, ItemFlag... flags) {
return serialize(stack1, flags).equals(serialize(stack2, flags));
}
}

View File

@ -0,0 +1,23 @@
package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item;
import java.util.Comparator;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer.ItemFlag;
/**
* An old remnant of CS-CoreLib.
* This will be removed once we updated everything.
* Don't look at the code, it will be gone soon, don't worry.
*
* @deprecated This was a horrible idea. Don't use it.
*
*/
@Deprecated
public class ItemFlagComparator implements Comparator<ItemFlag> {
@Override
public int compare(ItemFlag flag1, ItemFlag flag2) {
return flag1.getWeight() - flag2.getWeight();
}
}

View File

@ -0,0 +1,64 @@
package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.plugin.Plugin;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.MenuClickHandler;
/**
* An old {@link Listener} for CS-CoreLib
*
* @deprecated This is an old remnant of CS-CoreLib, the last bits of the past. They will be removed once everything is
* updated.
*
*/
@Deprecated
public class MenuListener implements Listener {
static final Map<UUID, ChestMenu> menus = new HashMap<>();
public MenuListener(Plugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onClose(InventoryCloseEvent e) {
ChestMenu menu = menus.remove(e.getPlayer().getUniqueId());
if (menu != null) {
menu.getMenuCloseHandler().onClose((Player) e.getPlayer());
}
}
@EventHandler
public void onClick(InventoryClickEvent e) {
ChestMenu menu = menus.get(e.getWhoClicked().getUniqueId());
if (menu != null) {
if (e.getRawSlot() < e.getInventory().getSize()) {
MenuClickHandler handler = menu.getMenuClickHandler(e.getSlot());
if (handler == null) {
e.setCancelled(!menu.isEmptySlotsClickable() && (e.getCurrentItem() == null || e.getCurrentItem().getType() == Material.AIR));
} else if (handler instanceof AdvancedMenuClickHandler) {
e.setCancelled(!((AdvancedMenuClickHandler) handler).onClick(e, (Player) e.getWhoClicked(), e.getSlot(), e.getCursor(), new ClickAction(e.isRightClick(), e.isShiftClick())));
} else {
e.setCancelled(!handler.onClick((Player) e.getWhoClicked(), e.getSlot(), e.getCurrentItem(), new ClickAction(e.isRightClick(), e.isShiftClick())));
}
} else {
e.setCancelled(!menu.getPlayerInventoryClickHandler().onClick((Player) e.getWhoClicked(), e.getSlot(), e.getCurrentItem(), new ClickAction(e.isRightClick(), e.isShiftClick())));
}
}
}
}

View File

@ -0,0 +1,5 @@
/**
* Old CS-CoreLib 1.X code.
*/
@java.lang.Deprecated
package me.mrCookieSlime.CSCoreLibPlugin;

View File

@ -74,8 +74,6 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
processing.remove(b);
return true;
});
registerDefaultRecipes();
}
@ParametersAreNonnullByDefault
@ -239,6 +237,8 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
warn("Make sure to call '" + getClass().getSimpleName() + "#setProcessingSpeed(...)' before registering!");
}
registerDefaultRecipes();
if (getCapacity() > 0 && getEnergyConsumption() > 0 && getSpeed() > 0) {
super.register(addon);
}

View File

@ -2,10 +2,11 @@ package me.mrCookieSlime.Slimefun.api;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.configuration.file.FileConfiguration;
@ -39,12 +40,13 @@ public class BlockInfoConfig extends Config {
this.data = data;
}
@Nonnull
public Map<String, String> getMap() {
return data;
}
@Override
protected void store(String path, Object value) {
public void setValue(String path, Object value) {
if (value != null && !(value instanceof String)) {
throw new UnsupportedOperationException("Can't set \"" + path + "\" to \"" + value + "\" (type: " + value.getClass().getSimpleName() + ") because BlockInfoConfig only supports Strings");
}
@ -76,70 +78,42 @@ public class BlockInfoConfig extends Config {
return data.keySet();
}
@Override
public int getInt(String path) {
throw invalidType(path);
}
@Override
public boolean getBoolean(String path) {
throw invalidType(path);
}
@Override
public List<String> getStringList(String path) {
throw invalidType(path);
}
@Override
public List<Integer> getIntList(String path) {
throw invalidType(path);
}
@Override
public Double getDouble(String path) {
throw invalidType(path);
}
@Override
public Set<String> getKeys(String path) {
throw invalidType(path);
}
private UnsupportedOperationException invalidType(String path) {
return new UnsupportedOperationException("Can't get \"" + path + "\" because BlockInfoConfig only supports String values");
throw new UnsupportedOperationException("Cannot get keys for BlockInfoConfig");
}
@Override
public File getFile() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs do not have a File");
}
@Override
public FileConfiguration getConfiguration() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs do not have a FileConfiguration");
}
@Override
public void save() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs cannot be saved to a File");
}
@Override
public void save(File file) {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs cannot be saved to a File");
}
@Override
public void createFile() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs cannot be created from a File");
}
@Override
public void reload() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("BlockInfoConfigs cannot be reloaded");
}
@Nonnull
public String toJSON() {
return new GsonBuilder().create().toJson(data);
}

View File

@ -18,6 +18,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import com.google.common.collect.ImmutableMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
@ -27,6 +28,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import org.apache.commons.lang.Validate;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@ -360,6 +363,36 @@ public class BlockStorage {
}
}
/**
* This will return an {@link ImmutableMap} of the underline {@code Map<String, Config>} of
* this worlds {@link BlockStorage}.
*
* @return An {@link ImmutableMap} of the raw data.
*/
@Nonnull
public Map<Location, Config> getRawStorage() {
return ImmutableMap.copyOf(this.storage);
}
/**
* This will return an {@link ImmutableMap} of the underline {@code Map<String, Config>} of
* this worlds {@link BlockStorage}. If there is no registered world then this will return null.
*
* @param world The world of which to fetch the data from.
* @return An {@link ImmutableMap} of the raw data or null if the world isn't registered.
*/
@Nullable
public static Map<Location, Config> getRawStorage(@Nonnull World world) {
Validate.notNull(world, "World cannot be null!");
BlockStorage storage = getStorage(world);
if (storage != null) {
return storage.getRawStorage();
} else {
return null;
}
}
public static void store(Block block, ItemStack item) {
SlimefunItem sfitem = SlimefunItem.getByItem(item);

View File

@ -17,7 +17,7 @@ class EmptyBlockData extends BlockInfoConfig {
}
@Override
protected void store(String path, Object value) {
public void setValue(String path, Object value) {
throw new UnsupportedOperationException("Cannot store values (" + path + ':' + value + " on a read-only data object!");
}

View File

@ -105,15 +105,18 @@ public class DirtyChestMenu extends ChestMenu {
if (stack == null) {
replaceExistingItem(slot, item);
return null;
} else if (stack.getAmount() < stack.getMaxStackSize()) {
if (wrapper == null) {
wrapper = new ItemStackWrapper(item);
}
} else {
int maxStackSize = Math.min(stack.getMaxStackSize(), toInventory().getMaxStackSize());
if (stack.getAmount() < maxStackSize) {
if (wrapper == null) {
wrapper = new ItemStackWrapper(item);
}
if (ItemUtils.canStack(wrapper, stack)) {
amount -= (stack.getMaxStackSize() - stack.getAmount());
stack.setAmount(Math.min(stack.getAmount() + item.getAmount(), stack.getMaxStackSize()));
item.setAmount(amount);
if (ItemUtils.canStack(wrapper, stack)) {
amount -= (maxStackSize - stack.getAmount());
stack.setAmount(Math.min(stack.getAmount() + item.getAmount(), maxStackSize));
item.setAmount(amount);
}
}
}
}

View File

@ -0,0 +1,5 @@
/**
* Old Slimefun 4.0 code.
*/
@java.lang.Deprecated
package me.mrCookieSlime.Slimefun;

View File

@ -0,0 +1,7 @@
/**
* These are the old packages, the remnants of past versions that have not been rewritten yet.
* Don't look too close at the code that lays here. It's horrible, believe me.
* Once we updated everything, all of these classes will be removed.
*/
@java.lang.Deprecated
package me.mrCookieSlime;

View File

@ -134,6 +134,7 @@ messages:
wizard: "&a&oВашия Талисман Ви даде по-добро Ниво Fortune, но може да е намалило
Нивата на някои от другите Ви Enchantment-и"
caveman: "&a&oВашия Талисман Ви дава haste или познато, като бързо чупене"
wise: "&a&oВашия Талисман удвой вашите XP дропове"
soulbound-rune:
fail: "& cМожете да свържете само един елемент с душата си наведнъж."
success: "&aВие успешно свързахте този предмет с душата си! Когато умрете Вие

View File

@ -136,6 +136,7 @@ messages:
wizard: "&a&oDein Talisman hat dein Glück-Level erhöht aber möglicherweise das
Level einer anderen Verzauberung vermindert"
caveman: "&a&oDein Talisman hat dir einen Abbau-Boost verschafft"
wise: "&a&oDein Talisman hat soeben deine Erfahrungspunkte verdoppelt"
soulbound-rune:
fail: "&cDu kannst nicht mehrere Items auf einmal an deine Seele binden"
success: "&aDu hast dieses Item erfolgreich an deine Seele gebunden! Solltest
@ -319,6 +320,7 @@ android:
own: "&4Du kannst nicht dein eigenes Skript bewerten!"
already: "&4Du hast dieses Skript bereits bewertet!"
editor: Skripteditor
too-long: "&cDieses Skript ist zu lang, um es zu bearbeiten!"
languages:
default: Server-Standard
en: Englisch

View File

@ -158,6 +158,7 @@ messages:
whirlwind: '&a&oYour Talisman reflected the Projectile'
wizard: '&a&oYour Talisman has given you a better Fortune Level but maybe also lowered some other Enchantment Levels'
caveman: '&a&oYour Talisman gave you haste'
wise: '&a&oYour Talisman has doubled your experience drops'
soulbound-rune:
fail: '&cYou can only bind one item to your soul at a time.'

View File

@ -25,9 +25,9 @@ commands:
not-rechargeable: Este ítem no puede ser cargado!
timings:
description: Timings para Slimefun y sus addon
please-wait: "&ePor favor, espere un segundo... ¡Los resultados están llegando!"
verbose-player: "&4La flag verbose no puede ser usada por un jugador!"
unknown-flag: "&4flag desconocida: &c%flag%"
please-wait: "&ePor favor, espere un segundo... ¡Los resultados están llegando!"
guide:
search:
message: "&b¿Qué te gustaría buscar?"
@ -134,6 +134,7 @@ messages:
wizard: "&a&oTu talismán te ha dado un mayor nivel de fortuna, pero pudo disminuír
el nivel de otros encantamientos."
caveman: "&a&oTu Talismán te ha dado Haste"
wise: "&a&oTu Talismán ha duplicado tu drop de experiencia"
soulbound-rune:
fail: "&cSolo puedes ligar un objeto a tu alma."
success: "&a¡Has ligado este objeto a tu alma exitosamente! No lo perderás al
@ -189,9 +190,9 @@ messages:
invalid-item: "&c¡&4%item% &cno es un objeto válido!"
invalid-amount: "&c¡&4%amount% &cno es un valor válido: tiene que ser mayor a 0!"
invalid-research: "&c¡&4%research% &cno es un conocimiento válido!"
mode-change: 'El modo de &b%device% ha cambiado a: &9%mode%'
bee-suit-slow-fall: "&eTus alas de Abeja te ayudarán a llegar seguro y lento al
piso"
mode-change: 'El modo de &b%device% ha cambiado a: &9%mode%'
machines:
pattern-not-found: "&eLo siento, no puedo reconocer esta receta. Por favor coloca
el objeto en el patrón correcto dentro del dispensador."
@ -314,6 +315,7 @@ android:
own: "&4¡No puedes calificar tu propio script!"
already: "&4¡Ya has calificado este script!"
editor: Editor de Guión
too-long: "&cEl script es demasiado largo para editar!"
languages:
default: Predeterminado
en: Inglés
@ -335,6 +337,7 @@ languages:
zh-CN: Chino (China)
el: Griego
he: Hebreo
pt: Portugués (Portugal)
ar: Árabe
af: Africano
da: Danés
@ -346,7 +349,6 @@ languages:
fa: Persa
th: Tailandés
ro: Rumano
pt: Portugués (Portugal)
pt-BR: Portugués (Brasil)
bg: Búlgaro
ko: Coreano

View File

@ -0,0 +1,359 @@
---
commands:
help: הצגת מסך עזרה
teleporter: ראה נקודות ציון של שחקנים אחרים
search: חפש במדריך את המונח הנתון
cheat: מאפשר לזמן פריטים ברמאות
give: נותן למישהו פירטי סליים פאן
guide: תן לעצמך מדריך סליים פאן
backpack:
description: אחזר עותק של גיבוי קיים
invalid-id: " המספר המזהה חייב להיות מספר לא שלילי!&4"
player-never-joined: "&4!שום שחקן בשם זה לא נמצא"
backpack-does-not-exist: "&4!הגיבוי שצוין אינו קיים"
restored-backpack-given: "&a!הגיבוי שוחזר ונוסף למלאי"
charge:
description: טוען את הפריט שאתה מחזיק
charge-success: "!הפריט נטען"
not-rechargeable: הפריט הזה לא יכול להטען!
timings:
description: תזמונים עבור סליים פאן והתוסף שלה
please-wait: "&e !אנא המתן שנייה ... התוצאות מגיעות"
verbose-player: "&4 האיתות המילולי אינו יכול להיות בשימוש על ידי שחקן!"
unknown-flag: "&4איתות לא ידוע: &c%flag%"
versions: הצגת תוספים מותקנים
open_guide: פתח את המדריך בלי להשתמש בספר
stats: מציג כמה נתונים סטטיסטיים על שחקן
research:
description: 'בטל נעילה /מחקרים עבור שחקן זה '
reset: "&c איפס את הידע של %שחקן% זה"
reset-target: "&cהידע שלך אופס "
guide:
title:
main: סליים פאן מדריך
credits: סליים פאן4 תורמים
wiki: סליים פאן4 ויקי
addons: 'תוספות לסליים פאן '
versions: גרסאות מותקנות
settings: 'הגדרות & מידע '
languages: בחר את השפה המועדפת עליך
bugs: דיווחי שגיאות
source: קוד מקור
back:
guide: חזור למדריך סליים פאן
title: חזור
settings: חזור ללוח ההגדרות
tooltips:
wiki: ראה פריט זה באתר הרשימי של סליים פאן ויקי
recipes:
climbing-pick: משטחים שאפשר לטפס עליהם
machine: מתכונים שנעשו במכונה זו
miner: משאבים שתוכלו להשיג אצל כורה זה
generator: סוגי דלק זמינים
gold-pan: משאבים שתוכלו להשיג
open-category: לחץ לפתיחה
versions-notice: אלה חשובים מאוד כשמדווחים על באגים!
work-in-progress: תכונה זו עדיין לא הושלמה במלואה!
languages:
change: לחץ כדי לבחור שפה חדשה
description:
- "&7כעת יש לך אפשרות לשנות"
- "&7השפה שבה סליים פאן"
- "&7יוצג בפניך. פריטים"
- "&7לא ניתן לתרגם לעת עתה."
updated: "&a :השפה שלך הוגדרה בהצלחה ל &b%lang%"
translations:
name: "&aIs משהו חסר?"
lore: לחץ כדי להוסיף תרגום משלך
select: 'לחץ כדי לבחור שפה זאת '
select-default: לחץ לבחירת שפת ברירת המחדל
selected-language: 'נבחר כעת:'
credits:
open: לחץ כדי לראות את התורמים שלנו
description:
- "&7 סליים פאן הוא פרויקט קוד פתוח "
- "&7ומתוחזק על ידי קהילה גדולה של אנשים."
- "&7על &e%contributors% &7אנשים עבדו"
- "&7.סליים פאן לאורך כל השנים האלה "
commit: להתחייב
commits: מתחייב
roles:
developer: "&6 מפתח"
wiki: "&3ויקי עורך "
resourcepack: "&c אמן חבילת משאבים"
translator: "&9מתרגם"
profile-link: "לחץ כדי לבקר את הפרופיל שלהם \nב -GitHub"
search:
message: &b מה תרצה לחפש?
name: "&7חפש...."
tooltip: "&b לחץ לחפש פריט"
inventory: "%item% מחפש עבור: "
cheat:
no-multiblocks: "&4 אתה לא יכול לרמות בריבוי מבנים אתה חייב לבנות אותם!"
pages:
previous: עמוד קודם
next: עמוד הבא
locked: נעול
locked-category:
- כדי לבטל את הנעילה של קטגוריה זו
- 'צריך לפתוח את כל הפריטים מה '
- הקטגוריות הבאות
messages:
not-enough-xp: "&4אין לך מספיק נקודות ניסיון\nכדי לפתוח את זה "
talisman:
angel: "&a&o הקמע שלך הציל אותך מלקבל נזק נפילה"
fire: "&a&oהקמע שלך הציל אותך מלהישרף למוות"
caveman: "&a&oהקמע שלך נתן לך מהירות"
anvil: "&a&o הקמע שלך הציל את הכלי שלך מלהישבר"
miner: "&a&o הקמע שלך בכפיל את הפריטים הנופלים"
hunter: "&a&o הקמע שלך בכפיל את הפריטים הנופלים"
lava: "&a&oהקמע שלך הציל אותך מלהישרף למוות"
water: "&a&oהקמע שלך הציל אותך מלטבוע "
magician: "&a%o הקמע שלך העניק לך כישוף נוסף"
traveller: "&a&o הקמע שלך נתן לך דחיפת מהירות"
warrior: "&a&oהקמע שלך שיפר את כוחך לזמן מה"
knight: "&a&o הקמע שלך נתן לך 5 שניות ריפוי"
whirlwind: "&a&o הקמע שלך שיקף את הטיל"
wizard: "&a&o הקמע שלך העניק לך רמת הון טובה יותר אבל אולי גם הוריד כמה רמות הקסם
אחרות"
fortune-cookie:
- "&7 עזור לי, אני כלוא במפעל לעוגיות "
- "&7אתה תמות מחר על ידי.... קריפר "
- "&7 בשלב מסוים בחיים שלך משהו רע יקרה!!!"
- "&7בשבוע הבא תבחין שזה לא העולם האמיתי, אתה נמצא במשחק מחשב"
- "עוגיה זו תהיה טעימה תוך כמה שניות &7"
- '&7 המילה האחרונה שתשמע תהיה "להשמיד !!!"'
- "&7מה שלא תעשה אל תחבק קריפר ניסיתי. זה מרגיש טוב,אבל לא שווה את זה"
- "&7התשובה היא 42"
- "&7 וולשי ביום ירחיקו את הצרות."
- "&7 לעולם אל תחפור ישר למטה!"
- "&7זו רק שריטה!"
- "&7תמיד תסתכל על הצד הטוב שבחיים"
- "&7זה היה למעשה ביסקוויט ולא עוגיה"
- "&7שלטי הניאון דולקים"
piglin-barter: &4 אתה לא יכול לסחור עם חזירונים באמצעות חפצי סליים פאן
enchantment-rune:
fail: "&cאת לא יכול להחליף עם חזירונים חפצים של סליים פאן."
no-enchantment: "&cלא נמצא שום קסם ישים לפריט הזה."
success: "&aהחלת בהצלחה קסם אקראי החל על פריט זה."
tape-measure:
no-anchor: "&c אתה צריך להגדיר עוגן לפני שתוכל להתחיל למדוד!"
wrong-world: "!&cנראה שהעוגן שלך נמצא בעולם אחר"
distance: "&7המדידה נלקחה &eDistance: %distance%."
anchor-set: "&aהעוגן הוגדר בהצלחה:&e %anchor%"
multi-tool:
mode-change: "&b%device% &9: המצב השתנה ל: %mode%"
not-shears: "&c מולטי טול לא יכול לשמש כמזמרה!"
climbing-pick:
dual-wielding: "&4אתה צריך להחזיק מכושי טיפוס בשתי !הידיים כדי להשתמש בהם"
wrong-material: "&cאתה לא יכול לטפס על המשטח הזה. עיין במדריך סליים פאן למידע
נוסף!"
bee-suit-slow-fall: "&eכנפי הדבורה שלך יעזרו לך להגיע לקרקע בצורה איטית ובטוחה"
not-researched: "&4אין לך מספיק ידע להבין זאת "
unlocked: "&b אתה פתחת %research% "
only-players: "&4 פקודה זו מיועדת רק לשחקנים"
unknown-player: "&4 :שחקן לא מוכר &c%player%"
no-permission: "&4 אין לך את ההרשאה הנדרשת לעשות זאת"
usage: "&4 שימוש: &c %שימוש%"
not-online: "&4 %שחקן%cis לא ברשת!"
given-item: "&b ניתן לך סכום &a %amount% &7\"%item%7"
give-item: '&b %ניתן לך %amount% &a %item%'
give-research: "&b נתת %player% את המח\"%research%&7\""
hungry: "&cאתה רעב מדיי כדי לעשות את זה!"
disabled-in-world: "&4 פריט זה הושבת בעולם זה"
disabled-item: "&4 פריט זה הושבת איך בכלל השגת את זה ?"
no-tome-yourself: "&c אינך יכול להשתמש ב- 4 כרך של מידע צמך...."
multimeter: '&bStored Energy: &3%stored% &b/ &3%capacity%'
soulbound-rune:
fail: "&c אתה יכול לקשור פריט אחד בלבד לנשמתך בכל פעם."
success: "&a אתה קושרת פריט זה בהצלחה לנשמתך! אתה תשמור עליו כשתמות."
research:
start: "&7 הרוחות העתיקות לוחשות לך מילים מסתוריות לאזן"
progress: "&7 אתה מתחיל לתהות לגביי &b%research% &e(%progress%)"
fire-extinguish: "&7אתה כיבית את עצמך"
cannot-place: "&c אתה לא יכול למקם את הבנייה שם!"
no-pvp: "&cאתה לא יכול להילחם כאן!"
radiation: |-
&4 אתה נחשפת לקרינה קטלנית!
&c היפטר מהפריט הרדיואקטיבי או תלבש חליפת ההסמת מלאה!
opening-guide: "&b פותח מדריך עלול לקח כמה שניות..."
opening-backpack: "&b פותח תרמיל עלול לקחת כמה שניות..."
no-iron-golem-heal: "&c זה לא מטיל ברזל אתה לא יכול להשתמש בזה כדי לרפאות גולמי
ברזל!"
link-prompt: "&e לחץ כאן:"
diet-cookie: "&eאתה מתחיל להרגיש מרחף...."
invalid-item: "&4%item% &cאינו פריט בר תוקף!"
invalid-amount: "&4%amount% &cאינו כמות ברת תוקף : המספר חייב להיות גדול מאפס!"
invalid-research: "&4%research% &cאינו מחקר בר תוקף!"
anvil:
not-working: "&4אתה לא יכול להשתמש בפרטי סליים פאן בתוך סדן"
mcmmo-salvaging: "&4אתה לא יכול להציל (לתקן) חפצי סליים פאן"
workbench:
not-enhanced: "&4 אתה לא יכול להשתמש בפרטי סליים פאן על שולחן עבודה רגיל"
gps:
geo:
scan-required: |-
&4-סורק גאולוגי נדרש!
&c סרוק את הנתח הזה באמצעות סורק !גאולוגי-קודם
waypoint:
duplicate: "&4כבר יצרת נקודת ציון בשם: &f%waypoint%"
new: "&eהקלד שם לנקודת הדרך החדשה שלך בצ'אט. (קודי צבע נתמכים!)"
added: "&a נוסף בהצלחה נקודת דרך חדשה"
max: "&4 הגעת לכמות הנקודות הדרך המרבית"
deathpoint: |-
&4נקודת מוות
&7%date%
insufficient-complexity:
- "&4אין מספיק מורכבות ברשת ה GPS: &c%complexity%"
- "&4a) אין לך הגדרות רשת GPS עדיין"
- "&4b) הגדרות ה GPS שלך לא מספיק מורכבות"
languages:
zh-CN: "(מנדרינית(סין"
tl: טגלית
default: שרת ברירת מחדל
en: אנגלית
de: 'גרמנית '
fr: צרפתית
it: איטלקית
es: ספרדית
pl: פולנית
sv: שוודית
nl: הולנדית
cs: צ'כית
hu: הונגרית
lv: לטבית
ru: רוסית
sk: סלובקית
zh-TW: "(סינית (טייוואן"
vi: וייטנאמי
id: אינדונזית
el: יווני
he: עברית
ar: ערבית
af: אפריקנית
da: דנית
fi: פינית
uk: אוקראינית
ms: מלאית
'no': נורווגית
ja: יפנית
fa: פרסית
th: תאילנדי
ro: רומנית
pt: "(פורטוגזית (פורטוגל"
pt-BR: "(פורטוגזית (ברזיל"
bg: בולגרית
ko: קוריאנית
tr: טורקי
hr: קרואטית
mk: מקדונית
sr: סרבית
be: בלארוסית
machines:
GPS_CONTROL_PANEL:
title: לוח בקרה - ג'י פי אס
transmitters: סקירה משדר
waypoints: סקירת נקודות דרך
INDUSTRIAL_MINER:
no-fuel: &c לכורה התעשייתית שלך אזל הדלק! שים את הדלק שלך בתיבה מעל.
piston-facing: "&cהכורה התעשייתי שלך דורש בוכנות מופנות כלפי מעלה!"
piston-space: "&cלשתי הבוכנות צריך להיות בלוק מעליהן!"
destroyed: "&cנראה שכורה התעשייתי שלך הושמד."
already-running: "&cהכורה התעשייתית הזה עדיין פועל."
full-chest: "&cהתיבה של הכורה התעשייתי שלך מלאה!"
no-permission: נראה שאין לך אישור להפעיל כאן כורה תעשייתי!&4
finished: "&eהכורה התעשייתי שלך סיים את !%ores% ore(s)!עבודתו! הוא חצב"
pattern-not-found: "&e סליחה לא יכולתי \n .לזהות את המתכון \nאנא הכנס את \nהפריטים
בדפוס הנכון לתוך המתקן "
unknown-material: "&e סליחה, לא יכולתי לזהות את הפריט במתקן שלי. אנא הכנס משהו שאני
יודע."
wrong-item: "&e סליחה, לא יכולתי לזהות את הפריט שאתה לחצת עליו ימני. בדוק את המתכונים
וראה באילו פריטים אתה יכול להשתמש."
full-inventory: "&e סליחה המלאי שלי יותר מדיי מלא!"
in-use: "&c המלאי של בלוק זה נפתח כרגע על ידי שחקן אחר."
ignition-chamber-no-flint: "&c תא הצתה חסר צור ופלדה."
ANCIENT_ALTAR:
not-enough-pedestals: "&4 המזבח אני מוקף בכמות המתאימה של כינים &c(%pedestals% /\n8( "
unknown-catalyst: "&4 זרז לא ידוע! &c השתמש במתכון הנכון במקום זאת!"
unknown-recipe: "&4 מתכון לא ידוע! &cהשתמש במתכון הנכון במקום!"
ANCIENT_PEDESTAL:
obstructed: |-
&4הכינים נחסמו!
&cהסר כל דבר מעל הכינים!
HOLOGRAM_PROJECTOR:
enter-text: "&7אנא הכנס את טקסט ההולוגרמה לצ'ט שלך. &r( קודי צבע נתמכים!)"
inventory-title: עורך הולוגרמה
ELEVATOR:
no-destinations: "&4לא נמצאו יעדים "
pick-a-floor: "&3- בחר קומה -"
current-floor: "&e אתה נמצא כרגע על קומה:"
click-to-teleport: "&eלחץ &7 כדי להשתגר לקומה:"
enter-name: "&7 אנא הכנס את שם הרצפה הרצוי לצ'אט שלך. &r (קודי צבע נתמכים!)"
named: "&2 בהצלחה נקבע שם קומה זאת: &f%floor%"
TELEPORTER:
teleporting: "&3משתגר...."
teleported: "&3שוגר!"
cancelled: "&4 השתגרות בוטלה!"
invulnerability: "&b&l ניתן לך 30 שניות של פגיעות!"
gui:
title: נקודות הדרך שלך
tooltip: לחץ לשיגור
time: זמן משוער
CARGO_NODES:
must-be-placed: "&4חייב להיות מונח על תיבה או מכונה!"
brewing_stand:
not-working: "&4אתה לא יכול לשים חפצי סליים פאן במבשלה!"
villagers:
no-trading: "&4אתה לא יכול להחליף עם ויליג'רים חפצי סליים פאן!"
cartography_table:
not-working: "&4אתה לא יכול להשתמש בחפצי סליים פאן בשולחן קרטוגרפיה!"
cauldron:
no-discoloring: "&4אתה לא יכול לשנות את הצבע של שריון סליים פאן"
android:
scripts:
too-long: "&c!התסריט ארוך מכדי לערוך"
already-uploaded: "&4 התסריט הזה כבר הועלה."
instructions:
START: "&2 התחל תסריט"
REPEAT: "&9תסריט חוזר"
WAIT: "&e חכה0.5ש"
GO_FORWARD: "&7 זוז קדימה"
GO_UP: "&7 זוז כלפי מעלה "
GO_DOWN: "&7זוז כלפי מטה "
TURN_LEFT: "&7פנה שמאלה "
TURN_RIGHT: "&7פנה ימינה "
DIG_UP: "&bחפור כלפי מעלה "
DIG_FORWARD: "&bחפור קדימה "
DIG_DOWN: "&bחפור מטה "
MOVE_AND_DIG_UP: "&b זוז ותחפור כלפי מעלה "
MOVE_AND_DIG_FORWARD: "&b זוז ותחפור קדימה "
MOVE_AND_DIG_DOWN: "&bזוז ותחפור כלפי מטה"
ATTACK_MOBS_ANIMALS: "&4תקוף&c(חיות עוינות ובעלי חיים)"
ATTACK_MOBS: "&4תקוף&c(חיות עוינות)"
ATTACK_ANIMALS: "&4תקוף &c(בעלי חיים)"
ATTACK_ANIMALS_ADULT: "&4תקוף &c(בעלי חיים &7[בוגרים]&c)"
CHOP_TREE: "&c קצוץ ושתול מחדש"
CATCH_FISH: "&bתפוס דג"
FARM_FORWARD: "&bקצור ושתול מחדש "
FARM_DOWN: "&bקצור ובשתול מחדש &7(מתחת לבלוק)"
FARM_EXOTIC_FORWARD: "&bהתקדם קצור ושתול מחדש"
FARM_EXOTIC_DOWN: "&bהתקדם קצור ושתול מחדש &7 (מתחת לבלוק"
INTERFACE_ITEMS: "&9 דחוף את המלאי אל ממשק הפנים"
INTERFACE_FUEL: "&c משוך דלק ממשק הפנים"
enter-name:
-
- "&e אנא כתוב שם לתסריט שלך"
uploaded:
- "&b טוען..."
- "&aהתסריט שלך ניטען בהצלחה!"
rating:
own: "&4אתה לא יכול לדרג את התסריט שלך עצמך!"
already: "&4אתה כבר השארת דירוג לתסריט זה!"
editor: עורך תסריט
started: "&7 האנדרואיד שלך ממשיך את תסריט"
stopped: "&7 האנדרואיד שלך \nעצר את התסריט "
backpack:
already-open: "&cסליחה התרמיל הזה פתוח במקום אחר "
no-stack: "&cאתה לא יכול לערום תרמילים"
inventory:
no-access: "&4אין לך גישה לבלוק הזה"

View File

@ -134,6 +134,7 @@ messages:
wizard: "&a&oA talizmánod erősítette a Szerencse varázslatot, de néhány egyéb
varázslatot gyengített"
caveman: "&a&oA talizmánod adott Sietség effektet"
wise: "&a&oA talizmánod megduplázta a dobott tapasztalat pontokat"
soulbound-rune:
fail: "&cEgyszerre csak egy tárgyat köthetsz a lelkedhez."
success: "&aSikeresen hozzákötötted ezt a tárgyat a lelkedhez! Megmarad, amikor
@ -313,6 +314,7 @@ android:
own: "&4Nem értékelheted a saját szkriptedet!"
already: "&4Ezt a szkriptet már értékelted!"
editor: Szkript szerkesztő
too-long: "&cA szkript túl hosszú a szerkesztéshez!"
languages:
default: Szerver-alapértelmezett
en: Angol

View File

@ -133,6 +133,7 @@ messages:
whirlwind: "&a&oタリスマンが飛び道具から身を護った"
wizard: "&a&oタリスマンが高レベルの幸運を付与したが、他のエンチャントレベルは下がってしまった"
caveman: "&a&oタリスマンが採掘速度を上昇させた"
wise: "&a&oタリスマンが経験値のドロップを倍にした"
soulbound-rune:
fail: "&c一度に複数アイテムとのバインディングはできません"
success: "&aアイテムとのバインディングが確立したリスポーン時に当該アイテムは手繰り寄せられます"
@ -318,7 +319,6 @@ languages:
zh-CN: 中国語(中国)
el: ギリシャ語
he: ヘブライ語
pt: ポルトガル語(ポルトガル)
pt-BR: ポルトガル語(ブラジル)
ar: アラビア語
af: アフリカーンス語
@ -331,6 +331,7 @@ languages:
fa: ペルシア語
th: タイ語
ro: ルーマニア語
pt: ポルトガル語(ポルトガル)
bg: ブルガリア語
ko: 韓国語
tr: トルコ語

View File

@ -133,6 +133,7 @@ messages:
whirlwind: "&a&o你的护身符反弹了所有射向你的投掷物"
wizard: "&a&o你的护身符随机提高了一个附魔的等级, 但其他的附魔等级将会下降"
caveman: "&a&o你的护身符给予了你急迫效果"
wise: "&a&o你的护身符让掉落的经验翻倍了"
soulbound-rune:
fail: "&c灵魂一次只能绑定一个物品."
success: "&a物品绑定成功! 在你死亡后此物品将不会掉落."
@ -296,6 +297,7 @@ android:
own: "&4你不能评价你自己的脚本!"
already: "&4你已经给这个脚本留下评价了!"
editor: 脚本编辑器
too-long: "&c脚本长度已达上限, 你不能再编辑了!"
languages:
default: 服务器默认语言
en: 英语

View File

@ -296,6 +296,7 @@ android:
own: "&4你不能評價自己的程式"
already: "&4你已經評價過此程式"
editor: 程式編輯器
too-long: "&c程式太長了無法編輯"
languages:
default: 預設
en: 英語
@ -318,6 +319,7 @@ languages:
el: 希臘語
he: 希伯來語
pt: 葡萄牙文(葡萄牙)
pt-BR: 葡萄牙文(巴西)
ar: 阿拉伯文
af: 南非語
da: 丹麥文
@ -329,7 +331,6 @@ languages:
fa: 波斯語
th: 泰語
ro: 羅馬尼亞語
pt-BR: 葡萄牙文(巴西)
bg: 保加利亞語
ko: 韓語
tr: 土耳其

View File

@ -248,3 +248,4 @@ slimefun:
elytra_cap: Harter Aufprall
energy_connectors: Kupferkabel
bee_armor: Bienen-Rüstung
wise_talisman: Stein der Weisen

View File

@ -62,6 +62,7 @@ slimefun:
angel_talisman: Talisman of the Angel
fire_talisman: Talisman of the Firefighter
caveman_talisman: Talisman of the Caveman
wise_talisman: Talisman of the Wise
lava_crystal: Firey Situation
magician_talisman: Talisman of the Magician
traveller_talisman: Talisman of the Traveller
@ -247,3 +248,4 @@ slimefun:
villager_rune: Reset Villager Trades
elytra_cap: Crash Gear
bee_armor: Bee Armor
book_binder: Enchantment Book Binding

View File

@ -248,3 +248,4 @@ slimefun:
elytra_cap: Equipo de choque
energy_connectors: Conexiones Instaladas
bee_armor: Armadura de Abeja
wise_talisman: Talismán del Sabio

View File

@ -248,3 +248,4 @@ slimefun:
elytra_cap: Ütközésvédelem
energy_connectors: Vezetékes csatlakozás
bee_armor: Méhpáncél
wise_talisman: A Bölcs talizmánja

View File

@ -248,3 +248,5 @@ slimefun:
elytra_cap: 衝撃緩和装備
energy_connectors: 有線接続
bee_armor: 蜂アーマー
wise_talisman: 知恵のタリスマン
book_binder: エンチャントの本の製本

View File

@ -248,3 +248,4 @@ slimefun:
elytra_cap: 无伤落地
energy_connectors: 有线连接
bee_armor: 蜜蜂服
wise_talisman: 智者的护身符

View File

@ -13,7 +13,6 @@ api-version: '1.14'
# (Soft) dependencies of Slimefun, we hook into these plugins.
softdepend:
- CS-CoreLib
- PlaceholderAPI
- WorldEdit
- ClearLag

View File

@ -12,10 +12,12 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
class TestPluginClass {
private static SlimefunPlugin plugin;
@BeforeAll
public static void load() {
MockBukkit.mock();
MockBukkit.load(SlimefunPlugin.class);
plugin = MockBukkit.load(SlimefunPlugin.class);
}
@AfterAll
@ -28,6 +30,7 @@ class TestPluginClass {
void verifyTestEnvironment() {
MinecraftVersion version = SlimefunPlugin.getMinecraftVersion();
Assertions.assertTrue(plugin.isUnitTest());
Assertions.assertEquals(MinecraftVersion.UNIT_TEST, version);
Assertions.assertEquals("Unit Test Environment", version.getName());
}

View File

@ -0,0 +1,47 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.items;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import be.seeseemelk.mockbukkit.MockBukkit;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactivity;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.RadioactiveItem;
import io.github.thebusybiscuit.slimefun4.testing.TestUtilities;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
class TestRadioactiveItem {
private static SlimefunPlugin plugin;
@BeforeAll
public static void load() {
MockBukkit.mock();
plugin = MockBukkit.load(SlimefunPlugin.class);
}
@AfterAll
public static void unload() {
MockBukkit.unmock();
}
@ParameterizedTest
@EnumSource(value = Radioactivity.class)
@DisplayName("Test radioactive items being radioactive")
void testWikiPages(Radioactivity radioactivity) {
Category category = TestUtilities.getCategory(plugin, "radioactivity_test");
SlimefunItemStack stack = new SlimefunItemStack("RADIOACTIVE_" + radioactivity.name(), Material.EMERALD, "&4Radioactive!!!", "Imagine dragons");
RadioactiveItem item = new RadioactiveItem(category, radioactivity, stack, RecipeType.NULL, new ItemStack[9]);
Assertions.assertEquals(radioactivity, item.getRadioactivity());
}
}

View File

@ -17,9 +17,15 @@ import org.junit.jupiter.params.ParameterizedTest;
import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.cargo.CargoInputNode;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CargoNodeListener;
import io.github.thebusybiscuit.slimefun4.testing.TestUtilities;
import io.github.thebusybiscuit.slimefun4.testing.annotations.SlimefunItemsSource;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
class TestCargoNodeListener {
@ -53,20 +59,42 @@ class TestCargoNodeListener {
Assertions.assertFalse(event.isCancelled());
}
@ParameterizedTest
@Test
@DisplayName("Test placing Cargo nodes on invalid sides of blocks")
@SlimefunItemsSource(items = { "CARGO_INPUT_NODE", "CARGO_OUTPUT_NODE", "CARGO_OUTPUT_NODE_2" })
void testInvalidPlacement(ItemStack item) {
SlimefunPlugin.getRegistry().setBackwardsCompatible(true);
void testInvalidSidePlacement() {
Player player = server.addPlayer();
Location l = new Location(player.getWorld(), 190, 50, 400);
Block b = l.getBlock();
Block against = b.getRelative(BlockFace.DOWN);
Category category = TestUtilities.getCategory(plugin, "cargo_test");
SlimefunItemStack item = new SlimefunItemStack("MOCK_CARGO_NODE", new CustomItem(Material.PLAYER_HEAD, "&4Cargo node!"));
CargoInputNode node = new CargoInputNode(category, item, RecipeType.NULL, new ItemStack[9], null);
node.register(plugin);
BlockPlaceEvent event = new BlockPlaceEvent(b, b.getState(), against, item, player, true, EquipmentSlot.HAND);
listener.onCargoNodePlace(event);
Assertions.assertTrue(event.isCancelled());
SlimefunPlugin.getRegistry().setBackwardsCompatible(false);
}
@Test
@DisplayName("Test placing Cargo nodes on grass")
void testGrassPlacement() {
Player player = server.addPlayer();
Location l = new Location(player.getWorld(), 300, 25, 1200);
Block b = l.getBlock();
b.setType(Material.GRASS);
Category category = TestUtilities.getCategory(plugin, "cargo_test");
SlimefunItemStack item = new SlimefunItemStack("MOCK_CARGO_NODE_2", new CustomItem(Material.PLAYER_HEAD, "&4Cargo node!"));
CargoInputNode node = new CargoInputNode(category, item, RecipeType.NULL, new ItemStack[9], null);
node.register(plugin);
BlockPlaceEvent event = new BlockPlaceEvent(b, b.getState(), b, item, player, true, EquipmentSlot.HAND);
listener.onCargoNodePlace(event);
Assertions.assertTrue(event.isCancelled());
}
@Test

View File

@ -20,11 +20,11 @@ import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.ItemEntityMock;
import be.seeseemelk.mockbukkit.inventory.HopperInventoryMock;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.CSCoreLibPlugin.cscorelib2.item.CustomItem;
class TestItemPickupListener {

View File

@ -6,6 +6,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import be.seeseemelk.mockbukkit.MockBukkit;
@ -15,7 +16,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.testing.TestUtilities;
public class TestPlayerProfileListener {
class TestPlayerProfileListener {
private static SlimefunPlugin plugin;
private static PlayerProfileListener listener;
@ -34,7 +35,8 @@ public class TestPlayerProfileListener {
}
@Test
public void testPlayerLeave() throws InterruptedException {
@DisplayName("Test PlayerProfile being marked for deletion when Player leaves")
void testPlayerLeave() throws InterruptedException {
Player player = server.addPlayer();
PlayerProfile profile = TestUtilities.awaitProfile(player);
PlayerQuitEvent event = new PlayerQuitEvent(player, "bye");
@ -44,7 +46,8 @@ public class TestPlayerProfileListener {
}
@Test
public void testUnloadedPlayerLeave() {
@DisplayName("Test PlayerProfile being unloaded when Player leaves")
void testUnloadedPlayerLeave() {
Player player = server.addPlayer();
PlayerQuitEvent event = new PlayerQuitEvent(player, "bye");
listener.onDisconnect(event);
@ -53,7 +56,8 @@ public class TestPlayerProfileListener {
}
@Test
public void testPlayerKick() throws InterruptedException {
@DisplayName("Test PlayerProfile being marked for deletion when Player is kicked")
void testPlayerKick() throws InterruptedException {
Player player = server.addPlayer();
PlayerProfile profile = TestUtilities.awaitProfile(player);
PlayerKickEvent event = new PlayerKickEvent(player, "You're not welcome anymore", "bye");
@ -63,7 +67,8 @@ public class TestPlayerProfileListener {
}
@Test
public void testUnloadedPlayerKick() {
@DisplayName("Test PlayerProfile being unloaded when Player is kicked")
void testUnloadedPlayerKick() {
Player player = server.addPlayer();
PlayerKickEvent event = new PlayerKickEvent(player, "You're not welcome anymore", "bye");
listener.onKick(event);

View File

@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.networks;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -172,4 +173,18 @@ class TestNetworkManager {
Assertions.assertEquals(3, network.getSize());
}
@Test
@DisplayName("Test empty network list for null locations")
void testNullLocations() {
NetworkManager manager = new NetworkManager(10, false, false);
Optional<Network> optional = manager.getNetworkFromLocation(null, Network.class);
Assertions.assertNotNull(optional);
Assertions.assertFalse(optional.isPresent());
List<Network> list = manager.getNetworksFromLocation(null, Network.class);
Assertions.assertNotNull(list);
Assertions.assertTrue(list.isEmpty());
}
}

View File

@ -0,0 +1,13 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.profiles;
import org.bukkit.OfflinePlayer;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
class MockProfile extends PlayerProfile {
MockProfile(OfflinePlayer p) {
super(p);
}
}

View File

@ -0,0 +1,93 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.profiles;
import org.bukkit.OfflinePlayer;
import org.bukkit.event.Event;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.OfflinePlayerMock;
import io.github.thebusybiscuit.slimefun4.api.events.AsyncProfileLoadEvent;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.testing.TestUtilities;
class TestAsyncProfileLoadEvent {
private static ServerMock server;
@BeforeAll
public static void load() {
server = MockBukkit.mock();
MockBukkit.load(SlimefunPlugin.class);
}
@AfterAll
public static void unload() {
MockBukkit.unmock();
}
@Test
@DisplayName("Test AsyncProfileLoadEvent being thrown")
void testEventFired() throws InterruptedException {
server.getPluginManager().clearEvents();
OfflinePlayer player = new OfflinePlayerMock("EventFire");
TestUtilities.awaitProfile(player);
server.getPluginManager().assertEventFired(AsyncProfileLoadEvent.class, Event::isAsynchronous);
}
@Test
@DisplayName("Test AsyncProfileLoadEvent#getProfile()")
void testEventGetter() throws InterruptedException {
server.getPluginManager().clearEvents();
OfflinePlayer player = new OfflinePlayerMock("GetProfile");
PlayerProfile profile = TestUtilities.awaitProfile(player);
server.getPluginManager().assertEventFired(AsyncProfileLoadEvent.class, e -> e.getProfile().equals(profile));
}
@Test
@DisplayName("Test Profile Injection")
void testProfileInjection() throws InterruptedException {
OfflinePlayer player = new OfflinePlayerMock("SomePlayer");
PlayerProfile profile = TestUtilities.awaitProfile(player);
PlayerProfile mockProfile = new MockProfile(player);
AsyncProfileLoadEvent event = new AsyncProfileLoadEvent(profile);
Assertions.assertEquals(player.getUniqueId(), event.getPlayerUUID());
Assertions.assertEquals(profile, event.getProfile());
Assertions.assertFalse(event.getProfile() instanceof MockProfile);
event.setProfile(mockProfile);
Assertions.assertEquals(mockProfile, event.getProfile());
Assertions.assertTrue(event.getProfile() instanceof MockProfile);
}
@Test
@DisplayName("Test Profile Mismatch")
void testProfileMismatch() throws InterruptedException {
OfflinePlayer player = new OfflinePlayerMock("ValidProfile");
OfflinePlayer player2 = new OfflinePlayerMock("UnrelatedProfile");
PlayerProfile profile = TestUtilities.awaitProfile(player);
PlayerProfile profile2 = TestUtilities.awaitProfile(player2);
AsyncProfileLoadEvent event = new AsyncProfileLoadEvent(profile);
// Profile is null
Assertions.assertThrows(IllegalArgumentException.class, () -> event.setProfile(null));
// UUID mismatch
Assertions.assertThrows(IllegalArgumentException.class, () -> event.setProfile(profile2));
}
}

View File

@ -10,6 +10,7 @@ import org.bukkit.entity.Player;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@ -53,6 +54,7 @@ class TestResearchUnlocking {
@ParameterizedTest
@DisplayName("Test Unlocking Researches")
@Disabled(value = "Blocked by a concurrency issue in MockBukkit")
@ValueSource(booleans = { true, false })
void testUnlock(boolean instant) throws InterruptedException {
SlimefunPlugin.getRegistry().setResearchingEnabled(true);

View File

@ -2,6 +2,8 @@ package io.github.thebusybiscuit.slimefun4.testing.tests.tags;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
@ -101,6 +103,19 @@ class TestSlimefunTags {
}
}
@Test
@DisplayName("Test SlimefunTag#stream()")
void testStream() throws TagMisconfigurationException {
SlimefunTag.reloadAll();
for (SlimefunTag tag : SlimefunTag.values()) {
Set<Material> values = tag.getValues();
Stream<Material> stream = tag.stream();
Assertions.assertEquals(values, stream.collect(Collectors.toSet()));
}
}
@Test
@DisplayName("Test static SlimefunTag accessors")
void testGetTag() {

View File

@ -0,0 +1,91 @@
package io.github.thebusybiscuit.slimefun4.testing.tests.utils;
import java.util.Arrays;
import java.util.Collections;
import be.seeseemelk.mockbukkit.MockBukkit;
import io.github.thebusybiscuit.cscorelib2.data.PersistentDataAPI;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.ChargeUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class TestChargeUtils {
@BeforeAll
public static void load() {
MockBukkit.mock();
MockBukkit.load(SlimefunPlugin.class);
}
@AfterAll
public static void unload() {
MockBukkit.unmock();
}
@Test
@DisplayName("Test setting charge")
void testSetCharge() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = item.getItemMeta();
// Make sure the lore is set
ChargeUtils.setCharge(meta, 1, 10);
Assertions.assertTrue(meta.hasLore());
Assertions.assertEquals(1, meta.getLore().size());
// Make sure the lore is correct
ChargeUtils.setCharge(meta, 10.1f, 100.5f);
Assertions.assertEquals("&8\u21E8 &e\u26A1 &710.1 / 100.5 J".replace('&', ChatColor.COLOR_CHAR), meta.getLore().get(0));
// Make sure the persistent data was set
Assertions.assertEquals(10.1, PersistentDataAPI.getFloat(meta, SlimefunPlugin.getRegistry().getItemChargeDataKey()), 0.001);
// Test exceptions
Assertions.assertThrows(IllegalArgumentException.class, () -> ChargeUtils.setCharge(null, 1, 1));
Assertions.assertThrows(IllegalArgumentException.class, () -> ChargeUtils.setCharge(meta, -1, 10));
Assertions.assertThrows(IllegalArgumentException.class, () -> ChargeUtils.setCharge(meta, 100, 10));
Assertions.assertThrows(IllegalArgumentException.class, () -> ChargeUtils.setCharge(meta, 10, -10));
}
@Test
@DisplayName("Test getting charge")
void testGetCharge() {
// Test with persistent data
ItemStack itemWithData = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta metaWithData = itemWithData.getItemMeta();
PersistentDataAPI.setFloat(metaWithData, SlimefunPlugin.getRegistry().getItemChargeDataKey(), 10.5f);
Assertions.assertEquals(10.5f, ChargeUtils.getCharge(metaWithData), 0.001);
// Test with lore
ItemStack itemWithLore = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta metaWithLore = itemWithLore.getItemMeta();
metaWithLore.setLore(Collections.singletonList("&8\u21E8 &e\u26A1 &710.5 / 100.5 J".replace('&', ChatColor.COLOR_CHAR)));
Assertions.assertEquals(10.5, ChargeUtils.getCharge(metaWithLore), 0.001);
Assertions.assertTrue(PersistentDataAPI.hasFloat(metaWithLore, SlimefunPlugin.getRegistry().getItemChargeDataKey()));
// Test no data and empty lore
ItemStack itemWithEmptyLore = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta metaWithEmptyLore = itemWithEmptyLore.getItemMeta();
metaWithEmptyLore.setLore(Collections.emptyList());
Assertions.assertEquals(0, ChargeUtils.getCharge(metaWithEmptyLore));
// Test no data and no lore
ItemStack itemWithNoDataOrLore = new ItemStack(Material.DIAMOND_SWORD);
Assertions.assertEquals(0, ChargeUtils.getCharge(itemWithNoDataOrLore.getItemMeta()));
// Test exceptions
Assertions.assertThrows(IllegalArgumentException.class, () -> ChargeUtils.getCharge(null));
}
}