aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java9
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java14
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigOption.java1
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java4
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CookieWarning.java211
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/MutingComposter.java57
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java61
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/world/FrozenTreasuresHighlighter.java56
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/CalendarOverlay.java14
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java9
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java5
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Garden.java (renamed from src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java)27
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java9
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MiscOverlays.java10
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/PetOverlay.java9
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java16
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java63
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/LevelPage.java15
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/CoreTaskLevel.java50
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/DungeonTaskLevel.java14
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EssenceTaskLevel.java16
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EventTaskLevel.java106
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/MiscTaskLevel.java46
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SkillRelatedTaskLevel.java90
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SlayingTaskLevel.java13
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/StoryTaskLevel.java6
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java45
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java7
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java41
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt2
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/OldSkyBlockMenu.kt195
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/ApiCache.kt217
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/ErrorUtil.kt43
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.kt47
-rw-r--r--src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/completablefuture.kt33
40 files changed, 1272 insertions, 299 deletions
diff --git a/README.md b/README.md
index 4249abc8..10cd88e5 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
<div align="center">
<!-- release -->
<a href="https://github.com/Moulberry/NotEnoughUpdates/releases/latest" target="_blank">
- <img src="https://img.shields.io/github/v/release/Moulberry/NotEnoughUpdates-REPO?color=informational&include_prereleases&label=release&logo=github&logoColor=white" alt="release">
+ <img src="https://img.shields.io/github/v/release/Moulberry/NotEnoughUpdates?color=informational&include_prereleases&label=release&logo=github&logoColor=white" alt="release">
</a>
<!-- lines -->
<a href="https://github.com/NotEnoughUpdates/NotEnoughUpdates/graphs/code-frequency" target="_blank">
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
index 7159ef89..927b5e76 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
@@ -1500,7 +1500,14 @@ public class NEUOverlay extends Gui {
int actualIndex = index + getSlotsXSize() * getSlotsYSize() * page;
List<JsonObject> searchedItems = getSearchedItems();
if (0 <= actualIndex && actualIndex < searchedItems.size()) {
- return searchedItems.get(actualIndex);
+ try {
+ return searchedItems.get(actualIndex);
+ } catch (IndexOutOfBoundsException e) {
+ System.out.println("searchedItems size: " + searchedItems.size());
+ System.out.println("actualIndex: " + actualIndex);
+ e.printStackTrace();
+ return null;
+ }
} else {
return null;
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
index 35bb40bf..7f0136ee 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
@@ -97,7 +97,7 @@ import java.util.Set;
guiFactory = "io.github.moulberry.notenoughupdates.core.config.MoulConfigGuiForgeInterop")
public class NotEnoughUpdates {
public static final String MODID = "notenoughupdates";
- public static final String VERSION = "2.1.0-REL";
+ public static final String VERSION = "2.1.1-PRE";
public static final int VERSION_ID = 20101; //2.1.1 only so update notif works
public static final int PRE_VERSION_ID = 0;
public static final int HOTFIX_VERSION_ID = 0;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
index 5ec3724a..ac60ffd9 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
@@ -292,7 +292,7 @@ public class APIManager {
.newMoulberryRequest("lowestbin.json.gz")
.gunzip()
.requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (lowestBins == null) {
lowestBins = new JsonObject();
}
@@ -465,12 +465,12 @@ public class APIManager {
};
manager.apiUtils.newMoulberryRequest("auctionLast.json.gz")
- .gunzip().requestJson().thenAccept(process);
+ .gunzip().requestJson().thenAcceptAsync(process);
manager.apiUtils
.newMoulberryRequest("auction.json.gz")
.gunzip().requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (jsonObject.get("success").getAsBoolean()) {
long apiUpdate = (long) jsonObject.get("time").getAsFloat();
if (lastApiUpdate == apiUpdate) {
@@ -683,7 +683,7 @@ public class APIManager {
manager.apiUtils
.newAnonymousHypixelApiRequest("skyblock/auctions")
.requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (jsonObject == null) return;
if (jsonObject.get("success").getAsBoolean()) {
@@ -733,7 +733,7 @@ public class APIManager {
manager.apiUtils
.newAnonymousHypixelApiRequest("skyblock/bazaar")
.requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (!jsonObject.get("success").getAsBoolean()) return;
craftCost.clear();
@@ -789,7 +789,7 @@ public class APIManager {
public void updateAvgPrices() {
manager.apiUtils
.newMoulberryRequest("auction_averages/3day.json.gz")
- .gunzip().requestJson().thenAccept((jsonObject) -> {
+ .gunzip().requestJson().thenAcceptAsync((jsonObject) -> {
craftCost.clear();
auctionPricesJson = jsonObject;
lastAuctionAvgUpdate = System.currentTimeMillis();
@@ -797,7 +797,7 @@ public class APIManager {
manager.apiUtils
.newMoulberryRequest("auction_averages_lbin/1day.json.gz")
.gunzip().requestJson()
- .thenAccept((jsonObject) -> {
+ .thenAcceptAsync((jsonObject) -> {
auctionPricesAvgLowestBinJson = jsonObject;
});
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigOption.java b/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigOption.java
index 920cb326..ddd1e71f 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigOption.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/core/config/annotations/ConfigOption.java
@@ -30,6 +30,7 @@ public @interface ConfigOption {
String name();
String desc();
+
String[] searchTags() default "";
int subcategoryId() default -1;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java
index 4a7c1939..984a7931 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java
@@ -150,7 +150,7 @@ public class CapeManager {
NotEnoughUpdates.INSTANCE.manager.apiUtils
.newMoulberryRequest("activecapes.json")
.requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (jsonObject.get("success").getAsBoolean()) {
lastJsonSync = jsonObject;
@@ -171,7 +171,7 @@ public class CapeManager {
NotEnoughUpdates.INSTANCE.manager.apiUtils
.newMoulberryRequest("permscapes.json")
.requestJson()
- .thenAccept(jsonObject -> {
+ .thenAcceptAsync(jsonObject -> {
if (!jsonObject.get("success").getAsBoolean()) return;
permSyncTries = 0;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CookieWarning.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CookieWarning.java
index 80751371..f130a993 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CookieWarning.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CookieWarning.java
@@ -31,9 +31,13 @@ public class CookieWarning {
private static boolean hasNotified;
private static boolean hasErrorMessage;
+ private static long cookieEndTime = 0;
+ private static boolean hasCookie = true;
+ private static long lastChecked = 0;
public static void resetNotification() {
hasNotified = false;
+ hasCookie = true;
NotificationHandler.cancelNotification();
}
@@ -41,96 +45,135 @@ public class CookieWarning {
* Checks the tab list for a cookie timer, and sends a notification if the timer is within the tolerance
*/
public static void checkCookie() {
- if (NotEnoughUpdates.INSTANCE.config.notifications.doBoosterNotif &&
- NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) {
- String[] lines;
- try {
- lines = ((AccessorGuiPlayerTabOverlay) Minecraft.getMinecraft().ingameGUI.getTabList())
- .getFooter()
- .getUnformattedText()
- .split("\n");
- } catch (NullPointerException e) {
- return; // if the footer is null or somehow doesn't exist, stop
+ if (!NotEnoughUpdates.INSTANCE.config.notifications.doBoosterNotif ||
+ !NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) {
+ return;
+ }
+ String timeLine = getTimeLine();
+ if (!hasCookie) {
+ if (!hasNotified) {
+ NotificationHandler.displayNotification(Lists.newArrayList(
+ "§cBooster Cookie Ran Out!",
+ "§7Your Booster Cookie expired!",
+ "§7",
+ "§7Press X on your keyboard to close this notification"
+ ), true, true);
+ hasNotified = true;
}
- boolean hasCookie = true;
- String timeLine = null; // the line that contains the cookie timer
- for (int i = 0; i < lines.length; i++) {
- if (lines[i].startsWith("Cookie Buff")) {
- timeLine = lines[i + 1]; // the line after the "Cookie Buff" line
- }
- if (lines[i].startsWith("Not active! Obtain booster cookies from the")) {
- hasCookie = false;
- }
+ return;
+ }
+ if (timeLine == null) return;
+
+ int minutes = getMinutesRemaining(timeLine);
+ if (minutes < NotEnoughUpdates.INSTANCE.config.notifications.boosterCookieWarningMins && !hasNotified) {
+ NotificationHandler.displayNotification(Lists.newArrayList(
+ "§cBooster Cookie Running Low!",
+ "§7Your Booster Cookie will expire in " + timeLine,
+ "§7",
+ "§7Press X on your keyboard to close this notification"
+ ), true, true);
+ hasNotified = true;
+ }
+ }
+
+ private static int getMinutesRemaining(String timeLine) {
+ String clean = timeLine.replaceAll("(§.)", "");
+ clean = clean.replaceAll("(\\d)([smhdy])", "$1 $2");
+ String[] digits = clean.split(" ");
+ int minutes = 0;
+ try {
+ for (int i = 0; i < digits.length; i++) {
+ if (i % 2 == 1) continue;
+
+ String number = digits[i];
+ String unit = digits[i + 1];
+ long val = Integer.parseInt(number);
+ switch (unit) {
+ case "Years":
+ case "Year":
+ minutes += val * 525600;
+ break;
+ case "Months":
+ case "Month":
+ minutes += val * 43200;
+ break;
+ case "Days":
+ case "Day":
+ minutes += val * 1440;
+ break;
+ case "Hours":
+ case "Hour":
+ case "h":
+ minutes += val * 60;
+ break;
+ case "Minutes":
+ case "Minute":
+ case "m":
+ minutes += val;
+ break;
+ } // ignore seconds
}
- if (!hasCookie) {
- if (!hasNotified) {
- NotificationHandler.displayNotification(Lists.newArrayList(
- "\u00a7cBooster Cookie Ran Out!",
- "\u00a77Your Booster Cookie expired!",
- "\u00a77",
- "\u00a77Press X on your keyboard to close this notification"
- ), true, true);
- hasNotified = true;
- }
- return;
+ } catch (NumberFormatException e) {
+ if (!hasErrorMessage) {
+ e.printStackTrace();
+ Utils.addChatMessage(EnumChatFormatting.RED +
+ "NEU ran into an issue when retrieving the Booster Cookie Timer. Check the logs for details.");
+ hasErrorMessage = true;
}
- if (timeLine != null) {
- String clean = timeLine.replaceAll("(\u00a7.)", "");
- clean = clean.replaceAll("(\\d)([smhdy])", "$1 $2");
- String[] digits = clean.split(" ");
- int minutes = 0;
- try {
- for (int i = 0; i < digits.length; i++) {
- if (i % 2 == 1) continue;
+ hasNotified = true;
+ }
+ return minutes;
+ }
- String number = digits[i];
- String unit = digits[i + 1];
- long val = Integer.parseInt(number);
- switch (unit) {
- case "Years":
- case "Year":
- minutes += val * 525600;
- break;
- case "Months":
- case "Month":
- minutes += val * 43200;
- break;
- case "Days":
- case "Day":
- minutes += val * 1440;
- break;
- case "Hours":
- case "Hour":
- case "h":
- minutes += val * 60;
- break;
- case "Minutes":
- case "Minute":
- case "m":
- minutes += val;
- break;
- } // ignore seconds
- }
- } catch (NumberFormatException e) {
- if (!hasErrorMessage) {
- e.printStackTrace();
- Utils.addChatMessage(EnumChatFormatting.RED +
- "NEU ran into an issue when retrieving the Booster Cookie Timer. Check the logs for details.");
- hasErrorMessage = true;
- }
- hasNotified = true;
- }
- if (minutes < NotEnoughUpdates.INSTANCE.config.notifications.boosterCookieWarningMins && !hasNotified) {
- NotificationHandler.displayNotification(Lists.newArrayList(
- "\u00a7cBooster Cookie Running Low!",
- "\u00a77Your Booster Cookie will expire in " + timeLine,
- "\u00a77",
- "\u00a77Press X on your keyboard to close this notification"
- ), true, true);
- hasNotified = true;
- }
+ private static String getTimeLine() {
+ String[] lines;
+ try {
+ lines = ((AccessorGuiPlayerTabOverlay) Minecraft.getMinecraft().ingameGUI.getTabList())
+ .getFooter()
+ .getUnformattedText()
+ .split("\n");
+ } catch (NullPointerException ignored) {
+ return null;
+ }
+ String timeLine = null; // the line that contains the cookie timer
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i].startsWith("Cookie Buff")) {
+ timeLine = lines[i + 1]; // the line after the "Cookie Buff" line
+ }
+ if (lines[i].startsWith("Not active! Obtain booster cookies from the")) {
+ hasCookie = false;
}
}
+ return timeLine;
+ }
+
+ public static boolean hasActiveBoosterCookie() {
+ long cookieEndTime = getCookieEndTime();
+ return cookieEndTime > System.currentTimeMillis();
+ }
+
+ private static long getCookieEndTime() {
+ // Only updating every 10 seconds
+// if (System.currentTimeMillis() > lastChecked + 10_000) return cookieEndTime;
+ if (lastChecked + 3_000 > System.currentTimeMillis()) return cookieEndTime;
+
+ String timeLine = getTimeLine();
+ if (hasCookie && timeLine != null) {
+ int minutes = getMinutesRemaining(timeLine);
+ cookieEndTime = System.currentTimeMillis() + (long) minutes * 60 * 1000;
+ } else {
+ cookieEndTime = 0;
+ }
+
+ lastChecked = System.currentTimeMillis();
+ return cookieEndTime;
+ }
+ public static void onProfileSwitch() {
+ resetNotification();
+ hasErrorMessage = false;
+ cookieEndTime = 0;
+ hasCookie = true;
+ lastChecked = 0;
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/MutingComposter.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/MutingComposter.java
new file mode 100644
index 00000000..4b4d655b
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/MutingComposter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscfeatures;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe;
+import io.github.moulberry.notenoughupdates.util.SBInfo;
+import net.minecraftforge.client.event.sound.PlaySoundEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.Arrays;
+import java.util.List;
+
+@NEUAutoSubscribe
+public class MutingComposter {
+
+ private static final MutingComposter INSTANCE = new MutingComposter();
+ private final List<String> mutableSounds = Arrays.asList(
+ "mob.wolf.growl",
+ "tile.piston.out",
+ "liquid.water",
+ "mob.chicken.plop"
+ );
+
+ public static MutingComposter getInstance() {
+ return INSTANCE;
+ }
+
+ protected boolean isEnabled() {
+ return SBInfo.getInstance().getLocation().equals("garden")
+ && NotEnoughUpdates.INSTANCE.config.garden.muteComposterSounds;
+ }
+
+ @SubscribeEvent
+ public void onSoundPlay(PlaySoundEvent event) {
+ if (mutableSounds.contains(event.name) && isEnabled() && event.sound.getXPosF() == -11.5 && event.sound.getYPosF() == 72.5 && event.sound.getZPosF() == -26.5) {
+ event.result = null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
index 716fb37d..ceae782b 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/PetInfoOverlay.java
@@ -473,29 +473,44 @@ public class PetInfoOverlay extends TextOverlay {
}
}
JsonObject pets = Constants.PETS;
- if (pets != null && pets.has("custom_pet_leveling") && pets.get("custom_pet_leveling").getAsJsonObject().has(pet.petType.toUpperCase()) &&
- pets.get("custom_pet_leveling").getAsJsonObject().get(pet.petType.toUpperCase()).getAsJsonObject().has("xp_multiplier")) {
- xp *= pets.get("custom_pet_leveling").getAsJsonObject().get(pet.petType.toUpperCase()).getAsJsonObject().get("xp_multiplier").getAsFloat();
+ if (pets != null && pets.has("custom_pet_leveling") &&
+ pets.get("custom_pet_leveling").getAsJsonObject().has(pet.petType.toUpperCase()) &&
+ pets.get("custom_pet_leveling").getAsJsonObject().get(pet.petType.toUpperCase()).getAsJsonObject().has(
+ "xp_multiplier")) {
+ xp *= pets.get("custom_pet_leveling").getAsJsonObject().get(pet.petType.toUpperCase()).getAsJsonObject().get(
+ "xp_multiplier").getAsFloat();
}
return xp;
}
+ private int firstPetLines = 0;
+ private int secondPetLines = 0;
+
@Override
public void updateFrequent() {
Pet currentPet = getCurrentPet();
if (!NotEnoughUpdates.INSTANCE.config.petOverlay.enablePetInfo || currentPet == null) {
overlayStrings = null;
} else {
+ firstPetLines = 0;
+ secondPetLines = 0;
overlayStrings = new ArrayList<>();
overlayStrings.addAll(createStringsForPet(currentPet, false));
+ firstPetLines = overlayStrings.size();
Pet currentPet2 = getCurrentPet2();
if (currentPet2 != null) {
overlayStrings.add("");
+ if (firstPetLines == 1) {
+ overlayStrings.add("");
+ }
overlayStrings.addAll(createStringsForPet(currentPet2, true));
+ secondPetLines = overlayStrings.size() - firstPetLines - 1;
+ if (firstPetLines == 1) {
+ secondPetLines--;
+ }
}
-
}
}
@@ -519,14 +534,20 @@ public class PetInfoOverlay extends TextOverlay {
currentPet.rarity.chatFormatting +
WordUtils.capitalizeFully(currentPet.petType.replace("_", " "));
- String lvlStringShort = EnumChatFormatting.AQUA + "" + roundFloat(levelXp) + "/" +
- roundFloat(currentPet.petLevel.getExpRequiredForNextLevel())
- + EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
+ float levelPercent = getLevelPercent(currentPet);
+ String lvlStringShort = null;
+ String lvlString = null;
+
+ if (levelPercent != 100 || !NotEnoughUpdates.INSTANCE.config.petOverlay.hidePetLevelProgress) {
+ lvlStringShort = EnumChatFormatting.AQUA + "" + roundFloat(levelXp) + "/" +
+ roundFloat(currentPet.petLevel.getExpRequiredForNextLevel())
+ + EnumChatFormatting.YELLOW + " (" + levelPercent + "%)";
- String lvlString = EnumChatFormatting.AQUA + "" +
- Utils.shortNumberFormat(Math.min(levelXp, currentPet.petLevel.getExpRequiredForNextLevel()), 0) + "/" +
- Utils.shortNumberFormat(currentPet.petLevel.getExpRequiredForNextLevel(), 0)
- + EnumChatFormatting.YELLOW + " (" + getLevelPercent(currentPet) + "%)";
+ lvlString = EnumChatFormatting.AQUA + "" +
+ Utils.shortNumberFormat(Math.min(levelXp, currentPet.petLevel.getExpRequiredForNextLevel()), 0) + "/" +
+ Utils.shortNumberFormat(currentPet.petLevel.getExpRequiredForNextLevel(), 0)
+ + EnumChatFormatting.YELLOW + " (" + levelPercent + "%)";
+ }
float xpGain;
if (!secondPet) {
@@ -593,6 +614,8 @@ public class PetInfoOverlay extends TextOverlay {
String finalEtaMaxStr = etaMaxStr;
String finalXpGainString = xpGainString;
String finalPetItemStr = petItemStr;
+ String finalLvlString = lvlString;
+ String finalLvlStringShort = lvlStringShort;
return new ArrayList<String>() {{
for (int index : NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText) {
switch (index) {
@@ -600,10 +623,10 @@ public class PetInfoOverlay extends TextOverlay {
add(petName);
break;
case 1:
- add(lvlStringShort);
+ if (finalLvlStringShort != null) add(finalLvlStringShort);
break;
case 2:
- add(lvlString);
+ if (finalLvlString != null) add(finalLvlString);
break;
case 3:
add(finalXpGainString);
@@ -716,6 +739,10 @@ public class PetInfoOverlay extends TextOverlay {
GlStateManager.enableDepth();
GlStateManager.pushMatrix();
Utils.pushGuiScale(NotEnoughUpdates.INSTANCE.config.locationedit.guiScale);
+
+ if (firstPetLines == 1) y -= 9;
+ if (firstPetLines == 2) y -= 3;
+
GlStateManager.translate(x - 2, y - 2, 0);
GlStateManager.scale(2, 2, 1);
Utils.drawItemStack(stack, 0, 0);
@@ -730,12 +757,16 @@ public class PetInfoOverlay extends TextOverlay {
if (petItem2 != null) {
Vector2f position = getPosition(overlayWidth, overlayHeight, true);
int x = (int) position.x;
- int y = (int) position.y + NotEnoughUpdates.INSTANCE.config.petOverlay.petOverlayText.size() * 10;
+ int y = (int) position.y + (overlayStrings.size() - secondPetLines) * 10;
ItemStack stack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(petItem2);
GlStateManager.enableDepth();
GlStateManager.pushMatrix();
Utils.pushGuiScale(NotEnoughUpdates.INSTANCE.config.locationedit.guiScale);
+
+ if (secondPetLines == 1) y -= 9;
+ if (secondPetLines == 2) y -= 3;
+
GlStateManager.translate(x - 2, y - 2, 0);
GlStateManager.scale(2, 2, 1);
Utils.drawItemStack(stack, 0, 0);
@@ -1114,7 +1145,7 @@ public class PetInfoOverlay extends TextOverlay {
PetInfoOverlay.config.selectedPet = -1;
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
EnumChatFormatting.RED + "[NEU] Can't find pet \u00a7" + petStringMatch +
- EnumChatFormatting.RED + " try revisiting all pages of /pets."));
+ EnumChatFormatting.RED + " try revisiting all pages of /pets."));
}
}
} else if ((chatMessage.toLowerCase().startsWith("you despawned your")) || (chatMessage.toLowerCase().contains(
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java
index 54fcc204..f41654e7 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java
@@ -186,7 +186,7 @@ public class AutoUpdater {
int fullReleaseVersion =
o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1;
int preReleaseVersion =
- o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1;
+ o.has("pre_version_id") && o.get("pre_version_id").isJsonPrimitive() ? o.get("pre_version_id").getAsInt() : -1;
int hotfixVersion =
o.has("hotfix_id") && o.get("hotfix_id").isJsonPrimitive() ? o.get("hotfix_id").getAsInt() : -1;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/world/FrozenTreasuresHighlighter.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/world/FrozenTreasuresHighlighter.java
index 2f8071a0..1a541433 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/world/FrozenTreasuresHighlighter.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/world/FrozenTreasuresHighlighter.java
@@ -28,11 +28,14 @@ import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.init.Blocks;
+import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
+import java.util.ArrayList;
+import java.util.Base64;
import java.util.List;
@NEUAutoSubscribe
@@ -40,6 +43,15 @@ public class FrozenTreasuresHighlighter extends GenericBlockHighlighter {
private static final FrozenTreasuresHighlighter INSTANCE = new FrozenTreasuresHighlighter();
+ private static final List<String> rideablePetTextureUrls = new ArrayList<String>() {{
+ // Armadillo
+ add("http://textures.minecraft.net/texture/c1eb6df4736ae24dd12a3d00f91e6e3aa7ade6bbefb0978afef2f0f92461018f");
+ // Rock
+ add("http://textures.minecraft.net/texture/cb2b5d48e57577563aca31735519cb622219bc058b1f34648b67b8e71bc0fa");
+ // Rat
+ add("http://textures.minecraft.net/texture/a8abb471db0ab78703011979dc8b40798a941f3a4dec3ec61cbeec2af8cffe8");
+ }};
+
public static FrozenTreasuresHighlighter getInstance() {return INSTANCE;}
@Override
@@ -62,10 +74,46 @@ public class FrozenTreasuresHighlighter extends GenericBlockHighlighter {
World w = Minecraft.getMinecraft().theWorld;
if (w == null) return;
List<Entity> entities = w.getLoadedEntityList();
- for (Entity e : entities) {
- if ((e instanceof EntityArmorStand) && ((EntityArmorStand) e).getCurrentArmor(3) != null) highlightedBlocks.add(e
- .getPosition()
- .add(0, 1, 0));
+ for (Entity entity : entities) {
+ if ((entity instanceof EntityArmorStand) &&
+ ((EntityArmorStand) entity).getCurrentArmor(3) != null) {
+
+ // If an armor stand has a 'hat' with a NBTTagCompound check if it has a pet texture url
+ if (((EntityArmorStand) entity).getCurrentArmor(3).hasTagCompound()) {
+ NBTTagCompound nbtTagCompound = ((EntityArmorStand) entity).getCurrentArmor(3).getTagCompound();
+
+ // Get Base64 texture value from the tag compound
+ String textureValue = nbtTagCompound
+ .getCompoundTag("SkullOwner")
+ .getCompoundTag("Properties")
+ .getTagList("textures", 10)
+ .getCompoundTagAt(0)
+ .getString("Value");
+
+ // Decode and find texture url from the texture value
+ String trimmedJson = new String(Base64.getDecoder().decode(textureValue)).replace(" ", "");
+
+
+ String textureUrl = "";
+ if (trimmedJson.contains("url")) {
+ textureUrl = trimmedJson.substring(
+ trimmedJson.indexOf("url")+6, // Start of url
+ trimmedJson.substring( // Get the substring from the start of the url to the end of string
+ trimmedJson.indexOf("url")+6).indexOf("\"") // Get index of first " after start of url
+ + trimmedJson.indexOf("url")+6); // Add on the length of numbers up until the start of url to get correct index from overall string
+ }
+
+
+ // If the list of rideable pet texture urls doesn't include the found texture then it is a frozen treasure
+ if (!rideablePetTextureUrls.contains(textureUrl)) {
+ highlightedBlocks.add(entity.getPosition().add(0, 1, 0));
+ }
+ } else {
+ // This is for frozen treasures which are just blocks i.e. Packed Ice, Enchanted Packed Ice etc.
+ // (Since I don't believe the blocks have NBTTagCompound data)
+ highlightedBlocks.add(entity.getPosition().add(0, 1, 0));
+ }
+ }
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/CalendarOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/CalendarOverlay.java
index 87691631..301a90b9 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/CalendarOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/CalendarOverlay.java
@@ -555,11 +555,11 @@ public class CalendarOverlay {
if (mouseX >= guiLeft + 151 && mouseX <= guiLeft + 151 + 14) {
if (mouseY <= guiTop + 26 + 70) {
Minecraft.getMinecraft().playerController.windowClick(cc.windowId,
- 50, 2, 3, Minecraft.getMinecraft().thePlayer
+ 41, 2, 3, Minecraft.getMinecraft().thePlayer
);
} else {
Minecraft.getMinecraft().playerController.windowClick(cc.windowId,
- 45, 2, 3, Minecraft.getMinecraft().thePlayer
+ 36, 2, 3, Minecraft.getMinecraft().thePlayer
);
}
}
@@ -653,7 +653,7 @@ public class CalendarOverlay {
}
//Special Events
- for (int i = 0; i < 21; i++) {
+ for (int i = 0; i < 14; i++) {
int itemIndex = 10 + i + (i / 7) * 2;
ItemStack item = cc.getLowerChestInventory().getStackInSlot(itemIndex);
if (item == null) continue;
@@ -1117,7 +1117,7 @@ public class CalendarOverlay {
int specialLen = fr.getStringWidth("Special");
fr.drawString("Special", guiLeft + 139 - specialLen, guiTop + 30, 0xffffaa00);
- ItemStack mayorStack = cc.getLowerChestInventory().getStackInSlot(46);
+ ItemStack mayorStack = cc.getLowerChestInventory().getStackInSlot(37);
if (mayorStack != null) {
String mayor = mayorStack.getDisplayName();
float verticalHeight = Utils.getVerticalHeight(mayor);
@@ -1144,11 +1144,11 @@ public class CalendarOverlay {
tooltipToDisplay = mayorStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
} else if (mouseX >= guiLeft + 151 && mouseX <= guiLeft + 151 + 14) {
if (mouseY <= guiTop + 26 + 70) {
- ItemStack calendarStack = cc.getLowerChestInventory().getStackInSlot(50);
+ ItemStack calendarStack = cc.getLowerChestInventory().getStackInSlot(41);
if (calendarStack != null)
tooltipToDisplay = calendarStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
} else {
- ItemStack rewardsStack = cc.getLowerChestInventory().getStackInSlot(45);
+ ItemStack rewardsStack = cc.getLowerChestInventory().getStackInSlot(36);
if (rewardsStack != null)
tooltipToDisplay = rewardsStack.getTooltip(Minecraft.getMinecraft().thePlayer, false);
}
@@ -1244,7 +1244,7 @@ public class CalendarOverlay {
}
//Special Events
- for (int i = 0; i < 21; i++) {
+ for (int i = 0; i < 14; i++) {
int itemIndex = 10 + i + (i / 7) * 2;
ItemStack item = cc.getLowerChestInventory().getStackInSlot(itemIndex);
if (item == null) continue;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java
index b20cad30..ecf02236 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java
@@ -47,7 +47,6 @@ import java.util.Map;
public class MinionHelperApiLoader {
private final MinionHelperManager manager;
private boolean dirty = true;
- private int ticks = 0;
private boolean collectionApiEnabled = true;
private boolean ignoreWorldSwitches = false;
private boolean readyToUse = false;
@@ -72,7 +71,6 @@ public class MinionHelperApiLoader {
if (Minecraft.getMinecraft().thePlayer == null) return;
if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return;
if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
- ticks++;
if (dirty && "Crafted Minions".equals(Utils.getOpenChestName())) {
load();
} else {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
index a289ca51..6d6963e3 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
@@ -34,6 +34,8 @@ import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour;
import io.github.moulberry.notenoughupdates.miscgui.GuiInvButtonEditor;
import io.github.moulberry.notenoughupdates.miscgui.NEUOverlayPlacements;
import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag;
+import io.github.moulberry.notenoughupdates.options.seperateSections.Garden;
+import io.github.moulberry.notenoughupdates.options.seperateSections.WorldConfig;
import io.github.moulberry.notenoughupdates.options.seperateSections.AHGraph;
import io.github.moulberry.notenoughupdates.options.seperateSections.AHTweaks;
import io.github.moulberry.notenoughupdates.options.seperateSections.AccessoryBag;
@@ -295,6 +297,13 @@ public class NEUConfig extends Config {
@Expose
@Category(
+ name = "Garden",
+ desc = "Garden"
+ )
+ public Garden garden = new Garden();
+
+ @Expose
+ @Category(
name = "NEU Auction House",
desc = "NEU Auction House"
)
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java
index 50f459c0..90ef93bb 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java
@@ -31,6 +31,7 @@ public enum NEUDebugFlag {
WISHING("Wishing Compass Solver"),
MAP("Dungeon Map Player Information"),
SEARCH("SearchString Matches"),
+ API_CACHE("Api Cache"),
;
private final String description;
@@ -43,6 +44,10 @@ public enum NEUDebugFlag {
return description;
}
+ public void log(String message) {
+ NEUDebugLogger.log(this, message);
+ }
+
public boolean isSet() {
return NEUDebugLogger.isFlagEnabled(this);
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Garden.java
index bf973b76..8d0e564c 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Garden.java
@@ -17,21 +17,18 @@
* along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
*/
-package io.github.moulberry.notenoughupdates.util;
+package io.github.moulberry.notenoughupdates.options.seperateSections;
-import net.minecraft.client.Minecraft;
-import org.jetbrains.annotations.NotNull;
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorBoolean;
+import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigOption;
-import java.util.concurrent.Executor;
-
-public class MinecraftExecutor implements Executor {
-
- public static MinecraftExecutor INSTANCE = new MinecraftExecutor();
-
- private MinecraftExecutor() {}
-
- @Override
- public void execute(@NotNull Runnable runnable) {
- Minecraft.getMinecraft().addScheduledTask(runnable);
- }
+public class Garden {
+ @Expose
+ @ConfigOption(
+ name = "Mute Composter",
+ desc = "Mute sounds of composting"
+ )
+ @ConfigEditorBoolean
+ public boolean muteComposterSounds = false;
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java
index 47e51eec..affaa68a 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java
@@ -311,4 +311,13 @@ public class Misc {
@ConfigEditorBoolean
public boolean dungeonGroupsPV = true;
+ @Expose
+ @ConfigOption(
+ name = "Old SkyBlock Menu",
+ desc = "Show old buttons in the SkyBlock Menu: Trade, Accessories, Potions, Quiver, Fishing and Sacks. " +
+ "§cOnly works with the booster cookie effect active."
+ )
+ @ConfigEditorBoolean
+ public boolean oldSkyBlockMenu = false;
+
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MiscOverlays.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MiscOverlays.java
index 714dd7b7..5634a4b7 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MiscOverlays.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MiscOverlays.java
@@ -24,7 +24,6 @@ import io.github.moulberry.notenoughupdates.core.config.Position;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigAccordionId;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorAccordion;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorBoolean;
-import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorButton;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorDraggableList;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorDropdown;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigOption;
@@ -61,6 +60,15 @@ public class MiscOverlays {
@Expose
@ConfigOption(
+ name = "Todo Overlay Hide Bingo",
+ desc = "Hide some tasks from the todo overlay while on a bingo profile: Cookie Buff, Godpot, Heavy Pearls, Crimson Isle Quests"
+ )
+ @ConfigEditorBoolean
+ @ConfigAccordionId(id = 0)
+ public boolean todoOverlayHideAtBingo = true;
+
+ @Expose
+ @ConfigOption(
name = "Todo Text",
desc = "\u00a7eDrag text to change the appearance of the overlay\n" +
"\u00a7rIf you want to see the time until something is available, click \"Add\" and then the respective timer"
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/PetOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/PetOverlay.java
index fce1f9af..9d88e8f1 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/PetOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/PetOverlay.java
@@ -22,7 +22,6 @@ package io.github.moulberry.notenoughupdates.options.seperateSections;
import com.google.gson.annotations.Expose;
import io.github.moulberry.notenoughupdates.core.config.Position;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorBoolean;
-import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorButton;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorDraggableList;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorDropdown;
import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigOption;
@@ -131,4 +130,12 @@ public class PetOverlay {
)
@ConfigEditorBoolean
public boolean showKatSitting = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Hide Pet Level Progress",
+ desc = "Hide the pet level progress information for maxed out pets."
+ )
+ @ConfigEditorBoolean
+ public boolean hidePetLevelProgress = false;
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
index 419c9785..90aa2984 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java
@@ -898,12 +898,26 @@ public class TimersOverlay extends TextTabOverlay {
overlayStrings = new ArrayList<>();
for (int index : NotEnoughUpdates.INSTANCE.config.miscOverlays.todoText2) {
if (map.containsKey(index)) {
- overlayStrings.add(map.get(index));
+ String text = map.get(index);
+ if (hideBecauseOfBingo(text)) continue;
+ overlayStrings.add(text);
}
}
if (overlayStrings.isEmpty()) overlayStrings = null;
}
+ private boolean hideBecauseOfBingo(String text) {
+ if (!SBInfo.getInstance().bingo) return false;
+ if (!NotEnoughUpdates.INSTANCE.config.miscOverlays.todoOverlayHideAtBingo) return false;
+
+ if (text.contains("Cookie Buff")) return true;
+ if (text.contains("Godpot")) return true;
+ if (text.contains("Heavy Pearls")) return true;
+ if (text.contains("Crimson Isle Quests")) return true;
+
+ return false;
+ }
+
public static int beforePearls = -1;
public static int afterPearls = -1;
public static int availablePearls = -1;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java
index 265177fc..5939e122 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/BasicPage.java
@@ -313,17 +313,25 @@ public class BasicPage extends GuiProfileViewerPage {
} else if (networth == -1) {
networth = profile.getNetWorth(profileId);
}
-
+ int fontWidth = fr.getStringWidth("Net Worth: " + GuiProfileViewer.numberFormat.format(networth));
+ int offset = (fontWidth >= 117 ? 63 + (fontWidth - 117) : 63);
if (networth > 0) {
- Utils.drawStringCentered(
- EnumChatFormatting.GREEN + "Net Worth: " + EnumChatFormatting.GOLD +
- GuiProfileViewer.numberFormat.format(networth),
- fr,
- guiLeft + 165,
- guiTop + 38,
- true,
- 0
- );
+ if (fontWidth >= 117) {
+
+ fr.drawString(EnumChatFormatting.GREEN + "Net Worth: " + EnumChatFormatting.GOLD +
+ GuiProfileViewer.numberFormat.format(networth), guiLeft + 8, guiTop + 38 - fr.FONT_HEIGHT / 2f, 0, true);
+
+ } else {
+ Utils.drawStringCentered(
+ EnumChatFormatting.GREEN + "Net Worth: " + EnumChatFormatting.GOLD +
+ GuiProfileViewer.numberFormat.format(networth),
+ fr,
+ guiLeft + 68,
+ guiTop + 38,
+ true,
+ 0
+ );
+ }
if (NotEnoughUpdates.INSTANCE.manager.auctionManager.getBazaarInfo("BOOSTER_COOKIE") != null &&
NotEnoughUpdates.INSTANCE.manager.auctionManager.getBazaarInfo("BOOSTER_COOKIE").has("avg_buy")) {
double networthInCookies =
@@ -337,8 +345,8 @@ public class BasicPage extends GuiProfileViewerPage {
String networthIRLMoney = GuiProfileViewer.numberFormat.format(Math.round(
((networthInCookies * 325) / 675) * 4.99));
- int fontWidth = fr.getStringWidth("Net Worth: " + GuiProfileViewer.numberFormat.format(networth));
- if (mouseX > guiLeft + 165 - fontWidth / 2 && mouseX < guiLeft + 165 + fontWidth / 2) {
+
+ if (mouseX > guiLeft + offset - fontWidth / 2 && mouseX < guiLeft + offset + fontWidth / 2) {
if (mouseY > guiTop + 32 && mouseY < guiTop + 38 + fr.FONT_HEIGHT) {
getInstance().tooltipToDisplay = new ArrayList<>();
getInstance()
@@ -384,14 +392,21 @@ public class BasicPage extends GuiProfileViewerPage {
}
}
} else {
- Utils.drawStringCentered(
- EnumChatFormatting.GREEN + "Net Worth: " + stateStr,
- fr,
- guiLeft + 165,
- guiTop + 38,
- true,
- 0
- );
+ int errFontWidth = fr.getStringWidth("Net Worth: " + stateStr);
+ if (errFontWidth >= 117) {
+ fr.drawString(EnumChatFormatting.GREEN + "Net Worth: " + stateStr,
+ guiLeft + 8, guiTop + 38 - fr.FONT_HEIGHT / 2f, 0, true
+ );
+ } else {
+ Utils.drawStringCentered(
+ EnumChatFormatting.GREEN + "Net Worth: " + stateStr,
+ fr,
+ guiLeft + 63,
+ guiTop + 38,
+ true,
+ 0
+ );
+ }
}
if (status != null) {
@@ -794,7 +809,7 @@ public class BasicPage extends GuiProfileViewerPage {
EnumChatFormatting.GOLD +
GuiProfileViewer.numberFormat.format(roundToNearestInt(senitherWeight.getTotalWeight().getRaw())),
fr,
- guiLeft + 165,
+ guiLeft + 63,
guiTop + 18,
true,
0
@@ -804,7 +819,7 @@ public class BasicPage extends GuiProfileViewerPage {
"Senither Weight: " +
GuiProfileViewer.numberFormat.format(roundToNearestInt(senitherWeight.getTotalWeight().getRaw()))
);
- if (mouseX > guiLeft + 165 - textWidth / 2 && mouseX < guiLeft + 165 + textWidth / 2) {
+ if (mouseX > guiLeft + 63 - textWidth / 2 && mouseX < guiLeft + 63 + textWidth / 2) {
if (mouseY > guiTop + 12 && mouseY < guiTop + 12 + fr.FONT_HEIGHT) {
getInstance().tooltipToDisplay = new ArrayList<>();
getInstance()
@@ -862,7 +877,7 @@ public class BasicPage extends GuiProfileViewerPage {
EnumChatFormatting.GOLD +
GuiProfileViewer.numberFormat.format(roundToNearestInt(lilyWeight.getTotalWeight().getRaw())),
fr,
- guiLeft + 165,
+ guiLeft + 63,
guiTop + 28,
true,
0
@@ -871,7 +886,7 @@ public class BasicPage extends GuiProfileViewerPage {
int fontWidth = fr.getStringWidth(
"Lily Weight: " + GuiProfileViewer.numberFormat.format(roundToNearestInt(lilyWeight.getTotalWeight().getRaw()))
);
- if (mouseX > guiLeft + 165 - fontWidth / 2 && mouseX < guiLeft + 165 + fontWidth / 2) {
+ if (mouseX > guiLeft + 63 - fontWidth / 2 && mouseX < guiLeft + 63 + fontWidth / 2) {
if (mouseY > guiTop + 22 && mouseY < guiTop + 22 + fr.FONT_HEIGHT) {
getInstance().tooltipToDisplay = new ArrayList<>();
getInstance()
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java
index 17a14d1f..6f8427ae 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java
@@ -42,6 +42,7 @@ import net.minecraft.util.EnumChatFormatting;
import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
@@ -537,6 +538,7 @@ public class ProfileViewer {
manager.apiUtils
.newHypixelApiRequest("player")
.queryArgument("name", nameF)
+ .maxCacheAge(Duration.ofSeconds(30))
.requestJson()
.thenAccept(jsonObject -> {
if (
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/LevelPage.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/LevelPage.java
index 16abf251..770b295a 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/LevelPage.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/LevelPage.java
@@ -26,6 +26,7 @@ import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.CoreTaskLevel;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.DungeonTaskLevel;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.EssenceTaskLevel;
+import io.github.moulberry.notenoughupdates.profileviewer.level.task.EventTaskLevel;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.MiscTaskLevel;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.SkillRelatedTaskLevel;
import io.github.moulberry.notenoughupdates.profileviewer.level.task.SlayingTaskLevel;
@@ -61,6 +62,8 @@ public class LevelPage {
private final SlayingTaskLevel slayingTaskLevel;
private final StoryTaskLevel storyTaskLevel;
+ private final EventTaskLevel eventTaskLevel;
+
private static final ResourceLocation pv_levels = new ResourceLocation("notenoughupdates:pv_levels.png");
public LevelPage(GuiProfileViewer instance, BasicPage basicPage) {
@@ -75,6 +78,7 @@ public class LevelPage {
skillRelatedTaskLevel = new SkillRelatedTaskLevel(this);
slayingTaskLevel = new SlayingTaskLevel(this);
storyTaskLevel = new StoryTaskLevel(this);
+ eventTaskLevel = new EventTaskLevel(this);
}
public void drawPage(int mouseX, int mouseY) {
@@ -97,13 +101,14 @@ public class LevelPage {
JsonObject profileInfo = profile.getProfileInformation(profileId);
drawMainBar(skyblockLevel, mouseX, mouseY, guiLeft, guiTop);
- coreTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
dungeonTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
essenceTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
miscTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
skillRelatedTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
slayingTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
storyTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
+ eventTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
+ coreTaskLevel.drawTask(profileInfo, mouseX, mouseY, guiLeft, guiTop);
}
public void renderLevelBar(
@@ -175,14 +180,12 @@ public class LevelPage {
renderLevelBar(
"Level",
BasicPage.skull,
- guiLeft + 163,
- guiTop + 30,
+ guiLeft + 163, guiTop + 30,
110,
skyblockLevel,
- (skyblockLevel - (long) skyblockLevel) * 100,
+ Math.round((skyblockLevel - (long) skyblockLevel) * 100),
100,
- mouseX,
- mouseY,
+ mouseX, mouseY,
false,
Collections.emptyList()
);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/CoreTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/CoreTaskLevel.java
index be333359..8bb26b09 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/CoreTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/CoreTaskLevel.java
@@ -19,8 +19,9 @@
package io.github.moulberry.notenoughupdates.profileviewer.level.task;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import io.github.moulberry.notenoughupdates.profileviewer.PlayerStats;
import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer;
import io.github.moulberry.notenoughupdates.profileviewer.level.LevelPage;
import io.github.moulberry.notenoughupdates.util.Constants;
@@ -80,11 +81,20 @@ public class CoreTaskLevel {
}
// mp acc
- int sbXpGainedMp = levelPage.getProfile().getMagicalPower(levelPage.getProfileId());
+ int sbXpGainedMp = 0;
+ if (object.has("accessory_bag_storage") &&
+ object.getAsJsonObject("accessory_bag_storage").has("highest_magical_power")) {
+ sbXpGainedMp = object.getAsJsonObject("accessory_bag_storage").get("highest_magical_power").getAsInt();
+ }
// pets
- int petScore = PlayerStats.getPetScore(object);
+ int petScore = 0;
+ if (object.has("leveling") &&
+ object.getAsJsonObject("leveling").has("highest_pet_score")) {
+ petScore = object.getAsJsonObject("leveling").get("highest_pet_score").getAsInt();
+
+ }
int sbXpPetScore = petScore * coreTask.get("pet_score_xp").getAsInt();
// museum is not possible
@@ -116,13 +126,32 @@ public class CoreTaskLevel {
}
}
}
+
+ int sbXpBankUpgrades = 0;
+
+ JsonArray completedTasks = object.getAsJsonObject("leveling").get("completed_tasks").getAsJsonArray();
+ JsonObject bankUpgradesXp = coreTask.getAsJsonObject("bank_upgrades_xp");
+ for (JsonElement completedTask : completedTasks) {
+ String name = completedTask.getAsString();
+ if (bankUpgradesXp.has(name)) {
+ sbXpBankUpgrades += bankUpgradesXp.get(name).getAsInt();
+ }
+ }
+
List<String> lore = new ArrayList<>();
lore.add(levelPage.buildLore("Skill Level Up",
sbXpGainedSkillLVL, coreTask.get("skill_level_up").getAsInt(), false
));
- lore.add(levelPage.buildLore("Museum Progression",
- 0, 0, false
+
+ int totalXp = sbXpGainedSkillLVL + sbXpGainedFairy +
+ sbXpCollection + sbXpMinionTier + sbXpBankUpgrades;
+
+ lore.add(levelPage.buildLore(
+ "Museum Progression",
+ 0,
+ 0,
+ false
));
lore.add(levelPage.buildLore(
"Fairy Soul",
@@ -141,21 +170,18 @@ public class CoreTaskLevel {
sbXpMinionTier, coreTask.get("craft_minions").getAsInt(), false
));
lore.add(levelPage.buildLore("Bank Upgrade",
- 0, 0, false
+ sbXpBankUpgrades, coreTask.get("bank_upgrades").getAsInt(), false
));
levelPage.renderLevelBar(
"Core Task",
new ItemStack(Items.nether_star),
- guiLeft + 23,
- guiTop + 25,
+ guiLeft + 23, guiTop + 25,
110,
0,
- sbXpGainedSkillLVL + sbXpGainedFairy +
- sbXpCollection + sbXpMinionTier,
+ totalXp,
levelPage.getConstant().getAsJsonObject("category_xp").get("core_task").getAsInt(),
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/DungeonTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/DungeonTaskLevel.java
index 6f0a029f..6900cdf3 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/DungeonTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/DungeonTaskLevel.java
@@ -44,9 +44,9 @@ public class DungeonTaskLevel {
Map<String, ProfileViewer.Level> skyblockInfo =
levelPage.getProfile().getSkyblockInfo(levelPage.getProfileId());
- double sbLevelGainedFloor = 0;
- double sbXpGainedClass = 0;
- double sbXpGainedLvl = 0;
+ int sbLevelGainedFloor = 0;
+ int sbXpGainedClass = 0;
+ int sbXpGainedLvl = 0;
int catacombsLvl = 0;
if (skyblockInfo != null && skyblockInfo.containsKey("catacombs")) {
ProfileViewer.Level catacombs = skyblockInfo.get("catacombs");
@@ -105,20 +105,20 @@ public class DungeonTaskLevel {
lore.add(levelPage.buildLore("Class Level Up", sbXpGainedClass, classLevelUp, false));
lore.add(levelPage.buildLore("Complete Dungeons", sbLevelGainedFloor, completeDungeon, false));
+ int totalSbXpGain = sbXpGainedLvl + sbXpGainedClass + sbLevelGainedFloor;
+
levelPage.renderLevelBar(
"Dungeon Task",
NotEnoughUpdates.INSTANCE.manager
.createItemResolutionQuery()
.withKnownInternalName("WITHER_RELIC")
.resolveToItemStack(),
- guiLeft + 23,
- guiTop + 55,
+ guiLeft + 23, guiTop + 55,
110,
catacombsLvl,
totalXp,
totalGainful,
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EssenceTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EssenceTaskLevel.java
index 11e291b0..c6df6fc1 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EssenceTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EssenceTaskLevel.java
@@ -25,6 +25,7 @@ import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.profileviewer.level.LevelPage;
import io.github.moulberry.notenoughupdates.util.Constants;
+import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.util.EnumChatFormatting;
import java.util.ArrayList;
@@ -88,10 +89,15 @@ public class EssenceTaskLevel {
for (Map.Entry<String, EssenceShop> stringEssenceShopEntry : loreMap.entrySet()) {
String key = stringEssenceShopEntry.getKey();
EssenceShop value = stringEssenceShopEntry.getValue();
- value.name = NotEnoughUpdates.INSTANCE.manager
+ JsonObject jsonObject = NotEnoughUpdates.INSTANCE.manager
.createItemResolutionQuery()
.withKnownInternalName(key)
- .resolveToItemListJson()
+ .resolveToItemListJson();
+ if (jsonObject == null){
+ Utils.showOutdatedRepoNotification();
+ continue;
+ }
+ value.name = jsonObject
.get("displayname")
.getAsString();
String name = key.toLowerCase() + "_shop";
@@ -111,14 +117,12 @@ public class EssenceTaskLevel {
NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery()
.withKnownInternalName("ESSENCE_WITHER")
.resolveToItemStack(),
- guiLeft + 299,
- guiTop + 25,
+ guiLeft + 299, guiTop + 25,
110,
total,
total,
categoryXp.get("essence_shop_task").getAsInt(),
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EventTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EventTaskLevel.java
new file mode 100644
index 00000000..04ace872
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/EventTaskLevel.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.profileviewer.level.task;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.profileviewer.level.LevelPage;
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EventTaskLevel {
+
+ private final LevelPage levelPage;
+
+ public EventTaskLevel(LevelPage levelPage) {this.levelPage = levelPage;}
+
+ public void drawTask(JsonObject object, int mouseX, int mouseY, int guiLeft, int guiTop) {
+ List<String> lore = new ArrayList<>();
+
+ int sbXpMiningFiesta = 0;
+ int sbXpFishingFestival = 0;
+ int sbXpSpookyFestival = 0;
+ JsonObject constant = levelPage.getConstant();
+ JsonObject eventTask = constant.getAsJsonObject("event_task");
+
+ if (object.has("leveling")) {
+ JsonObject leveling = object.getAsJsonObject("leveling");
+ int miningFiestaOresMined = 0;
+ int fishingFestivalSharksKilled = 0;
+ if (leveling.has("mining_fiesta_ores_mined"))
+ miningFiestaOresMined = leveling.get("mining_fiesta_ores_mined").getAsInt();
+ if (leveling.has("fishing_festival_sharks_killed")) fishingFestivalSharksKilled = leveling.get(
+ "fishing_festival_sharks_killed").getAsInt();
+
+ sbXpMiningFiesta = getCapOrAmount(miningFiestaOresMined, 1_000_000, 5_000);
+ sbXpFishingFestival = getCapOrAmount(fishingFestivalSharksKilled, 5_000, 50);
+
+ JsonArray completedTasks = leveling.get("completed_tasks").getAsJsonArray();
+ JsonObject spookyFestivalXp = eventTask.getAsJsonObject("spooky_festival_xp");
+ for (JsonElement completedTask : completedTasks) {
+ String name = completedTask.getAsString();
+ if (spookyFestivalXp.has(name)) {
+ sbXpSpookyFestival += spookyFestivalXp.get(name).getAsInt();
+ }
+ }
+ }
+
+ lore.add(levelPage.buildLore("Mining Fiesta", sbXpMiningFiesta, eventTask.get("mining_fiesta").getAsInt(), false));
+ lore.add(levelPage.buildLore(
+ "Fishing Festival",
+ sbXpFishingFestival,
+ eventTask.get("fishing_festival").getAsInt(),
+ false
+ ));
+ lore.add(levelPage.buildLore(
+ "Spooky Festival",
+ sbXpSpookyFestival,
+ eventTask.get("spooky_festival").getAsInt(),
+ false
+ ));
+
+ int totalXp = sbXpMiningFiesta + sbXpSpookyFestival +
+ sbXpFishingFestival;
+ levelPage.renderLevelBar(
+ "Event Task",
+ new ItemStack(Items.clock),
+ guiLeft + 299, guiTop + 115,
+ 110,
+ 0,
+ totalXp,
+ levelPage.getConstant().getAsJsonObject("category_xp").get("event_task").getAsInt(),
+ mouseX, mouseY,
+ true,
+ lore
+ );
+ }
+
+ private int getCapOrAmount(int miningFiestaOresMined, int cap, int per) {
+ if (miningFiestaOresMined == 0) return 0;
+ if (miningFiestaOresMined > cap) {
+ return cap / per;
+ }
+ return miningFiestaOresMined / per;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/MiscTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/MiscTaskLevel.java
index e0ea89b3..8b7b9d2b 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/MiscTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/MiscTaskLevel.java
@@ -27,12 +27,13 @@ import io.github.moulberry.notenoughupdates.profileviewer.level.LevelPage;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
-import net.minecraft.util.EnumChatFormatting;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
public class MiscTaskLevel {
@@ -86,24 +87,27 @@ public class MiscTaskLevel {
}
// abiphone
- if (netherIslandPlayerData.has("abiphone")) {
- JsonObject abiphone = netherIslandPlayerData.getAsJsonObject("abiphone");
- if (abiphone.has("active_contacts")) sbXpAbiphone =
- abiphone.getAsJsonArray("active_contacts").size() * miscellaneousTask.get("abiphone_contacts_xp").getAsInt();
+ JsonObject leveling = object.getAsJsonObject("leveling");
+ JsonArray completedTask = leveling.get("completed_tasks").getAsJsonArray();
+ Stream<JsonElement> stream = StreamSupport.stream(completedTask.spliterator(), true);
+ long activeContacts = stream.map(JsonElement::getAsString).filter(s -> s.startsWith("ABIPHONE_")).count();
+ JsonObject abiphone = netherIslandPlayerData.getAsJsonObject("abiphone");
+ if (abiphone.has("active_contacts")) {
+ sbXpAbiphone = (int) activeContacts * miscellaneousTask.get("abiphone_contacts_xp").getAsInt();
}
}
// harp
int sbXpGainedHarp = 0;
JsonObject harpSongsNames = miscellaneousTask.get("harp_songs_names").getAsJsonObject();
- if (object.has("harp_quest")) {
- JsonObject harpQuest = object.get("harp_quest").getAsJsonObject();
- for (Map.Entry<String, JsonElement> stringJsonElementEntry : harpSongsNames.entrySet()) {
- String key = stringJsonElementEntry.getKey();
- int value = stringJsonElementEntry.getValue().getAsInt();
- if (harpQuest.has(key)) {
- sbXpGainedHarp += value;
- }
+
+ JsonObject leveling = object.get("leveling").getAsJsonObject();
+ if (leveling.has("completed_tasks")) {
+ JsonArray completedTasks = leveling.get("completed_tasks").getAsJsonArray();
+ for (JsonElement completedTask : completedTasks) {
+ String name = completedTask.getAsString();
+ String harpName = name.substring(0, name.lastIndexOf("_"));
+ if(harpSongsNames.has(harpName))sbXpGainedHarp += harpSongsNames.get(harpName).getAsInt() / 4;
}
}
@@ -158,10 +162,10 @@ public class MiscTaskLevel {
sbXpDojo, miscellaneousTask.get("the_dojo").getAsInt(), false
));
lore.add(levelPage.buildLore(
- EnumChatFormatting.ITALIC + "Harp Songs",
+ "Harp Songs",
sbXpGainedHarp, miscellaneousTask.get("harp_songs").getAsInt(), false
));
- lore.add(levelPage.buildLore(EnumChatFormatting.ITALIC + "Abiphone Contacts",
+ lore.add(levelPage.buildLore("Abiphone Contacts",
sbXpAbiphone, miscellaneousTask.get("abiphone_contacts").getAsInt(), false
));
lore.add(levelPage.buildLore("Community Shop Upgrades",
@@ -171,22 +175,22 @@ public class MiscTaskLevel {
sbXpPersonalBank, miscellaneousTask.get("personal_bank_upgrades").getAsInt(), false
));
+ int totalXp = sbXpReaperPeppers + sbXpDojo + sbXpGainedHarp + sbXpAbiphone +
+ sbXpCommunityUpgrade + sbXpPersonalBank;
levelPage.renderLevelBar(
"Misc. Task",
new ItemStack(Items.map),
- guiLeft + 299,
- guiTop + 55,
+ guiLeft + 299, guiTop + 55,
110,
0,
- sbXpReaperPeppers + sbXpDojo + sbXpGainedHarp + sbXpAbiphone +
- sbXpCommunityUpgrade + sbXpPersonalBank,
+ totalXp,
levelPage.getConstant().getAsJsonObject("category_xp").get("miscellaneous_task").getAsInt(),
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
+ totalXp += sbXpAccessoryUpgrade + sbXpUnlockedPowers;
}
private int getRankIndex(int pointsTotal) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SkillRelatedTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SkillRelatedTaskLevel.java
index 4ba7951c..06ab6f8c 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SkillRelatedTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SkillRelatedTaskLevel.java
@@ -43,13 +43,6 @@ public class SkillRelatedTaskLevel {
JsonObject skillRelatedTask = levelPage.getConstant().get("skill_related_task").getAsJsonObject();
JsonObject miningObj = skillRelatedTask.get("mining").getAsJsonObject();
- float mithrilPowder = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_mithril"), 0);
- float gemstonePowder = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_gemstone"), 0);
- float mithril = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_spent_mithril"), 0) +
- mithrilPowder;
- float gemstone = (Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_spent_gemstone"), 0)) +
- gemstonePowder;
-
float hotmXp = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.experience"), 0);
ProfileViewer.Level levelObjHotm =
ProfileViewer.getLevel(
@@ -66,29 +59,31 @@ public class SkillRelatedTaskLevel {
hotmXP += hotmXpArray.get(i - 1).getAsInt();
}
- int gainByFirstMithrilThing;
- if (mithril >= 350_000) {
- gainByFirstMithrilThing = (int) (350000 / 2400d);
- mithril -= 350_000;
- } else {
- gainByFirstMithrilThing = (int) (mithril / 2400d);
- mithril = 0;
- }
+ float mithrilPowder = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_mithril"), 0);
+ float gemstonePowder = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_gemstone"), 0);
+ float mithril = Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_spent_mithril"), 0) +
+ mithrilPowder;
+ float gemstone = (Utils.getElementAsFloat(Utils.getElement(object, "mining_core.powder_spent_gemstone"), 0)) +
+ gemstonePowder;
- int gainByFirstGemstoneThing;
- if (gemstone >= 350_000) {
- gainByFirstGemstoneThing = (int) (350_000 / 2500d);
- gemstone -= 350_000;
- } else {
- gainByFirstGemstoneThing = (int) (gemstone / 2500d);
- gemstone = 0;
- }
+ // PUNKT NULL
+
+ double totalMithril = mithril + mithrilPowder;
+ double totalGemstone = gemstone + gemstonePowder;
+ double mithrilUnder = Math.min(350000.0, totalMithril);
+ double mithrilOver = Math.max(0, Math.min(totalMithril, 12_500_000.0) - 350000.0);
+ double gemstoneUnder = Math.min(350000.0, totalGemstone);
+ double gemstoneOver = Math.max(0, Math.min(totalGemstone, 20_000_000.0) - 350000.0);
- int sbXpMithrilPowder = (int) powder(3.75, mithril, 12_500_000);
- int sbXpGemstonePowder = (int) powder(4.25, gemstone, 20_000_000);
+ double mithrilXP = Math.floor(mithrilUnder / 2400.0);
+ double gemstoneXP = Math.floor(gemstoneUnder / 2500.0);
+ double mithrilExcess = Math.floor(
+ 3.75 * (Math.sqrt(1 + 8 * Math.sqrt((1758267.0 / 12_500_000.0) * mithrilOver + 9)) - 3));
+ double gemstoneExcess = Math.floor(
+ 4.25 * (Math.sqrt(1 + 8 * Math.sqrt((1758267.0 / 20_000_000.0) * gemstoneOver + 9)) - 3));
double sbXpHotmTier =
- (sbXpMithrilPowder + gainByFirstMithrilThing) + (sbXpGemstonePowder + gainByFirstGemstoneThing)
+ (mithrilXP + mithrilExcess) + (gemstoneXP + gemstoneExcess)
+ hotmXP;
int sbXpPotmTier = 0;
@@ -171,6 +166,18 @@ public class SkillRelatedTaskLevel {
}
}
+ int sbXpNucleus = 0;
+ JsonObject leveling = object.get("leveling").getAsJsonObject();
+ if (leveling.has("completions") && leveling.getAsJsonObject("completions").has("NUCLEUS_RUNS")) {
+ int nucleusRuns = leveling.getAsJsonObject("completions").get("NUCLEUS_RUNS").getAsInt();
+ JsonElement nucleusXp = miningObj.get("crystal_nucleus_xp");
+ if (nucleusXp == null) {
+ Utils.showOutdatedRepoNotification();
+ } else {
+ sbXpNucleus += nucleusRuns * nucleusXp.getAsInt();
+ }
+ }
+
List<String> lore = new ArrayList<>();
lore.add(levelPage.buildLore("Heart of the Mountain", sbXpHotmTier, miningObj.get("hotm").getAsInt(), false));
lore.add(levelPage.buildLore(
@@ -179,7 +186,7 @@ public class SkillRelatedTaskLevel {
miningObj.get("commission_milestone").getAsInt(),
false
));
- lore.add(levelPage.buildLore("Crystal Nucleus", 0, 0, false));
+ lore.add(levelPage.buildLore("Crystal Nucleus", sbXpNucleus, miningObj.get("crystal_nucleus").getAsInt(), false));
lore.add(levelPage.buildLore(
"Anita's Shop Upgrade",
sbXpGainedByAnita,
@@ -189,37 +196,22 @@ public class SkillRelatedTaskLevel {
lore.add(levelPage.buildLore("Peak of the Mountain", sbXpPotmTier, miningObj.get("potm").getAsInt(), false));
lore.add(levelPage.buildLore("Trophy Fish", sbXpTrophyFish, fishingObj.get("trophy_fish").getAsInt(), false));
lore.add(levelPage.buildLore("Rock Milestone", sbXpRockPet, miningObj.get("rock_milestone").getAsInt(), false));
- lore.add(levelPage.buildLore(
- "Dolphin Milestone",
- sbXpDolphinPet,
- fishingObj.get("dolphin_milestone").getAsInt(),
- false
- ));
+ lore.add(levelPage.buildLore("Dolphin Milestone", sbXpDolphinPet, fishingObj.get("dolphin_milestone").getAsInt(), false));
+ int totalXp =
+ (int) (sbXpHotmTier + sbXpCommissionMilestone + sbXpGainedByAnita + sbXpPotmTier + sbXpTrophyFish + sbXpRockPet +
+ sbXpDolphinPet + sbXpNucleus);
levelPage.renderLevelBar(
"Skill Related Task",
new ItemStack(Items.diamond_sword),
- guiLeft + 23,
- guiTop + 115,
+ guiLeft + 23, guiTop + 115,
110,
0,
- sbXpHotmTier + sbXpCommissionMilestone + sbXpGainedByAnita + sbXpPotmTier + sbXpTrophyFish + sbXpRockPet +
- sbXpDolphinPet,
+ totalXp,
levelPage.getConstant().getAsJsonObject("category_xp").get("skill_related_task").getAsInt(),
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
}
-
- private static double powder(double multiplier, float left, int CAP) {
- double cons = 1758267;
- if (left <= 0) return 0;
-
- left = Math.min(CAP, left);
-
- return multiplier * (Math.sqrt(1 + 8 * (Math.sqrt((cons / CAP) * left + 9))) - 3);
- }
-
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SlayingTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SlayingTaskLevel.java
index 3aded435..6b624a52 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SlayingTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/SlayingTaskLevel.java
@@ -233,20 +233,21 @@ public class SlayingTaskLevel {
int slayingTaskMax = levelPage.getConstant().getAsJsonObject("category_xp").get("slaying_task").getAsInt();
+ int totalXp = sbXpGainedSlayer + bossCollectionXp + mythologicalKillsXp +
+ sbXpFromDragonKills + sbXpFromSlayerDefeat + sbXpDefeatKuudra + sbXpGainedArachne;
levelPage.renderLevelBar(
"Slaying Task",
new ItemStack(Items.golden_sword),
- guiLeft + 23,
- guiTop + 85,
+ guiLeft + 23, guiTop + 85,
110,
- 0, sbXpGainedSlayer + bossCollectionXp + mythologicalKillsXp +
- sbXpFromDragonKills + sbXpFromSlayerDefeat + sbXpDefeatKuudra + sbXpGainedArachne,
+ 0,
+ totalXp,
slayingTaskMax,
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
+ totalXp += sbXpBestiary;
}
private int loopThroughCollection(int[] array, double value, JsonArray jsonArray) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/StoryTaskLevel.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/StoryTaskLevel.java
index 2b1b59cb..3846db5f 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/StoryTaskLevel.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/level/task/StoryTaskLevel.java
@@ -61,14 +61,12 @@ public class StoryTaskLevel {
levelPage.renderLevelBar(
"Story Task",
new ItemStack(Items.map),
- guiLeft + 299,
- guiTop + 85,
+ guiLeft + 299, guiTop + 85,
110,
0,
sbXpStory,
levelPage.getConstant().getAsJsonObject("category_xp").get("story_task").getAsInt(),
- mouseX,
- mouseY,
+ mouseX, mouseY,
true,
lore
);
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java
index 023be060..28298fe0 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java
@@ -48,18 +48,26 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.GZIPInputStream;
public class ApiUtil {
private static final Gson gson = new Gson();
+
+ private static final Comparator<NameValuePair> nameValuePairComparator = Comparator
+ .comparing(NameValuePair::getName)
+ .thenComparing(NameValuePair::getValue);
+
private static final ExecutorService executorService = Executors.newFixedThreadPool(3);
private static String getUserAgent() {
if (NotEnoughUpdates.INSTANCE.config.hidden.customUserAgent != null) {
@@ -110,6 +118,7 @@ public class ApiUtil {
private final List<NameValuePair> queryArguments = new ArrayList<>();
private String baseUrl = null;
private boolean shouldGunzip = false;
+ private Duration maxCacheAge = Duration.ofSeconds(500);
private String method = "GET";
private String postData = null;
private String postContentType = null;
@@ -119,6 +128,15 @@ public class ApiUtil {
return this;
}
+ /**
+ * Specify a cache timeout of {@code null} to signify an uncacheable request.
+ * Non {@code GET} requests are always uncacheable.
+ */
+ public Request maxCacheAge(Duration maxCacheAge) {
+ this.maxCacheAge = maxCacheAge;
+ return this;
+ }
+
public Request url(String baseUrl) {
this.baseUrl = baseUrl;
return this;
@@ -160,7 +178,17 @@ public class ApiUtil {
return fut;
}
- public CompletableFuture<String> requestString() {
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ private ApiCache.CacheKey getCacheKey() {
+ if (!"GET".equals(method)) return null;
+ queryArguments.sort(nameValuePairComparator);
+ return new ApiCache.CacheKey(baseUrl, queryArguments, shouldGunzip);
+ }
+
+ private CompletableFuture<String> requestString0() {
return buildUrl().thenApplyAsync(url -> {
try {
InputStream inputStream = null;
@@ -183,7 +211,7 @@ public class ApiUtil {
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
try {
- os.write(this.postData.getBytes("utf-8"));
+ os.write(this.postData.getBytes(StandardCharsets.UTF_8));
} finally {
os.close();
}
@@ -213,7 +241,16 @@ public class ApiUtil {
} catch (IOException e) {
throw new RuntimeException(e); // We can rethrow, since supplyAsync catches exceptions.
}
- }, executorService);
+ }, executorService).handle((obj, t) -> {
+ if (t != null) {
+ System.err.println(ErrorUtil.printStackTraceWithoutApiKey(t));
+ }
+ return obj;
+ });
+ }
+
+ public CompletableFuture<String> requestString() {
+ return ApiCache.INSTANCE.cacheRequest(this, getCacheKey(), this::requestString0, maxCacheAge);
}
public CompletableFuture<JsonObject> requestJson() {
@@ -221,7 +258,7 @@ public class ApiUtil {
}
public <T> CompletableFuture<T> requestJson(Class<? extends T> clazz) {
- return requestString().thenApply(str -> gson.fromJson(str, clazz));
+ return requestString().thenApplyAsync(str -> gson.fromJson(str, clazz));
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
index 8ce765aa..a89b281d 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
@@ -24,6 +24,7 @@ import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe;
import io.github.moulberry.notenoughupdates.listener.ScoreboardLocationChangeListener;
+import io.github.moulberry.notenoughupdates.miscfeatures.CookieWarning;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent;
import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
import io.github.moulberry.notenoughupdates.overlays.OverlayManager;
@@ -86,6 +87,7 @@ public class SBInfo {
public String objective = "";
public String slayer = "";
public boolean stranded = false;
+ public boolean bingo = false;
public String mode = null;
@@ -388,6 +390,7 @@ public class SBInfo {
isInDungeon = tempIsInDungeon;
boolean containsStranded = false;
+ boolean containsBingo = false;
for (String line : lines) { //Slayer stuff
if (line.contains("Tarantula Broodfather")) {
slayer = "Tarantula";
@@ -422,8 +425,10 @@ public class SBInfo {
}
}
if (line.contains("☀ Stranded")) containsStranded = true;
+ if (line.contains("Ⓑ Bingo")) containsBingo = true;
}
stranded = containsStranded;
+ bingo = containsBingo;
if (lines.size() >= 5) {
date = Utils.cleanColour(lines.get(1)).trim();
@@ -473,7 +478,6 @@ public class SBInfo {
.thenAccept(newJson -> mayorJson = newJson);
}
-
public JsonObject getMayorJson() {
return mayorJson;
}
@@ -482,6 +486,7 @@ public class SBInfo {
if (!newProfile.equals(currentProfile)) {
currentProfile = newProfile;
MinionHelperManager.getInstance().onProfileSwitch();
+ CookieWarning.onProfileSwitch();
}
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
index d5abcb77..38debbbc 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
@@ -925,6 +925,10 @@ public class Utils {
return createItemStack(item, displayName, 0, lore);
}
+ public static ItemStack createItemStackArray(Item item, String displayName, String[] lore) {
+ return createItemStack(item, displayName, 0, lore);
+ }
+
public static ItemStack createItemStack(Block item, String displayName, String... lore) {
return createItemStack(Item.getItemFromBlock(item), displayName, lore);
}
@@ -932,17 +936,7 @@ public class Utils {
public static ItemStack createItemStack(Item item, String displayName, int damage, String... lore) {
ItemStack stack = new ItemStack(item, 1, damage);
NBTTagCompound tag = new NBTTagCompound();
- NBTTagCompound display = new NBTTagCompound();
- NBTTagList Lore = new NBTTagList();
-
- for (String line : lore) {
- Lore.appendTag(new NBTTagString(line));
- }
-
- display.setString("Name", displayName);
- display.setTag("Lore", Lore);
-
- tag.setTag("display", display);
+ addNameAndLore(tag, displayName, lore);
tag.setInteger("HideFlags", 254);
stack.setTagCompound(tag);
@@ -950,6 +944,22 @@ public class Utils {
return stack;
}
+ private static void addNameAndLore(NBTTagCompound tag, String displayName, String[] lore) {
+ NBTTagCompound display = new NBTTagCompound();
+
+ display.setString("Name", displayName);
+
+ if (lore != null) {
+ NBTTagList tagLore = new NBTTagList();
+ for (String line : lore) {
+ tagLore.appendTag(new NBTTagString(line));
+ }
+ display.setTag("Lore", tagLore);
+ }
+
+ tag.setTag("display", display);
+ }
+
public static ItemStack editItemStackInfo(
ItemStack itemStack,
String displayName,
@@ -979,15 +989,17 @@ public class Utils {
return itemStack;
}
-
public static ItemStack createSkull(String displayName, String uuid, String value) {
+ return createSkull(displayName, uuid, value, null);
+ }
+
+ public static ItemStack createSkull(String displayName, String uuid, String value, String[] lore) {
ItemStack render = new ItemStack(Items.skull, 1, 3);
NBTTagCompound tag = new NBTTagCompound();
NBTTagCompound skullOwner = new NBTTagCompound();
NBTTagCompound properties = new NBTTagCompound();
NBTTagList textures = new NBTTagList();
NBTTagCompound textures_0 = new NBTTagCompound();
- NBTTagCompound display = new NBTTagCompound();
skullOwner.setString("Id", uuid);
skullOwner.setString("Name", uuid);
@@ -995,8 +1007,7 @@ public class Utils {
textures_0.setString("Value", value);
textures.appendTag(textures_0);
- display.setString("Name", displayName);
- tag.setTag("display", display);
+ addNameAndLore(tag, displayName, lore);
properties.setTag("textures", textures);
skullOwner.setTag("Properties", properties);
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt
index a21e39b8..b8abb99e 100644
--- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt
@@ -162,7 +162,7 @@ class MiscCommands {
nc.printChatMessage(ChatComponentText("§e[NEU] §a$it"))
}
null
- }, MinecraftExecutor.INSTANCE)
+ }, MinecraftExecutor.OnThread)
}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/OldSkyBlockMenu.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/OldSkyBlockMenu.kt
new file mode 100644
index 00000000..b871a672
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/OldSkyBlockMenu.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscfeatures
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates
+import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe
+import io.github.moulberry.notenoughupdates.events.ReplaceItemEvent
+import io.github.moulberry.notenoughupdates.events.SlotClickEvent
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.client.player.inventory.ContainerLocalMenu
+import net.minecraft.init.Items
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+@NEUAutoSubscribe
+object OldSkyBlockMenu {
+
+ val map: Map<Int, SkyBlockButton> by lazy {
+ val map = mutableMapOf<Int, SkyBlockButton>()
+ for (button in SkyBlockButton.values()) {
+ map[button.slot] = button
+ }
+ map
+ }
+
+ @SubscribeEvent
+ fun replaceItem(event: ReplaceItemEvent) {
+ if (!isRightInventory()) return
+ if (event.inventory !is ContainerLocalMenu) return
+
+ val skyBlockButton = map[event.slotNumber] ?: return
+
+ if (skyBlockButton.requiresBoosterCookie && !CookieWarning.hasActiveBoosterCookie()) {
+ event.replaceWith(skyBlockButton.itemWithCookieWarning)
+ } else {
+ event.replaceWith(skyBlockButton.itemWithoutCookieWarning)
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onStackClick(event: SlotClickEvent) {
+ if (!isRightInventory()) return
+
+ val skyBlockButton = map[event.slotId] ?: return
+ event.isCanceled = true
+
+ if (!skyBlockButton.requiresBoosterCookie || CookieWarning.hasActiveBoosterCookie()) {
+ NotEnoughUpdates.INSTANCE.sendChatMessage("/" + skyBlockButton.command)
+ }
+ }
+
+ private fun isRightInventory(): Boolean {
+ return NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard() &&
+ NotEnoughUpdates.INSTANCE.config.misc.oldSkyBlockMenu &&
+ Utils.getOpenChestName() == "SkyBlock Menu"
+ }
+
+ enum class SkyBlockButton(
+ val command: String,
+ val slot: Int,
+ private val displayName: String,
+ private vararg val displayDescription: String,
+ private val itemData: ItemData,
+ val requiresBoosterCookie: Boolean = true,
+ ) {
+ TRADES(
+ "trades", 40,
+ "Trades",
+ "View your available trades.",
+ "These trades are always",
+ "available and accessible through",
+ "the SkyBlock Menu.",
+ itemData = NormalItemData(Items.emerald),
+ requiresBoosterCookie = false
+ ),
+ ACCESSORY(
+ "accessories", 53,
+ "Accessory Bag",
+ "A special bag which can hold",
+ "Talismans, Rings, Artifacts, Relics, and",
+ "Orbs within it. All will still",
+ "work while in this bag!",
+ itemData = SkullItemData(
+ "2b73dd76-5fc1-4ac3-8139-6a8992f8ce80",
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTYxYTkxOGMw" +
+ "YzQ5YmE4ZDA1M2U1MjJjYjkxYWJjNzQ2ODkzNjdiNGQ4YWEwNmJmYzFiYTkxNTQ3MzA5ODVmZiJ9fX0="
+ )
+ ),
+ POTION(
+ "potionbag", 52,
+ "Potion Bag",
+ "A handy bag for holding your",
+ "Potions in.",
+ itemData = SkullItemData(
+ "991c4a18-3283-4629-b0fc-bbce23cd658c",
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWY4Yjg" +
+ "yNDI3YjI2MGQwYTYxZTY0ODNmYzNiMmMzNWE1ODU4NTFlMDhhOWE5ZGYzNzI1NDhiNDE2OGNjODE3YyJ9fX0="
+ )
+ ),
+ QUIVER(
+ "quiver", 44,
+ "Quiver",
+ "A masterfully crafted Quiver",
+ "which holds any kind of",
+ "projectile you can think of!",
+ itemData = SkullItemData(
+ "41758912-e6b1-4700-9de5-04f2cfb9c422",
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGNiM2FjZ" +
+ "GMxMWNhNzQ3YmY3MTBlNTlmNGM4ZTliM2Q5NDlmZGQzNjRjNjg2OTgzMWNhODc4ZjA3NjNkMTc4NyJ9fX0="
+ )
+ ),
+ FISHING(
+ "fishingbag", 43,
+ "Fishing Bag",
+ "A useful bag which can hold all",
+ "types of fish, baits, and fishing",
+ "loot!",
+ itemData = SkullItemData(
+ "508c01d6-eabe-430b-9811-874691ee7ee4",
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWI4ZT" +
+ "I5N2RmNmI4ZGZmY2YxMzVkYmE4NGVjNzkyZDQyMGFkOGVjYjQ1OGQxNDQyODg1NzJhODQ2MDNiMTYzMSJ9fX0="
+ )
+ ),
+ SACK_OF_SACKS(
+ "sacks", 35,
+ "Sack of Sacks",
+ "A sack which contains other",
+ "sacks. Sackception!",
+ itemData = SkullItemData(
+ "a206a7eb-70fc-4f9f-8316-c3f69d6ba2ca",
+ "ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0" +
+ "ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVl" +
+ "LAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5l" +
+ "Y3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0" +
+ "YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0="
+ ),
+ requiresBoosterCookie = false
+ ),
+ ;
+
+ val itemWithCookieWarning: ItemStack by lazy { createItem(true) }
+ val itemWithoutCookieWarning: ItemStack by lazy { createItem(false) }
+
+ private fun createItem(showCookieWarning: Boolean): ItemStack {
+ val lore = mutableListOf<String>()
+ for (line in displayDescription) {
+ lore.add("§7$line")
+ }
+ lore.add("")
+
+ if (showCookieWarning) {
+ lore.add("§cYou need a booster cookie active")
+ lore.add("§cto use this shortcut!")
+ } else {
+ lore.add("§eClick to execute /${command}")
+ }
+ val array = lore.toTypedArray()
+ val name = "§a${displayName}"
+ return when (itemData) {
+ is NormalItemData -> Utils.createItemStackArray(itemData.displayIcon, name, array)
+ is SkullItemData -> Utils.createSkull(
+ name,
+ itemData.uuid,
+ itemData.value,
+ array
+ )
+ }
+ }
+ }
+
+ sealed interface ItemData
+
+ class NormalItemData(val displayIcon: Item) : ItemData
+
+ class SkullItemData(val uuid: String, val value: String) : ItemData
+}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ApiCache.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ApiCache.kt
new file mode 100644
index 00000000..59fc2dd5
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ApiCache.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates
+import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag
+import io.github.moulberry.notenoughupdates.util.ApiUtil.Request
+import io.github.moulberry.notenoughupdates.util.kotlin.supplyImmediate
+import org.apache.http.NameValuePair
+import java.nio.file.Files
+import java.nio.file.Path
+import java.time.Duration
+import java.util.*
+import java.util.concurrent.CompletableFuture
+import java.util.function.Supplier
+import kotlin.io.path.deleteIfExists
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.ExperimentalTime
+import kotlin.time.TimeSource
+import kotlin.time.toKotlinDuration
+
+@OptIn(ExperimentalTime::class)
+
+object ApiCache {
+ data class CacheKey(
+ val baseUrl: String,
+ val requestParameters: List<NameValuePair>,
+ val shouldGunzip: Boolean,
+ )
+
+ data class CacheResult internal constructor(
+ var cacheState: CacheState,
+ val firedAt: TimeSource.Monotonic.ValueTimeMark,
+ ) {
+ constructor(future: CompletableFuture<String>, firedAt: TimeSource.Monotonic.ValueTimeMark) : this(
+ CacheState.WaitingForFuture(future),
+ firedAt
+ ) {
+ future.thenAccept { text ->
+ synchronized(this) {
+ val f = Files.createTempFile(cacheBaseDir, "api-cache", ".bin")
+ log("Writing cache to disk: $f")
+ f.toFile().deleteOnExit()
+ f.writeText(text)
+ cacheState = CacheState.FileCached(f)
+ }
+ }
+ }
+
+ sealed interface CacheState {
+ object Disposed : CacheState
+ data class WaitingForFuture(val future: CompletableFuture<String>) : CacheState
+ data class FileCached(val file: Path) : CacheState
+ }
+
+ val isAvailable get() = cacheState is CacheState.FileCached
+
+ fun getCachedFuture(): CompletableFuture<String> {
+ synchronized(this) {
+ return when (val cs = cacheState) {
+ CacheState.Disposed -> supplyImmediate {
+ throw IllegalStateException("Attempting to read from a disposed future. Most likely caused by non synchronized access to ApiCache.cachedRequests")
+ }
+
+ is CacheState.FileCached -> supplyImmediate {
+ cs.file.readText()
+ }
+
+ is CacheState.WaitingForFuture -> cs.future
+ }
+ }
+ }
+
+ /**
+ * Should be called when removing / replacing a request from [cachedRequests].
+ * Should only be called while holding a lock on [ApiCache].
+ * This deletes the disk cache and smashes the internal state for it to be GCd.
+ * After calling this method no other method may be called on this object.
+ */
+ internal fun dispose() {
+ synchronized(this) {
+ val file = (cacheState as? CacheState.FileCached)?.file
+ log("Disposing cache for $file")
+ cacheState = CacheState.Disposed
+ file?.deleteIfExists()
+ }
+ }
+ }
+
+ private val cacheBaseDir by lazy {
+ val d = Files.createTempDirectory("neu-cache")
+ d.toFile().deleteOnExit()
+ d
+ }
+ private val cachedRequests = mutableMapOf<CacheKey, CacheResult>()
+ val histogramTotalRequests: MutableMap<String, Int> = mutableMapOf()
+ val histogramNonCachedRequests: MutableMap<String, Int> = mutableMapOf()
+
+ private val timeout = 10.seconds
+ private val globalMaxCacheAge = 1.hours
+
+ private fun log(message: String) {
+ NEUDebugFlag.API_CACHE.log(message)
+ }
+
+ private fun traceApiRequest(
+ request: Request,
+ failReason: String?,
+ ) {
+ if (!NotEnoughUpdates.INSTANCE.config.hidden.dev) return
+ val callingClass = Thread.currentThread().stackTrace
+ .find {
+ !it.className.startsWith("java.") &&
+ !it.className.startsWith("kotlin.") &&
+ it.className != ApiCache::class.java.name &&
+ it.className != ApiUtil::class.java.name &&
+ it.className != Request::class.java.name
+ }
+ val callingClassText = callingClass?.let {
+ "${it.className}.${it.methodName} (${it.fileName}:${it.lineNumber})"
+ } ?: "no calling class found"
+ callingClass?.className?.let {
+ histogramTotalRequests[it] = (histogramTotalRequests[it] ?: 0) + 1
+ if (failReason != null)
+ histogramNonCachedRequests[it] = (histogramNonCachedRequests[it] ?: 0) + 1
+ }
+ if (failReason != null) {
+ log("Executing api request for url ${request.baseUrl} by $callingClassText: $failReason")
+ } else {
+ log("Cache hit for api request for url ${request.baseUrl} by $callingClassText.")
+ }
+ }
+
+ private fun evictCache() {
+ synchronized(this) {
+ val it = cachedRequests.iterator()
+ while (it.hasNext()) {
+ val next = it.next()
+ if (next.value.firedAt.elapsedNow() >= globalMaxCacheAge) {
+ next.value.dispose()
+ it.remove()
+ }
+ }
+ }
+ }
+
+ fun cacheRequest(
+ request: Request,
+ cacheKey: CacheKey?,
+ futureSupplier: Supplier<CompletableFuture<String>>,
+ maxAge: Duration?
+ ): CompletableFuture<String> {
+ evictCache()
+ if (cacheKey == null) {
+ traceApiRequest(request, "uncacheable request (probably POST)")
+ return futureSupplier.get()
+ }
+ if (maxAge == null) {
+ traceApiRequest(request, "manually specified as uncacheable")
+ return futureSupplier.get()
+ }
+ fun recache(): CompletableFuture<String> {
+ return futureSupplier.get().also {
+ cachedRequests[cacheKey]?.dispose() // Safe to dispose like this because this function is always called in a synchronized block
+ cachedRequests[cacheKey] = CacheResult(it, TimeSource.Monotonic.markNow())
+ }
+ }
+ synchronized(this) {
+ val cachedRequest = cachedRequests[cacheKey]
+ if (cachedRequest == null) {
+ traceApiRequest(request, "no cache found")
+ return recache()
+ }
+
+ return if (cachedRequest.isAvailable) {
+ if (cachedRequest.firedAt.elapsedNow() > maxAge.toKotlinDuration()) {
+ traceApiRequest(request, "outdated cache")
+ recache()
+ } else {
+ // Using local cached request
+ traceApiRequest(request, null)
+ cachedRequest.getCachedFuture()
+ }
+ } else {
+ if (cachedRequest.firedAt.elapsedNow() > timeout) {
+ traceApiRequest(request, "suspiciously slow api response")
+ recache()
+ } else {
+ // Joining ongoing request
+ traceApiRequest(request, null)
+ cachedRequest.getCachedFuture()
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ErrorUtil.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ErrorUtil.kt
new file mode 100644
index 00000000..f849a40d
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/ErrorUtil.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+import java.nio.charset.StandardCharsets
+
+object ErrorUtil {
+ @JvmStatic
+ fun printCensoredStackTrace(e: Throwable, toCensor: List<String>): String {
+ val baos = ByteArrayOutputStream()
+ e.printStackTrace(PrintStream(baos, true, StandardCharsets.UTF_8.name()))
+ var string = String(baos.toByteArray(), StandardCharsets.UTF_8)
+ toCensor.forEach {
+ string = string.replace(it, "*".repeat(it.length))
+ }
+ return string
+ }
+
+ @JvmStatic
+ fun printStackTraceWithoutApiKey(e: Throwable): String {
+ return printCensoredStackTrace(e, listOf(NotEnoughUpdates.INSTANCE.config.apiData.apiKey))
+ }
+}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.kt
new file mode 100644
index 00000000..bb0bc8b4
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util
+
+import net.minecraft.client.Minecraft
+import java.util.concurrent.Executor
+import java.util.concurrent.ForkJoinPool
+
+object MinecraftExecutor {
+
+ @JvmField
+ val OnThread = Executor {
+ val mc = Minecraft.getMinecraft()
+ if (mc.isCallingFromMinecraftThread) {
+ it.run()
+ } else {
+ Minecraft.getMinecraft().addScheduledTask(it)
+ }
+ }
+
+ @JvmField
+ val OffThread = Executor {
+ val mc = Minecraft.getMinecraft()
+ if (mc.isCallingFromMinecraftThread) {
+ ForkJoinPool.commonPool().execute(it)
+ } else {
+ it.run()
+ }
+ }
+}
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/completablefuture.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/completablefuture.kt
new file mode 100644
index 00000000..de45c1e3
--- /dev/null
+++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/completablefuture.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.util.kotlin
+
+import java.util.concurrent.CompletableFuture
+
+inline fun <R> supplyImmediate(block: () -> R): CompletableFuture<R> {
+ val cf = CompletableFuture<R>()
+ try {
+ cf.complete(block())
+ } catch (t: Throwable) {
+ cf.completeExceptionally(t)
+ }
+ return cf
+}
+