aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/ledger
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-04-02 16:01:38 +0200
committerLinnea Gräf <nea@nea.moe>2024-04-02 16:01:38 +0200
commit3e0198e3a803d2dd46988ba0ac445412c7da8390 (patch)
tree6f08cebe11894769ead7ce7473b27dbdf21523d5 /src/main/kotlin/moe/nea/ledger
parent8d26a206297071775e88b99d4b38efcc5577af0a (diff)
downloadmoney-ledger-3e0198e3a803d2dd46988ba0ac445412c7da8390.tar.gz
money-ledger-3e0198e3a803d2dd46988ba0ac445412c7da8390.tar.bz2
money-ledger-3e0198e3a803d2dd46988ba0ac445412c7da8390.zip
Add bits stuff
Diffstat (limited to 'src/main/kotlin/moe/nea/ledger')
-rw-r--r--src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt88
-rw-r--r--src/main/kotlin/moe/nea/ledger/BazaarDetection.kt39
-rw-r--r--src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt70
-rw-r--r--src/main/kotlin/moe/nea/ledger/BitsDetection.kt48
-rw-r--r--src/main/kotlin/moe/nea/ledger/BitsShop.kt48
-rw-r--r--src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt9
-rw-r--r--src/main/kotlin/moe/nea/ledger/Ledger.kt27
-rw-r--r--src/main/kotlin/moe/nea/ledger/LedgerLogger.kt1
-rw-r--r--src/main/kotlin/moe/nea/ledger/NumberUtil.kt2
-rw-r--r--src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt29
-rw-r--r--src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt5
11 files changed, 365 insertions, 1 deletions
diff --git a/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt b/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt
new file mode 100644
index 0000000..a73d6b1
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt
@@ -0,0 +1,88 @@
+package moe.nea.ledger
+
+import net.minecraft.client.gui.inventory.GuiChest
+import net.minecraft.inventory.ContainerChest
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class AuctionHouseDetection(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+ data class LastViewedItem(
+ val count: Int,
+ val id: String,
+ )
+ /*
+ You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction!
+ You collected 60,000 coins from selling Walnut to [MVP++] Alea1337 in an auction!
+ You purchased 2x Walnut for 69 coins!
+ You purchased ◆ Ice Rune I for 4,000 coins!
+ */
+
+ val collectSold =
+ Pattern.compile("You collected (?<coins>$SHORT_NUMBER_PATTERN) coins? from selling (?<what>.*) to (?<buyer>.*) in an auction!")
+ val purchased =
+ Pattern.compile("You purchased (?:(?<amount>[0-9]+)x )?(?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+ var lastViewedItems: MutableList<LastViewedItem> = mutableListOf()
+
+ @SubscribeEvent
+ fun onEvent(event: ChatReceived) {
+ collectSold.useMatcher(event.message) {
+ val lastViewedItem = lastViewedItems.removeLastOrNull()
+ ledger.logEntry(
+ LedgerEntry(
+ "AUCTION_SOLD",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ lastViewedItem?.id,
+ lastViewedItem?.count
+ )
+ )
+ }
+ purchased.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ "AUCTION_BOUGHT",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ ids.findForName(group("what")),
+ group("amount")?.toInt() ?: 1
+ )
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onBeforeAuctionCollected(event: BeforeGuiAction) {
+ val chest = (event.gui as? GuiChest) ?: return
+ val slots = chest.inventorySlots as ContainerChest
+ val name = slots.lowerChestInventory.displayName.unformattedText.unformattedString()
+
+ if (name == "BIN Auction View" || name == "Auction View") {
+ handleCollectSingleAuctionView(slots)
+ }
+ if (name == "Manage Auctions") {
+ handleCollectMultipleAuctionsView(slots)
+ }
+ }
+
+ private fun handleCollectMultipleAuctionsView(slots: ContainerChest) {
+ lastViewedItems =
+ (0 until slots.lowerChestInventory.sizeInventory)
+ .mapNotNull { slots.lowerChestInventory.getStackInSlot(it) }
+ .filter {
+ it.getLore().contains("§7Status: §aSold!") // BINs
+ || it.getLore().contains("§7Status: §aEnded!") // Auctions
+ }
+ .mapNotNull { LastViewedItem(it.stackSize, it.getInternalId() ?: return@mapNotNull null) }
+ .toMutableList()
+ }
+
+
+ fun handleCollectSingleAuctionView(slots: ContainerChest) {
+ val soldItem = slots.lowerChestInventory.getStackInSlot(9 + 4) ?: return
+ val id = soldItem.getInternalId() ?: return
+ val count = soldItem.stackSize
+ lastViewedItems = mutableListOf(LastViewedItem(count, id))
+ }
+
+
+}
diff --git a/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt b/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt
new file mode 100644
index 0000000..8f7c007
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt
@@ -0,0 +1,39 @@
+package moe.nea.ledger
+
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class BazaarDetection(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+
+ val instaBuyPattern =
+ Pattern.compile("\\[Bazaar\\] Bought (?<count>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+ val instaSellPattern =
+ Pattern.compile("\\[Bazaar\\] Sold (?<count>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+
+
+ @SubscribeEvent
+ fun onInstSellChat(event: ChatReceived) {
+ instaBuyPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BAZAAR_BUY_INSTANT",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ ids.findForName(group("what")),
+ parseShortNumber(group("count")).toInt(),
+ )
+ )
+ }
+ instaSellPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BAZAAR_SELL_INSTANT",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ ids.findForName(group("what")),
+ parseShortNumber(group("count")).toInt(),
+ )
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt b/src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt
new file mode 100644
index 0000000..79ba65b
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt
@@ -0,0 +1,70 @@
+package moe.nea.ledger
+
+import moe.nea.ledger.mixin.AccessorGuiEditSign
+import net.minecraft.client.gui.inventory.GuiEditSign
+import net.minecraftforge.client.event.GuiScreenEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class BazaarOrderDetection(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+
+ val buyOrderClaimed =
+ Pattern.compile("\\[Bazaar] Claimed (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) worth (?<coins>$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!")
+ val sellOrderClaimed =
+ Pattern.compile("\\[Bazaar] Claimed (?<coins>$SHORT_NUMBER_PATTERN) coins? from selling (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) at $SHORT_NUMBER_PATTERN each!")
+ val orderFlipped =
+ Pattern.compile("\\[Bazaar] Order Flipped! (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins? of total expected profit.")
+ val previousPricePattern =
+ Pattern.compile("(?<price>$SHORT_NUMBER_PATTERN)/u")
+ var lastFlippedPreviousPrice = 0.0
+
+ @SubscribeEvent
+ fun detectSignFlip(event: GuiScreenEvent.InitGuiEvent) {
+ val gui = event.gui
+ if (gui !is GuiEditSign) return
+ gui as AccessorGuiEditSign
+ val text = gui.tileEntity_ledger.signText
+ if (text[2].unformattedText != "Previous price:") return
+ previousPricePattern.useMatcher(text[3].unformattedText) {
+ lastFlippedPreviousPrice = parseShortNumber(group("price"))
+ }
+ }
+
+ @SubscribeEvent
+ fun detectBuyOrders(event: ChatReceived) {
+ orderFlipped.useMatcher(event.message) {
+ val amount = parseShortNumber(group("amount")).toInt()
+ ledger.logEntry(
+ LedgerEntry(
+ "BAZAAR_BUY_ORDER",
+ event.timestamp,
+ lastFlippedPreviousPrice * amount,
+ ids.findForName(group("what")),
+ amount,
+ )
+ )
+ }
+ buyOrderClaimed.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BAZAAR_BUY_ORDER",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ ids.findForName(group("what")),
+ parseShortNumber(group("amount")).toInt(),
+ )
+ )
+ }
+ sellOrderClaimed.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BAZAAR_SELL_ORDER",
+ event.timestamp,
+ parseShortNumber(group("coins")),
+ ids.findForName(group("what")),
+ parseShortNumber(group("amount")).toInt(),
+ )
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/ledger/BitsDetection.kt b/src/main/kotlin/moe/nea/ledger/BitsDetection.kt
new file mode 100644
index 0000000..2d26b3d
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/BitsDetection.kt
@@ -0,0 +1,48 @@
+package moe.nea.ledger
+
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.time.Instant
+
+class BitsDetection(val ledger: LedgerLogger) {
+
+ var lastBits = -1
+
+ val bitScoreboardRegex = "Bits: (?<purse>$SHORT_NUMBER_PATTERN)".toPattern()
+
+ @SubscribeEvent
+ fun onWorldSwitch(event: LateWorldLoadEvent) {
+ ScoreboardUtil.getScoreboardStrings().forEach {
+ bitScoreboardRegex.useMatcher<Unit>(it.unformattedString()) {
+ val bits = parseShortNumber(group("purse")).toInt()
+ if (lastBits != bits) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BITS_PURSE_STATUS",
+ Instant.now(),
+ 0.0,
+ null,
+ bits
+ )
+ )
+ lastBits = bits
+ }
+ return
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onEvent(event: ChatReceived) {
+ if (event.message.startsWith("You consumed a Booster Cookie!")) {
+ ledger.logEntry(
+ LedgerEntry(
+ "BOOSTER_COOKIE_ATE",
+ Instant.now(),
+ 0.0,
+ null,
+ null,
+ )
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/ledger/BitsShop.kt b/src/main/kotlin/moe/nea/ledger/BitsShop.kt
new file mode 100644
index 0000000..ea9e4d2
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/BitsShop.kt
@@ -0,0 +1,48 @@
+package moe.nea.ledger
+
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.time.Instant
+
+class BitsShop(val ledger: LedgerLogger) {
+
+
+ data class BitShopEntry(
+ val id: String,
+ val bitPrice: Int,
+ val timestamp: Long = System.currentTimeMillis()
+ )
+
+ var lastClickedBitShopItem: BitShopEntry? = null
+ var bitCostPattern = "(?<cost>$SHORT_NUMBER_PATTERN) Bits".toPattern()
+
+ @SubscribeEvent
+ fun recordLastBitPrice(event: GuiClickEvent) {
+ val slot = event.slotIn ?: return
+ val name = slot.inventory.displayName.unformattedText.unformattedString()
+ if (name != "Community Shop" && !name.startsWith("Bits Shop"))
+ return
+ val stack = slot.stack ?: return
+ val id = stack.getInternalId() ?: return
+ val bitPrice = stack.getLore()
+ .firstNotNullOfOrNull { bitCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")).toInt() } }
+ ?: return
+ lastClickedBitShopItem = BitShopEntry(id, bitPrice)
+ }
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ if (event.message.startsWith("You bought ")) {
+ val lastBit = lastClickedBitShopItem ?: return
+ if (System.currentTimeMillis() - lastBit.timestamp > 5000) return
+ ledger.logEntry(
+ LedgerEntry(
+ "COMMUNITY_SHOP_BUY", Instant.now(),
+ lastBit.bitPrice.toDouble(),
+ lastBit.id,
+ 1
+ )
+ )
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt b/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt
new file mode 100644
index 0000000..13e74f1
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt
@@ -0,0 +1,9 @@
+package moe.nea.ledger
+
+import net.minecraft.inventory.Slot
+import net.minecraftforge.fml.common.eventhandler.Event
+
+data class GuiClickEvent(
+ val slotIn: Slot?, val slotId: Int, val clickedButton: Int, val clickType: Int
+) : Event() {
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt
index 0164f3b..1fc7954 100644
--- a/src/main/kotlin/moe/nea/ledger/Ledger.kt
+++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt
@@ -1,11 +1,15 @@
package moe.nea.ledger
+import net.minecraft.client.Minecraft
import net.minecraftforge.client.event.ClientChatReceivedEvent
import net.minecraftforge.common.MinecraftForge
+import net.minecraftforge.event.entity.EntityJoinWorldEvent
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.event.FMLInitializationEvent
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent
import org.apache.logging.log4j.LogManager
@Mod(modid = "ledger", useMetadata = true)
@@ -52,9 +56,32 @@ class Ledger {
BazaarDetection(ledger, ids),
BazaarOrderDetection(ledger, ids),
AuctionHouseDetection(ledger, ids),
+ BitsDetection(ledger),
+ BitsShop(ledger),
).forEach(MinecraftForge.EVENT_BUS::register)
}
+ var lastJoin = -1L
+
+ @SubscribeEvent
+ fun worldSwitchEvent(event: EntityJoinWorldEvent) {
+ if (event.entity == Minecraft.getMinecraft().thePlayer) {
+ lastJoin = System.currentTimeMillis()
+ }
+ }
+
+ @SubscribeEvent
+ fun tickEvent(event: ClientTickEvent) {
+ if (event.phase == TickEvent.Phase.END
+ && lastJoin > 0
+ && System.currentTimeMillis() - lastJoin > 10_000
+ && Minecraft.getMinecraft().thePlayer != null
+ ) {
+ lastJoin = -1
+ MinecraftForge.EVENT_BUS.post(LateWorldLoadEvent())
+ }
+ }
+
@SubscribeEvent(receiveCanceled = true, priority = EventPriority.HIGHEST)
fun onChat(event: ClientChatReceivedEvent) {
if (event.type != 2.toByte())
diff --git a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
index 83268e7..ba7fd96 100644
--- a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
+++ b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
@@ -60,6 +60,7 @@ class LedgerLogger {
}
fun logEntry(entry: LedgerEntry) {
+ printToChat(entry)
Ledger.logger.info("Logging entry of type ${entry.transactionType}")
entries.add(entry.intoJson())
commit()
diff --git a/src/main/kotlin/moe/nea/ledger/NumberUtil.kt b/src/main/kotlin/moe/nea/ledger/NumberUtil.kt
index 267a8c5..d26b047 100644
--- a/src/main/kotlin/moe/nea/ledger/NumberUtil.kt
+++ b/src/main/kotlin/moe/nea/ledger/NumberUtil.kt
@@ -27,7 +27,7 @@ fun parseShortNumber(string: String): Double {
return k.toDouble() * scalarMultiplier
}
-fun <T> Pattern.useMatcher(string: String, block: Matcher.() -> T): T? =
+inline fun <T> Pattern.useMatcher(string: String, block: Matcher.() -> T): T? =
matcher(string).takeIf { it.matches() }?.let(block)
fun String.unformattedString(): String = replace("§.".toRegex(), "") \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt b/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt
new file mode 100644
index 0000000..783664b
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt
@@ -0,0 +1,29 @@
+package moe.nea.ledger
+
+import net.minecraft.client.Minecraft
+import net.minecraft.scoreboard.ScorePlayerTeam
+
+object ScoreboardUtil {
+
+ val sidebarSlot = 1
+ fun getScoreboardStrings(): List<String> {
+ val scoreboard = Minecraft.getMinecraft().theWorld.scoreboard
+ val objective = scoreboard.getObjectiveInDisplaySlot(sidebarSlot)
+ val scoreList = scoreboard.getSortedScores(objective).take(15)
+ .map {
+ ScorePlayerTeam.formatPlayerName(scoreboard.getPlayersTeam(it.playerName), it.playerName)
+ }
+ .map { stripAlien(it) }
+ .reversed()
+ return scoreList
+ }
+
+ fun stripAlien(string: String): String {
+ val sb = StringBuilder()
+ for (c in string) {
+ if (Minecraft.getMinecraft().fontRendererObj.getCharWidth(c) > 0 || c == '§')
+ sb.append(c)
+ }
+ return sb.toString()
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt b/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt
new file mode 100644
index 0000000..4b7fa6d
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt
@@ -0,0 +1,5 @@
+package moe.nea.ledger
+
+import net.minecraftforge.fml.common.eventhandler.Event
+
+class LateWorldLoadEvent : Event()