aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/me/xmrvizzy
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/me/xmrvizzy')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java114
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java1
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java8
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java54
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java17
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java140
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java168
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java16
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java (renamed from src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java)36
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java92
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java54
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java93
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java8
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java10
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java96
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java170
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java1
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java1
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java8
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Http.java89
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java63
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java3
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java85
54 files changed, 1326 insertions, 291 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index 5c7f2a99..b28ad3d4 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -4,10 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.skyblock.*;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.TicTacToe;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.*;
import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
import me.xmrvizzy.skyblocker.skyblock.item.*;
@@ -15,6 +12,8 @@ import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNav;
import me.xmrvizzy.skyblocker.skyblock.rift.TheRift;
import me.xmrvizzy.skyblocker.skyblock.shortcut.Shortcuts;
+import me.xmrvizzy.skyblocker.skyblock.special.SpecialEffects;
+import me.xmrvizzy.skyblocker.skyblock.spidersden.Relics;
import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster;
import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
@@ -40,14 +39,11 @@ import java.nio.file.Path;
* this class.
*/
public class SkyblockerMod implements ClientModInitializer {
+ public static final String VERSION = FabricLoader.getInstance().getModContainer("skyblocker").get().getMetadata().getVersion().getFriendlyString();
public static final String NAMESPACE = "skyblocker";
public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve(NAMESPACE);
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static SkyblockerMod INSTANCE;
-
- @SuppressWarnings("deprecation")
- public final Scheduler scheduler = new Scheduler();
- public final MessageScheduler messageScheduler = new MessageScheduler();
public final ContainerSolverManager containerSolverManager = new ContainerSolverManager();
public final StatusBarTracker statusBarTracker = new StatusBarTracker();
@@ -79,6 +75,7 @@ public class SkyblockerMod implements ClientModInitializer {
ItemRegistry.init();
NEURepo.init();
FairySouls.init();
+ Relics.init();
BackpackPreview.init();
QuickNav.init();
DwarvenHud.init();
@@ -91,6 +88,7 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonMap.init();
DungeonSecrets.init();
DungeonBlaze.init();
+ DungeonChestProfit.init();
TheRift.init();
TitleContainer.init();
ScreenMaster.init();
@@ -101,15 +99,16 @@ public class SkyblockerMod implements ClientModInitializer {
CustomArmorTrims.init();
TicTacToe.init();
QuiverWarning.init();
+ SpecialEffects.init();
containerSolverManager.init();
statusBarTracker.init();
- scheduler.scheduleCyclic(Utils::update, 20);
- scheduler.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100);
- scheduler.scheduleCyclic(TicTacToe::tick, 4);
- scheduler.scheduleCyclic(LividColor::update, 10);
- scheduler.scheduleCyclic(BackpackPreview::tick, 50);
- scheduler.scheduleCyclic(DwarvenHud::update, 40);
- scheduler.scheduleCyclic(PlayerListMgr::updateList, 20);
+ Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20);
+ Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100);
+ Scheduler.INSTANCE.scheduleCyclic(TicTacToe::tick, 4);
+ Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10);
+ Scheduler.INSTANCE.scheduleCyclic(BackpackPreview::tick, 50);
+ Scheduler.INSTANCE.scheduleCyclic(DwarvenHud::update, 40);
+ Scheduler.INSTANCE.scheduleCyclic(PlayerListMgr::updateList, 20);
}
/**
@@ -119,7 +118,7 @@ public class SkyblockerMod implements ClientModInitializer {
* @param client the Minecraft client.
*/
public void tick(MinecraftClient client) {
- scheduler.tick();
- messageScheduler.tick();
+ Scheduler.INSTANCE.tick();
+ MessageScheduler.INSTANCE.tick();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java
index c6fc1924..8b96499d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java
@@ -26,7 +26,7 @@ public class MixinPlugin implements IMixinConfigPlugin {
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
//OptiFabric Compatibility
if (mixinClassName.endsWith("WorldRendererMixin") && OPTIFABRIC_LOADED) return false;
-
+
return true;
}
@@ -41,7 +41,7 @@ public class MixinPlugin implements IMixinConfigPlugin {
}
@Override
- public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
+ public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
//Do nothing
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index 127bc601..5848ed15 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -20,7 +20,9 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
+import org.eclipse.jgit.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
@@ -67,11 +69,19 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("button3")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button3 = new QuickNavItem(true, new ItemData("bone"), "\\(\\d+/\\d+\\) Pets", "/pets");
+ public QuickNavItem button3 = new QuickNavItem(true, new ItemData("bone"), "Pets(:? \\(\\d+\\/\\d+\\))?", "/pets");
+ /* REGEX Explanation
+ * "Pets" : simple match on letters
+ * "(?: \\(\\d+\\/\\d+\\))?" : optional match on the non-capturing group for the page in the format " ($number/$number)"
+ */
@ConfigEntry.Category("button4")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button4 = new QuickNavItem(true, new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]/2\\)", "/wardrobe");
+ public QuickNavItem button4 = new QuickNavItem(true, new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]\\/2\\)", "/wardrobe");
+ /* REGEX Explanation
+ * "Wardrobe" : simple match on letters
+ * " \\([12]\\/2\\)" : match on the page either " (1/2)" or " (2/2)"
+ */
@ConfigEntry.Category("button5")
@ConfigEntry.Gui.CollapsibleObject()
@@ -79,7 +89,12 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("button6")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage");
+ public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "(?:Rift )?Storage(?: \\([12]\\/2\\))?", "/storage");
+ /* REGEX Explanation
+ * "(?:Rift )?" : optional match on the non-capturing group "Rift "
+ * "Storage" : simple match on letters
+ * "(?: \\([12]\\/2\\))?" : optional match on the non-capturing group " (1/2)" or " (2/2)"
+ */
@ConfigEntry.Category("button7")
@ConfigEntry.Gui.CollapsibleObject()
@@ -145,7 +160,9 @@ public class SkyblockerConfig implements ConfigData {
public static class General {
public boolean acceptReparty = true;
public boolean backpackPreviewWithoutShift = false;
+ public boolean compactorDeletorPreview = true;
public boolean hideEmptyTooltips = true;
+ public boolean hideStatusEffectOverlay = false;
@ConfigEntry.Category("tabHud")
@ConfigEntry.Gui.CollapsibleObject()
@@ -185,11 +202,15 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("itemTooltip")
@ConfigEntry.Gui.CollapsibleObject()
public ItemTooltip itemTooltip = new ItemTooltip();
-
+
@ConfigEntry.Category("itemInfoDisplay")
@ConfigEntry.Gui.CollapsibleObject
public ItemInfoDisplay itemInfoDisplay = new ItemInfoDisplay();
+ @ConfigEntry.Category("specialEffects")
+ @ConfigEntry.Gui.CollapsibleObject
+ public SpecialEffects specialEffects = new SpecialEffects();
+
@ConfigEntry.Category("hitbox")
@ConfigEntry.Gui.CollapsibleObject()
public Hitbox hitbox = new Hitbox();
@@ -259,7 +280,6 @@ public class SkyblockerConfig implements ConfigData {
public BarPosition defenceBarPosition = BarPosition.LAYER1;
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
public BarPosition experienceBarPosition = BarPosition.LAYER1;
-
}
public enum BarPosition {
@@ -295,6 +315,9 @@ public class SkyblockerConfig implements ConfigData {
public static class FairySouls {
public boolean enableFairySoulsHelper = false;
+ public boolean highlightFoundSouls = true;
+ @ConfigEntry.Gui.Tooltip()
+ public boolean highlightOnlyNearbySouls = false;
}
public static class Shortcuts {
@@ -401,12 +424,17 @@ public class SkyblockerConfig implements ConfigData {
public boolean enableBazaarPrice = true;
public boolean enableMuseumDate = true;
}
-
+
public static class ItemInfoDisplay {
- @ConfigEntry.Gui.Tooltip
+ @ConfigEntry.Gui.Tooltip
public boolean attributeShardInfo = true;
}
+ public static class SpecialEffects {
+ @ConfigEntry.Gui.Tooltip
+ public boolean rareDungeonDropEffects = true;
+ }
+
public static class Locations {
@ConfigEntry.Category("barn")
@ConfigEntry.Gui.CollapsibleObject()
@@ -423,11 +451,17 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("rift")
@ConfigEntry.Gui.CollapsibleObject()
public Rift rift = new Rift();
+
+ @ConfigEntry.Category("spidersden")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public SpidersDen spidersDen = new SpidersDen();
}
public static class Dungeons {
@ConfigEntry.Gui.CollapsibleObject
public SecretWaypoints secretWaypoints = new SecretWaypoints();
+ @ConfigEntry.Gui.CollapsibleObject
+ public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit();
@ConfigEntry.Gui.Tooltip()
public boolean croesusHelper = true;
public boolean enableMap = true;
@@ -466,6 +500,61 @@ public class SkyblockerConfig implements ConfigData {
public boolean enableDefaultWaypoints = true;
}
+ public static class DungeonChestProfit {
+ @ConfigEntry.Gui.Tooltip
+ public boolean enableProfitCalculator = true;
+ @ConfigEntry.Gui.Tooltip
+ public boolean includeKismet = false;
+ @ConfigEntry.Gui.Tooltip
+ public boolean includeEssence = true;
+ @ConfigEntry.Gui.Tooltip
+ public int neutralThreshold = 1000;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
+ public FormattingOption neutralColor = FormattingOption.DARK_GRAY;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
+ public FormattingOption profitColor = FormattingOption.DARK_GREEN;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
+ public FormattingOption lossColor = FormattingOption.RED;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
+ @ConfigEntry.Gui.Tooltip
+ public FormattingOption incompleteColor = FormattingOption.BLUE;
+ }
+
+ public enum FormattingOption {
+ BLACK(Formatting.BLACK),
+ DARK_BLUE(Formatting.DARK_BLUE),
+ DARK_GREEN(Formatting.DARK_GREEN),
+ DARK_AQUA(Formatting.DARK_AQUA),
+ DARK_RED(Formatting.DARK_RED),
+ DARK_PURPLE(Formatting.DARK_PURPLE),
+ GOLD(Formatting.GOLD),
+ GRAY(Formatting.GRAY),
+ DARK_GRAY(Formatting.DARK_GRAY),
+ BLUE(Formatting.BLUE),
+ GREEN(Formatting.GREEN),
+ AQUA(Formatting.AQUA),
+ RED(Formatting.RED),
+ LIGHT_PURPLE(Formatting.LIGHT_PURPLE),
+ YELLOW(Formatting.YELLOW),
+ WHITE(Formatting.WHITE),
+ OBFUSCATED(Formatting.OBFUSCATED),
+ BOLD(Formatting.BOLD),
+ STRIKETHROUGH(Formatting.STRIKETHROUGH),
+ UNDERLINE(Formatting.UNDERLINE),
+ ITALIC(Formatting.ITALIC),
+ RESET(Formatting.RESET);
+ public final Formatting formatting;
+
+ FormattingOption(Formatting formatting) {
+ this.formatting = formatting;
+ }
+
+ @Override
+ public String toString() {
+ return StringUtils.capitalize(formatting.getName().replaceAll("_", " "));
+ }
+ }
+
public static class LividColor {
@ConfigEntry.Gui.Tooltip()
public boolean enableLividColor = true;
@@ -524,6 +613,17 @@ public class SkyblockerConfig implements ConfigData {
public int mcGrubberStacks = 0;
}
+ public static class SpidersDen {
+ @ConfigEntry.Category("relics")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public Relics relics = new Relics();
+ }
+
+ public static class Relics {
+ public boolean enableRelicsHelper = false;
+ public boolean highlightFoundRelics = true;
+ }
+
public static class Slayer {
@ConfigEntry.Category("vampire")
@ConfigEntry.Gui.CollapsibleObject()
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java
new file mode 100644
index 00000000..db517411
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen;
+
+@Mixin(AbstractInventoryScreen.class)
+public class AbstractInventoryScreenMixin {
+
+ @Inject(method = "drawStatusEffects", at = @At("HEAD"), cancellable = true)
+ private void skyblocker$dontDrawStatusEffects(CallbackInfo ci) {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hideStatusEffectOverlay) ci.cancel();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java
index 86c4e672..3eb13073 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java
@@ -6,7 +6,6 @@ import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.passive.BatEntity;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
@Mixin(BatEntity.class)
public abstract class BatEntityMixin extends AmbientEntity {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
index 0a1084cf..cfe979d0 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
@@ -36,7 +36,7 @@ public abstract class DrawContextMixin {
@Shadow
public abstract void fill(RenderLayer layer, int x1, int x2, int y1, int y2, int color);
-
+
@Shadow
public abstract int drawText(TextRenderer textRenderer, @Nullable String text, int x, int y, int color, boolean shadow);
@@ -96,35 +96,35 @@ public abstract class DrawContextMixin {
matrices.pop();
RenderSystem.enableDepthTest();
}
-
+
@Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD"))
private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef<String> countOverride) {
if (!SkyblockerConfig.get().general.itemInfoDisplay.attributeShardInfo) return;
-
+
NbtCompound nbt = stack.getNbt();
-
+
if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) {
NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes");
-
+
if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) {
NbtCompound attributesTag = extraAttributes.getCompound("attributes");
String[] attributes = attributesTag.getKeys().toArray(String[]::new);
-
+
if (attributes.length != 0) {
String attributeId = attributes[0];
int attributeLevel = attributesTag.getInt(attributeId);
-
+
//Set item count
countOverride.set(Integer.toString(attributeLevel));
-
+
//Draw the attribute name
this.matrices.push();
this.matrices.translate(0f, 0f, 200f);
-
+
String attributeInitials = AttributeShards.getShortName(attributeId);
-
+
this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true);
-
+
this.matrices.pop();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
index af6f6aa7..33532788 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
@@ -2,12 +2,14 @@ package me.xmrvizzy.skyblocker.mixin;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.skyblock.BackpackPreview;
import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver;
import me.xmrvizzy.skyblocker.skyblock.experiment.ExperimentSolver;
import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver;
import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver;
+import me.xmrvizzy.skyblocker.skyblock.item.BackpackPreview;
+import me.xmrvizzy.skyblocker.skyblock.item.CompactorDeletorPreview;
import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
import me.xmrvizzy.skyblocker.utils.Utils;
import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver;
import net.minecraft.client.gui.DrawContext;
@@ -31,6 +33,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
+import java.util.regex.Matcher;
@Mixin(HandledScreen.class)
public abstract class HandledScreenMixin extends Screen {
@@ -49,18 +52,31 @@ public abstract class HandledScreenMixin extends Screen {
}
}
- @Inject(at = @At("HEAD"), method = "drawMouseoverTooltip", cancellable = true)
+ @SuppressWarnings("DataFlowIssue")
+ // makes intellij be quiet about this.focusedSlot maybe being null. It's already null checked in mixined method.
+ @Inject(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V"), cancellable = true)
public void skyblocker$drawMouseOverTooltip(DrawContext context, int x, int y, CallbackInfo ci) {
+ if (!Utils.isOnSkyblock()) return;
+
// Hide Empty Tooltips
- if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hideEmptyTooltips && this.focusedSlot != null && focusedSlot.getStack().getName().getString().equals(" ")) {
+ if (SkyblockerConfig.get().general.hideEmptyTooltips && focusedSlot.getStack().getName().getString().equals(" ")) {
ci.cancel();
}
// Backpack Preview
boolean shiftDown = SkyblockerConfig.get().general.backpackPreviewWithoutShift ^ Screen.hasShiftDown();
- if (this.client != null && this.client.player != null && this.focusedSlot != null && shiftDown && this.getTitle().getString().equals("Storage") && this.focusedSlot.inventory != this.client.player.getInventory() && BackpackPreview.renderPreview(context, this.focusedSlot.getIndex(), x, y)) {
+ if (shiftDown && getTitle().getString().equals("Storage") && focusedSlot.inventory != client.player.getInventory() && BackpackPreview.renderPreview(context, focusedSlot.getIndex(), x, y)) {
ci.cancel();
}
+
+ // Compactor Preview
+ if (SkyblockerConfig.get().general.compactorDeletorPreview) {
+ ItemStack stack = focusedSlot.getStack();
+ Matcher matcher = CompactorDeletorPreview.NAME.matcher(ItemRegistry.getInternalName(stack));
+ if (matcher.matches() && CompactorDeletorPreview.drawPreview(context, stack, matcher.group("type"), matcher.group("size"), x, y)) {
+ ci.cancel();
+ }
+ }
}
@Redirect(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/slot/Slot;getStack()Lnet/minecraft/item/ItemStack;", ordinal = 0))
@@ -73,6 +89,7 @@ public abstract class HandledScreenMixin extends Screen {
return skyblocker$experimentSolvers$getStack(slot, stack);
}
+
@Unique
private ItemStack skyblocker$experimentSolvers$getStack(Slot slot, ItemStack stack) {
ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
index bc3df266..752b102a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
@@ -61,4 +61,9 @@ public abstract class InGameHudMixin {
if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.bars.enableBars && !Utils.isInTheRift())
ci.cancel();
}
+
+ @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"), cancellable = true)
+ private void skyblocker$dontRenderStatusEffects(CallbackInfo ci) {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hideStatusEffectOverlay) ci.cancel();
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java
new file mode 100644
index 00000000..854c4e17
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import java.util.Map;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListEntry;
+import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListWidget;
+
+@Mixin(SocialInteractionsPlayerListWidget.class)
+public class SocialInteractionsPlayerListWidgetMixin {
+
+ @WrapOperation(method = "setPlayers", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", remap = false))
+ private Object skyblocker$hideInvalidPlayers(Map<Object, Object> map, Object uuid, Object entry, Operation<Object> operation) {
+ if (Utils.isOnSkyblock() && !((SocialInteractionsPlayerListEntry) entry).getName().matches("[A-Za-z0-9_]+")) return null;
+
+ return operation.call(map, uuid, entry);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
index 3b796c38..53dd57a7 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
@@ -16,16 +16,16 @@ import net.minecraft.entity.Entity;
@Mixin(WorldRenderer.class)
public class WorldRendererMixin {
-
+
@ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;hasOutline(Lnet/minecraft/entity/Entity;)Z"))
private boolean skyblocker$shouldStarredMobGlow(boolean original, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
boolean isAStarredMobThatShouldGlow = SkyblockerConfig.get().locations.dungeons.starredMobGlow && StarredMobGlow.shouldMobGlow(entity);
-
+
isGlowingStarredMob.set(isAStarredMobThatShouldGlow);
-
+
return original || isAStarredMobThatShouldGlow;
}
-
+
@ModifyVariable(method = "render", at = @At("STORE"), ordinal = 0)
private int skyblocker$modifyGlowColor(int color, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
return isGlowingStarredMob.get() ? StarredMobGlow.getGlowColor(entity) : color;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java
new file mode 100644
index 00000000..d9668100
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java
@@ -0,0 +1,54 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.mojang.authlib.yggdrasil.YggdrasilServicesKeyInfo;
+
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import org.slf4j.Logger;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+
+import java.util.Base64;
+import java.util.Map;
+
+@Mixin(value = YggdrasilServicesKeyInfo.class, remap = false)
+public class YggdrasilServicesKeyInfoMixin {
+ @Shadow
+ @Final
+ private static Logger LOGGER;
+ @Unique
+ private static final Map<String, String> REPLACEMENT_MAP = Map.of();
+ @Unique
+ private static final IntList ERRONEUS_SIGNATURE_HASHES = new IntArrayList();
+
+ @WrapOperation(method = "validateProperty", at = @At(value = "INVOKE", target = "Ljava/util/Base64$Decoder;decode(Ljava/lang/String;)[B", remap = false), remap = false)
+ private byte[] skyblocker$replaceKnownWrongBase64(Base64.Decoder decoder, String signature, Operation<byte[]> decode) {
+ try {
+ return decode.call(decoder, signature);
+ } catch (IllegalArgumentException e) {
+ try {
+ return decode.call(decoder, signature.replaceAll("[^A-Za-z0-9+/=]", ""));
+ } catch (IllegalArgumentException e2) {
+ if (Utils.isOnSkyblock()) {
+ if (REPLACEMENT_MAP.containsKey(signature)) {
+ return decode.call(decoder, REPLACEMENT_MAP.get(signature));
+ }
+ int signatureHashCode = signature.hashCode();
+ if (!ERRONEUS_SIGNATURE_HASHES.contains(signatureHashCode)) {
+ ERRONEUS_SIGNATURE_HASHES.add(signatureHashCode);
+ LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode base64 string No.{}: {}", ERRONEUS_SIGNATURE_HASHES.size() - 1, signature);
+ } else {
+ LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode the base64 string No.{} again", ERRONEUS_SIGNATURE_HASHES.indexOf(signatureHashCode));
+ }
+ }
+ }
+ throw e;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java
new file mode 100644
index 00000000..f1e5b684
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java
@@ -0,0 +1,17 @@
+package me.xmrvizzy.skyblocker.mixin.accessor;
+
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.tooltip.TooltipComponent;
+import net.minecraft.client.gui.tooltip.TooltipPositioner;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+import java.util.List;
+
+@Mixin(DrawContext.class)
+public interface DrawContextInvoker {
+
+ @Invoker
+ void invokeDrawTooltip(TextRenderer textRenderer, List<TooltipComponent> components, int x, int y, TooltipPositioner positioner);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java
new file mode 100644
index 00000000..6a671601
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java
@@ -0,0 +1,14 @@
+package me.xmrvizzy.skyblocker.mixin.accessor;
+
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(Screen.class)
+public interface ScreenAccessor {
+ @Accessor
+ @Mutable
+ void setTitle(Text title);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java
index ab6fa767..fcd6be7a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java
@@ -5,17 +5,21 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import com.mojang.brigadier.CommandDispatcher;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.NEURepo;
+import me.xmrvizzy.skyblocker.utils.PosUtils;
import me.xmrvizzy.skyblocker.utils.Utils;
import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.DyeColor;
@@ -37,6 +41,7 @@ public class FairySouls {
private static final Map<String, Set<BlockPos>> fairySouls = new HashMap<>();
private static final Map<String, Map<String, Set<BlockPos>>> foundFairies = new HashMap<>();
+ @SuppressWarnings("UnusedReturnValue")
public static CompletableFuture<Void> runAsyncAfterFairySoulsLoad(Runnable runnable) {
if (fairySoulsLoaded == null) {
LOGGER.error("Fairy Souls have not being initialized yet! Please ensure the Fairy Souls module is initialized before modules calling this method in SkyblockerMod#onInitializeClient. This error can be safely ignore in a test environment.");
@@ -50,9 +55,16 @@ public class FairySouls {
}
public static void init() {
+ loadFairySouls();
+ ClientLifecycleEvents.CLIENT_STOPPING.register(FairySouls::saveFoundFairySouls);
+ ClientCommandRegistrationCallback.EVENT.register(FairySouls::registerCommands);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(FairySouls::render);
+ ClientReceiveMessageEvents.GAME.register(FairySouls::onChatMessage);
+ }
+
+ private static void loadFairySouls() {
fairySoulsLoaded = NEURepo.runAsyncAfterLoad(() -> {
- try {
- BufferedReader reader = new BufferedReader(new FileReader(NEURepo.LOCAL_REPO_DIR.resolve("constants").resolve("fairy_souls.json").toFile()));
+ try (BufferedReader reader = new BufferedReader(new FileReader(NEURepo.LOCAL_REPO_DIR.resolve("constants").resolve("fairy_souls.json").toFile()))) {
for (Map.Entry<String, JsonElement> fairySoulJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
if (fairySoulJson.getKey().equals("//") || fairySoulJson.getKey().equals("Max Souls")) {
if (fairySoulJson.getKey().equals("Max Souls")) {
@@ -62,61 +74,44 @@ public class FairySouls {
}
ImmutableSet.Builder<BlockPos> fairySoulsForLocation = ImmutableSet.builder();
for (JsonElement fairySoul : fairySoulJson.getValue().getAsJsonArray().asList()) {
- fairySoulsForLocation.add(parseBlockPos(fairySoul));
+ fairySoulsForLocation.add(PosUtils.parsePosString(fairySoul.getAsString()));
}
fairySouls.put(fairySoulJson.getKey(), fairySoulsForLocation.build());
}
- reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()));
+ LOGGER.debug("[Skyblocker] Loaded fairy soul locations");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load fairy soul locations", e);
+ }
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) {
for (Map.Entry<String, JsonElement> foundFairiesForProfileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
Map<String, Set<BlockPos>> foundFairiesForProfile = new HashMap<>();
for (Map.Entry<String, JsonElement> foundFairiesForLocationJson : foundFairiesForProfileJson.getValue().getAsJsonObject().asMap().entrySet()) {
Set<BlockPos> foundFairiesForLocation = new HashSet<>();
for (JsonElement foundFairy : foundFairiesForLocationJson.getValue().getAsJsonArray().asList()) {
- foundFairiesForLocation.add(parseBlockPos(foundFairy));
+ foundFairiesForLocation.add(PosUtils.parsePosString(foundFairy.getAsString()));
}
foundFairiesForProfile.put(foundFairiesForLocationJson.getKey(), foundFairiesForLocation);
}
foundFairies.put(foundFairiesForProfileJson.getKey(), foundFairiesForProfile);
}
- reader.close();
+ LOGGER.debug("[Skyblocker] Loaded found fairy souls");
+ } catch (FileNotFoundException ignored) {
} catch (IOException e) {
- LOGGER.error("Failed to load found fairy souls", e);
- } catch (Exception e) {
- LOGGER.error("Encountered unknown exception loading fairy souls", e);
+ LOGGER.error("[Skyblocker] Failed to load found fairy souls", e);
}
});
- ClientLifecycleEvents.CLIENT_STOPPING.register(FairySouls::saveFoundFairySouls);
- WorldRenderEvents.AFTER_TRANSLUCENT.register(FairySouls::render);
- ClientReceiveMessageEvents.GAME.register(FairySouls::onChatMessage);
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE)
- .then(literal("fairySouls")
- .then(literal("markAllInCurrentIslandFound").executes(context -> {
- FairySouls.markAllFairiesFound();
- context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllFound"));
- return 1;
- }))
- .then(literal("markAllInCurrentIslandMissing").executes(context -> {
- FairySouls.markAllFairiesNotFound();
- context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllMissing"));
- return 1;
- })))));
}
- private static BlockPos parseBlockPos(JsonElement posJson) {
- String[] posArray = posJson.getAsString().split(",");
- return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2]));
- }
-
- public static void saveFoundFairySouls(MinecraftClient client) {
- try {
- BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()));
+ private static void saveFoundFairySouls(MinecraftClient client) {
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) {
JsonObject foundFairiesJson = new JsonObject();
for (Map.Entry<String, Map<String, Set<BlockPos>>> foundFairiesForProfile : foundFairies.entrySet()) {
JsonObject foundFairiesForProfileJson = new JsonObject();
for (Map.Entry<String, Set<BlockPos>> foundFairiesForLocation : foundFairiesForProfile.getValue().entrySet()) {
JsonArray foundFairiesForLocationJson = new JsonArray();
for (BlockPos foundFairy : foundFairiesForLocation.getValue()) {
- foundFairiesForLocationJson.add(foundFairy.getX() + "," + foundFairy.getY() + "," + foundFairy.getZ());
+ foundFairiesForLocationJson.add(PosUtils.getPosString(foundFairy));
}
foundFairiesForProfileJson.add(foundFairiesForLocation.getKey(), foundFairiesForLocationJson);
}
@@ -124,57 +119,84 @@ public class FairySouls {
}
SkyblockerMod.GSON.toJson(foundFairiesJson, writer);
writer.close();
+ LOGGER.info("[Skyblocker] Saved found fairy souls");
} catch (IOException e) {
- LOGGER.error("Failed to write found fairy souls to file.");
+ LOGGER.error("[Skyblocker] Failed to write found fairy souls to file", e);
}
}
- public static void render(WorldRenderContext context) {
- if (SkyblockerConfig.get().general.fairySouls.enableFairySoulsHelper && fairySoulsLoaded.isDone() && fairySouls.containsKey(Utils.getLocationRaw())) {
- for (BlockPos fairySoul : fairySouls.get(Utils.getLocationRaw())) {
- float[] colorComponents = isFairySoulNotFound(fairySoul) ? DyeColor.GREEN.getColorComponents() : DyeColor.RED.getColorComponents();
- RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoul, colorComponents, 0.5F);
- }
- }
+ private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) {
+ dispatcher.register(literal(SkyblockerMod.NAMESPACE)
+ .then(literal("fairySouls")
+ .then(literal("markAllInCurrentIslandFound").executes(context -> {
+ FairySouls.markAllFairiesOnCurrentIslandFound();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllFound"));
+ return 1;
+ }))
+ .then(literal("markAllInCurrentIslandMissing").executes(context -> {
+ FairySouls.markAllFairiesOnCurrentIslandMissing();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllMissing"));
+ return 1;
+ }))));
}
- private static boolean isFairySoulNotFound(BlockPos fairySoul) {
- Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile());
- if (foundFairiesForProfile == null) {
- return true;
- }
- Set<BlockPos> foundFairiesForProfileAndLocation = foundFairiesForProfile.get(Utils.getLocationRaw());
- if (foundFairiesForProfileAndLocation == null) {
- return true;
+ private static void render(WorldRenderContext context) {
+ SkyblockerConfig.FairySouls fairySoulsConfig = SkyblockerConfig.get().general.fairySouls;
+
+ if (fairySoulsConfig.enableFairySoulsHelper && fairySoulsLoaded.isDone() && fairySouls.containsKey(Utils.getLocationRaw())) {
+ for (BlockPos fairySoulPos : fairySouls.get(Utils.getLocationRaw())) {
+ boolean fairySoulNotFound = isFairySoulMissing(fairySoulPos);
+ if (!fairySoulsConfig.highlightFoundSouls && !fairySoulNotFound || fairySoulsConfig.highlightOnlyNearbySouls && fairySoulPos.getSquaredDistance(context.camera().getPos()) > 2500) {
+ continue;
+ }
+ float[] colorComponents = fairySoulNotFound ? DyeColor.GREEN.getColorComponents() : DyeColor.RED.getColorComponents();
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F);
+ }
}
- return !foundFairiesForProfileAndLocation.contains(fairySoul);
}
- public static void onChatMessage(Text text, boolean overlay) {
+ private static void onChatMessage(Text text, boolean overlay) {
String message = text.getString();
- if (message.equals("You have already found that Fairy Soul!") || message.equals("SOUL! You found a Fairy Soul!")) {
+ if (message.equals("You have already found that Fairy Soul!") || message.equals("§d§lSOUL! §fYou found a §dFairy Soul§f!")) {
markClosestFairyFound();
}
}
private static void markClosestFairyFound() {
+ if (!fairySoulsLoaded.isDone()) return;
PlayerEntity player = MinecraftClient.getInstance().player;
if (player == null) {
- LOGGER.warn("Failed to mark closest fairy soul as found because player is null.");
+ LOGGER.warn("[Skyblocker] Failed to mark closest fairy soul as found because player is null");
return;
}
- fairySouls.get(Utils.getLocationRaw()).stream().filter(FairySouls::isFairySoulNotFound).min(Comparator.comparingDouble(fairySoul -> fairySoul.getSquaredDistance(player.getPos()))).ifPresent(fairySoul -> {
- initializeFoundFairiesForCurrentProfileAndLocation();
- foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).add(fairySoul);
- });
+ fairySouls.get(Utils.getLocationRaw()).stream()
+ .filter(FairySouls::isFairySoulMissing)
+ .min(Comparator.comparingDouble(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos())))
+ .filter(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos()) <= 16)
+ .ifPresent(fairySoulPos -> {
+ initializeFoundFairiesForCurrentProfileAndLocation();
+ foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).add(fairySoulPos);
+ });
+ }
+
+ private static boolean isFairySoulMissing(BlockPos fairySoulPos) {
+ Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile());
+ if (foundFairiesForProfile == null) {
+ return true;
+ }
+ Set<BlockPos> foundFairiesForProfileAndLocation = foundFairiesForProfile.get(Utils.getLocationRaw());
+ if (foundFairiesForProfileAndLocation == null) {
+ return true;
+ }
+ return !foundFairiesForProfileAndLocation.contains(fairySoulPos);
}
- public static void markAllFairiesFound() {
+ public static void markAllFairiesOnCurrentIslandFound() {
initializeFoundFairiesForCurrentProfileAndLocation();
foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).addAll(fairySouls.get(Utils.getLocationRaw()));
}
- public static void markAllFairiesNotFound() {
+ public static void markAllFairiesOnCurrentIslandMissing() {
Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile());
if (foundFairiesForProfile != null) {
foundFairiesForProfile.remove(Utils.getLocationRaw());
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java
index cf793461..381bf94c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java
@@ -1,8 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.InGameHud;
@@ -16,7 +16,7 @@ public class QuiverWarning {
public static void init() {
ClientReceiveMessageEvents.ALLOW_GAME.register(QuiverWarning::onChatMessage);
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(QuiverWarning::update, 10);
+ Scheduler.INSTANCE.scheduleCyclic(QuiverWarning::update, 10);
}
public static boolean onChatMessage(Text text, boolean overlay) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
index 6e354ddc..8d676d0b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
@@ -1,10 +1,10 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
@@ -35,7 +35,7 @@ public class DungeonBlaze {
private static ArmorStandEntity nextLowestBlaze = null;
public static void init() {
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonBlaze::update, 4);
+ Scheduler.INSTANCE.scheduleCyclic(DungeonBlaze::update, 4);
WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer);
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
new file mode 100644
index 00000000..b223e1a2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
@@ -0,0 +1,168 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import com.google.gson.JsonObject;
+import it.unimi.dsi.fastutil.ints.IntBooleanPair;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.mixin.accessor.ScreenAccessor;
+import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.client.item.TooltipContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.GenericContainerScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.DecimalFormat;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DungeonChestProfit {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DungeonChestProfit.class);
+ private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)");
+ private static final DecimalFormat FORMATTER = new DecimalFormat("#,###");
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> ScreenEvents.afterTick(screen).register(screen1 -> {
+ if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getScreenHandler().getType() == ScreenHandlerType.GENERIC_9X6) {
+ ((ScreenAccessor) screen).setTitle(getChestProfit(genericContainerScreen.getScreenHandler(), screen.getTitle(), client));
+ }
+ }));
+ }
+
+ public static Text getChestProfit(GenericContainerScreenHandler handler, Text title, MinecraftClient client) {
+ try {
+ if (SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator && isDungeonChest(title.getString())) {
+ int profit = 0;
+ boolean hasIncompleteData = false, usedKismet = false;
+ List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);
+
+ //If the item stack for the "Open Reward Chest" button or the kismet button hasn't been sent to the client yet
+ if (slots.get(31).getStack().isEmpty() || slots.get(50).getStack().isEmpty()) return title;
+
+ for (Slot slot : slots) {
+ ItemStack stack = slot.getStack();
+
+ if (!stack.isEmpty()) {
+ String name = stack.getName().getString();
+ String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false);
+
+ //Regular item price
+ if (id != null) {
+ IntBooleanPair priceData = getItemPrice(id);
+
+ if (!priceData.rightBoolean()) hasIncompleteData = true;
+
+ //Add the item price to the profit
+ profit += priceData.leftInt();
+
+ continue;
+ }
+
+ //Essence price
+ if (name.contains("Essence") && SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.includeEssence) {
+ Matcher matcher = ESSENCE_PATTERN.matcher(name);
+
+ if (matcher.matches()) {
+ String type = matcher.group("type");
+ int amount = Integer.parseInt(matcher.group("amount"));
+
+ IntBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase());
+
+ if (!priceData.rightBoolean()) hasIncompleteData = true;
+
+ //Add the price of the essence to the profit
+ profit += priceData.leftInt() * amount;
+
+ continue;
+ }
+ }
+
+ //Determine the cost of the chest
+ if (name.contains("Open Reward Chest")) {
+ String foundString = searchLoreFor(stack, client, "Coins");
+
+ //Incase we're searching the free chest
+ if (!StringUtils.isBlank(foundString)) {
+ profit -= Integer.parseInt(foundString.replaceAll("[^0-9]", ""));
+ }
+
+ continue;
+ }
+
+ //Determine if a kismet was used or not
+ if (name.contains("Reroll Chest")) {
+ usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!"));
+ }
+ }
+ }
+
+ if (SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) {
+ IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER");
+
+ if (!kismetPriceData.rightBoolean()) hasIncompleteData = true;
+
+ profit -= kismetPriceData.leftInt();
+ }
+
+ return Text.literal(title.getString()).append(getProfitText(profit, hasIncompleteData));
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e);
+ }
+
+ return title;
+ }
+
+ /**
+ * @return An {@link IntBooleanPair} with the {@code left int} representing the item's price, and the {@code right boolean} indicating if the price
+ * was based on complete data.
+ */
+ private static IntBooleanPair getItemPrice(String id) {
+ JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices();
+ JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices();
+
+ if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false);
+
+ if (bazaarPrices.has(id)) {
+ JsonObject item = bazaarPrices.get(id).getAsJsonObject();
+ boolean isPriceNull = item.get("sellPrice").isJsonNull();
+
+ return IntBooleanPair.of(isPriceNull ? 0 : (int) item.get("sellPrice").getAsDouble(), !isPriceNull);
+ }
+
+ if (lbinPrices.has(id)) {
+ return IntBooleanPair.of((int) lbinPrices.get(id).getAsDouble(), true);
+ }
+
+ return IntBooleanPair.of(0, false);
+ }
+
+ /**
+ * Searches for a specific string of characters in the name and lore of an item
+ */
+ private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) {
+ return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).filter(line -> line.contains(searchString)).findAny().orElse(null);
+ }
+
+ private static boolean isDungeonChest(String name) {
+ return name.equals("Wood Chest") || name.equals("Gold Chest") || name.equals("Diamond Chest") || name.equals("Emerald Chest") || name.equals("Obsidian Chest") || name.equals("Bedrock Chest");
+ }
+
+ private static Text getProfitText(int profit, boolean hasIncompleteData) {
+ SkyblockerConfig.DungeonChestProfit config = SkyblockerConfig.get().locations.dungeons.dungeonChestProfit;
+ return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting);
+ }
+
+ static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) {
+ return Text.literal((profit > 0 ? " +" : " ") + FORMATTER.format(profit)).formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
index 4701c485..e5d8a720 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
@@ -1,8 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.BlockPos;
@@ -23,13 +23,13 @@ public class LividColor {
if (tenTicks != 0) {
if (SkyblockerConfig.get().locations.dungeons.lividColor.enableLividColor && Utils.isInDungeons() && client.world != null) {
if (tenTicks == 1) {
- SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red"));
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red"));
tenTicks = 0;
return;
}
String key = client.world.getBlockState(new BlockPos(5, 110, 42)).getBlock().getTranslationKey();
if (key.startsWith("block.minecraft.") && key.endsWith("wool") && !key.endsWith("red_wool")) {
- SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5)));
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5)));
tenTicks = 0;
return;
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
index e1194632..8d8a840a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
@@ -1,10 +1,11 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener;
+import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.minecraft.client.MinecraftClient;
@@ -35,7 +36,7 @@ public class Reparty extends ChatPatternListener {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("rp").executes(context -> {
if (!Utils.isOnSkyblock() || this.repartying || client.player == null) return 0;
this.repartying = true;
- SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown("/p list");
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown("/p list");
return 0;
})));
}
@@ -57,7 +58,7 @@ public class Reparty extends ChatPatternListener {
}
} else if (matcher.group("disband") != null && !matcher.group("disband").equals(client.getSession().getUsername())) {
partyLeader = matcher.group("disband");
- SkyblockerMod.getInstance().scheduler.schedule(() -> partyLeader = null, 61);
+ Scheduler.INSTANCE.schedule(() -> partyLeader = null, 61);
return false;
} else if (matcher.group("invite") != null && matcher.group("invite").equals(partyLeader)) {
String command = "/party accept " + partyLeader;
@@ -84,10 +85,10 @@ public class Reparty extends ChatPatternListener {
String command = "/p invite " + this.players[i];
sendCommand(command, i + 2);
}
- SkyblockerMod.getInstance().scheduler.schedule(() -> this.repartying = false, this.players.length + 2);
+ Scheduler.INSTANCE.schedule(() -> this.repartying = false, this.players.length + 2);
}
private void sendCommand(String command, int delay) {
- SkyblockerMod.getInstance().messageScheduler.queueMessage(command, delay * BASE_DELAY);
+ MessageScheduler.INSTANCE.queueMessage(command, delay * BASE_DELAY);
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
index a7420bf5..ac389d34 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
@@ -13,6 +13,7 @@ import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
@@ -140,7 +141,7 @@ public class DungeonSecrets {
LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
return null;
});
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonSecrets::update, 10);
+ Scheduler.INSTANCE.scheduleCyclic(DungeonSecrets::update, 10);
WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render);
ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage);
ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage);
@@ -178,8 +179,8 @@ public class DungeonSecrets {
}
dungeonFutures.add(CompletableFuture.runAsync(() -> {
try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) {
- SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class).asMap().forEach((room, jsonElement) -> roomsJson.put(room.toLowerCase(), jsonElement));
- SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class).asMap().forEach((room, jsonElement) -> waypointsJson.put(room.toLowerCase(), jsonElement));
+ loadJson(roomsReader, roomsJson);
+ loadJson(waypointsReader, waypointsJson);
LOGGER.debug("[Skyblocker] Loaded dungeon secrets json");
} catch (Exception e) {
LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e);
@@ -200,6 +201,15 @@ public class DungeonSecrets {
}
}
+ /**
+ * Loads the json from the given {@link BufferedReader} into the given {@link Map}.
+ * @param reader the reader to read the json from
+ * @param map the map to load into
+ */
+ private static void loadJson(BufferedReader reader, Map<String, JsonElement> map) {
+ SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement));
+ }
+
private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) {
return argument("secret", IntegerArgumentType.integer()).executes(context -> {
int secretIndex = IntegerArgumentType.getInteger(context, "secret");
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
index 6825d779..ae5afaa3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
@@ -8,7 +8,7 @@ import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import it.unimi.dsi.fastutil.ints.IntSortedSets;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState;
@@ -249,7 +249,7 @@ public class Room {
// If no rooms match, reset the fields and scan again after 50 ticks.
matched = TriState.FALSE;
DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size());
- SkyblockerMod.getInstance().scheduler.schedule(() -> matched = TriState.DEFAULT, 50);
+ Scheduler.INSTANCE.schedule(() -> matched = TriState.DEFAULT, 50);
reset();
return true;
} else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java
index c8335699..23caa180 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java
@@ -1,6 +1,7 @@
package me.xmrvizzy.skyblocker.skyblock.filters;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Constants;
import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener;
import net.minecraft.text.Text;
@@ -13,14 +14,13 @@ public class AdFilter extends ChatPatternListener {
Pattern.compile("^(?:i(?:m|'m| am)? |(?:is )?any(?: ?one|1) )?(?:buy|sell|lowball|trade?)(?:ing)?(?:\\W|$)", Pattern.CASE_INSENSITIVE),
Pattern.compile("(.)\\1{7,}"),
Pattern.compile("\\W(?:on|in|check|at) my (?:ah|bin)(?:\\W|$)", Pattern.CASE_INSENSITIVE), };
- private static final String EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u12DE";
public AdFilter() {
// Groups:
// 1. Player name
// 2. Message
// (?:§8\[[§feadbc0-9]+§8\] )?(?:[§76l]+[<INSERT EMBLEMS>] )?§[67abc](?:\[[§A-Za-z0-9+]+\] )?([A-Za-z0-9_]+)§[f7]: (.+)
- super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)§[f7]: (.+)");
+ super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)§[f7]: (.+)");
}
@Override
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java
index 6692e612..85be9e2a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java
@@ -1,14 +1,14 @@
package me.xmrvizzy.skyblocker.skyblock.filters;
+import me.xmrvizzy.skyblocker.utils.Constants;
import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
public class ShowOffFilter extends SimpleChatFilter {
- private static final String EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u12DE";
private static final String[] SHOW_TYPES = { "is holding", "is wearing", "is friends with a", "has" };
public ShowOffFilter() {
- super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)[§f7]+ (?:" + String.join("|", SHOW_TYPES) + ") §8\\[(.+)§8\\]");
+ super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)[§f7]+ (?:" + String.join("|", SHOW_TYPES) + ") §8\\[(.+)§8\\]");
}
@Override
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java
index 0f106cdf..8f71e7b9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java
@@ -4,7 +4,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
public class AttributeShards {
private static final Object2ObjectOpenHashMap<String, String> ID_2_SHORT_NAME = new Object2ObjectOpenHashMap<>();
-
+
static {
//Weapons
ID_2_SHORT_NAME.put("arachno", "A");
@@ -18,11 +18,11 @@ public class AttributeShards {
ID_2_SHORT_NAME.put("mana_steal", "MS");
ID_2_SHORT_NAME.put("midas_touch", "MT");
ID_2_SHORT_NAME.put("undead", "U");
-
+
//Swords & Bows
ID_2_SHORT_NAME.put("warrior", "W");
ID_2_SHORT_NAME.put("deadeye", "DE");
-
+
//Armor or Equipment
ID_2_SHORT_NAME.put("arachno_resistance", "AR");
ID_2_SHORT_NAME.put("blazing_resistance", "BR");
@@ -40,7 +40,7 @@ public class AttributeShards {
ID_2_SHORT_NAME.put("speed", "S");
ID_2_SHORT_NAME.put("undead_resistance", "UR");
ID_2_SHORT_NAME.put("veteran", "V");
-
+
//Fishing Gear
ID_2_SHORT_NAME.put("blazing_fortune", "BF");
ID_2_SHORT_NAME.put("fishing_experience", "FE");
@@ -50,9 +50,9 @@ public class AttributeShards {
ID_2_SHORT_NAME.put("fishing_speed", "FS");
ID_2_SHORT_NAME.put("hunter", "H");
ID_2_SHORT_NAME.put("trophy_hunter", "TH");
-
+
}
-
+
public static String getShortName(String id) {
return ID_2_SHORT_NAME.getOrDefault(id, "");
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java
index d89a18e0..dc8f77cd 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java
@@ -1,4 +1,4 @@
-package me.xmrvizzy.skyblocker.skyblock;
+package me.xmrvizzy.skyblocker.skyblock.item;
import com.mojang.blaze3d.systems.RenderSystem;
import me.xmrvizzy.skyblocker.SkyblockerMod;
@@ -10,27 +10,25 @@ import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
-import net.minecraft.client.network.PlayerListEntry;
-import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.*;
import net.minecraft.util.Identifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BackpackPreview {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackpackPreview.class);
private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png");
- private static final BackpackPreview instance = new BackpackPreview();
- private static final Pattern PROFILE_PATTERN = Pattern.compile("Profile: ([a-zA-Z]+)");
private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)");
private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)");
private static final int STORAGE_SIZE = 27;
@@ -56,8 +54,8 @@ public class BackpackPreview {
saveStorage();
// update save dir based on uuid and sb profile
String uuid = MinecraftClient.getInstance().getSession().getUuid().replaceAll("-", "");
- String profile = getSkyblockProfile();
- if (profile != null) {
+ String profile = Utils.getProfile();
+ if (profile != null && !profile.isEmpty()) {
save_dir = FabricLoader.getInstance().getConfigDir().resolve("skyblocker/backpack-preview/" + uuid + "/" + profile);
save_dir.toFile().mkdirs();
if (loaded.equals(uuid + "/" + profile)) {
@@ -87,7 +85,7 @@ public class BackpackPreview {
NbtCompound root = NbtIo.read(file);
storage[index] = new DummyInventory(root);
} catch (Exception e) {
- e.printStackTrace();
+ LOGGER.error("Failed to load backpack preview file: " + file.getName(), e);
}
}
}
@@ -119,7 +117,7 @@ public class BackpackPreview {
NbtIo.write(root, save_dir.resolve(index + ".nbt").toFile());
dirty[index] = false;
} catch (Exception e) {
- e.printStackTrace();
+ LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e);
}
}
}
@@ -144,19 +142,20 @@ public class BackpackPreview {
int rows = (storage[index].size() - 9) / 9;
Screen screen = MinecraftClient.getInstance().currentScreen;
+ if (screen == null) return false;
int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8;
int y = Math.max(0, mouseY - 16);
RenderSystem.disableDepthTest();
RenderSystem.setShaderTexture(0, TEXTURE);
context.drawTexture(TEXTURE, x, y, 0, 0, 176, 7);
- for (int i = 0; i < rows; ++i)
+ for (int i = 0; i < rows; ++i) {
context.drawTexture(TEXTURE, x, y + i * 18 + 7, 0, 7, 176, 18);
+ }
context.drawTexture(TEXTURE, x, y + rows * 18 + 7, 0, 25, 176, 7);
RenderSystem.enableDepthTest();
MatrixStack matrices = context.getMatrices();
- ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
for (int i = 9; i < storage[index].size(); ++i) {
int itemX = x + (i - 9) % 9 * 18 + 8;
@@ -178,19 +177,6 @@ public class BackpackPreview {
if (backpack.find()) return Integer.parseInt(backpack.group(1)) + 8;
return -1;
}
-
- private static String getSkyblockProfile() {
- Collection<PlayerListEntry> list = MinecraftClient.getInstance().getNetworkHandler().getPlayerList();
- for (PlayerListEntry entry : list) {
- if (entry.getDisplayName() != null) {
- Matcher matcher = PROFILE_PATTERN.matcher(entry.getDisplayName().getString());
- if (matcher.find()) {
- return matcher.group(1);
- }
- }
- }
- return null;
- }
}
class DummyInventory implements Inventory {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java
new file mode 100644
index 00000000..7b93fe1e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java
@@ -0,0 +1,92 @@
+package me.xmrvizzy.skyblocker.skyblock.item;
+
+import it.unimi.dsi.fastutil.ints.IntIntPair;
+import it.unimi.dsi.fastutil.ints.IntObjectPair;
+import me.xmrvizzy.skyblocker.mixin.accessor.DrawContextInvoker;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.tooltip.HoveredTooltipPositioner;
+import net.minecraft.client.gui.tooltip.TooltipComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class CompactorDeletorPreview {
+ /**
+ * The width and height in slots of the compactor/deletor
+ */
+ private static final Map<String, IntIntPair> DIMENSIONS = Map.of(
+ "4000", IntIntPair.of(1, 1),
+ "5000", IntIntPair.of(1, 3),
+ "6000", IntIntPair.of(1, 7),
+ "7000", IntIntPair.of(2, 6)
+ );
+ private static final IntIntPair DEFAULT_DIMENSION = IntIntPair.of(1, 6);
+ public static final Pattern NAME = Pattern.compile("PERSONAL_(?<type>COMPACTOR|DELETOR)_(?<size>\\d+)");
+ private static final MinecraftClient client = MinecraftClient.getInstance();
+
+ public static boolean drawPreview(DrawContext context, ItemStack stack, String type, String size, int x, int y) {
+ List<Text> tooltips = Screen.getTooltipFromItem(client, stack);
+ int targetIndex = getTargetIndex(tooltips);
+ if (targetIndex == -1) return false;
+
+ // Get items in compactor or deletor
+ NbtCompound nbt = stack.getNbt();
+ if (nbt == null || !nbt.contains("ExtraAttributes", 10)) {
+ return false;
+ }
+ NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes");
+ // Get the slots and their items from the nbt, which is in the format personal_compact_<slot_number> or personal_deletor_<slot_number>
+ List<IntObjectPair<ItemStack>> slots = extraAttributes.getKeys().stream().filter(slot -> slot.contains(type.toLowerCase().substring(0, 7))).map(slot -> IntObjectPair.of(Integer.parseInt(slot.substring(17)), ItemRegistry.getItemStack(extraAttributes.getString(slot)))).toList();
+
+ List<TooltipComponent> components = tooltips.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList());
+ IntIntPair dimensions = DIMENSIONS.getOrDefault(size, DEFAULT_DIMENSION);
+
+ // If there are no items in compactor or deletor
+ if (slots.isEmpty()) {
+ int slotsCount = dimensions.leftInt() * dimensions.rightInt();
+ components.add(targetIndex, TooltipComponent.of(Text.literal(slotsCount + (slotsCount == 1 ? " slot" : " slots")).formatted(Formatting.GRAY).asOrderedText()));
+
+ ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE);
+ return true;
+ }
+
+ // Add the preview tooltip component
+ components.add(targetIndex, new CompactorPreviewTooltipComponent(slots, dimensions));
+
+ // Render accompanying text
+ components.add(targetIndex, TooltipComponent.of(Text.literal("Contents:").asOrderedText()));
+ if (extraAttributes.contains("PERSONAL_DELETOR_ACTIVE")) {
+ components.add(targetIndex, TooltipComponent.of(Text.literal("Active: ")
+ .append(extraAttributes.getBoolean("PERSONAL_DELETOR_ACTIVE") ? Text.literal("YES").formatted(Formatting.BOLD).formatted(Formatting.GREEN) : Text.literal("NO").formatted(Formatting.BOLD).formatted(Formatting.RED)).asOrderedText()));
+ }
+ ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE);
+ return true;
+ }
+
+ /**
+ * Finds the target index to insert the preview component, which is the second empty line
+ */
+ private static int getTargetIndex(List<Text> tooltips) {
+ int targetIndex = -1;
+ int lineCount = 0;
+ for (int i = 0; i < tooltips.size(); i++) {
+ if (tooltips.get(i).getString().isEmpty()) {
+ lineCount += 1;
+ }
+ if (lineCount == 2) {
+ targetIndex = i;
+ break;
+ }
+ }
+ return targetIndex;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java
new file mode 100644
index 00000000..45e3c635
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java
@@ -0,0 +1,54 @@
+package me.xmrvizzy.skyblocker.skyblock.item;
+
+import it.unimi.dsi.fastutil.ints.IntIntPair;
+import it.unimi.dsi.fastutil.ints.IntObjectPair;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.tooltip.TooltipComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Identifier;
+
+public class CompactorPreviewTooltipComponent implements TooltipComponent {
+ private static final Identifier INVENTORY_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png");
+ private final Iterable<IntObjectPair<ItemStack>> items;
+ private final IntIntPair dimensions;
+
+ public CompactorPreviewTooltipComponent(Iterable<IntObjectPair<ItemStack>> items, IntIntPair dimensions) {
+ this.items = items;
+ this.dimensions = dimensions;
+ }
+
+ @Override
+ public int getHeight() {
+ return dimensions.leftInt() * 18 + 14;
+ }
+
+ @Override
+ public int getWidth(TextRenderer textRenderer) {
+ return dimensions.rightInt() * 18 + 14;
+ }
+
+ @Override
+ public void drawItems(TextRenderer textRenderer, int x, int y, DrawContext context) {
+ context.drawTexture(INVENTORY_TEXTURE, x, y, 0, 0, 7 + dimensions.rightInt() * 18, 7);
+ context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y, 169, 0, 7, 7);
+
+ for (int i = 0; i < dimensions.leftInt(); i++) {
+ context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + i * 18, 0, 7, 7, 18);
+ for (int j = 0; j < dimensions.rightInt(); j++) {
+ context.drawTexture(INVENTORY_TEXTURE, x + 7 + j * 18, y + 7 + i * 18, 7, 7, 18, 18);
+ }
+ context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + i * 18, 169, 7, 7, 18);
+ }
+ context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + dimensions.leftInt() * 18, 0, 25, 7 + dimensions.rightInt() * 18, 7);
+ context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + dimensions.leftInt() * 18, 169, 25, 7, 7);
+
+ for (IntObjectPair<ItemStack> entry : items) {
+ int itemX = x + entry.leftInt() % dimensions.rightInt() * 18 + 8;
+ int itemY = y + entry.leftInt() / dimensions.rightInt() * 18 + 8;
+ context.drawItem(entry.right(), itemX, itemY);
+ context.drawItemInSlot(textRenderer, entry.right(), itemX, itemY);
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java
index c744144a..8c160456 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java
@@ -53,11 +53,11 @@ public class CustomItemNames {
}
} else {
//If the text is provided then set the item's custom name to it
-
+
//Set italic to false if it hasn't been changed (or was already false)
Style currentStyle = text.getStyle();
((MutableText) text).setStyle(currentStyle.withItalic((currentStyle.isItalic() ? true : false)));
-
+
customItemNames.put(itemUuid, text);
SkyblockerConfig.save();
source.sendFeedback(Text.translatable("skyblocker.customItemNames.added"));
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
index 84325042..d20aa0e3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
@@ -2,9 +2,10 @@ package me.xmrvizzy.skyblocker.skyblock.item;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Http;
import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.ItemStack;
@@ -15,10 +16,7 @@ import net.minecraft.util.Formatting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
+import java.net.http.HttpHeaders;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -29,11 +27,9 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
-import java.util.zip.GZIPInputStream;
public class PriceInfoTooltip {
private static final Logger LOGGER = LoggerFactory.getLogger(PriceInfoTooltip.class.getName());
- private static final SkyblockerMod skyblocker = SkyblockerMod.getInstance();
private static final MinecraftClient client = MinecraftClient.getInstance();
private static JsonObject npcPricesJson;
private static JsonObject bazaarPricesJson;
@@ -45,14 +41,23 @@ public class PriceInfoTooltip {
private static boolean nullMsgSend = false;
private final static Gson gson = new Gson();
private static final Map<String, String> apiAddresses;
+ private static long npcHash = 0;
+ private static long museumHash = 0;
+ private static long motesHash = 0;
public static void onInjectTooltip(ItemStack stack, TooltipContext context, List<Text> lines) {
if (!Utils.isOnSkyblock() || client.player == null) return;
String name = getInternalNameFromNBT(stack, false);
String internalID = getInternalNameFromNBT(stack, true);
+ String neuName = name;
if (name == null || internalID == null) return;
+ if(name.startsWith("ISSHINY_")){
+ name = "SHINY_" + internalID;
+ neuName = internalID;
+ }
+
int count = stack.getCount();
boolean bazaarOpened = lines.stream().anyMatch(each -> each.getString().contains("Buy price:") || each.getString().contains("Sell price:"));
@@ -120,12 +125,12 @@ public class PriceInfoTooltip {
*/
switch (internalID) {
case "PET" -> {
- name = name.replaceAll("LVL_\\d*_", "");
- String[] parts = name.split("_");
+ neuName = neuName.replaceAll("LVL_\\d*_", "");
+ String[] parts = neuName.split("_");
String type = parts[0];
- name = name.replaceAll(type + "_", "");
- name = name + "-" + type;
- name = name.replace("UNCOMMON", "1")
+ neuName = neuName.replaceAll(type + "_", "");
+ neuName = neuName + "-" + type;
+ neuName = neuName.replace("UNCOMMON", "1")
.replace("COMMON", "0")
.replace("RARE", "2")
.replace("EPIC", "3")
@@ -133,14 +138,14 @@ public class PriceInfoTooltip {
.replace("MYTHIC", "5")
.replace("-", ";");
}
- case "RUNE" -> name = name.replaceAll("_(?!.*_)", ";");
- case "POTION" -> name = "";
+ case "RUNE" -> neuName = neuName.replaceAll("_(?!.*_)", ";");
+ case "POTION" -> neuName = "";
case "ATTRIBUTE_SHARD" ->
- name = internalID + "+" + name.replace("SHARD-", "").replaceAll("_(?!.*_)", ";");
- default -> name = name.replace(":", "-");
+ neuName = internalID + "+" + neuName.replace("SHARD-", "").replaceAll("_(?!.*_)", ";");
+ default -> neuName = neuName.replace(":", "-");
}
- if (!name.isEmpty() && lbinExist) {
+ if (!neuName.isEmpty() && lbinExist) {
SkyblockerConfig.Average type = SkyblockerConfig.get().general.itemTooltip.avg;
// "No data" line because of API not keeping old data, it causes NullPointerException
@@ -148,9 +153,9 @@ public class PriceInfoTooltip {
lines.add(
Text.literal(String.format("%-19s", "1 Day Avg. Price:"))
.formatted(Formatting.GOLD)
- .append(oneDayAvgPricesJson.get(name) == null
+ .append(oneDayAvgPricesJson.get(neuName) == null
? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(oneDayAvgPricesJson.get(name).getAsDouble(), count)
+ : getCoinsMessage(oneDayAvgPricesJson.get(neuName).getAsDouble(), count)
)
);
}
@@ -158,9 +163,9 @@ public class PriceInfoTooltip {
lines.add(
Text.literal(String.format("%-19s", "3 Day Avg. Price:"))
.formatted(Formatting.GOLD)
- .append(threeDayAvgPricesJson.get(name) == null
+ .append(threeDayAvgPricesJson.get(neuName) == null
? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(threeDayAvgPricesJson.get(name).getAsDouble(), count)
+ : getCoinsMessage(threeDayAvgPricesJson.get(neuName).getAsDouble(), count)
)
);
}
@@ -258,6 +263,10 @@ public class PriceInfoTooltip {
}
// Transformation to API format.
+ if (ea.contains("is_shiny")){
+ return "ISSHINY_" + internalName;
+ }
+
switch (internalName) {
case "ENCHANTED_BOOK" -> {
if (ea.contains("enchantments")) {
@@ -320,7 +329,6 @@ public class PriceInfoTooltip {
return message;
}
-
private static Text getMotesMessage(int price, int count) {
float motesMultiplier = SkyblockerConfig.get().locations.rift.mcGrubberStacks * 0.05f + 1;
@@ -346,7 +354,7 @@ public class PriceInfoTooltip {
public static int minute = -1;
public static void init() {
- skyblocker.scheduler.scheduleCyclic(() -> {
+ Scheduler.INSTANCE.scheduleCyclic(() -> {
if (!Utils.isOnSkyblock() && 0 < minute++) {
nullMsgSend = false;
return;
@@ -367,10 +375,10 @@ public class PriceInfoTooltip {
futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg")));
}
}
- if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN)
+ if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN || SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins")));
- if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice)
+ if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice || SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar")));
if (SkyblockerConfig.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null)
@@ -391,24 +399,43 @@ public class PriceInfoTooltip {
private static JsonObject downloadPrices(String type) {
try {
String url = apiAddresses.get(type);
- URL apiAddress = new URL(url);
- InputStream src = apiAddress.openStream();
- InputStreamReader reader = new InputStreamReader(url.contains(".gz") ? new GZIPInputStream(src) : src);
- return new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e) {
+
+ if (type.equals("npc") || type.equals("museum") || type.equals("motes")) {
+ HttpHeaders headers = Http.sendHeadRequest(url);
+ long combinedHash = Http.getEtag(headers).hashCode() + Http.getLastModified(headers).hashCode();
+
+ switch (type) {
+ case "npc": if (npcHash == combinedHash) return npcPricesJson; else npcHash = combinedHash;
+ case "museum": if (museumHash == combinedHash) return isMuseumJson; else museumHash = combinedHash;
+ case "motes": if (motesHash == combinedHash) return motesPricesJson; else motesHash = combinedHash;
+ }
+ }
+
+ String apiResponse = Http.sendGetRequest(url);
+
+ return new Gson().fromJson(apiResponse, JsonObject.class);
+ } catch (Exception e) {
LOGGER.warn("[Skyblocker] Failed to download " + type + " prices!", e);
return null;
}
}
+
+ public static JsonObject getBazaarPrices() {
+ return bazaarPricesJson;
+ }
+
+ public static JsonObject getLBINPrices() {
+ return lowestPricesJson;
+ }
static {
apiAddresses = new HashMap<>();
- apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json.gz");
- apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json.gz");
+ apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json");
+ apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json");
apiAddresses.put("bazaar", "https://hysky.de/api/bazaar");
apiAddresses.put("lowest bins", "https://hysky.de/api/auctions/lowestbins");
apiAddresses.put("npc", "https://hysky.de/api/npcprice");
apiAddresses.put("museum", "https://hysky.de/api/museum");
apiAddresses.put("motes", "https://hysky.de/api/motesprice");
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
index 04dc6820..4fc24e6c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
@@ -6,8 +6,6 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.gui.Drawable;
-import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.util.math.MatrixStack;
@@ -16,7 +14,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
@Environment(value = EnvType.CLIENT)
-public class ItemListWidget extends RecipeBookWidget implements Drawable, Selectable {
+public class ItemListWidget extends RecipeBookWidget {
private int parentWidth;
private int parentHeight;
private int leftOffset;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
index c0685ec7..5eb9e488 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
@@ -129,5 +129,9 @@ public class ItemRegistry {
if (itemStack.getNbt() == null) return "";
return itemStack.getNbt().getCompound("ExtraAttributes").getString("id");
}
+
+ public static ItemStack getItemStack(String internalName) {
+ return itemsMap.get(internalName);
+ }
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
index e74c145d..8028099a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
@@ -5,6 +5,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.xmrvizzy.skyblocker.utils.NEURepo;
+import net.minecraft.item.FireworkRocketItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.*;
import net.minecraft.text.Text;
@@ -90,6 +91,16 @@ public class ItemStackBuilder {
enchantments.add(new NbtCompound());
tag.put("Enchantments", enchantments);
}
+
+ // Add firework star color
+ Matcher explosionColorMatcher = Pattern.compile("\\{Explosion:\\{(?:Type:[0-9a-z]+,)?Colors:\\[(?<color>[0-9]+)\\]\\}").matcher(nbttag);
+ if (explosionColorMatcher.find()) {
+ NbtCompound explosion = new NbtCompound();
+
+ explosion.putInt("Type", FireworkRocketItem.Type.SMALL_BALL.getId()); //Forget about the actual ball type because it probably doesn't matter
+ explosion.putIntArray("Colors", new int[] { Integer.parseInt(explosionColorMatcher.group("color")) });
+ tag.put("Explosion", explosion);
+ }
return ItemStack.fromNbt(root);
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
index 12636ce1..19f656e5 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
@@ -1,19 +1,13 @@
package me.xmrvizzy.skyblocker.skyblock.itemlist;
import java.util.List;
-import java.util.function.Function;
import java.util.ArrayList;
-import com.google.common.collect.Lists;
-import com.mojang.blaze3d.systems.RenderSystem;
-
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.gui.widget.ClickableWidget;
-import net.minecraft.client.render.GameRenderer;
-import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.text.OrderedText;
@@ -55,17 +49,17 @@ public class ResultButtonWidget extends ClickableWidget {
MinecraftClient client = MinecraftClient.getInstance();
List<Text> tooltip = Screen.getTooltipFromItem(client, this.itemStack);
List<OrderedText> orderedTooltip = new ArrayList<>();
-
+
for(int i = 0; i < tooltip.size(); i++) {
orderedTooltip.add(tooltip.get(i).asOrderedText());
}
-
+
client.currentScreen.setTooltip(orderedTooltip);
}
@Override
protected void appendClickableNarrations(NarrationMessageBuilder builder) {
// TODO Auto-generated method stub
-
+
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
index 7f628a19..feb7f30b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
@@ -126,7 +126,7 @@ public class SearchResultsWidget implements Drawable {
craftText = textRenderer.trimToWidth(craftText, MAX_TEXT_WIDTH) + ELLIPSIS;
}
context.drawTextWithShadow(textRenderer, craftText, this.parentX + 11, this.parentY + 31, 0xffffffff);
-
+
//Item name
Text resultText = this.recipeResults.get(this.currentPage).result.getName();
if (textRenderer.getWidth(Formatting.strip(resultText.getString())) > MAX_TEXT_WIDTH) {
@@ -134,7 +134,7 @@ public class SearchResultsWidget implements Drawable {
resultText = Text.literal(getLegacyFormatting(resultText.getString()) + textRenderer.trimToWidth(Formatting.strip(resultText.getString()), MAX_TEXT_WIDTH) + ELLIPSIS).setStyle(resultText.getStyle());
}
context.drawTextWithShadow(textRenderer, resultText, this.parentX + 11, this.parentY + 43, 0xffffffff);
-
+
//Arrow pointing to result item from the recipe
context.drawTextWithShadow(textRenderer, "â–¶", this.parentX + 96, this.parentY + 90, 0xaaffffff);
}
@@ -149,7 +149,7 @@ public class SearchResultsWidget implements Drawable {
if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta);
RenderSystem.enableDepthTest();
}
-
+
/**
* Used for drawing tooltips over truncated text
*/
@@ -160,7 +160,7 @@ public class SearchResultsWidget implements Drawable {
}
RenderSystem.enableDepthTest();
}
-
+
/**
* @see #drawTooltip(TextRenderer, DrawContext, Text, int, int, int, int)
*/
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
index 351cef48..fb8f438b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
@@ -5,20 +5,25 @@ import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.fabric.api.client.screen.v1.Screens;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
+import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.StringNbtReader;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.regex.PatternSyntaxException;
public class QuickNav {
private static final String skyblockHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}";
private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}";
public static void init() {
- ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?> && client.player != null && !client.player.isCreative()) {
String screenTitle = screen.getTitle().getString().trim();
List<QuickNavButton> buttons = QuickNav.init(screenTitle);
@@ -54,8 +59,18 @@ public class QuickNav {
String nbtString = "{id:\"minecraft:" + itemData.itemName.toLowerCase(Locale.ROOT) + "\",Count:1";
if (itemData.nbt.length() > 2) nbtString += "," + itemData.nbt;
nbtString += "}";
+ boolean uiTitleMatches = false;
+ try {
+ uiTitleMatches = screenTitle.matches(buttonInfo.uiTitle);
+ } catch (PatternSyntaxException e) {
+ e.printStackTrace();
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ if (player != null) {
+ player.sendMessage(Text.of(Formatting.RED + "[Skyblocker] Invalid regex in quicknav button " + (id + 1) + "!"), false);
+ }
+ }
return new QuickNavButton(id,
- screenTitle.matches(buttonInfo.uiTitle),
+ uiTitleMatches,
buttonInfo.clickEvent,
ItemStack.fromNbt(StringNbtReader.parse(nbtString))
);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
index d9e97976..e41ea768 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
@@ -2,8 +2,8 @@ package me.xmrvizzy.skyblocker.skyblock.quicknav;
import com.mojang.blaze3d.systems.RenderSystem;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.mixin.accessor.HandledScreenAccessor;
+import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
@@ -54,7 +54,7 @@ public class QuickNavButton extends ClickableWidget {
public void onClick(double mouseX, double mouseY) {
if (!this.toggled) {
this.toggled = true;
- SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(command);
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown(command);
// TODO : add null check with log error
}
}
@@ -102,6 +102,6 @@ public class QuickNavButton extends ClickableWidget {
@Override
protected void appendClickableNarrations(NarrationMessageBuilder builder) {
// TODO Auto-generated method stub
-
+
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java
index 5ca89dcf..4b11fcb0 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java
@@ -1,7 +1,7 @@
package me.xmrvizzy.skyblocker.skyblock.rift;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
public class TheRift {
@@ -13,9 +13,9 @@ public class TheRift {
public static void init() {
WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render);
WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render);
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(EffigyWaypoints::updateEffigies, SkyblockerConfig.get().slayer.vampireSlayer.effigyUpdateFrequency);
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(TwinClawsIndicator::updateIce, SkyblockerConfig.get().slayer.vampireSlayer.holyIceUpdateFrequency);
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(ManiaIndicator::updateMania, SkyblockerConfig.get().slayer.vampireSlayer.maniaUpdateFrequency);
- SkyblockerMod.getInstance().scheduler.scheduleCyclic(StakeIndicator::updateStake, SkyblockerConfig.get().slayer.vampireSlayer.steakStakeUpdateFrequency);
+ Scheduler.INSTANCE.scheduleCyclic(EffigyWaypoints::updateEffigies, SkyblockerConfig.get().slayer.vampireSlayer.effigyUpdateFrequency);
+ Scheduler.INSTANCE.scheduleCyclic(TwinClawsIndicator::updateIce, SkyblockerConfig.get().slayer.vampireSlayer.holyIceUpdateFrequency);
+ Scheduler.INSTANCE.scheduleCyclic(ManiaIndicator::updateMania, SkyblockerConfig.get().slayer.vampireSlayer.maniaUpdateFrequency);
+ Scheduler.INSTANCE.scheduleCyclic(StakeIndicator::updateStake, SkyblockerConfig.get().slayer.vampireSlayer.steakStakeUpdateFrequency);
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java
index e141b6a8..6e6fad2d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java
@@ -1,12 +1,12 @@
package me.xmrvizzy.skyblocker.skyblock.rift;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.SlayerUtils;
import me.xmrvizzy.skyblocker.utils.Utils;
import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
import me.xmrvizzy.skyblocker.utils.render.title.Title;
import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer;
+import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.minecraft.entity.Entity;
import net.minecraft.util.Formatting;
@@ -29,7 +29,7 @@ public class TwinClawsIndicator {
anyClaws = true;
if (!TitleContainer.containsTitle(title) && !scheduled) {
scheduled = true;
- SkyblockerMod.getInstance().scheduler.schedule(() -> {
+ Scheduler.INSTANCE.schedule(() -> {
RenderHelper.displayInTitleContainerAndPlaySound(title);
scheduled = false;
}, SkyblockerConfig.get().slayer.vampireSlayer.holyIceIndicatorTickDelay);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java
index 89329c6c..14bc1db4 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java
@@ -63,7 +63,7 @@ public class Shortcuts {
LOGGER.info("[Skyblocker] Loaded {} command shortcuts and {} command argument shortcuts", commands.size(), commandArgs.size());
} catch (FileNotFoundException e) {
registerDefaultShortcuts();
- LOGGER.warn("[Skyblocker] Shortcuts file not found, using default shortcuts. This is normal when using for the first time.", e);
+ LOGGER.warn("[Skyblocker] Shortcuts file not found, using default shortcuts. This is normal when using for the first time.");
} catch (IOException e) {
LOGGER.error("[Skyblocker] Failed to load shortcuts file", e);
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java
new file mode 100644
index 00000000..a3d7e5c5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java
@@ -0,0 +1,96 @@
+package me.xmrvizzy.skyblocker.skyblock.special;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.enchantment.Enchantments;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.StringNbtReader;
+import net.minecraft.particle.ParticleTypes;
+import net.minecraft.text.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SpecialEffects {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpecialEffects.class);
+ private static final Pattern DROP_PATTERN = Pattern.compile("(?:\\[[A-Z+]+] )?(?<player>[A-Za-z0-9_]+) unlocked (?<item>.+)!");
+ private static final ItemStack NECRON_HANDLE = new ItemStack(Items.STICK);
+ private static final ItemStack SCROLL = new ItemStack(Items.WRITABLE_BOOK);
+ private static ItemStack TIER_5_SKULL;
+ private static ItemStack FIFTH_STAR;
+
+ static {
+ NECRON_HANDLE.addEnchantment(Enchantments.PROTECTION, 1);
+ SCROLL.addEnchantment(Enchantments.PROTECTION, 1);
+ try {
+ TIER_5_SKULL = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-1613868903,-527154034,-1445577520,748807544],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTEwZjlmMTA4NWQ0MDcxNDFlYjc3NjE3YTRhYmRhYWEwOGQ4YWYzM2I5NjAyMDBmZThjMTI2YzFkMTQ0NTY4MiJ9fX0=\"}]}}}}"));
+ FIFTH_STAR = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1904417095,756174249,-1302927470,1407004198],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzFjODA0MjUyN2Y4MWM4ZTI5M2UyODEwMTEzNDg5ZjQzOTRjYzZlZmUxNWQxYWZhYzQzMTU3MWM3M2I2MmRjNCJ9fX0=\"}]}}}}"));
+ } catch (Exception e) {
+ TIER_5_SKULL = ItemStack.EMPTY;
+ FIFTH_STAR = ItemStack.EMPTY;
+ LOGGER.error("[Skyblocker Special Effects] Failed to parse NBT for a player head!", e);
+ }
+ }
+
+ public static void init() {
+ ClientReceiveMessageEvents.GAME.register(SpecialEffects::displayRareDropEffect);
+ }
+
+ private static void displayRareDropEffect(Text message, boolean overlay) {
+ //We don't check if we're in dungeons because that check doesn't work in m7 which defeats the point of this
+ //It might also allow it to work with Croesus
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.specialEffects.rareDungeonDropEffects) {
+ try {
+ String stringForm = message.getString();
+ Matcher matcher = DROP_PATTERN.matcher(stringForm);
+
+ if (matcher.matches()) {
+ MinecraftClient client = MinecraftClient.getInstance();
+ String player = matcher.group("player");
+
+ if (player.equals(client.getSession().getUsername())) {
+ ItemStack stack = getStackFromName(matcher.group("item"));
+
+ if (!stack.isEmpty()) {
+ if (RenderSystem.isOnRenderThread()) {
+ client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30);
+ client.gameRenderer.showFloatingItem(stack);
+ } else {
+ RenderSystem.recordRenderCall(() -> {
+ client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30);
+ client.gameRenderer.showFloatingItem(stack);
+ });
+ }
+ }
+ }
+ }
+ } catch (Exception e) { //In case there's a regex failure or something else bad happens
+ LOGGER.error("[Skyblocker Special Effects] An unexpected exception was encountered: ", e);
+ }
+ }
+ }
+
+ private static ItemStack getStackFromName(String itemName) {
+ return switch (itemName) {
+ //M7
+ case "Necron Dye" -> new ItemStack(Items.ORANGE_DYE);
+ case "Dark Claymore" -> new ItemStack(Items.STONE_SWORD);
+ case "Necron's Handle", "Shiny Necron's Handle" -> NECRON_HANDLE;
+ case "Enchanted Book (Thunderlord VII)" -> new ItemStack(Items.ENCHANTED_BOOK);
+ case "Master Skull - Tier 5" -> TIER_5_SKULL;
+ case "Shadow Warp", "Wither Shield", "Implosion" -> SCROLL;
+ case "Fifth Master Star" -> FIFTH_STAR;
+
+ //M6
+ case "Giant's Sword" -> new ItemStack(Items.IRON_SWORD);
+
+ default -> ItemStack.EMPTY;
+ };
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java
new file mode 100644
index 00000000..12ce0715
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java
@@ -0,0 +1,170 @@
+package me.xmrvizzy.skyblocker.skyblock.spidersden;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.mojang.brigadier.CommandDispatcher;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.PosUtils;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.command.CommandRegistryAccess;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.text.Text;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class Relics {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Relics.class);
+ private static CompletableFuture<Void> relicsLoaded;
+ @SuppressWarnings({"unused", "FieldCanBeLocal"})
+ private static int totalRelics = 0;
+ private static final List<BlockPos> relics = new ArrayList<>();
+ private static final Map<String, Set<BlockPos>> foundRelics = new HashMap<>();
+
+ public static void init() {
+ ClientLifecycleEvents.CLIENT_STARTED.register(Relics::loadRelics);
+ ClientLifecycleEvents.CLIENT_STOPPING.register(Relics::saveFoundRelics);
+ ClientCommandRegistrationCallback.EVENT.register(Relics::registerCommands);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(Relics::render);
+ ClientReceiveMessageEvents.GAME.register(Relics::onChatMessage);
+ }
+
+ private static void loadRelics(MinecraftClient client) {
+ relicsLoaded = CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = client.getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "spidersden/relics.json"))) {
+ for (Map.Entry<String, JsonElement> json : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
+ if (json.getKey().equals("total")) {
+ totalRelics = json.getValue().getAsInt();
+ } else if (json.getKey().equals("locations")) {
+ for (JsonElement locationJson : json.getValue().getAsJsonArray().asList()) {
+ JsonObject posData = locationJson.getAsJsonObject();
+ relics.add(new BlockPos(posData.get("x").getAsInt(), posData.get("y").getAsInt(), posData.get("z").getAsInt()));
+ }
+ }
+ }
+ LOGGER.info("[Skyblocker] Loaded relics locations");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load relics locations", e);
+ }
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) {
+ for (Map.Entry<String, JsonElement> profileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
+ Set<BlockPos> foundRelicsForProfile = new HashSet<>();
+ for (JsonElement foundRelicsJson : profileJson.getValue().getAsJsonArray().asList()) {
+ foundRelicsForProfile.add(PosUtils.parsePosString(foundRelicsJson.getAsString()));
+ }
+ foundRelics.put(profileJson.getKey(), foundRelicsForProfile);
+ }
+ LOGGER.debug("[Skyblocker] Loaded found relics");
+ } catch (FileNotFoundException ignored) {
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load found relics", e);
+ }
+ });
+ }
+
+ private static void saveFoundRelics(MinecraftClient client) {
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) {
+ JsonObject json = new JsonObject();
+ for (Map.Entry<String, Set<BlockPos>> foundRelicsForProfile : foundRelics.entrySet()) {
+ JsonArray foundRelicsJson = new JsonArray();
+ for (BlockPos foundRelic : foundRelicsForProfile.getValue()) {
+ foundRelicsJson.add(PosUtils.getPosString(foundRelic));
+ }
+ json.add(foundRelicsForProfile.getKey(), foundRelicsJson);
+ }
+ SkyblockerMod.GSON.toJson(json, writer);
+ LOGGER.debug("[Skyblocker] Saved found relics");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to write found relics to file", e);
+ }
+ }
+
+ private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) {
+ dispatcher.register(literal(SkyblockerMod.NAMESPACE)
+ .then(literal("relics")
+ .then(literal("markAllFound").executes(context -> {
+ Relics.markAllFound();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllFound"));
+ return 1;
+ }))
+ .then(literal("markAllMissing").executes(context -> {
+ Relics.markAllMissing();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllMissing"));
+ return 1;
+ }))));
+ }
+
+ private static void render(WorldRenderContext context) {
+ SkyblockerConfig.Relics config = SkyblockerConfig.get().locations.spidersDen.relics;
+
+ if (config.enableRelicsHelper && relicsLoaded.isDone() && Utils.getLocationRaw().equals("combat_1")) {
+ for (BlockPos fairySoulPos : relics) {
+ boolean isRelicMissing = isRelicMissing(fairySoulPos);
+ if (!isRelicMissing && !config.highlightFoundRelics) continue;
+ float[] colorComponents = isRelicMissing ? DyeColor.YELLOW.getColorComponents() : DyeColor.BROWN.getColorComponents();
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F);
+ }
+ }
+ }
+
+ private static void onChatMessage(Text text, boolean overlay) {
+ String message = text.getString();
+ if (message.equals("You've already found this relic!") || message.startsWith("+10,000 Coins! (") && message.endsWith("/28 Relics)")) {
+ markClosestRelicFound();
+ }
+ }
+
+ private static void markClosestRelicFound() {
+ if (!relicsLoaded.isDone()) return;
+ PlayerEntity player = MinecraftClient.getInstance().player;
+ if (player == null) {
+ LOGGER.warn("[Skyblocker] Failed to mark closest relic as found because player is null");
+ return;
+ }
+ relics.stream()
+ .filter(Relics::isRelicMissing)
+ .min(Comparator.comparingDouble(relicPos -> relicPos.getSquaredDistance(player.getPos())))
+ .filter(relicPos -> relicPos.getSquaredDistance(player.getPos()) <= 16)
+ .ifPresent(relicPos -> {
+ foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>());
+ foundRelics.get(Utils.getProfile()).add(relicPos);
+ });
+ }
+
+ private static boolean isRelicMissing(BlockPos relicPos) {
+ Set<BlockPos> foundRelicsForProfile = foundRelics.get(Utils.getProfile());
+ return foundRelicsForProfile == null || !foundRelicsForProfile.contains(relicPos);
+ }
+
+ private static void markAllFound() {
+ foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>());
+ foundRelics.get(Utils.getProfile()).addAll(relics);
+ }
+
+ private static void markAllMissing() {
+ Set<BlockPos> foundRelicsForProfile = foundRelics.get(Utils.getProfile());
+ if (foundRelicsForProfile != null) {
+ foundRelicsForProfile.clear();
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java
index 084a6ffd..f02c7dc6 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java
@@ -99,8 +99,6 @@ public class ScreenBuilder {
}
// reflect something together for the "normal" ones.
- // TODO don't get package list for every widget; do it once and cache.
- // fine for now, as this would only shorten the load time anyways
// list all packages that might contain widget classes
// using Package isn't reliable, as some classes might not be loaded yet,
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java
index 5eb575c3..078927ef 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java
@@ -94,7 +94,6 @@ public class StackStage extends PipelineStage {
}
public void stackWidgetsHoriz(int screenW) {
- // TODO not centered (?)
int compWidth = -ScreenConst.WIDGET_PAD;
for (Widget wid : primary) {
compWidth += wid.getWidth() + ScreenConst.WIDGET_PAD;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
index b7f58763..1d056e0c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
@@ -10,7 +10,6 @@ import java.util.Arrays;
import java.util.Comparator;
// this widget shows a list of obtained dungeon buffs
-// TODO: could be more pretty, can't be arsed atm
public class DungeonBuffWidget extends Widget {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
index 2e8e2c40..afa883be 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
@@ -60,7 +60,7 @@ public class IslandOwnersWidget extends Widget {
PlainTextComponent ptc = new PlainTextComponent(entry);
this.addComponent(ptc);
}
-
+
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
index 1be6adda..9f60ed34 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
@@ -19,7 +19,7 @@ public class PlayerComponent extends Component {
private final Identifier tex;
public PlayerComponent(PlayerListEntry ple) {
-
+
boolean plainNames = SkyblockerConfig.get().general.tabHud.plainPlayerNames;
Team team = ple.getScoreboardTeam();
String username = ple.getProfile().getName();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java
new file mode 100644
index 00000000..aef55687
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java
@@ -0,0 +1,8 @@
+package me.xmrvizzy.skyblocker.utils;
+
+/**
+ * Holds generic static constants
+ */
+public interface Constants {
+ String LEVEL_EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u26C2\u12DE";
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java
new file mode 100644
index 00000000..3461189c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java
@@ -0,0 +1,89 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpClient.Version;
+import java.net.http.HttpHeaders;
+import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.time.Duration;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import net.minecraft.SharedConstants;
+
+/**
+ * @implNote All http requests are sent using HTTP 2
+ */
+public class Http {
+ private static final String USER_AGENT = "Skyblocker/" + SkyblockerMod.VERSION + " (" + SharedConstants.getGameVersion().getName() + ")";
+ private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(10))
+ .build();
+
+ public static String sendGetRequest(String url) throws IOException, InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .GET()
+ .header("Accept", "application/json")
+ .header("Accept-Encoding", "gzip, deflate")
+ .header("User-Agent", USER_AGENT)
+ .version(Version.HTTP_2)
+ .uri(URI.create(url))
+ .build();
+
+ HttpResponse<InputStream> response = HTTP_CLIENT.send(request, BodyHandlers.ofInputStream());
+ InputStream decodedInputStream = getDecodedInputStream(response);
+ String body = new String(decodedInputStream.readAllBytes());
+
+ return body;
+ }
+
+ public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .method("HEAD", BodyPublishers.noBody())
+ .header("User-Agent", USER_AGENT)
+ .version(Version.HTTP_2)
+ .uri(URI.create(url))
+ .build();
+
+ HttpResponse<Void> response = HTTP_CLIENT.send(request, BodyHandlers.discarding());
+ return response.headers();
+ }
+
+ private static InputStream getDecodedInputStream(HttpResponse<InputStream> response) {
+ String encoding = getContentEncoding(response);
+
+ try {
+ switch (encoding) {
+ case "":
+ return response.body();
+ case "gzip":
+ return new GZIPInputStream(response.body());
+ case "deflate":
+ return new InflaterInputStream(response.body());
+ default:
+ throw new UnsupportedOperationException("The server sent content in an unexpected encoding: " + encoding);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static String getContentEncoding(HttpResponse<InputStream> response) {
+ return response.headers().firstValue("Content-Encoding").orElse("");
+ }
+
+ public static String getEtag(HttpHeaders headers) {
+ return headers.firstValue("Etag").orElse("");
+ }
+
+ public static String getLastModified(HttpHeaders headers) {
+ return headers.firstValue("Last-Modified").orElse("");
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java
new file mode 100644
index 00000000..4f32292c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java
@@ -0,0 +1,14 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import net.minecraft.util.math.BlockPos;
+
+public final class PosUtils {
+ public static BlockPos parsePosString(String posData) {
+ String[] posArray = posData.split(",");
+ return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2]));
+ }
+
+ public static String getPosString(BlockPos blockPos) {
+ return blockPos.getX() + "," + blockPos.getY() + "," + blockPos.getZ();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
index 839e0dae..755e191d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
@@ -2,13 +2,14 @@ package me.xmrvizzy.skyblocker.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
import me.xmrvizzy.skyblocker.skyblock.rift.TheRift;
+import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
+import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity;
@@ -19,6 +20,8 @@ import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.scoreboard.Team;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
@@ -28,6 +31,7 @@ import java.util.List;
* Utility variables and methods for retrieving Skyblock related information.
*/
public class Utils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
@@ -45,7 +49,7 @@ public class Utils {
private static String map = "";
private static long clientWorldJoinTime = 0;
private static boolean sentLocRaw = false;
- private static long lastLocRaw = 0;
+ private static boolean canSendLocRaw = false;
public static boolean isOnHypixel() {
return isOnHypixel;
@@ -124,19 +128,25 @@ public class Utils {
public static void updateFromScoreboard(MinecraftClient client) {
List<String> sidebar;
- if (client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null) {
- isOnSkyblock = false;
- isInDungeons = false;
- return;
+ FabricLoader fabricLoader = FabricLoader.getInstance();
+ if ((client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null)) {
+ if (fabricLoader.isDevelopmentEnvironment()) {
+ sidebar = Collections.emptyList();
+ } else {
+ isOnSkyblock = false;
+ isInDungeons = false;
+ return;
+ }
}
+
+ if (sidebar.isEmpty() && !fabricLoader.isDevelopmentEnvironment()) return;
String string = sidebar.toString();
- if (sidebar.isEmpty()) return;
- if (isConnectedToHypixel(client)) {
+ if (fabricLoader.isDevelopmentEnvironment() || isConnectedToHypixel(client)) {
if (!isOnHypixel) {
isOnHypixel = true;
}
- if (sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) {
+ if (fabricLoader.isDevelopmentEnvironment() || sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) {
if (!isOnSkyblock) {
if (!isInjected) {
isInjected = true;
@@ -148,19 +158,18 @@ public class Utils {
} else {
leaveSkyblock();
}
- isInDungeons = isOnSkyblock && string.contains("The Catacombs");
+ isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs");
} else if (isOnHypixel) {
isOnHypixel = false;
leaveSkyblock();
}
}
-
+
private static boolean isConnectedToHypixel(MinecraftClient client) {
- String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
- String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : "";
- boolean isOnHypixel = (serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"));
-
- return isOnHypixel;
+ String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
+ String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : "";
+
+ return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord");
}
private static void leaveSkyblock() {
@@ -184,7 +193,7 @@ public class Utils {
location = location.strip();
}
} catch (IndexOutOfBoundsException e) {
- e.printStackTrace();
+ LOGGER.error("[Skyblocker] Failed to get location from sidebar", e);
}
return location;
}
@@ -206,7 +215,7 @@ public class Utils {
else purse = 0;
} catch (IndexOutOfBoundsException e) {
- e.printStackTrace();
+ LOGGER.error("[Skyblocker] Failed to get purse from sidebar", e);
}
return purse;
}
@@ -225,12 +234,11 @@ public class Utils {
bits = Integer.parseInt(bitsString.replaceAll("[^0-9.]", "").strip());
}
} catch (IndexOutOfBoundsException e) {
- e.printStackTrace();
+ LOGGER.error("[Skyblocker] Failed to get bits from sidebar", e);
}
return bits;
}
-
public static List<String> getSidebar() {
try {
ClientPlayerEntity client = MinecraftClient.getInstance().player;
@@ -242,7 +250,7 @@ public class Utils {
Team team = scoreboard.getPlayerTeam(score.getPlayerName());
if (team != null) {
String line = team.getPrefix().getString() + team.getSuffix().getString();
- if (line.trim().length() > 0) {
+ if (!line.trim().isEmpty()) {
String formatted = Formatting.strip(line);
lines.add(formatted);
}
@@ -285,10 +293,10 @@ public class Utils {
private static void updateLocRaw() {
if (isOnSkyblock) {
long currentTime = System.currentTimeMillis();
- if (!sentLocRaw && currentTime > clientWorldJoinTime + 1000 && currentTime > lastLocRaw + 15000) {
- SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown("/locraw");
+ if (!sentLocRaw && canSendLocRaw && currentTime > clientWorldJoinTime + 1000) {
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw");
sentLocRaw = true;
- lastLocRaw = currentTime;
+ canSendLocRaw = false;
}
} else {
resetLocRawInfo();
@@ -315,7 +323,11 @@ public class Utils {
if (locRaw.has("map")) {
map = locRaw.get("map").getAsString();
}
- return !sentLocRaw;
+
+ boolean shouldFilter = !sentLocRaw;
+ sentLocRaw = false;
+
+ return shouldFilter;
}
}
return true;
@@ -323,6 +335,7 @@ public class Utils {
private static void resetLocRawInfo() {
sentLocRaw = false;
+ canSendLocRaw = true;
server = "";
gameType = "";
locationRaw = "";
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
index aaf92d68..907896e2 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
@@ -95,7 +95,7 @@ public class RenderHelper {
matrices.translate(-camera.getX(), -camera.getY(), -camera.getZ());
buffer.begin(DrawMode.LINES, VertexFormats.LINES);
- WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0] * 255f, colorComponents[1] * 255f, colorComponents[2] * 255f, 1f);
+ WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0], colorComponents[1], colorComponents[2], 1f);
tessellator.draw();
matrices.pop();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java
index 6e15c871..2555572c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java
@@ -1,6 +1,5 @@
package me.xmrvizzy.skyblocker.utils.render.title;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
@@ -66,7 +65,7 @@ public class TitleContainer {
*/
public static boolean addTitle(Title title, int ticks) {
if (addTitle(title)) {
- SkyblockerMod.getInstance().scheduler.schedule(() -> TitleContainer.removeTitle(title), ticks);
+ Scheduler.INSTANCE.schedule(() -> TitleContainer.removeTitle(title), ticks);
return true;
}
return false;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java
index bde29c13..b8ffa548 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java
@@ -3,19 +3,22 @@ package me.xmrvizzy.skyblocker.utils.scheduler;
import net.minecraft.client.MinecraftClient;
/**
- * A scheduler for sending chat messages or commands. Use the instance in {@link me.xmrvizzy.skyblocker.SkyblockerMod#messageScheduler SkyblockerMod.messageScheduler}. Do not instantiate this class.
+ * A scheduler for sending chat messages or commands. Use the instance in {@link #INSTANCE}. Do not instantiate this class.
*/
-@SuppressWarnings("deprecation")
public class MessageScheduler extends Scheduler {
/**
* The minimum delay that the server will accept between chat messages.
*/
private static final int MIN_DELAY = 200;
+ public static final MessageScheduler INSTANCE = new MessageScheduler();
/**
* The timestamp of the last message send,
*/
private long lastMessage = 0;
+ protected MessageScheduler() {
+ }
+
/**
* Sends a chat message or command after the minimum cooldown. Prefer this method to send messages or commands to the server.
*
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java
index 76112e0d..700bdce3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java
@@ -1,30 +1,28 @@
package me.xmrvizzy.skyblocker.utils.scheduler;
import com.mojang.brigadier.Command;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
+import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.PriorityQueue;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Supplier;
/**
- * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link SkyblockerMod#scheduler}. Do not instantiate this class.
+ * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link #INSTANCE}. Do not instantiate this class.
*/
public class Scheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class);
+ public static final Scheduler INSTANCE = new Scheduler();
private int currentTick = 0;
- private final PriorityQueue<ScheduledTask> tasks = new PriorityQueue<>();
+ private final AbstractInt2ObjectMap<List<ScheduledTask>> tasks = new Int2ObjectOpenHashMap<>();
- /**
- * Do not instantiate this class. Use {@link SkyblockerMod#scheduler} instead.
- */
- @SuppressWarnings("DeprecatedIsStillUsed")
- @Deprecated
- public Scheduler() {
+ protected Scheduler() {
}
/**
@@ -34,11 +32,11 @@ public class Scheduler {
* @param delay the delay in ticks
*/
public void schedule(Runnable task, int delay) {
- if (delay < 0) {
+ if (delay >= 0) {
+ addTask(new ScheduledTask(task), currentTick + delay);
+ } else {
LOGGER.warn("Scheduled a task with negative delay");
}
- ScheduledTask tmp = new ScheduledTask(task, currentTick + delay);
- tasks.add(tmp);
}
/**
@@ -48,15 +46,15 @@ public class Scheduler {
* @param period the period in ticks
*/
public void scheduleCyclic(Runnable task, int period) {
- if (period <= 0) {
- LOGGER.error("Attempted to schedule a cyclic task with period lower than 1");
+ if (period > 0) {
+ addTask(new CyclicTask(task, period), currentTick);
} else {
- new CyclicTask(task, period).run();
+ LOGGER.error("Attempted to schedule a cyclic task with period lower than 1");
}
}
public static Command<FabricClientCommandSource> queueOpenScreenCommand(Supplier<Screen> screenSupplier) {
- return context -> SkyblockerMod.getInstance().scheduler.queueOpenScreen(screenSupplier);
+ return context -> INSTANCE.queueOpenScreen(screenSupplier);
}
/**
@@ -71,11 +69,18 @@ public class Scheduler {
}
public void tick() {
- currentTick += 1;
- ScheduledTask task;
- while ((task = tasks.peek()) != null && task.schedule <= currentTick && runTask(task)) {
- tasks.poll();
+ if (tasks.containsKey(currentTick)) {
+ List<ScheduledTask> currentTickTasks = tasks.get(currentTick);
+ //noinspection ForLoopReplaceableByForEach (or else we get a ConcurrentModificationException)
+ for (int i = 0; i < currentTickTasks.size(); i++) {
+ ScheduledTask task = currentTickTasks.get(i);
+ if (!runTask(task)) {
+ tasks.computeIfAbsent(currentTick + 1, key -> new ArrayList<>()).add(task);
+ }
+ }
+ tasks.remove(currentTick);
}
+ currentTick += 1;
}
/**
@@ -89,30 +94,42 @@ public class Scheduler {
return true;
}
+ private void addTask(ScheduledTask scheduledTask, int schedule) {
+ if (tasks.containsKey(schedule)) {
+ tasks.get(schedule).add(scheduledTask);
+ } else {
+ List<ScheduledTask> list = new ArrayList<>();
+ list.add(scheduledTask);
+ tasks.put(schedule, list);
+ }
+ }
+
/**
* A task that runs every period ticks. More specifically, this task reschedules itself to run again after period ticks every time it runs.
- *
- * @param inner the task to run
- * @param period the period in ticks
*/
- protected record CyclicTask(Runnable inner, int period) implements Runnable {
+ protected class CyclicTask extends ScheduledTask {
+ private final int period;
+
+ CyclicTask(Runnable inner, int period) {
+ super(inner);
+ this.period = period;
+ }
+
@Override
public void run() {
- SkyblockerMod.getInstance().scheduler.schedule(this, period);
- inner.run();
+ super.run();
+ addTask(this, currentTick + period);
}
}
/**
* A task that runs at a specific tick, relative to {@link #currentTick}.
- *
- * @param inner the task to run
- * @param schedule the tick to run at
*/
- protected record ScheduledTask(Runnable inner, int schedule) implements Comparable<ScheduledTask>, Runnable {
- @Override
- public int compareTo(ScheduledTask o) {
- return schedule - o.schedule;
+ protected static class ScheduledTask implements Runnable {
+ private final Runnable inner;
+
+ public ScheduledTask(Runnable inner) {
+ this.inner = inner;
}
@Override