diff options
8 files changed, 230 insertions, 27 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index ec99fd5e..9523c739 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,8 +30,8 @@ plugins { id("com.github.johnrengelman.shadow") version "7.1.2" id("io.github.juuxel.loom-quiltflower") version "1.7.3" `maven-publish` - kotlin("jvm") version "1.8.0" - id("com.google.devtools.ksp") version "1.8.0-1.0.8" + kotlin("jvm") version "1.8.21" + id("com.google.devtools.ksp") version "1.8.21-1.0.11" } @@ -133,6 +133,7 @@ dependencies { // Please keep this version in sync with KotlinLoadingTweaker implementation(enforcedPlatform("org.jetbrains.kotlin:kotlin-bom:1.8.0")) kotlinDependencies(kotlin("stdlib")) + kotlinDependencies(kotlin("reflect")) ksp("dev.zacsweers.autoservice:auto-service-ksp:1.0.0") implementation("com.google.auto.service:auto-service-annotations:1.0.1") diff --git a/src/main/java/io/github/moulberry/notenoughupdates/ItemPriceInformation.java b/src/main/java/io/github/moulberry/notenoughupdates/ItemPriceInformation.java index 7d408e0f..6e7f83e4 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/ItemPriceInformation.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/ItemPriceInformation.java @@ -26,6 +26,7 @@ import io.github.moulberry.notenoughupdates.auction.APIManager; import io.github.moulberry.notenoughupdates.core.config.KeybindHelper; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.Utils; +import io.github.moulberry.notenoughupdates.util.hypixelapi.HypixelItemAPI; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import org.lwjgl.input.Keyboard; @@ -123,22 +124,18 @@ public class ItemPriceInformation { } } + int shiftStackMultiplier = useStackSize && stack.stackSize > 1 ? stack.stackSize : stack.getItem().getItemStackLimit(stack); + if (stack.getTagCompound() != null && stack.getTagCompound().hasKey(STACKSIZE_OVERRIDE)) { + shiftStackMultiplier = stack.getTagCompound().getInteger(STACKSIZE_OVERRIDE); + } + int stackMultiplier = 1; + boolean shiftPressed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); + if (shiftPressed) { + stackMultiplier = shiftStackMultiplier; + } + boolean added = false; if (bazaarItem) { List<Integer> lines = NotEnoughUpdates.INSTANCE.config.tooltipTweaks.priceInfoBaz; - - boolean added = false; - - boolean shiftPressed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); - - int stackMultiplier = 1; - int shiftStackMultiplier = useStackSize && stack.stackSize > 1 ? stack.stackSize : 64; - if (stack.getTagCompound() != null && stack.getTagCompound().hasKey(STACKSIZE_OVERRIDE)) { - shiftStackMultiplier = stack.getTagCompound().getInteger(STACKSIZE_OVERRIDE); - } - if (shiftPressed) { - stackMultiplier = shiftStackMultiplier; - } - //values = {"", "Buy", "Sell", "Buy (Insta)", "Sell (Insta)", "Raw Craft Cost", "Instabuys (Hourly)", "Instasells (Hourly)", "Instabuys (Daily)", "Instasells (Daily)", "Instabuys (Weekly)", "Instasells (Weekly)"} for (int lineId : lines) { switch (lineId) { @@ -288,7 +285,6 @@ public class ItemPriceInformation { } else if (auctionItem && !auctionInfoErrored) { List<Integer> lines = NotEnoughUpdates.INSTANCE.config.tooltipTweaks.priceInfoAuc; - boolean added = false; for (int lineId : lines) { switch (lineId) { @@ -444,14 +440,13 @@ public class ItemPriceInformation { } } else if (NotEnoughUpdates.INSTANCE.config.tooltipTweaks.rawCraft && craftCost != null && craftCost.fromRecipe) { - - if (craftCost.craftCost == 0) return; - double cost = craftCost.craftCost; - int shiftStackMultiplier = useStackSize && stack.stackSize > 1 ? stack.stackSize : 64; - if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) cost = cost * shiftStackMultiplier; - tooltip.add(""); - tooltip.add(formatPrice("Raw Craft Cost: ", cost)); - + if (craftCost.craftCost != 0) { + double cost = craftCost.craftCost; + cost = cost * stackMultiplier; + added = true; + tooltip.add(""); + tooltip.add(formatPrice("Raw Craft Cost: ", cost)); + } } else if (auctionInfoErrored && NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) { String message = EnumChatFormatting.RED.toString() + EnumChatFormatting.BOLD + "[NEU] API is down"; if (auctionableItems != null && !auctionableItems.isEmpty()) { @@ -462,6 +457,12 @@ public class ItemPriceInformation { tooltip.add(message + " and no item data is cached"); } } + Double npcSellPrice = HypixelItemAPI.getNPCSellPrice(internalname); + if (NotEnoughUpdates.INSTANCE.config.tooltipTweaks.npcSellPrice && npcSellPrice != null) { + if (!added) + tooltip.add(""); + tooltip.add(formatPrice("NPC Sell Price: ", npcSellPrice * stackMultiplier)); + } } private static String formatPrice(String label, double price) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 794df0f4..c6a0c04f 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -54,6 +54,7 @@ import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer; import io.github.moulberry.notenoughupdates.recipes.RecipeGenerator; import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.brigadier.BrigadierRoot; +import io.github.moulberry.notenoughupdates.util.hypixelapi.HypixelItemAPI; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; @@ -301,6 +302,7 @@ public class NotEnoughUpdates { manager.loadItemInformation(); overlay = new NEUOverlay(manager); profileViewer = new ProfileViewer(manager); + HypixelItemAPI.INSTANCE.loadItemData(); for (KeyBinding kb : manager.keybinds) { ClientRegistry.registerKeyBinding(kb); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java b/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java index bf4e746f..720dd540 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/loader/KotlinLoadingTweaker.java @@ -82,7 +82,7 @@ public class KotlinLoadingTweaker implements ITweaker { * Full version format: [1, 7, 20] (1.7.20) * RC version format: [1, 7, 20, 1] (1.7.20-rc1) */ - public static final int[] BUNDLED_KOTLIN_VERSION = new int[]{1, 8, 0}; + public static final int[] BUNDLED_KOTLIN_VERSION = new int[]{1, 8, 21}; @Override public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/TooltipTweaks.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/TooltipTweaks.java index cd6ecb26..2957015b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/TooltipTweaks.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/TooltipTweaks.java @@ -95,6 +95,15 @@ public class TooltipTweaks { @Expose @ConfigOption( + name = "Show NPC Sell price on Items", + desc = "Display for how much items can be sold to NPC Shops" + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 0) + public boolean npcSellPrice = false; + + @Expose + @ConfigOption( name = "Use Short Number Format", desc = "Use Short Numbers (5.1m) instead of 5,130,302" ) 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 0cda91fd..4cb1abc8 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java @@ -20,9 +20,11 @@ package io.github.moulberry.notenoughupdates.util; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.events.ProfileDataLoadedEvent; +import io.github.moulberry.notenoughupdates.util.kotlin.KotlinTypeAdapterFactory; import net.minecraft.client.Minecraft; import net.minecraft.util.EnumChatFormatting; import org.apache.commons.io.IOUtils; @@ -63,7 +65,9 @@ import java.util.concurrent.Executors; import java.util.zip.GZIPInputStream; public class ApiUtil { - private static final Gson gson = new Gson(); + private static final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(KotlinTypeAdapterFactory.INSTANCE) + .create(); private static final Comparator<NameValuePair> nameValuePairComparator = Comparator .comparing(NameValuePair::getName) diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/hypixelapi/HypixelItemAPI.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/hypixelapi/HypixelItemAPI.kt new file mode 100644 index 00000000..acb2b53f --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/hypixelapi/HypixelItemAPI.kt @@ -0,0 +1,58 @@ +/* + * 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.hypixelapi + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.util.kotlin.ExtraData +import io.github.moulberry.notenoughupdates.util.kotlin.KSerializable +import io.github.moulberry.notenoughupdates.util.kotlin.KSerializedName + +object HypixelItemAPI { + @KSerializable + data class ApiResponse( + val items: List<ApiItem>, + ) + + @KSerializable + data class ApiItem( + val id: String, + val name: String, + @param:KSerializedName("npc_sell_price") + val npcSellPrice: Double? = null, + @param:ExtraData + val extraData: Map<String, JsonElement>, + ) + + var itemData: Map<String, ApiItem> = mapOf() + private set + + @JvmStatic + fun getNPCSellPrice(itemId: String) = itemData[itemId]?.npcSellPrice + + fun loadItemData() { + NotEnoughUpdates.INSTANCE.manager.apiUtils.newAnonymousHypixelApiRequest("resources/skyblock/items") + .requestJson(ApiResponse::class.java) + .thenAccept { + itemData = it.items.associateBy { it.id } + } + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/KotlinTypeAdapterFactory.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/KotlinTypeAdapterFactory.kt new file mode 100644 index 00000000..e531364a --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/kotlin/KotlinTypeAdapterFactory.kt @@ -0,0 +1,128 @@ +/* + * 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 com.google.gson.* +import com.google.gson.annotations.SerializedName +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter +import kotlin.reflect.* +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.isSubtypeOf +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor +import com.google.gson.internal.`$Gson$Types` as InternalGsonTypes + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +annotation class KSerializable + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class ExtraData + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class KSerializedName(val serialName: String) + +object KotlinTypeAdapterFactory : TypeAdapterFactory { + + internal data class ParameterInfo( + val param: KParameter, + val adapter: TypeAdapter<Any?>, + val name: String, + val field: KProperty1<Any, Any?> + ) + + @OptIn(ExperimentalStdlibApi::class) + override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { + val kotlinClass = type.rawType.kotlin as KClass<T> + if (kotlinClass.findAnnotation<KSerializable>() == null) return null + if (!kotlinClass.isData) return null + val primaryConstructor = kotlinClass.primaryConstructor ?: return null + val params = primaryConstructor.parameters.filter { it.findAnnotation<ExtraData>() == null } + val extraDataParam = primaryConstructor.parameters + .find { it.findAnnotation<ExtraData>() != null && typeOf<MutableMap<String, JsonElement>>().isSubtypeOf(it.type) } + ?.let { param -> + param to kotlinClass.memberProperties.find { it.name == param.name && it.returnType.isSubtypeOf(typeOf<Map<String, JsonElement>>()) } as KProperty1<Any, Map<String, JsonElement>> + } + val parameterInfos = params.map { param -> + ParameterInfo( + param, + gson.getAdapter( + TypeToken.get(InternalGsonTypes.resolve(type.type, type.rawType, param.type.javaType)) + ) as TypeAdapter<Any?>, + param.findAnnotation<KSerializedName>()?.serialName ?: param.name!!, + kotlinClass.memberProperties.find { it.name == param.name }!! as KProperty1<Any, Any?> + ) + }.associateBy { it.name } + val jsonElementAdapter = gson.getAdapter(JsonElement::class.java) + + return object : TypeAdapter<T>() { + override fun write(out: JsonWriter, value: T?) { + if (value == null) { + out.nullValue() + return + } + out.beginObject() + parameterInfos.forEach { (name, paramInfo) -> + out.name(name) + paramInfo.adapter.write(out, paramInfo.field.get(value)) + } + if (extraDataParam != null) { + val extraData = extraDataParam.second.get(value) + extraData.forEach { (extraName, extraValue) -> + out.name(extraName) + jsonElementAdapter.write(out, extraValue) + } + } + out.endObject() + } + + override fun read(reader: JsonReader): T? { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull() + return null + } + reader.beginObject() + val args = mutableMapOf<KParameter, Any?>() + val extraData = mutableMapOf<String, JsonElement>() + while (reader.peek() != JsonToken.END_OBJECT) { + val name = reader.nextName() + val paramData = parameterInfos[name] + if (paramData == null) { + extraData[name] = jsonElementAdapter.read(reader) + continue + } + val value = paramData.adapter.read(reader) + args[paramData.param] = value + } + reader.endObject() + if (extraDataParam != null) { + args[extraDataParam.first] = extraData + } + return primaryConstructor.callBy(args) + } + } + } +} + |