From f7507f384459b57460af899bf9ceae4f52f1ea21 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Fri, 6 Dec 2024 19:27:07 +0100 Subject: refactor: Add DI and packages --- .../kotlin/moe/nea/ledger/AuctionHouseDetection.kt | 88 --------------- src/main/kotlin/moe/nea/ledger/BankDetection.kt | 33 ------ src/main/kotlin/moe/nea/ledger/BazaarDetection.kt | 39 ------- .../kotlin/moe/nea/ledger/BazaarOrderDetection.kt | 70 ------------ src/main/kotlin/moe/nea/ledger/BeforeGuiAction.kt | 6 - src/main/kotlin/moe/nea/ledger/BitsDetection.kt | 48 -------- src/main/kotlin/moe/nea/ledger/BitsShop.kt | 48 -------- src/main/kotlin/moe/nea/ledger/ChatReceived.kt | 14 --- .../kotlin/moe/nea/ledger/DungeonChestDetection.kt | 110 ------------------- src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt | 9 -- src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt | 1 + src/main/kotlin/moe/nea/ledger/Ledger.kt | 45 +++++--- src/main/kotlin/moe/nea/ledger/LedgerLogger.kt | 3 +- src/main/kotlin/moe/nea/ledger/MinionDetection.kt | 42 ------- src/main/kotlin/moe/nea/ledger/NpcDetection.kt | 38 ------- src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt | 5 - .../moe/nea/ledger/events/BeforeGuiAction.kt | 6 + .../kotlin/moe/nea/ledger/events/ChatReceived.kt | 15 +++ .../kotlin/moe/nea/ledger/events/GuiClickEvent.kt | 9 ++ .../kotlin/moe/nea/ledger/events/WorldLoadEvent.kt | 5 + .../nea/ledger/modules/AuctionHouseDetection.kt | 100 +++++++++++++++++ .../kotlin/moe/nea/ledger/modules/BankDetection.kt | 42 +++++++ .../moe/nea/ledger/modules/BazaarDetection.kt | 47 ++++++++ .../moe/nea/ledger/modules/BazaarOrderDetection.kt | 78 +++++++++++++ .../kotlin/moe/nea/ledger/modules/BitsDetection.kt | 58 ++++++++++ .../moe/nea/ledger/modules/BitsShopDetection.kt | 59 ++++++++++ .../nea/ledger/modules/DungeonChestDetection.kt | 121 +++++++++++++++++++++ .../moe/nea/ledger/modules/MinionDetection.kt | 54 +++++++++ .../kotlin/moe/nea/ledger/modules/NpcDetection.kt | 46 ++++++++ src/main/kotlin/moe/nea/ledger/utils/DI.kt | 63 +++++++++++ src/main/kotlin/moe/nea/ledger/utils/DIProvider.kt | 53 +++++++++ src/main/kotlin/moe/nea/ledger/utils/Inject.kt | 6 + 32 files changed, 793 insertions(+), 568 deletions(-) delete mode 100644 src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BankDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BazaarDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BeforeGuiAction.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BitsDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/BitsShop.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/ChatReceived.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/DungeonChestDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/MinionDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/NpcDetection.kt delete mode 100644 src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt create mode 100644 src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt create mode 100644 src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt create mode 100644 src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt create mode 100644 src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt create mode 100644 src/main/kotlin/moe/nea/ledger/utils/DI.kt create mode 100644 src/main/kotlin/moe/nea/ledger/utils/DIProvider.kt create mode 100644 src/main/kotlin/moe/nea/ledger/utils/Inject.kt (limited to 'src/main/kotlin') diff --git a/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt b/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt deleted file mode 100644 index a73d6b1..0000000 --- a/src/main/kotlin/moe/nea/ledger/AuctionHouseDetection.kt +++ /dev/null @@ -1,88 +0,0 @@ -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 (?$SHORT_NUMBER_PATTERN) coins? from selling (?.*) to (?.*) in an auction!") - val purchased = - Pattern.compile("You purchased (?:(?[0-9]+)x )?(?.*) for (?$SHORT_NUMBER_PATTERN) coins!") - var lastViewedItems: MutableList = 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/BankDetection.kt b/src/main/kotlin/moe/nea/ledger/BankDetection.kt deleted file mode 100644 index 2a39c6d..0000000 --- a/src/main/kotlin/moe/nea/ledger/BankDetection.kt +++ /dev/null @@ -1,33 +0,0 @@ -package moe.nea.ledger - -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class BankDetection(val ledger: LedgerLogger) { - val withdrawPattern = - Pattern.compile("^(You have withdrawn|Withdrew) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:left in the account!|in your account!)$") - val depositPattern = - Pattern.compile("^(?:You have deposited|Deposited) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:in your account!|in the account!)$") - @SubscribeEvent - fun onChat(event: ChatReceived) { - withdrawPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - "BANK_WITHDRAW", - event.timestamp, - parseShortNumber(group("amount")), - ) - ) - } - depositPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - "BANK_DEPOSIT", - event.timestamp, - parseShortNumber(group("amount")), - ) - ) - } - } - -} diff --git a/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt b/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt deleted file mode 100644 index 8f7c007..0000000 --- a/src/main/kotlin/moe/nea/ledger/BazaarDetection.kt +++ /dev/null @@ -1,39 +0,0 @@ -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 (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") - val instaSellPattern = - Pattern.compile("\\[Bazaar\\] Sold (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$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 deleted file mode 100644 index 79ba65b..0000000 --- a/src/main/kotlin/moe/nea/ledger/BazaarOrderDetection.kt +++ /dev/null @@ -1,70 +0,0 @@ -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 (?$SHORT_NUMBER_PATTERN)x (?.*) worth (?$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!") - val sellOrderClaimed = - Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN) coins? from selling (?$SHORT_NUMBER_PATTERN)x (?.*) at $SHORT_NUMBER_PATTERN each!") - val orderFlipped = - Pattern.compile("\\[Bazaar] Order Flipped! (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins? of total expected profit.") - val previousPricePattern = - Pattern.compile("(?$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/BeforeGuiAction.kt b/src/main/kotlin/moe/nea/ledger/BeforeGuiAction.kt deleted file mode 100644 index 1615263..0000000 --- a/src/main/kotlin/moe/nea/ledger/BeforeGuiAction.kt +++ /dev/null @@ -1,6 +0,0 @@ -package moe.nea.ledger - -import net.minecraft.client.gui.GuiScreen -import net.minecraftforge.fml.common.eventhandler.Event - -data class BeforeGuiAction(val gui: GuiScreen) : Event() diff --git a/src/main/kotlin/moe/nea/ledger/BitsDetection.kt b/src/main/kotlin/moe/nea/ledger/BitsDetection.kt deleted file mode 100644 index 2d26b3d..0000000 --- a/src/main/kotlin/moe/nea/ledger/BitsDetection.kt +++ /dev/null @@ -1,48 +0,0 @@ -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: (?$SHORT_NUMBER_PATTERN)".toPattern() - - @SubscribeEvent - fun onWorldSwitch(event: LateWorldLoadEvent) { - ScoreboardUtil.getScoreboardStrings().forEach { - bitScoreboardRegex.useMatcher(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 deleted file mode 100644 index ea9e4d2..0000000 --- a/src/main/kotlin/moe/nea/ledger/BitsShop.kt +++ /dev/null @@ -1,48 +0,0 @@ -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 = "(?$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/ChatReceived.kt b/src/main/kotlin/moe/nea/ledger/ChatReceived.kt deleted file mode 100644 index f3c4c50..0000000 --- a/src/main/kotlin/moe/nea/ledger/ChatReceived.kt +++ /dev/null @@ -1,14 +0,0 @@ -package moe.nea.ledger - -import net.minecraftforge.client.event.ClientChatReceivedEvent -import net.minecraftforge.fml.common.eventhandler.Event -import java.time.Instant - -data class ChatReceived( - val message: String, - val timestamp: Instant = Instant.now() -) : Event() { - constructor(event: ClientChatReceivedEvent) : this( - event.message.unformattedText.unformattedString() - ) -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/DungeonChestDetection.kt b/src/main/kotlin/moe/nea/ledger/DungeonChestDetection.kt deleted file mode 100644 index 2418354..0000000 --- a/src/main/kotlin/moe/nea/ledger/DungeonChestDetection.kt +++ /dev/null @@ -1,110 +0,0 @@ -package moe.nea.ledger - -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.common.gameevent.TickEvent -import java.time.Instant -import java.util.regex.Pattern - -class DungeonChestDetection(val logger: LedgerLogger) { - - /*{ - id: "minecraft:chest", - Count: 1b, - tag: { - display: { - Lore: ["§7Purchase this chest to receive the", "§7rewards above. You can only open", "§7one chest per Dungeons run -", "§7choose wisely!", "", "§7Cost", "§625,000 Coins", "§9Dungeon Chest Key", "", "§7§cNOTE: Coins are withdrawn from your", "§cbank if you don't have enough in", "§cyour purse."], - Name: "§aOpen Reward Chest" - } - }, - Damage: 0s - } - - { - id: "minecraft:feather", - Count: 1b, - tag: { - overrideMeta: 1b, - ench: [], - HideFlags: 254, - display: { - Lore: ["§7Consume a §9Kismet Feather §7to reroll", "§7the loot within this chest.", "", "§7You may only use a feather once", "§7per dungeon run.", "", "§eClick to reroll this chest!"], - Name: "§aReroll Chest" - }, - AttributeModifiers: [] - }, - Damage: 0s -} - */ - val costPattern = Pattern.compile("(?$SHORT_NUMBER_PATTERN) Coins") - - - data class ChestCost( - val cost: Double, - val openTimestamp: Long, - val hasKey: Boolean, - ) - - var lastOpenedChest: ChestCost? = null - - @SubscribeEvent - fun onKismetClick(event: GuiClickEvent) { - val slot = event.slotIn ?: return - if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return - val stack = slot.stack ?: return - if (stack.getDisplayNameU() == "§aReroll Chest") { - logger.logEntry( - LedgerEntry( - "KISMET_REROLL", - Instant.now(), - 0.0, - itemId = "KISMET_FEATHER", - itemAmount = 1 - ) - ) - } - } - - @SubscribeEvent - fun onRewardChestClick(event: GuiClickEvent) { - val slot = event.slotIn ?: return - if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return - val stack = slot.stack ?: return - val name = stack.getDisplayNameU() - if (name != "§aOpen Reward Chest") return - val lore = stack.getLore() - val costIndex = lore.indexOf("§7Cost") - if (costIndex < 0 || costIndex + 1 !in lore.indices) return - val cost = costPattern.useMatcher(lore[costIndex + 1].unformattedString()) { - parseShortNumber(group("cost")) - } ?: 0.0 // Free chest! - val hasKey = lore.contains("§9Dungeon Chest Key") - lastOpenedChest?.let(::completeTransaction) - lastOpenedChest = ChestCost(cost, System.currentTimeMillis(), hasKey) - } - - @SubscribeEvent - fun onChatMessage(event: ChatReceived) { - if (event.message == "You don't have that many coins in the bank!") - lastOpenedChest = null - } - - fun completeTransaction(toOpen: ChestCost) { - lastOpenedChest = null - logger.logEntry( - LedgerEntry( - "DUNGEON_CHEST_OPEN", - Instant.ofEpochMilli(toOpen.openTimestamp), - toOpen.cost, - itemId = if (toOpen.hasKey) "DUNGEON_CHEST_KEY" else null - ) - ) - } - - @SubscribeEvent - fun onTick(event: TickEvent) { - val toOpen = lastOpenedChest - if (toOpen != null && toOpen.openTimestamp + 1000L < System.currentTimeMillis()) { - completeTransaction(toOpen) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt b/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt deleted file mode 100644 index 13e74f1..0000000 --- a/src/main/kotlin/moe/nea/ledger/GuiClickEvent.kt +++ /dev/null @@ -1,9 +0,0 @@ -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/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt index fa0d8fa..65580fa 100644 --- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt +++ b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt @@ -1,5 +1,6 @@ package moe.nea.ledger +import moe.nea.ledger.events.BeforeGuiAction import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest import net.minecraft.nbt.NBTTagCompound diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt index a11480f..521fd9d 100644 --- a/src/main/kotlin/moe/nea/ledger/Ledger.kt +++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt @@ -4,6 +4,18 @@ import io.github.notenoughupdates.moulconfig.common.IMinecraft import io.github.notenoughupdates.moulconfig.managed.ManagedConfig import moe.nea.ledger.config.LedgerConfig import moe.nea.ledger.database.Database +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.LateWorldLoadEvent +import moe.nea.ledger.modules.AuctionHouseDetection +import moe.nea.ledger.modules.BankDetection +import moe.nea.ledger.modules.BazaarDetection +import moe.nea.ledger.modules.BazaarOrderDetection +import moe.nea.ledger.modules.BitsDetection +import moe.nea.ledger.modules.BitsShopDetection +import moe.nea.ledger.modules.DungeonChestDetection +import moe.nea.ledger.modules.MinionDetection +import moe.nea.ledger.modules.NpcDetection +import moe.nea.ledger.utils.DI import net.minecraft.client.Minecraft import net.minecraft.command.CommandBase import net.minecraft.command.ICommandSender @@ -96,22 +108,23 @@ class Ledger { return listOf("moneyledger") } }) - val ledger = LedgerLogger() - val ids = ItemIdProvider() - listOf( - this, - ids, - ledger, - BankDetection(ledger), - BazaarDetection(ledger, ids), - DungeonChestDetection(ledger), - BazaarOrderDetection(ledger, ids), - AuctionHouseDetection(ledger, ids), - BitsDetection(ledger), - BitsShop(ledger), - MinionDetection(ledger), - NpcDetection(ledger, ids), - ).forEach(MinecraftForge.EVENT_BUS::register) + val di = DI() + di.registerSingleton(this) + di.registerInjectableClasses( + LedgerLogger::class.java, + ItemIdProvider::class.java, + BankDetection::class.java, + BazaarDetection::class.java, + DungeonChestDetection::class.java, + BazaarOrderDetection::class.java, + AuctionHouseDetection::class.java, + BitsDetection::class.java, + BitsShopDetection::class.java, + MinionDetection::class.java, + NpcDetection::class.java, + ) + di.instantiateAll() + di.getAllInstances().forEach(MinecraftForge.EVENT_BUS::register) } var lastJoin = -1L diff --git a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt index 03d306c..691eae7 100644 --- a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt +++ b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt @@ -3,6 +3,7 @@ package moe.nea.ledger import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonObject +import moe.nea.ledger.events.ChatReceived import net.minecraft.client.Minecraft import net.minecraft.command.CommandBase import net.minecraft.command.ICommandSender @@ -11,8 +12,6 @@ import net.minecraftforge.client.ClientCommandHandler import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent import java.io.File -import java.sql.Connection -import java.sql.DriverManager import java.text.SimpleDateFormat import java.time.Instant import java.util.* diff --git a/src/main/kotlin/moe/nea/ledger/MinionDetection.kt b/src/main/kotlin/moe/nea/ledger/MinionDetection.kt deleted file mode 100644 index bb2a187..0000000 --- a/src/main/kotlin/moe/nea/ledger/MinionDetection.kt +++ /dev/null @@ -1,42 +0,0 @@ -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.time.Instant -import kotlin.time.Duration.Companion.seconds - -class MinionDetection(val ledger: LedgerLogger) { - // §aYou received §r§6367,516.8 coins§r§a! - val hopperCollectPattern = "You received (?$SHORT_NUMBER_PATTERN) coins?!".toPattern() - val minionNamePattern = "(?.*) Minion (?$ROMAN_NUMBER_PATTERN)".toPattern() - - var lastOpenedMinion = ExpiringValue.empty() - - @SubscribeEvent - fun onBeforeClaim(event: BeforeGuiAction) { - val container = event.gui as? GuiChest ?: return - val inv = (container.inventorySlots as ContainerChest).lowerChestInventory - val invName = inv.displayName.unformattedText.unformattedString() - minionNamePattern.useMatcher(invName) { - val name = group("name") - val level = parseRomanNumber(group("level")) - lastOpenedMinion = ExpiringValue(name.uppercase().replace(" ", "_") + "_" + level) - } - } - - - @SubscribeEvent - fun onChat(event: ChatReceived) { - hopperCollectPattern.useMatcher(event.message) { - val minionName = lastOpenedMinion.consume(3.seconds) - ledger.logEntry(LedgerEntry( - "AUTOMERCHANT_PROFIT_COLLECT", - Instant.now(), - parseShortNumber(group("amount")), - minionName, // TODO: switch to its own column idk - )) - } - } - -} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/NpcDetection.kt b/src/main/kotlin/moe/nea/ledger/NpcDetection.kt deleted file mode 100644 index 3c3fe87..0000000 --- a/src/main/kotlin/moe/nea/ledger/NpcDetection.kt +++ /dev/null @@ -1,38 +0,0 @@ -package moe.nea.ledger - -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Pattern - -class NpcDetection(val ledger: LedgerLogger, val ids: ItemIdProvider) { - - val npcBuyPattern = - Pattern.compile("You bought (back )?(?.*?) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins!") - val npcSellPattern = - Pattern.compile("You sold (?.*) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins!") - - @SubscribeEvent - fun onNpcBuy(event: ChatReceived) { - npcBuyPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - "NPC_BUY", - event.timestamp, - parseShortNumber(group("coins")), - ids.findForName(group("what")), - group("count")?.let(::parseShortNumber)?.toInt() ?: 1, - ) - ) - } - npcSellPattern.useMatcher(event.message) { - ledger.logEntry( - LedgerEntry( - "NPC_SELL", - event.timestamp, - parseShortNumber(group("coins")), - ids.findForName(group("what")), - group("count")?.let(::parseShortNumber)?.toInt() ?: 1, - ) - ) - } - } -} diff --git a/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt b/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt deleted file mode 100644 index 4b7fa6d..0000000 --- a/src/main/kotlin/moe/nea/ledger/WorldLoadEvent.kt +++ /dev/null @@ -1,5 +0,0 @@ -package moe.nea.ledger - -import net.minecraftforge.fml.common.eventhandler.Event - -class LateWorldLoadEvent : Event() diff --git a/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt b/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt new file mode 100644 index 0000000..69362bd --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.events + +import net.minecraft.client.gui.GuiScreen +import net.minecraftforge.fml.common.eventhandler.Event + +data class BeforeGuiAction(val gui: GuiScreen) : Event() diff --git a/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt b/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt new file mode 100644 index 0000000..e88c7a0 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/events/ChatReceived.kt @@ -0,0 +1,15 @@ +package moe.nea.ledger.events + +import moe.nea.ledger.unformattedString +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.Event +import java.time.Instant + +data class ChatReceived( + val message: String, + val timestamp: Instant = Instant.now() +) : Event() { + constructor(event: ClientChatReceivedEvent) : this( + event.message.unformattedText.unformattedString() + ) +} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt b/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt new file mode 100644 index 0000000..9e057dd --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/events/GuiClickEvent.kt @@ -0,0 +1,9 @@ +package moe.nea.ledger.events + +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/events/WorldLoadEvent.kt b/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt new file mode 100644 index 0000000..d60f3a4 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/events/WorldLoadEvent.kt @@ -0,0 +1,5 @@ +package moe.nea.ledger.events + +import net.minecraftforge.fml.common.eventhandler.Event + +class LateWorldLoadEvent : Event() diff --git a/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt new file mode 100644 index 0000000..cbbff12 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt @@ -0,0 +1,100 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +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 @Inject constructor(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 (?$SHORT_NUMBER_PATTERN) coins? from selling (?.*) to (?.*) in an auction!") + val purchased = + Pattern.compile("You purchased (?:(?[0-9]+)x )?(?.*) for (?$SHORT_NUMBER_PATTERN) coins!") + var lastViewedItems: MutableList = 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)) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt new file mode 100644 index 0000000..8d0fd81 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt @@ -0,0 +1,42 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + + +class BankDetection @Inject constructor(val ledger: LedgerLogger) { + val withdrawPattern = + Pattern.compile("^(You have withdrawn|Withdrew) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:left in the account!|in your account!)$") + val depositPattern = + Pattern.compile("^(?:You have deposited|Deposited) (?$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?$SHORT_NUMBER_PATTERN) coins? (?:in your account!|in the account!)$") + + @SubscribeEvent + fun onChat(event: ChatReceived) { + withdrawPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + "BANK_WITHDRAW", + event.timestamp, + parseShortNumber(group("amount")), + ) + ) + } + depositPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + "BANK_DEPOSIT", + event.timestamp, + parseShortNumber(group("amount")), + ) + ) + } + } + +} diff --git a/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt new file mode 100644 index 0000000..01c8bbc --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt @@ -0,0 +1,47 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class BazaarDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val instaBuyPattern = + Pattern.compile("\\[Bazaar\\] Bought (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins!") + val instaSellPattern = + Pattern.compile("\\[Bazaar\\] Sold (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$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/modules/BazaarOrderDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt new file mode 100644 index 0000000..7e611ac --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt @@ -0,0 +1,78 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.mixin.AccessorGuiEditSign +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +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 @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val buyOrderClaimed = + Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN)x (?.*) worth (?$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!") + val sellOrderClaimed = + Pattern.compile("\\[Bazaar] Claimed (?$SHORT_NUMBER_PATTERN) coins? from selling (?$SHORT_NUMBER_PATTERN)x (?.*) at $SHORT_NUMBER_PATTERN each!") + val orderFlipped = + Pattern.compile("\\[Bazaar] Order Flipped! (?$SHORT_NUMBER_PATTERN)x (?.*) for (?$SHORT_NUMBER_PATTERN) coins? of total expected profit.") + val previousPricePattern = + Pattern.compile("(?$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/modules/BitsDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt new file mode 100644 index 0000000..22b5392 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt @@ -0,0 +1,58 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.LateWorldLoadEvent +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.ScoreboardUtil +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class BitsDetection @Inject constructor(val ledger: LedgerLogger) { + + var lastBits = -1 + + val bitScoreboardRegex = "Bits: (?$SHORT_NUMBER_PATTERN)".toPattern() + + @SubscribeEvent + fun onWorldSwitch(event: LateWorldLoadEvent) { + ScoreboardUtil.getScoreboardStrings().forEach { + bitScoreboardRegex.useMatcher(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/modules/BitsShopDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt new file mode 100644 index 0000000..2033d8d --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt @@ -0,0 +1,59 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class BitsShopDetection @Inject constructor(val ledger: LedgerLogger) { + + + data class BitShopEntry( + val id: String, + val bitPrice: Int, + val timestamp: Long = System.currentTimeMillis() + ) + + var lastClickedBitShopItem: BitShopEntry? = null + var bitCostPattern = "(?$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/modules/DungeonChestDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt new file mode 100644 index 0000000..4bdd37c --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt @@ -0,0 +1,121 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.getLore +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.time.Instant +import java.util.regex.Pattern + +class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) { + + /*{ + id: "minecraft:chest", + Count: 1b, + tag: { + display: { + Lore: ["§7Purchase this chest to receive the", "§7rewards above. You can only open", "§7one chest per Dungeons run -", "§7choose wisely!", "", "§7Cost", "§625,000 Coins", "§9Dungeon Chest Key", "", "§7§cNOTE: Coins are withdrawn from your", "§cbank if you don't have enough in", "§cyour purse."], + Name: "§aOpen Reward Chest" + } + }, + Damage: 0s + } + + { + id: "minecraft:feather", + Count: 1b, + tag: { + overrideMeta: 1b, + ench: [], + HideFlags: 254, + display: { + Lore: ["§7Consume a §9Kismet Feather §7to reroll", "§7the loot within this chest.", "", "§7You may only use a feather once", "§7per dungeon run.", "", "§eClick to reroll this chest!"], + Name: "§aReroll Chest" + }, + AttributeModifiers: [] + }, + Damage: 0s +} + */ + val costPattern = Pattern.compile("(?$SHORT_NUMBER_PATTERN) Coins") + + + data class ChestCost( + val cost: Double, + val openTimestamp: Long, + val hasKey: Boolean, + ) + + var lastOpenedChest: ChestCost? = null + + @SubscribeEvent + fun onKismetClick(event: GuiClickEvent) { + val slot = event.slotIn ?: return + if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return + val stack = slot.stack ?: return + if (stack.getDisplayNameU() == "§aReroll Chest") { + logger.logEntry( + LedgerEntry( + "KISMET_REROLL", + Instant.now(), + 0.0, + itemId = "KISMET_FEATHER", + itemAmount = 1 + ) + ) + } + } + + @SubscribeEvent + fun onRewardChestClick(event: GuiClickEvent) { + val slot = event.slotIn ?: return + if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return + val stack = slot.stack ?: return + val name = stack.getDisplayNameU() + if (name != "§aOpen Reward Chest") return + val lore = stack.getLore() + val costIndex = lore.indexOf("§7Cost") + if (costIndex < 0 || costIndex + 1 !in lore.indices) return + val cost = costPattern.useMatcher(lore[costIndex + 1].unformattedString()) { + parseShortNumber(group("cost")) + } ?: 0.0 // Free chest! + val hasKey = lore.contains("§9Dungeon Chest Key") + lastOpenedChest?.let(::completeTransaction) + lastOpenedChest = ChestCost(cost, System.currentTimeMillis(), hasKey) + } + + @SubscribeEvent + fun onChatMessage(event: ChatReceived) { + if (event.message == "You don't have that many coins in the bank!") + lastOpenedChest = null + } + + fun completeTransaction(toOpen: ChestCost) { + lastOpenedChest = null + logger.logEntry( + LedgerEntry( + "DUNGEON_CHEST_OPEN", + Instant.ofEpochMilli(toOpen.openTimestamp), + toOpen.cost, + itemId = if (toOpen.hasKey) "DUNGEON_CHEST_KEY" else null + ) + ) + } + + @SubscribeEvent + fun onTick(event: TickEvent) { + val toOpen = lastOpenedChest + if (toOpen != null && toOpen.openTimestamp + 1000L < System.currentTimeMillis()) { + completeTransaction(toOpen) + } + } +} diff --git a/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt new file mode 100644 index 0000000..73d06fa --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt @@ -0,0 +1,54 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.ExpiringValue +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.ROMAN_NUMBER_PATTERN +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.parseRomanNumber +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant +import kotlin.time.Duration.Companion.seconds + +class MinionDetection @Inject constructor(val ledger: LedgerLogger) { + // §aYou received §r§6367,516.8 coins§r§a! + val hopperCollectPattern = "You received (?$SHORT_NUMBER_PATTERN) coins?!".toPattern() + val minionNamePattern = "(?.*) Minion (?$ROMAN_NUMBER_PATTERN)".toPattern() + + var lastOpenedMinion = ExpiringValue.empty() + + @SubscribeEvent + fun onBeforeClaim(event: BeforeGuiAction) { + val container = event.gui as? GuiChest ?: return + val inv = (container.inventorySlots as ContainerChest).lowerChestInventory + val invName = inv.displayName.unformattedText.unformattedString() + minionNamePattern.useMatcher(invName) { + val name = group("name") + val level = parseRomanNumber(group("level")) + lastOpenedMinion = ExpiringValue(name.uppercase().replace(" ", "_") + "_" + level) + } + } + + + @SubscribeEvent + fun onChat(event: ChatReceived) { + hopperCollectPattern.useMatcher(event.message) { + val minionName = lastOpenedMinion.consume(3.seconds) + ledger.logEntry(LedgerEntry( + "AUTOMERCHANT_PROFIT_COLLECT", + Instant.now(), + parseShortNumber(group("amount")), + minionName, // TODO: switch to its own column idk + )) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt new file mode 100644 index 0000000..68f0257 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt @@ -0,0 +1,46 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class NpcDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) { + + val npcBuyPattern = + Pattern.compile("You bought (back )?(?.*?) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins!") + val npcSellPattern = + Pattern.compile("You sold (?.*) (x(?$SHORT_NUMBER_PATTERN) )?for (?$SHORT_NUMBER_PATTERN) Coins!") + + @SubscribeEvent + fun onNpcBuy(event: ChatReceived) { + npcBuyPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + "NPC_BUY", + event.timestamp, + parseShortNumber(group("coins")), + ids.findForName(group("what")), + group("count")?.let(::parseShortNumber)?.toInt() ?: 1, + ) + ) + } + npcSellPattern.useMatcher(event.message) { + ledger.logEntry( + LedgerEntry( + "NPC_SELL", + event.timestamp, + parseShortNumber(group("coins")), + ids.findForName(group("what")), + group("count")?.let(::parseShortNumber)?.toInt() ?: 1, + ) + ) + } + } +} diff --git a/src/main/kotlin/moe/nea/ledger/utils/DI.kt b/src/main/kotlin/moe/nea/ledger/utils/DI.kt new file mode 100644 index 0000000..1114127 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/utils/DI.kt @@ -0,0 +1,63 @@ +package moe.nea.ledger.utils + +import java.lang.reflect.AnnotatedElement +import java.util.Collections +import java.util.Stack + +@Suppress("UNCHECKED_CAST") +class DI { + private fun internalProvide(type: Class, element: AnnotatedElement? = null): T { + val provider = providers[type] as BaseDIProvider + val context = if (element == null) provider.createEmptyContext() else provider.createContext(element) + val key = Pair(type, context) + val existingValue = values[key] + if (existingValue != null) return existingValue as T + if (type in injectionStack) { + error("Found injection cycle: ${injectionStack.joinToString(" -> ")} -> $type") + } + injectionStack.push(type) + val value = try { + provider.provideWithContext(this, context) + } catch (ex: Exception) { + throw RuntimeException("Could not create instance for type $type", ex) + } + val cycleCheckCookie = injectionStack.pop() + require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" } + values[key] = value + return value + } + + fun provide(type: Class, element: AnnotatedElement? = null): T { + return internalProvide(type, element) + } + + fun register(type: Class, provider: BaseDIProvider) { + providers[type] = provider + } + + fun registerInjectableClasses(vararg type: Class<*>) { + type.forEach { internalRegisterInjectableClass(it) } + } + + private fun internalRegisterInjectableClass(type: Class) { + register(type, DIProvider.fromInjectableClass(type)) + } + + fun instantiateAll() { + providers.keys.forEach { + provide(it, null) + } + } + + fun getAllInstances(): Collection = + Collections.unmodifiableCollection(values.values) + + fun registerSingleton(value: T) { + register(value.javaClass, DIProvider.singeleton(value)) + } + + private val injectionStack: Stack> = Stack() + private val values = mutableMapOf, *>, Any>() + private val providers = mutableMapOf, BaseDIProvider<*, *>>() + +} \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/utils/DIProvider.kt b/src/main/kotlin/moe/nea/ledger/utils/DIProvider.kt new file mode 100644 index 0000000..3cedf40 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/utils/DIProvider.kt @@ -0,0 +1,53 @@ +package moe.nea.ledger.utils + +import java.lang.reflect.AnnotatedElement +import java.lang.reflect.Constructor + +fun interface DIProvider : BaseDIProvider { + override fun provideWithContext(di: DI, context: Unit): T { + return provide(di) + } + + override fun createContext(element: AnnotatedElement) { + } + + override fun createEmptyContext() { + } + + fun provide(di: DI): T + + companion object { + + fun fromInjectableClass(clazz: Class): DIProvider { + @Suppress("UNCHECKED_CAST") + val cons = (clazz.constructors.find { it.getAnnotation(Inject::class.java) != null } + ?: clazz.constructors.find { it.parameterCount == 0 } + ?: error("Could not find DI injection entrypoint for class $clazz")) + as Constructor + return DIProvider { di -> + val typArgs = cons.parameters.map { + di.provide(it.type, it) + }.toTypedArray() + val instance = cons.newInstance(*typArgs) + for (it in clazz.fields) { + if (it.getAnnotation(Inject::class.java) != null) { + continue + } + it.set(instance, di.provide(it.type, it)) + } + instance + } + } + + fun singeleton(value: T): DIProvider { + return DIProvider { _ -> value } + } + } + +} + +interface BaseDIProvider { + fun createContext(element: AnnotatedElement): C + fun provideWithContext(di: DI, context: C): T + fun createEmptyContext(): C +} diff --git a/src/main/kotlin/moe/nea/ledger/utils/Inject.kt b/src/main/kotlin/moe/nea/ledger/utils/Inject.kt new file mode 100644 index 0000000..654f77e --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/utils/Inject.kt @@ -0,0 +1,6 @@ +package moe.nea.ledger.utils + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD) +annotation class Inject( +) -- cgit