diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d90b49af..92db11362 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,8 @@
* GEO Miner is now 2 seconds faster
* All Generators will now stop consuming fuel if no energy is needed
* /sf teleporter will now open your own Teleporter Menu if you specify no Player
+* Added counter-measures against Players who design Cargo networks in a way that intentionally lags out servers
+* API requests to Mojang are now spread across a longer time period to prevent rate-limits
#### Fixes
* Fixed error message when clicking empty slots in the Slimefun Guide
diff --git a/pom.xml b/pom.xml
index ccea26380..f9ba0cc44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -216,7 +216,7 @@
com.github.thebusybiscuit
CS-CoreLib2
- 0.14
+ 0.15
compile
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/SlimefunAddon.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/SlimefunAddon.java
index 115c6a3c7..8a3d69488 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/SlimefunAddon.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/SlimefunAddon.java
@@ -1,11 +1,17 @@
package io.github.thebusybiscuit.slimefun4.api;
+import java.util.Collection;
+import java.util.Locale;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import org.bukkit.NamespacedKey;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
@@ -89,4 +95,15 @@ public interface SlimefunAddon {
return description.getDepend().contains(dependency) || description.getSoftDepend().contains(dependency);
}
+ /**
+ * This returns a {@link Collection} holding every {@link Category} that can be directly
+ * linked to this {@link SlimefunAddon} based on its {@link NamespacedKey}.
+ *
+ * @return A {@link Collection} of every {@link Category} from this addon
+ */
+ default Collection getCategories() {
+ String namespace = getJavaPlugin().getName().toLowerCase(Locale.ROOT);
+ return SlimefunPlugin.getRegistry().getCategories().stream().filter(cat -> cat.getKey().getNamespace().equals(namespace)).collect(Collectors.toList());
+ }
+
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
index 7a004f3a6..b1cdfe962 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
@@ -73,7 +73,7 @@ public final class SlimefunGuideSettings {
return false;
});
- menu.addItem(2, new CustomItem(SkullItem.fromBase64("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTk1MmQyYjNmMzUxYTZiMDQ4N2NjNTlkYjMxYmY1ZjI2NDExMzNlNWJhMDAwNmIxODU3NmU5OTZhMDI5M2U1MiJ9fX0="), "&c" + SlimefunPlugin.getLocal().getMessage(p, "guide.title.credits"), "", "&7Contributors: &e" + SlimefunPlugin.getGitHubService().getContributors().size(), "", "&7Slimefun is an open-source project", "&7and maintained by a large community.", "&7Here you can find them all", "", "&7\u21E8 &eClick to see our contributors"), (pl, slot, action, item) -> {
+ menu.addItem(2, new CustomItem(SkullItem.fromHash("e952d2b3f351a6b0487cc59db31bf5f2641133e5ba0006b18576e996a0293e52"), "&c" + SlimefunPlugin.getLocal().getMessage(p, "guide.title.credits"), "", "&7Contributors: &e" + SlimefunPlugin.getGitHubService().getContributors().size(), "", "&7Slimefun is an open-source project", "&7and maintained by a large community of people.", "&7Here you can see who helped shape the project.", "", "&7\u21E8 &eClick to see our contributors"), (pl, slot, action, item) -> {
ContributorsMenu.open(pl, 0);
return false;
});
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/BlockUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/BlockUtils.java
deleted file mode 100644
index 402bfc741..000000000
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/BlockUtils.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.github.thebusybiscuit.slimefun4.core.networks.cargo;
-
-import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
-import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import org.bukkit.Material;
-import org.bukkit.block.Block;
-
-final class BlockUtils {
-
- private BlockUtils() {}
-
- public static boolean hasInventory(Block block) {
- if (block == null) return false;
-
- Material type = block.getType();
- switch (type) {
- case CHEST:
- case TRAPPED_CHEST:
- case FURNACE:
- case DISPENSER:
- case DROPPER:
- case HOPPER:
- case BREWING_STAND:
- return true;
- default:
- if (type.name().endsWith("SHULKER_BOX")) return true;
-
- return (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14) &&
- (type == Material.BARREL || type == Material.BLAST_FURNACE || type == Material.LECTERN || type == Material.SMOKER));
- }
- }
-}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java
index 3a4e4c65d..e9085e4df 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java
@@ -27,7 +27,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
public class CargoNet extends ChestTerminalNetwork {
private static final int RANGE = 5;
- private static final int TICK_DELAY = SlimefunPlugin.getCfg().getInt("URID.cargo-network-tick-delay");
+ private static final int TICK_DELAY = SlimefunPlugin.getCfg().getInt("networks.cargo-ticker-delay");
private final Set inputNodes = new HashSet<>();
private final Set outputNodes = new HashSet<>();
@@ -183,7 +183,9 @@ public class CargoNet extends ChestTerminalNetwork {
tickDelayThreshold++;
return;
}
- tickDelayThreshold = 0; // reset, so we can start skipping again
+
+ // Reset the internal threshold, so we can start skipping again
+ tickDelayThreshold = 0;
Map inputs = new HashMap<>();
Set providers = new HashSet<>();
@@ -208,6 +210,7 @@ public class CargoNet extends ChestTerminalNetwork {
// (Apart from ChestTerminal Buses)
for (Map.Entry entry : inputs.entrySet()) {
Location input = entry.getKey();
+
Optional attachedBlock = getAttachedBlock(input.getBlock());
if (!attachedBlock.isPresent()) {
continue;
@@ -231,11 +234,14 @@ public class CargoNet extends ChestTerminalNetwork {
if (roundrobin) {
int index = roundRobin.getOrDefault(input, 0);
+
if (index < outputlist.size()) {
+
for (int i = 0; i < index; i++) {
Location temp = outputlist.remove(0);
outputlist.add(temp);
}
+
index++;
}
else {
@@ -260,7 +266,7 @@ public class CargoNet extends ChestTerminalNetwork {
if (menu != null) {
menu.replaceExistingItem(previousSlot, stack);
}
- else if (BlockUtils.hasInventory(inputTarget)) {
+ else if (CargoUtils.hasInventory(inputTarget)) {
BlockState state = inputTarget.getState();
if (state instanceof InventoryHolder) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java
index dac7740be..2c757de7a 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java
@@ -12,9 +12,11 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
+import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
@@ -27,11 +29,52 @@ final class CargoUtils {
private CargoUtils() {}
+ public static boolean hasInventory(Block block) {
+ if (block == null) {
+ return false;
+ }
+
+ Material type = block.getType();
+
+ switch (type) {
+ case CHEST:
+ case TRAPPED_CHEST:
+ case FURNACE:
+ case DISPENSER:
+ case DROPPER:
+ case HOPPER:
+ case BREWING_STAND:
+ case SHULKER_BOX:
+ return true;
+ default:
+ break;
+ }
+
+ if (type.name().endsWith("_SHULKER_BOX")) {
+ return true;
+ }
+
+ if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
+ switch (type) {
+ case BARREL:
+ case BLAST_FURNACE:
+ case SMOKER:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+ }
+
public static ItemStack withdraw(Block node, Block target, ItemStack template) {
DirtyChestMenu menu = getChestMenu(target);
+
if (menu == null) {
- if (BlockUtils.hasInventory(target)) {
+ if (hasInventory(target)) {
BlockState state = target.getState();
+
if (state instanceof InventoryHolder) {
return withdrawFromVanillaInventory(node, template, ((InventoryHolder) state).getInventory());
}
@@ -106,7 +149,7 @@ final class CargoUtils {
}
}
}
- else if (BlockUtils.hasInventory(target)) {
+ else if (hasInventory(target)) {
BlockState state = target.getState();
if (state instanceof InventoryHolder) {
@@ -141,13 +184,16 @@ final class CargoUtils {
if (!matchesFilter(node, stack, index)) return stack;
DirtyChestMenu menu = getChestMenu(target);
+
if (menu == null) {
- if (BlockUtils.hasInventory(target)) {
+ if (hasInventory(target)) {
BlockState state = target.getState();
+
if (state instanceof InventoryHolder) {
return insertIntoVanillaInventory(stack, ((InventoryHolder) state).getInventory());
}
}
+
return stack;
}
@@ -162,6 +208,7 @@ final class CargoUtils {
int maxStackSize = itemInSlot.getType().getMaxStackSize();
int currentAmount = itemInSlot.getAmount();
+
if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false) && currentAmount < maxStackSize) {
int amount = currentAmount + stack.getAmount();
@@ -183,7 +230,7 @@ final class CargoUtils {
private static ItemStack insertIntoVanillaInventory(ItemStack stack, Inventory inv) {
ItemStack[] contents = inv.getContents();
- int minSlot = 0;
+ int minSlot = 0;
int maxSlot = contents.length;
// Check if it is a normal furnace
@@ -198,17 +245,18 @@ final class CargoUtils {
}
}
else if (inv instanceof BrewerInventory) {
- // Check if it goes in the potion slot,
+
if (stack.getType() == Material.POTION || stack.getType() == Material.LINGERING_POTION || stack.getType() == Material.SPLASH_POTION) {
+ // Potions slot
maxSlot = 3;
- // The blaze powder slot,
}
else if (stack.getType() == Material.BLAZE_POWDER) {
+ // Blaze Powder slot
minSlot = 4;
maxSlot = 5;
}
else {
- // Or the input
+ // Input slot
minSlot = 3;
maxSlot = 4;
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java
index 4adadefaf..d03fd15ed 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java
@@ -254,7 +254,7 @@ abstract class ChestTerminalNetwork extends Network {
handleWithdraw(blockMenu, items, l);
}
}
- else if (BlockUtils.hasInventory(target)) {
+ else if (CargoUtils.hasInventory(target)) {
BlockState state = target.getState();
if (state instanceof InventoryHolder) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java
index a251e0f96..bca4fbe3e 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java
@@ -46,7 +46,7 @@ public class AutoSavingService {
* This method saves every {@link PlayerProfile} in memory and removes profiles
* that were markes for deletion.
*/
- public void saveAllPlayers() {
+ private void saveAllPlayers() {
Iterator iterator = PlayerProfile.iterator();
int players = 0;
@@ -71,7 +71,7 @@ public class AutoSavingService {
/**
* This method saves the data of every {@link Block} marked dirty by {@link BlockStorage}.
*/
- public void saveAllBlocks() {
+ private void saveAllBlocks() {
Set worlds = new HashSet<>();
for (World world : Bukkit.getWorlds()) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java
index e673fd2fb..96acc867e 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java
@@ -50,6 +50,7 @@ public class GitHubService {
private void addDefaultContributors() {
addContributor("Fuffles_", "&dArtist");
addContributor("IMS_Art", "&dArtist");
+ addContributor("nahkd123", "&aWinner of the 2020 Addon Jam");
new Translators(contributors);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
index 43c55f6cf..a487d931d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
@@ -5,9 +5,14 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
+import java.util.logging.Level;
+
+import org.bukkit.Bukkit;
import io.github.thebusybiscuit.cscorelib2.players.MinecraftAccount;
import io.github.thebusybiscuit.cscorelib2.players.MinecraftAccount.TooManyRequestsException;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
* This {@link GitHubTask} represents a {@link Runnable} that is run every X minutes.
@@ -21,6 +26,8 @@ import io.github.thebusybiscuit.cscorelib2.players.MinecraftAccount.TooManyReque
*/
class GitHubTask implements Runnable {
+ private static final int MAX_REQUESTS_PER_MINUTE = 12;
+
private final GitHubService gitHubService;
GitHubTask(GitHubService github) {
@@ -31,9 +38,14 @@ class GitHubTask implements Runnable {
public void run() {
gitHubService.getConnectors().forEach(GitHubConnector::pullFile);
+ grabTextures();
+ }
+
+ private void grabTextures() {
// Store all queried usernames to prevent 429 responses for pinging the
// same URL twice in one run.
Map skins = new HashMap<>();
+ int count = 0;
for (Contributor contributor : gitHubService.getContributors().values()) {
if (!contributor.hasTexture()) {
@@ -43,18 +55,33 @@ class GitHubTask implements Runnable {
}
else {
contributor.setTexture(grabTexture(skins, contributor.getMinecraftName()));
+ count++;
+
+ if (count >= MAX_REQUESTS_PER_MINUTE) {
+ break;
+ }
}
}
catch (IllegalArgumentException x) {
// There cannot be a texture found because it is not a valid MC username
contributor.setTexture(null);
}
- catch (Exception x) {
+ catch (IOException | TooManyRequestsException x) {
// Too many requests
- break;
+ Slimefun.getLogger().log(Level.WARNING, "Attempted to connect to mojang.com, got this response: {0}: {1}", new Object[] { x.getClass().getSimpleName(), x.getMessage() });
+ Slimefun.getLogger().log(Level.WARNING, "This usually means mojang.com is down or started to rate-limit this connection, this is not an error message!");
+
+ // Retry after 2 minutes
+ Bukkit.getScheduler().runTaskLaterAsynchronously(SlimefunPlugin.instance, this::grabTextures, 2 * 60 * 20L);
+ return;
}
}
}
+
+ if (count >= MAX_REQUESTS_PER_MINUTE) {
+ // Slow down API requests and wait a minute after more than x requests were made
+ Bukkit.getScheduler().runTaskLaterAsynchronously(SlimefunPlugin.instance, this::grabTextures, 60 * 20L);
+ }
}
private String grabTexture(Map skins, String username) throws TooManyRequestsException, IOException {
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java b/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
index c2cc7fa62..09f77283e 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
@@ -162,7 +162,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
// Setting up Networks
gpsNetwork = new GPSNetwork();
- networkManager = new NetworkManager(config.getInt("options.max-network-size"));
+ networkManager = new NetworkManager(config.getInt("networks.max-size"));
// Setting up bStats
metricsService.start();
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 910deb8e1..5e1505d8f 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -28,10 +28,13 @@ researches:
URID:
info-delay: 3000
- cargo-network-tick-delay: 0
custom-ticker-delay: 12
enable-tickers: true
+networks:
+ max-size: 200
+ cargo-ticker-delay: 0
+
items:
talismans: true
backpacks: true