aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/bazaar/BazaarJson.kt36
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt100
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarApi.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarData.kt11
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt26
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt17
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt11
12 files changed, 197 insertions, 38 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index 6a0f0c9dd..dcb610423 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -52,6 +52,7 @@ import at.hannibal2.skyhanni.data.SlayerAPI
import at.hannibal2.skyhanni.data.TitleData
import at.hannibal2.skyhanni.data.TitleManager
import at.hannibal2.skyhanni.data.TrackerManager
+import at.hannibal2.skyhanni.data.bazaar.HypixelBazaarFetcher
import at.hannibal2.skyhanni.data.hypixel.chat.PlayerChatManager
import at.hannibal2.skyhanni.data.hypixel.chat.PlayerNameFormatter
import at.hannibal2.skyhanni.data.jsonobjects.local.FriendsJson
@@ -545,6 +546,7 @@ class SkyHanniMod {
loadModule(ChromaManager)
loadModule(ContributorManager)
loadModule(TabComplete)
+ loadModule(HypixelBazaarFetcher)
// APIs
loadModule(BazaarApi())
diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
index b8a0f2c54..b4a2c336e 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
@@ -12,6 +12,7 @@ import at.hannibal2.skyhanni.data.GuiEditManager
import at.hannibal2.skyhanni.data.PartyAPI
import at.hannibal2.skyhanni.data.SackAPI
import at.hannibal2.skyhanni.data.TitleManager
+import at.hannibal2.skyhanni.data.bazaar.HypixelBazaarFetcher
import at.hannibal2.skyhanni.features.bingo.card.BingoCardDisplay
import at.hannibal2.skyhanni.features.bingo.card.nextstephelper.BingoNextStepHelper
import at.hannibal2.skyhanni.features.chat.Translator
@@ -380,6 +381,10 @@ object Commands {
"shupdate",
"Updates the mod to the specified update stream."
) { forceUpdate(it) }
+ registerCommand(
+ "shUpdateBazaarPrices",
+ "Forcefully updating the bazaar prices right now."
+ ) { HypixelBazaarFetcher.fetchNow() }
}
private fun developersDebugFeatures() {
@@ -623,8 +628,9 @@ object Commands {
}
}
- private fun registerCommand(name: String, description: String, function: (Array<String>) -> Unit) {
- if (commands.any { it.name.equals(name, ignoreCase = true) }) {
+ private fun registerCommand(rawName: String, description: String, function: (Array<String>) -> Unit) {
+ val name = rawName.lowercase()
+ if (commands.any { it.name == name }) {
error("The command '$name is already registered!'")
}
ClientCommandHandler.instance.registerCommand(SimpleCommand(name, createCommand(function)))
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
index bbacd341a..6be8812b8 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
@@ -66,6 +66,11 @@ public class DebugConfig {
public boolean showNpcPrice = false;
@Expose
+ @ConfigOption(name = "Show BZ Price", desc = "Show BZ price in item lore.")
+ @ConfigEditorBoolean
+ public boolean showBZPrice = false;
+
+ @Expose
@ConfigOption(name = "Show Item UUID", desc = "Show the Unique Identifier of items in the lore.")
@ConfigEditorBoolean
public boolean showItemUuid = false;
diff --git a/src/main/java/at/hannibal2/skyhanni/data/bazaar/BazaarJson.kt b/src/main/java/at/hannibal2/skyhanni/data/bazaar/BazaarJson.kt
new file mode 100644
index 000000000..350f33db2
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/bazaar/BazaarJson.kt
@@ -0,0 +1,36 @@
+package at.hannibal2.skyhanni.data.bazaar
+
+import com.google.gson.annotations.Expose
+import com.google.gson.annotations.SerializedName
+
+class BazaarApiResponseJson(
+ @Expose val success: Boolean,
+ @Expose val cause: String,
+ @Expose val lastUpdated: Long,
+ @Expose val products: Map<String, BazaarProduct>,
+)
+
+data class BazaarProduct(
+ @Expose @SerializedName("product_id") val productId: String,
+ @Expose @SerializedName("quick_status") val quickStatus: BazaarQuickStatus,
+ @Expose @SerializedName("sell_summary") val sellSummary: List<BazaarSummary>,
+ @Expose @SerializedName("buy_summary") val buySummary: List<BazaarSummary>,
+)
+
+class BazaarQuickStatus(
+ @Expose val productId: String,
+ @Expose val sellPrice: Double,
+ @Expose val sellVolume: Long,
+ @Expose val sellMovingWeek: Long,
+ @Expose val sellOrders: Long,
+ @Expose val buyPrice: Double,
+ @Expose val buyVolume: Long,
+ @Expose val buyMovingWeek: Long,
+ @Expose val buyOrders: Long,
+)
+
+data class BazaarSummary(
+ @Expose val amount: Long,
+ @Expose val pricePerUnit: Double,
+ @Expose val orders: Long,
+)
diff --git a/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt
new file mode 100644
index 000000000..148fb633a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt
@@ -0,0 +1,100 @@
+package at.hannibal2.skyhanni.data.bazaar
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.ConfigManager
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarData
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.APIUtil
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.itemName
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.NEUItems
+import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.fromJson
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+// https://api.hypixel.net/#tag/SkyBlock/paths/~1v2~1skyblock~1bazaar/get
+object HypixelBazaarFetcher {
+ private const val URL = "https://api.hypixel.net/v2/skyblock/bazaar"
+ private const val HIDDEN_FAILED_ATTEMPTS = 3
+
+ var latestProductInformation = mapOf<NEUInternalName, BazaarData>()
+ private var nextFetchTime = SimpleTimeMark.farPast()
+ private var failedAttempts = 0
+ private var nextFetchIsManual = false
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!canFetch()) return
+ SkyHanniMod.coroutineScope.launch {
+ fetchAndProcessBazaarData()
+ }
+ }
+
+ private suspend fun fetchAndProcessBazaarData() {
+ nextFetchTime = SimpleTimeMark.now() + 2.minutes
+ val fetchType = if (nextFetchIsManual) "manual" else "automatic"
+ nextFetchIsManual = false
+ try {
+ val jsonResponse = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(URL) }.asJsonObject
+ val response = ConfigManager.gson.fromJson<BazaarApiResponseJson>(jsonResponse)
+ if (response.success) {
+ latestProductInformation = process(response.products)
+ failedAttempts = 0
+ } else {
+ onError(fetchType, Exception("success=false, cause=${response.cause}"))
+ }
+ } catch (e: Exception) {
+ onError(fetchType, e)
+ }
+ }
+
+ private fun process(products: Map<String, BazaarProduct>) = products.mapNotNull { (key, product) ->
+ val internalName = NEUItems.transHypixelNameToInternalName(key)
+ val sellOfferPrice = product.buySummary.minOfOrNull { it.pricePerUnit } ?: 0.0
+ val insantBuyPrice = product.sellSummary.maxOfOrNull { it.pricePerUnit } ?: 0.0
+ if (internalName.getItemStackOrNull() == null) {
+ // Items that exist in Hypixel's Bazaar API, but not in NEU repo (not visible in in the ingame bazaar).
+ // Should only include Enchants
+ if (LorenzUtils.debug)
+ println("Unknown bazaar product: $key/$internalName")
+ return@mapNotNull null
+ }
+ internalName to BazaarData(internalName.itemName, sellOfferPrice, insantBuyPrice, product)
+ }.toMap()
+
+ private fun onError(fetchType: String, e: Exception) {
+ val userMessage = "Failed fetching bazaar price data from hypixel"
+ failedAttempts++
+ if (failedAttempts <= HIDDEN_FAILED_ATTEMPTS) {
+ nextFetchTime = SimpleTimeMark.now() + 15.seconds
+ ChatUtils.debug("$userMessage. (errorMessage=${e.message}, failedAttepmts=$failedAttempts, $fetchType")
+ e.printStackTrace()
+ } else {
+ nextFetchTime = SimpleTimeMark.now() + 15.minutes
+ ErrorManager.logErrorWithData(
+ e,
+ userMessage,
+ "fetchType" to fetchType,
+ "failedAttepmts" to failedAttempts,
+ )
+ }
+ }
+
+ fun fetchNow() {
+ failedAttempts = 0
+ nextFetchIsManual = true
+ nextFetchTime = SimpleTimeMark.now()
+ ChatUtils.chat("Manually updating the bazaar prices right now..")
+ }
+
+ private fun canFetch() = LorenzUtils.onHypixel && nextFetchTime.isInPast()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarApi.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarApi.kt
index 5d976a7dd..9df83ec81 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarApi.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarApi.kt
@@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.features.inventory.bazaar
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
+import at.hannibal2.skyhanni.data.bazaar.HypixelBazaarFetcher
import at.hannibal2.skyhanni.events.BazaarOpenedProductEvent
import at.hannibal2.skyhanni.events.GuiContainerEvent
import at.hannibal2.skyhanni.events.InventoryCloseEvent
@@ -14,6 +15,7 @@ import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.InventoryUtils.getAllItems
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.itemName
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.LorenzUtils
@@ -41,20 +43,18 @@ class BazaarApi {
var currentlyOpenedProduct: NEUInternalName? = null
- fun NEUInternalName.getBazaarData() = if (isBazaarItem()) {
- holder.getData(this)
- } else null
+ fun NEUInternalName.getBazaarData(): BazaarData? = HypixelBazaarFetcher.latestProductInformation[this]
fun NEUInternalName.getBazaarDataOrError(): BazaarData = getBazaarData() ?: run {
ErrorManager.skyHanniError(
- "Can not find bazaar data for internal name",
+ "Can not find bazaar data for $itemName",
"internal name" to this
)
}
fun isBazaarItem(stack: ItemStack): Boolean = stack.getInternalName().isBazaarItem()
- fun NEUInternalName.isBazaarItem() = NEUItems.manager.auctionManager.getBazaarInfo(asString()) != null
+ fun NEUInternalName.isBazaarItem() = getBazaarData() != null
fun searchForBazaarItem(displayName: String, amount: Int = -1) {
if (!LorenzUtils.inSkyBlock) return
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarData.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarData.kt
index ed49359bb..db97d5e6b 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarData.kt
@@ -1,7 +1,14 @@
package at.hannibal2.skyhanni.features.inventory.bazaar
+import at.hannibal2.skyhanni.data.bazaar.BazaarProduct
+
data class BazaarData(
val displayName: String,
- val sellPrice: Double,
- val buyPrice: Double,
+ val sellOfferPrice: Double,
+ val instantBuyPrice: Double,
+ val product: BazaarProduct,
+ @Deprecated("outdated", ReplaceWith("instantBuyPrice"))
+ val sellPrice: Double = instantBuyPrice,
+ @Deprecated("outdated", ReplaceWith("sellOfferPrice"))
+ val buyPrice: Double = sellOfferPrice,
)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt
index 2cbc2474d..8911e1e2b 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt
@@ -6,22 +6,15 @@ import at.hannibal2.skyhanni.data.jsonobjects.other.SkyblockItemsDataJson
import at.hannibal2.skyhanni.features.rift.RiftAPI
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.APIUtil
-import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NEUItems
-import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull
-import at.hannibal2.skyhanni.utils.NEUItems.getPrice
-import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.fromJson
import kotlinx.coroutines.launch
-import kotlin.concurrent.fixedRateTimer
class BazaarDataHolder {
companion object {
- private val bazaarData = mutableMapOf<NEUInternalName, BazaarData>()
private var npcPrices = mapOf<NEUInternalName, Double>()
fun getNpcPrice(internalName: NEUInternalName) = npcPrices[internalName]
@@ -55,25 +48,6 @@ class BazaarDataHolder {
}
// TODO use SecondPassedEvent
- fixedRateTimer(name = "skyhanni-bazaar-update", period = 10_000L) {
- bazaarData.clear()
- }
}
- fun getData(internalName: NEUInternalName) = bazaarData[internalName] ?: createNewData(internalName)
-
- private fun createNewData(internalName: NEUInternalName): BazaarData? {
- val stack = internalName.getItemStackOrNull()
- if (stack == null) {
- ChatUtils.debug("Bazaar data is null: '$internalName'")
- return null
- }
- val displayName = stack.name.removeColor()
- val sellPrice = internalName.getPrice(true)
- val buyPrice = internalName.getPrice(false)
-
- val data = BazaarData(displayName, sellPrice, buyPrice)
- bazaarData[internalName] = data
- return data
- }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
index 7e9427996..b81a25599 100644
--- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
+++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
@@ -15,6 +15,7 @@ import at.hannibal2.skyhanni.events.LorenzToolTipEvent
import at.hannibal2.skyhanni.events.ReceiveParticleEvent
import at.hannibal2.skyhanni.features.garden.GardenNextJacobContest
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorColorNames
+import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi.Companion.getBazaarData
import at.hannibal2.skyhanni.test.GriffinUtils.drawWaypointFilled
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.CollectionUtils.editCopy
@@ -460,7 +461,21 @@ class SkyHanniDebugsAndTests {
val internalName = event.itemStack.getInternalNameOrNull() ?: return
val npcPrice = internalName.getNpcPriceOrNull() ?: return
- event.toolTip.add("§7NPC price: §6${npcPrice.addSeparators()}")
+ event.toolTip.add("§7NPC price: ${npcPrice.addSeparators()}")
+ }
+
+ @SubscribeEvent
+ fun onShowBzPrice(event: LorenzToolTipEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ if (!debugConfig.showBZPrice) return
+ val internalName = event.itemStack.getInternalNameOrNull() ?: return
+
+ val data = internalName.getBazaarData() ?: return
+ val instantBuyPrice = data.instantBuyPrice
+ val sellOfferPrice = data.sellOfferPrice
+
+ event.toolTip.add("§7BZ instantBuyPrice: ${instantBuyPrice.addSeparators()}")
+ event.toolTip.add("§7BZ sellOfferPrice: ${sellOfferPrice.addSeparators()}")
}
@SubscribeEvent
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt
index bd1077be4..46a0d7b57 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt
@@ -43,6 +43,11 @@ object APIUtil {
)
.useSystemProperties()
+ /**
+ * TODO
+ * make suspend
+ * use withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject
+ */
fun getJSONResponse(urlString: String, silentError: Boolean = false) =
getJSONResponseAsElement(urlString, silentError) as JsonObject
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
index 664eb86b9..63c2e4c67 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -85,6 +85,8 @@ object LorenzUtils {
return result
}
+ val debug: Boolean = onHypixel && SkyHanniMod.feature.dev.debug.enabled
+
private var previousApril = false
fun SimpleDateFormat.formatCurrentTime(): String = this.format(System.currentTimeMillis())
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt
index a5ffc6e14..430edc971 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt
@@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.data.jsonobjects.repo.MultiFilterJson
import at.hannibal2.skyhanni.events.NeuProfileDataLoadedEvent
import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi.Companion.getBazaarData
import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarDataHolder
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ItemBlink.checkBlinkItem
@@ -149,6 +150,7 @@ object NEUItems {
fun getInternalNameOrNull(nbt: NBTTagCompound): NEUInternalName? =
ItemResolutionQuery(manager).withItemNBT(nbt).resolveInternalName()?.asInternalName()
+
fun NEUInternalName.getPrice(useSellingPrice: Boolean = false) = getPriceOrNull(useSellingPrice) ?: -1.0
fun NEUInternalName.getNpcPrice() = getNpcPriceOrNull() ?: -1.0
@@ -167,8 +169,13 @@ object NEUItems {
if (this == NEUInternalName.WISP_POTION) {
return 20_000.0
}
- val result = manager.auctionManager.getBazaarOrBin(asString(), useSellingPrice)
- if (result != -1.0) return result
+
+ getBazaarData()?.let {
+ return if (useSellingPrice) it.sellOfferPrice else it.instantBuyPrice
+ }
+
+ val result = manager.auctionManager.getLowestBin(asString())
+ if (result != -1L) return result.toDouble()
if (equals("JACK_O_LANTERN")) {
return "PUMPKIN".asInternalName().getPrice(useSellingPrice) + 1