aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basetypes/src/main/kotlin/moe/nea/ledger/TransactionType.kt3
-rw-r--r--build.gradle.kts4
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt10
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt6
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListClause.kt8
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/ListExpression.kt22
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/SQLQueryComponent.kt19
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt3
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/TypedOperand.kt7
-rw-r--r--database/core/src/main/kotlin/moe/nea/ledger/database/sql/ValuedOperand.kt15
-rw-r--r--mod/log4j2.xml6
-rw-r--r--mod/src/main/java/moe/nea/ledger/init/AutoDiscoveryMixinPlugin.java2
-rw-r--r--mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerDispenser.java12
-rw-r--r--mod/src/main/java/moe/nea/ledger/mixin/AccessorContainerHopper.java13
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt4
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/Ledger.kt11
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/QueryCommand.kt2
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt12
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/events/LedgerEvent.kt22
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/CaducousFeederDetection.kt48
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt5
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt3
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/GhostCoinDropDetection.kt38
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/StonksAuctionDetection.kt59
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/telemetry/GuiContextValue.kt16
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/telemetry/TelemetryProvider.kt (renamed from mod/src/main/kotlin/moe/nea/ledger/TelemetryProvider.kt)6
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/utils/ScreenUtil.kt29
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestTrace.kt21
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/utils/network/RequestUtil.kt9
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/utils/telemetry/Span.kt3
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/AnalysisFilter.kt7
-rw-r--r--server/analysis/src/main/kotlin/moe/nea/ledger/analysis/CoinsSpentOnAuctions.kt12
-rw-r--r--settings.gradle.kts3
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")