diff options
33 files changed, 411 insertions, 29 deletions
diff --git a/basetypes/src/main/kotlin/moe/nea/ledger/TransactionType.kt b/basetypes/src/main/kotlin/moe/nea/ledger/TransactionType.kt index b353e29..d4c15e5 100644 --- a/basetypes/src/main/kotlin/moe/nea/ledger/TransactionType.kt +++ b/basetypes/src/main/kotlin/moe/nea/ledger/TransactionType.kt @@ -17,6 +17,7 @@ enum class TransactionType { BAZAAR_SELL_ORDER, BITS_PURSE_STATUS, BOOSTER_COOKIE_ATE, + CADUCOUS_FEEDER_USED, CAPSAICIN_EYEDROPS_USED, COMMUNITY_SHOP_BUY, CORPSE_DESECRATED, @@ -24,6 +25,7 @@ enum class TransactionType { DRACONIC_SACRIFICE, DUNGEON_CHEST_OPEN, FORGED, + GHOST_COIN_DROP, GOD_POTION_DRANK, GOD_POTION_MIXIN_DRANK, GUMMY_POLAR_BEAR_ATE, @@ -34,6 +36,7 @@ enum class TransactionType { NPC_BUY, NPC_SELL, PEST_REPELLENT_USED, + STONKS_AUCTION, VISITOR_BARGAIN, WYRM_EVOKED, }
\ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 3487b27..4c6ee45 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,12 +2,12 @@ import com.github.gmazzo.buildconfig.BuildConfigExtension import java.io.ByteArrayOutputStream plugins { - val kotlinVersion = "2.1.0" + val kotlinVersion = "2.0.21" kotlin("jvm") version kotlinVersion apply false kotlin("plugin.serialization") version kotlinVersion apply false id("com.github.gmazzo.buildconfig") version "5.5.0" apply false id("ledger-globals") - id("com.google.devtools.ksp") version "2.1.0-1.0.29" apply false + id("com.google.devtools.ksp") version "2.0.21-1.0.26" apply false id("com.github.johnrengelman.shadow") version "8.1.1" apply false } diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt index f4fc478..cb0ddfc 100644 --- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt @@ -1,17 +1,25 @@ package moe.nea.ledger.database.sql import moe.nea.ledger.database.Column +import moe.nea.ledger.database.DBType class ClauseBuilder { - fun <T, R> column(column: Column<T, R>): Operand<T, R> = ColumnOperand(column) + // TODO: should we match on T AND R? maybe allow explicit upcasting + fun <T, R> column(column: Column<T, R>): ColumnOperand<T, R> = ColumnOperand(column) fun string(string: String): StringOperand = StringOperand(string) + fun <T, R> value(dbType: DBType<T, R>, value: T): Operand<T, R> = ValuedOperand(dbType, value) infix fun <T> Operand<*, T>.eq(operand: Operand<*, T>): Clause = EqualsClause(this, operand) + infix fun <T, R> TypedOperand<T, R>.eq(value: T): Clause = EqualsClause(this, value(dbType, value)) infix fun Operand<*, String>.like(op: StringOperand): Clause = LikeClause(this, op) infix fun Operand<*, String>.like(op: String): Clause = LikeClause(this, string(op)) infix fun <T> Operand<*, T>.lt(op: Operand<*, T>): BooleanExpression = LessThanExpression(this, op) infix fun <T> Operand<*, T>.le(op: Operand<*, T>): BooleanExpression = LessThanEqualsExpression(this, op) infix fun <T> Operand<*, T>.gt(op: Operand<*, T>): BooleanExpression = op lt this infix fun <T> Operand<*, T>.ge(op: Operand<*, T>): BooleanExpression = op le this + infix fun <T> Operand<*, T>.inList(list: ListExpression<*, T>): Clause = ListClause(this, list) + infix fun <T, R> TypedOperand<T, R>.inList(list: List<T>): Clause = this inList list(dbType, list) + fun <T, R> list(dbType: DBType<T, R>, vararg values: T): ListExpression<T, R> = list(dbType, values.toList()) + fun <T, R> list(dbType: DBType<T, R>, values: List<T>): ListExpression<T, R> = ListExpression(values, dbType) infix fun BooleanExpression.and(clause: BooleanExpression): BooleanExpression = ANDExpression(listOf(this, clause)) infix fun BooleanExpression.or(clause: BooleanExpression): BooleanExpression = ORExpression(listOf(this, clause)) }
\ No newline at end of file diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt index d06d834..430d592 100644 --- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt @@ -1,9 +1,13 @@ package moe.nea.ledger.database.sql import moe.nea.ledger.database.Column +import moe.nea.ledger.database.DBType import java.sql.PreparedStatement -data class ColumnOperand<T, Raw>(val column: Column<T, Raw>) : Operand<T, Raw> { +data class ColumnOperand<T, Raw>(val column: Column<T, Raw>) : TypedOperand<T, Raw> { + override val dbType: DBType<T, Raw> + get() = column.type + override fun asSql(): String { return column.qualifiedSqlName } diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListClause.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListClause.kt new file mode 100644 index 0000000..d240472 --- /dev/null +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListClause.kt @@ -0,0 +1,8 @@ +package moe.nea.ledger.database.sql + +class ListClause<R>( + val lhs: Operand<*, R>, + val list: ListExpression<*, R>, +) : Clause, SQLQueryComponent by SQLQueryComponent.composite( + lhs, SQLQueryComponent.standalone("IN"), list +)
\ No newline at end of file diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListExpression.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListExpression.kt new file mode 100644 index 0000000..e1522d0 --- /dev/null +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListExpression.kt @@ -0,0 +1,22 @@ +package moe.nea.ledger.database.sql + +import moe.nea.ledger.database.DBType +import java.sql.PreparedStatement + +data class ListExpression<T, R>( + val elements: List<T>, + val dbType: DBType<T, R> +) : Operand<List<T>, List<R>> { + override fun asSql(): String { + return elements.joinToString(prefix = "(", postfix = ")") { "?" } + } + + override fun appendToStatement(stmt: PreparedStatement, startIndex: Int): Int { + var index = startIndex + for (element in elements) { + dbType.set(stmt, index, element) + index++ + } + return index + } +} diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/SQLQueryComponent.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/SQLQueryComponent.kt index 10b2be6..77d63d3 100644 --- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/SQLQueryComponent.kt +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/SQLQueryComponent.kt @@ -11,6 +11,25 @@ interface SQLQueryComponent { fun appendToStatement(stmt: PreparedStatement, startIndex: Int): Int companion object { + fun composite(vararg elements: SQLQueryComponent): SQLQueryComponent { + return object : SQLQueryComponent { + override fun asSql(): String { + return elements.joinToString(" ") { it.asSql() } + } + + override fun appendToStatement(stmt: PreparedStatement, startIndex: Int): Int { + var index = startIndex + for (element in elements) { + val lastIndex = index + index = element.appendToStatement(stmt, index) + require(lastIndex <= index) { "$element just tried to go back in time $index < $lastIndex" } + } + return index + + } + } + } + fun standalone(sql: String): SQLQueryComponent { return object : SQLQueryComponent { override fun asSql(): String { diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt index e7ede6a..b8d3690 100644 --- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt @@ -2,6 +2,9 @@ package moe.nea.ledger.database.sql import java.sql.PreparedStatement +/** + * As opposed to just any [Operand<*, String>][Operand], this string operand represents a string operand that is part of the query, as opposed to potentially the state of a column. + */ data class StringOperand(val value: String) : Operand<String, String> { override fun asSql(): String { return "?" diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/TypedOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/TypedOperand.kt new file mode 100644 index 0000000..8a1f723 --- /dev/null +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/TypedOperand.kt @@ -0,0 +1,7 @@ +package moe.nea.ledger.database.sql + +import moe.nea.ledger.database.DBType + +interface TypedOperand<T, Raw> : Operand<T, Raw> { + val dbType: DBType<T, Raw> +} diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ValuedOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ValuedOperand.kt new file mode 100644 index 0000000..714b4b5 --- /dev/null +++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ValuedOperand.kt @@ -0,0 +1,15 @@ +package moe.nea.ledger.database.sql + +import moe.nea.ledger.database.DBType +import java.sql.PreparedStatement + +class ValuedOperand<T, R>(val dbType: DBType<T, R>, val value: T) : Operand<T, R> { + override fun asSql(): String { + return "?" + } + + override fun appendToStatement(stmt: PreparedStatement, startIndex: Int): Int { + dbType.set(stmt, startIndex, value) + return startIndex + 1 + } +} diff --git a/mod/log4j2.xml b/mod/log4j2.xml index af9b1b7..ff7a816 100644 --- a/mod/log4j2.xml +++ b/mod/log4j2.xml @@ -1,5 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <!-- Filter out Hypixel scoreboard and sound errors --> - <RegexFilter regex="Error executing task.*|Unable to play unknown soundEvent.*" onMatch="DENY" onMismatch="NEUTRAL"/> + <RegexFilter regex="Unable to play unknown soundEvent.*" onMatch="DENY" onMismatch="NEUTRAL"/> + <RegexFilter regex="Zip file .* failed to read properly, it will be ignored.*" onMatch="DENY" onMismatch="NEUTRAL"/> + <RegexFilter regex="There was a problem reading the entry META-INF/versions/9/.* in the jar .* - probably a corrupt zip" onMatch="DENY" onMismatch="NEUTRAL"/> + <RegexFilter regex="Unable to read a class file correctly" onMatch="DENY" onMismatch="NEUTRAL"/> + <RegexFilter regex="Error executing task" onMatch="DENY" onMismatch="NEUTRAL"/> </Configuration>
\ No newline at end of file diff --git a/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java b/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java index 56841b5..64fa6c2 100644 --- a/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java +++ b/mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java @@ -96,6 +96,8 @@ public class AutoDiscoveryMixinPlugin implements IMixinConfigPlugin { * @param className the name or path of a class to be registered as a mixin. */ public void tryAddMixinClass(String className) { + if (!className.endsWith(".class")) return; + if (className.indexOf('$') >= 0) return; String norm = (className.endsWith(".class") ? className.substring(0, className.length() - ".class".length()) : className) .replace("\\", "/") .replace("/", "."); diff --git a/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerDispenser.java b/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerDispenser.java new file mode 100644 index 0000000..a3d32c4 --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerDispenser.java @@ -0,0 +1,12 @@ +package moe.nea.ledger.mixin; + +import net.minecraft.inventory.ContainerDispenser; +import net.minecraft.inventory.IInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ContainerDispenser.class) +public interface AccessorContainerDispenser { + @Accessor("dispenserInventory") + IInventory getDispenserInventory_ledger(); +} diff --git a/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerHopper.java b/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerHopper.java new file mode 100644 index 0000000..ee29d4f --- /dev/null +++ b/mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerHopper.java @@ -0,0 +1,13 @@ +package moe.nea.ledger.mixin; + +import net.minecraft.inventory.ContainerDispenser; +import net.minecraft.inventory.ContainerHopper; +import net.minecraft.inventory.IInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ContainerHopper.class) +public interface AccessorContainerHopper { + @Accessor("hopperInventory") + IInventory getHopperInventory_ledger(); +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt index 0bacf32..ff2c691 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt @@ -20,12 +20,12 @@ class ItemIdProvider { @SubscribeEvent fun onMouseInput(event: GuiScreenEvent.MouseInputEvent.Pre) { if (Mouse.getEventButton() == -1) return - MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) + BeforeGuiAction(event.gui).post() } @SubscribeEvent fun onKeyInput(event: GuiScreenEvent.KeyboardInputEvent.Pre) { - MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui)) + BeforeGuiAction(event.gui).post() } private val knownNames = mutableMapOf<String, ItemId>() diff --git a/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt b/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt index 134d519..87c990c 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/Ledger.kt @@ -22,6 +22,7 @@ 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.CaducousFeederDetection import moe.nea.ledger.modules.DragonEyePlacementDetection import moe.nea.ledger.modules.DragonSacrificeDetection import moe.nea.ledger.modules.DungeonChestDetection @@ -29,6 +30,7 @@ import moe.nea.ledger.modules.ExternalDataProvider import moe.nea.ledger.modules.EyedropsDetection import moe.nea.ledger.modules.ForgeDetection import moe.nea.ledger.modules.GambleDetection +import moe.nea.ledger.modules.GhostCoinDropDetection import moe.nea.ledger.modules.GodPotionDetection import moe.nea.ledger.modules.GodPotionMixinDetection import moe.nea.ledger.modules.GummyPolarBearDetection @@ -38,8 +40,10 @@ import moe.nea.ledger.modules.MineshaftCorpseDetection import moe.nea.ledger.modules.MinionDetection import moe.nea.ledger.modules.NpcDetection import moe.nea.ledger.modules.PestRepellentDetection +import moe.nea.ledger.modules.StonksAuctionDetection import moe.nea.ledger.modules.UpdateChecker import moe.nea.ledger.modules.VisitorDetection +import moe.nea.ledger.telemetry.TelemetryProvider import moe.nea.ledger.utils.ErrorUtil import moe.nea.ledger.utils.MinecraftExecutor import moe.nea.ledger.utils.di.DI @@ -110,7 +114,9 @@ class Ledger { tickQueue.add(runnable) } - val di = DI() + private val di = DI() + + fun leakDI() = di } @Mod.EventHandler @@ -135,6 +141,7 @@ class Ledger { BazaarOrderDetection::class.java, BitsDetection::class.java, BitsShopDetection::class.java, + CaducousFeederDetection::class.java, ConfigCommand::class.java, DebugDataCommand::class.java, DragonEyePlacementDetection::class.java, @@ -145,6 +152,7 @@ class Ledger { EyedropsDetection::class.java, ForgeDetection::class.java, GambleDetection::class.java, + GhostCoinDropDetection::class.java, GodPotionDetection::class.java, GodPotionMixinDetection::class.java, GummyPolarBearDetection::class.java, @@ -160,6 +168,7 @@ class Ledger { PestRepellentDetection::class.java, QueryCommand::class.java, RequestUtil::class.java, + StonksAuctionDetection::class.java, TriggerCommand::class.java, UpdateChecker::class.java, VisitorDetection::class.java, diff --git a/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt b/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt index abdc13a..80dd54c 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt @@ -172,7 +172,7 @@ class QueryCommand : CommandBase() { override val name: String get() = "withitem" - private val itemIdProvider = Ledger.di.provide<ItemIdProvider>() // TODO: close this escape hatch + private val itemIdProvider = Ledger.leakDI().provide<ItemIdProvider>() // TODO: close this escape hatch override fun getFilter(text: String): BooleanExpression { return Clause { column(DBItemEntry.itemId) like text } } diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt b/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt index 098912a..7f6eae9 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt @@ -1,11 +1,21 @@ package moe.nea.ledger.events +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import moe.nea.ledger.telemetry.GuiContextValue +import moe.nea.ledger.utils.telemetry.ContextValue import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.inventory.ContainerChest import net.minecraftforge.fml.common.eventhandler.Event -data class BeforeGuiAction(val gui: GuiScreen) : Event() { +data class BeforeGuiAction(val gui: GuiScreen) : LedgerEvent() { val chest = gui as? GuiChest val chestSlots = chest?.inventorySlots as ContainerChest? + override fun serialize(): JsonElement { + return JsonObject().apply { + add("gui", GuiContextValue(gui).serialize()) + } + } } diff --git a/mod/src/main/kotlin/moe/nea/ledger/events/LedgerEvent.kt b/mod/src/main/kotlin/moe/nea/ledger/events/LedgerEvent.kt new file mode 100644 index 0000000..cbb3f81 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/events/LedgerEvent.kt @@ -0,0 +1,22 @@ +package moe.nea.ledger.events + +import moe.nea.ledger.Ledger +import moe.nea.ledger.utils.ErrorUtil +import moe.nea.ledger.utils.telemetry.CommonKeys +import moe.nea.ledger.utils.telemetry.ContextValue +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.Event + +abstract class LedgerEvent : Event(), ContextValue { + fun post() { + Ledger.leakDI() + .provide<ErrorUtil>() + .catch( + CommonKeys.EVENT_MESSAGE to ContextValue.string("Error during event execution"), + "event_instance" to this, + "event_type" to ContextValue.string(javaClass.name) + ) { + MinecraftForge.EVENT_BUS.post(this) + } + } +}
\ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/CaducousFeederDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/CaducousFeederDetection.kt new file mode 100644 index 0000000..b64c7e5 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/CaducousFeederDetection.kt @@ -0,0 +1,48 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.GuiClickEvent +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.getDisplayNameU +import moe.nea.ledger.getInternalId +import moe.nea.ledger.unformattedString +import moe.nea.ledger.utils.di.Inject +import net.minecraft.client.Minecraft +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.time.Instant + +class CaducousFeederDetection { + + @Inject + lateinit var logger: LedgerLogger + + @Inject + lateinit var minecraft: Minecraft + + @SubscribeEvent + fun onFeederClick(event: GuiClickEvent) { + val slot = event.slotIn ?: return + val displayName = slot.inventory.displayName.unformattedText + if (!displayName.unformattedString().contains("Confirm Caducous Feeder")) return + val stack = slot.stack ?: return + val player = minecraft.thePlayer ?: return + if (!player.inventory.mainInventory.any { it?.getInternalId() == ItemIds.ULTIMATE_CARROT_CANDY }) return + if (stack.getDisplayNameU() != "§aUse Caducous Feeder") return + val petId = slot.inventory.getStackInSlot(13)?.getInternalId() ?: ItemId.NIL + + logger.logEntry( + LedgerEntry( + TransactionType.CADUCOUS_FEEDER_USED, + Instant.now(), + listOf( + ItemChange.lose(ItemIds.ULTIMATE_CARROT_CANDY, 1), + ItemChange(petId, 1.0, ItemChange.ChangeDirection.TRANSFORM), + ) + ) + ) + } +}
\ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt index 93bb453..42a1f42 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt @@ -14,6 +14,7 @@ import java.util.concurrent.CompletableFuture class ExternalDataProvider @Inject constructor( val requestUtil: RequestUtil ) { + // TODO: Save all the data locally, so in case of a failed request older versions can be used fun createAuxillaryDataRequest(path: String): Request { return requestUtil.createRequest("https://github.com/nea89o/ledger-auxiliary-data/raw/refs/heads/master/$path") @@ -22,7 +23,9 @@ class ExternalDataProvider @Inject constructor( private val itemNameFuture: CompletableFuture<Map<String, String>> = CompletableFuture.supplyAsync { val request = createAuxillaryDataRequest("data/item_names.json") val response = request.execute(requestUtil) - val nameMap = response.json(GsonUtil.typeToken<Map<String, String>>()) + val nameMap = + response?.json(GsonUtil.typeToken<Map<String, String>>()) + ?: mapOf() return@supplyAsync nameMap } diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt index 95811ed..b8974c0 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt @@ -9,6 +9,7 @@ import moe.nea.ledger.getInternalId import moe.nea.ledger.matches import moe.nea.ledger.unformattedString import moe.nea.ledger.utils.di.Inject +import net.minecraft.item.EnumDyeColor import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.time.Instant @@ -20,7 +21,9 @@ class ForgeDetection { fun onClick(event: GuiClickEvent) { val slot = event.slotIn ?: return val clickedItem = slot.stack ?: return + val dyeColor = EnumDyeColor.byMetadata(clickedItem.itemDamage) if (clickedItem.displayName.unformattedString() != "Confirm") return + if (dyeColor == EnumDyeColor.RED) return val furnaceSlotName = slot.inventory.getStackInSlot(furnaceSlot)?.displayName?.unformattedString() ?: return if (!furnaceName.matches(furnaceSlotName)) return diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/GhostCoinDropDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/GhostCoinDropDetection.kt new file mode 100644 index 0000000..42084e2 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/GhostCoinDropDetection.kt @@ -0,0 +1,38 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class GhostCoinDropDetection { + + val ghostCoinPattern = + Pattern.compile("The ghost's death materialized (?<coins>$SHORT_NUMBER_PATTERN) coins from the mists!") + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onGhostCoinDrop(event: ChatReceived) { + ghostCoinPattern.useMatcher(event.message) { + logger.logEntry( + LedgerEntry( + // TODO: merge this into a generic mob drop tt + TransactionType.GHOST_COIN_DROP, + event.timestamp, + listOf( + ItemChange.gainCoins(parseShortNumber(group("coins"))), + ) + ) + ) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/StonksAuctionDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/StonksAuctionDetection.kt new file mode 100644 index 0000000..4f3706c --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/modules/StonksAuctionDetection.kt @@ -0,0 +1,59 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.SHORT_NUMBER_PATTERN +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.gen.ItemIds +import moe.nea.ledger.parseShortNumber +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.di.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Matcher +import java.util.regex.Pattern + +class StonksAuctionDetection { + + val stonksPlacedBid = + Pattern.compile("Successfully placed your Stonk bid of (?<coins>$SHORT_NUMBER_PATTERN) Coins!") + val stonksIncreaseBid = + Pattern.compile("Successfully increased your Stonk bid by (?<coins>$SHORT_NUMBER_PATTERN) Coins!") + val stonksClaim = + Pattern.compile("You claimed (?<count>$SHORT_NUMBER_PATTERN)x Stock of Stonks from the Stonks Auction!") + + @Inject + lateinit var logger: LedgerLogger + + @SubscribeEvent + fun onChat(event: ChatReceived) { + fun Matcher.logBidding() { + logger.logEntry( + LedgerEntry( + TransactionType.STONKS_AUCTION, + event.timestamp, + listOf( + ItemChange.loseCoins(parseShortNumber(group("coins"))), + ) + ) + ) + } + + fun Matcher.logClaim() { + logger.logEntry( + LedgerEntry( + TransactionType.STONKS_AUCTION, + event.timestamp, + listOf( + ItemChange.gain(ItemIds.STOCK_OF_STONKS, parseShortNumber(group("count"))) + ) + ) + ) + } + + stonksPlacedBid.useMatcher(event.message) { logBidding() } + stonksIncreaseBid.useMatcher(event.message) { logBidding() } + stonksClaim.useMatcher(event.message) { logClaim() } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/telemetry/GuiContextValue.kt b/mod/src/main/kotlin/moe/nea/ledger/telemetry/GuiContextValue.kt new file mode 100644 index 0000000..2d7db39 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/telemetry/GuiContextValue.kt @@ -0,0 +1,16 @@ +package moe.nea.ledger.telemetry + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import moe.nea.ledger.utils.ScreenUtil +import moe.nea.ledger.utils.telemetry.ContextValue +import net.minecraft.client.gui.GuiScreen + +class GuiContextValue(val gui: GuiScreen) : ContextValue { + override fun serialize(): JsonElement { + return JsonObject().apply { + addProperty("class", gui.javaClass.name) + addProperty("name", ScreenUtil.estimateName(gui)) + } + } +} diff --git a/mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt b/mod/src/main/kotlin/moe/nea/ledger/telemetry/TelemetryProvider.kt index d9c7108..c2fff23 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/telemetry/TelemetryProvider.kt @@ -1,8 +1,10 @@ -package moe.nea.ledger +package moe.nea.ledger.telemetry import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject +import moe.nea.ledger.DevUtil +import moe.nea.ledger.Ledger import moe.nea.ledger.gen.BuildConfig import moe.nea.ledger.utils.di.DI import moe.nea.ledger.utils.di.DIProvider @@ -40,7 +42,7 @@ object TelemetryProvider { } fun setupDefaultSpan() { - val sp = Span.current() + val sp = Span.rootSpan sp.add(USER, MinecraftUser(Minecraft.getMinecraft().session)) sp.add(MINECRAFT_VERSION, ContextValue.compound( "static" to "1.8.9", diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/ScreenUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/ScreenUtil.kt new file mode 100644 index 0000000..0305126 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/ScreenUtil.kt @@ -0,0 +1,29 @@ +package moe.nea.ledger.utils + +import moe.nea.ledger.mixin.AccessorContainerDispenser +import moe.nea.ledger.mixin.AccessorContainerHopper +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.ContainerChest +import net.minecraft.inventory.IInventory + +object ScreenUtil { + fun estimateInventory(screen: GuiScreen?): IInventory? { + if (screen !is GuiContainer) { + return null + } + val container = screen.inventorySlots ?: return null + if (container is ContainerChest) + return container.lowerChestInventory + if (container is AccessorContainerDispenser) + return container.dispenserInventory_ledger + if (container is AccessorContainerHopper) + return container.hopperInventory_ledger + return null + + } + + fun estimateName(screen: GuiScreen?): String? { + return estimateInventory(screen)?.name + } +}
\ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestTrace.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestTrace.kt new file mode 100644 index 0000000..3953e09 --- /dev/null +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestTrace.kt @@ -0,0 +1,21 @@ +package moe.nea.ledger.utils.network + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import moe.nea.ledger.utils.telemetry.ContextValue + +class RequestTrace(val request: Request) : ContextValue { + override fun serialize(): JsonElement { + return JsonObject().apply { + addProperty("url", request.url.toString()) + addProperty("method", request.method.name) + addProperty("content-type", request.headers["content-type"]) + addProperty("accept", request.headers["accept"]) + } + } + + companion object { + val KEY = "http_request" + fun createTrace(request: Request): Pair<String, RequestTrace> = KEY to RequestTrace(request) + } +}
\ No newline at end of file diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt index a49c65a..8101527 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt @@ -2,6 +2,8 @@ package moe.nea.ledger.utils.network import moe.nea.ledger.utils.ErrorUtil import moe.nea.ledger.utils.di.Inject +import moe.nea.ledger.utils.telemetry.CommonKeys +import moe.nea.ledger.utils.telemetry.ContextValue import java.net.URL import java.net.URLConnection import java.security.KeyStore @@ -38,7 +40,10 @@ class RequestUtil @Inject constructor(val errorUtil: ErrorUtil) { fun createRequest(url: String) = createRequest(URL(url)) fun createRequest(url: URL) = Request(url, Request.Method.GET, null, mapOf()) - fun executeRequest(request: Request): Response { + fun executeRequest(request: Request): Response? = errorUtil.catch( + CommonKeys.EVENT_MESSAGE to ContextValue.string("Failed to execute request"), + RequestTrace.createTrace(request) + ) { val connection = request.url.openConnection() enhanceConnection(connection) connection.setRequestProperty("accept-encoding", "gzip") @@ -56,7 +61,7 @@ class RequestUtil @Inject constructor(val errorUtil: ErrorUtil) { val text = stream.bufferedReader().readText() stream.close() // Do NOT call connection.disconnect() to allow for connection reuse - return Response(request, text, connection.headerFields) + return@catch Response(request, text, connection.headerFields) } diff --git a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt index 0d680a9..8b8e284 100644 --- a/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt +++ b/mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt @@ -2,9 +2,10 @@ package moe.nea.ledger.utils.telemetry class Span(val parent: Span?) : AutoCloseable { companion object { + val rootSpan = Span(null) private val _current = object : InheritableThreadLocal<Span>() { override fun initialValue(): Span { - return Span(null) + return Span(rootSpan) } override fun childValue(parentValue: Span?): Span { diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt index b9178cc..10d9b9c 100644 --- a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt +++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt @@ -2,6 +2,7 @@ package moe.nea.ledger.analysis import moe.nea.ledger.database.DBLogEntry import moe.nea.ledger.database.Query +import moe.nea.ledger.database.columns.DBUlid import moe.nea.ledger.database.sql.Clause import moe.nea.ledger.utils.ULIDWrapper import java.time.Instant @@ -10,9 +11,9 @@ import java.util.UUID interface AnalysisFilter { fun applyTo(query: Query) { - query.where(Clause { column(DBLogEntry.transactionId) ge string(ULIDWrapper.lowerBound(startWindow).wrapped) }) - .where(Clause { column(DBLogEntry.transactionId) le string(ULIDWrapper.upperBound(endWindow).wrapped) }) - // TODO: apply profiles filter + query.where(Clause { column(DBLogEntry.transactionId) ge value(DBUlid, ULIDWrapper.lowerBound(startWindow)) }) + .where(Clause { column(DBLogEntry.transactionId) le value(DBUlid, ULIDWrapper.upperBound(endWindow)) }) +//TODO: .where(Clause { column(DBLogEntry.profileId) inList profiles }) } fun timeZone(): ZoneId { diff --git a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt index 5e2e633..d1ce52b 100644 --- a/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt +++ b/server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt @@ -20,15 +20,9 @@ class CoinsSpentOnAuctions : Analysis { override fun perform(database: Connection, filter: AnalysisFilter): AnalysisResult { val query = DBLogEntry.from(database) .join(DBItemEntry, Clause { column(DBItemEntry.transactionId) eq column(DBLogEntry.transactionId) }) - .where(Clause { - (column(DBItemEntry.itemId) eq string(ItemId.COINS.string)) - }) - .where(Clause { - (column(DBItemEntry.mode) eq string(ItemChange.ChangeDirection.LOST.name)) - }) - .where(Clause { - (column(DBLogEntry.type) eq string(TransactionType.AUCTION_BOUGHT.name)) - }) + .where(Clause { column(DBItemEntry.itemId) eq ItemId.COINS }) + .where(Clause { column(DBItemEntry.mode) eq ItemChange.ChangeDirection.LOST }) + .where(Clause { column(DBLogEntry.type) eq TransactionType.AUCTION_BOUGHT }) .select(DBItemEntry.size, DBLogEntry.transactionId) filter.applyTo(query) val spentThatDay = mutableMapOf<LocalDate, Double>() diff --git a/settings.gradle.kts b/settings.gradle.kts index 615b9ea..cab8376 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,8 @@ pluginManagement { "gg.essential.loom" -> useModule("gg.essential:architectury-loom:${requested.version}") } } - }} + } +} plugins { id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") |