diff options
author | Linnea Gräf <nea@nea.moe> | 2024-04-02 16:01:38 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-04-02 16:01:38 +0200 |
commit | 3e0198e3a803d2dd46988ba0ac445412c7da8390 (patch) | |
tree | 6f08cebe11894769ead7ce7473b27dbdf21523d5 /src/main/kotlin/moe/nea/ledger | |
parent | 8d26a206297071775e88b99d4b38efcc5577af0a (diff) | |
download | LocalTransactionLedger-3e0198e3a803d2dd46988ba0ac445412c7da8390.tar.gz LocalTransactionLedger-3e0198e3a803d2dd46988ba0ac445412c7da8390.tar.bz2 LocalTransactionLedger-3e0198e3a803d2dd46988ba0ac445412c7da8390.zip |
Add bits stuff
Diffstat (limited to 'src/main/kotlin/moe/nea/ledger')
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt | 88 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/BazaarDetection.kt | 39 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt | 70 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/BitsDetection.kt | 48 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/BitsShop.kt | 48 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt | 9 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/Ledger.kt | 27 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/LedgerLogger.kt | 1 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/NumberUtil.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/ScoreboardUtil.kt | 29 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt | 5 |
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() |