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