aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/moe/nea')
-rw-r--r--src/main/kotlin/moe/nea/ledger/DebouncedValue.kt38
-rw-r--r--src/main/kotlin/moe/nea/ledger/ExpiringValue.kt6
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemId.kt4
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt19
-rw-r--r--src/main/kotlin/moe/nea/ledger/Ledger.kt28
-rw-r--r--src/main/kotlin/moe/nea/ledger/TransactionType.kt5
-rw-r--r--src/main/kotlin/moe/nea/ledger/TriggerCommand.kt34
-rw-r--r--src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt4
-rw-r--r--src/main/kotlin/moe/nea/ledger/config/MainOptions.kt27
-rw-r--r--src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt17
-rw-r--r--src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt6
-rw-r--r--src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt7
-rw-r--r--src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt6
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt46
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt71
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt34
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt6
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt167
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt9
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt10
-rw-r--r--src/main/kotlin/moe/nea/ledger/utils/di/DI.kt37
21 files changed, 558 insertions, 23 deletions
diff --git a/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt b/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt
new file mode 100644
index 0000000..66fba8d
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/DebouncedValue.kt
@@ -0,0 +1,38 @@
+package moe.nea.ledger
+
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.nanoseconds
+import kotlin.time.Duration.Companion.seconds
+
+class DebouncedValue<T>(private val value: T) {
+ companion object {
+ fun <T> farFuture(): DebouncedValue<T> {
+ val value = DebouncedValue(Unit)
+ value.take()
+ @Suppress("UNCHECKED_CAST")
+ return value as DebouncedValue<T>
+ }
+ }
+
+ val lastSeenAt = System.nanoTime()
+ val age get() = (System.nanoTime() - lastSeenAt).nanoseconds
+ var taken = false
+ private set
+
+ fun get(debounce: Duration): T? {
+ return if (!taken && age >= debounce) value
+ else null
+ }
+
+ fun replace(): T? {
+ return consume(0.seconds)
+ }
+
+ fun consume(debounce: Duration): T? {
+ return get(debounce)?.also { take() }
+ }
+
+ fun take() {
+ taken = true
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt b/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt
index dac4751..b50b14e 100644
--- a/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt
+++ b/src/main/kotlin/moe/nea/ledger/ExpiringValue.kt
@@ -7,8 +7,10 @@ class ExpiringValue<T>(private val value: T) {
val lastSeenAt: Long = System.nanoTime()
val age get() = (System.nanoTime() - lastSeenAt).nanoseconds
var taken = false
+ private set
+
fun get(expiry: Duration): T? {
- return if (!taken && expiry > age) value
+ return if (!taken && age < expiry) value
else null
}
@@ -21,7 +23,7 @@ class ExpiringValue<T>(private val value: T) {
}
}
- fun consume(expiry: Duration): T? = get(expiry).also { take() }
+ fun consume(expiry: Duration): T? = get(expiry)?.also { take() }
fun take() {
taken = true
}
diff --git a/src/main/kotlin/moe/nea/ledger/ItemId.kt b/src/main/kotlin/moe/nea/ledger/ItemId.kt
index 2b83357..fdfa19f 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemId.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemId.kt
@@ -21,6 +21,7 @@ value class ItemId(
val GEMSTONE_POWDER = ItemId("SKYBLOCK_POWDER_GEMSTONE")
val MITHRIL_POWDER = ItemId("SKYBLOCK_POWDER_MITHRIL")
val GOLD_ESSENCE = ItemId("ESSENCE_GOLD")
+ val DRAGON_ESSENCE = ItemId("ESSENCE_DRAGON")
val PELT = ItemId("SKYBLOCK_PELT")
val COINS = ItemId("SKYBLOCK_COIN")
val FINE_FLOUR = ItemId("FINE_FLOUR")
@@ -29,7 +30,10 @@ value class ItemId(
val NIL = ItemId("SKYBLOCK_NIL")
val ARCHFIEND_LOW_CLASS = ItemId("ARCHFIEND_DICE")
val ARCHFIEND_HIGH_CLASS = ItemId("HIGH_CLASS_ARCHFIEND_DICE")
+ val CAP_EYEDROPS = ItemId("CAPSAICIN_EYEDROPS_NO_CHARGES")
val ARCHFIEND_DYE = ItemId("DYE_ARCHFIEND")
+ val SLEEPING_EYE = ItemId("SLEEPING_EYE")
+ val SUMMONING_EYE = ItemId("SUMMONING_EYE")
val DUNGEON_CHEST_KEY = ItemId("DUNGEON_CHEST_KEY")
val BOOSTER_COOKIE = ItemId("BOOSTER_COOKIE")
val KISMET_FEATHER = ItemId("KISMET_FEATHER")
diff --git a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
index 72f1d09..4d85713 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
@@ -105,7 +105,9 @@ class ItemIdProvider {
private val coinRegex = "(?<amount>$SHORT_NUMBER_PATTERN) Coins?".toPattern()
private val stackedItemRegex = "(?<name>.*) x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
+ private val reverseStackedItemRegex = "(?<count>$SHORT_NUMBER_PATTERN)x (?<name>.*)".toPattern()
private val essenceRegex = "(?<essence>.*) Essence x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
+ private val numberedItemRegex = "(?<count>$SHORT_NUMBER_PATTERN) (?<what>.*)".toPattern()
fun findCostItemsFromSpan(lore: List<String>): List<Pair<ItemId, Double>> {
return lore.iterator().asSequence()
@@ -155,12 +157,27 @@ class ItemIdProvider {
parseShortNumber(group("count")))
}
stackedItemRegex.useMatcher(properName) {
- var item = findForName(group("name"), fallbackToGenerated)
+ val item = findForName(group("name"), fallbackToGenerated)
if (item != null) {
val count = parseShortNumber(group("count"))
return Pair(item, count)
}
}
+ reverseStackedItemRegex.useMatcher(properName) {
+ val item = findForName(group("name"), fallbackToGenerated)
+ if (item != null) {
+ val count = parseShortNumber(group("count"))
+ return Pair(item, count)
+ }
+ }
+ numberedItemRegex.useMatcher(properName) {
+ val item = findForName(group("what"), fallbackToGenerated)
+ if (item != null) {
+ val count = parseShortNumber(group("count"))
+ return Pair(item, count)
+ }
+ }
+
return findForName(properName, fallbackToGenerated)?.let { Pair(it, 1.0) }
}
diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt
index bb69621..d2d09eb 100644
--- a/src/main/kotlin/moe/nea/ledger/Ledger.kt
+++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt
@@ -3,10 +3,13 @@ package moe.nea.ledger
import com.google.gson.Gson
import io.github.notenoughupdates.moulconfig.managed.ManagedConfig
import moe.nea.ledger.config.LedgerConfig
+import moe.nea.ledger.config.UpdateUi
+import moe.nea.ledger.config.UpdateUiMarker
import moe.nea.ledger.database.Database
import moe.nea.ledger.events.ChatReceived
import moe.nea.ledger.events.LateWorldLoadEvent
import moe.nea.ledger.events.RegistrationFinishedEvent
+import moe.nea.ledger.events.WorldSwitchEvent
import moe.nea.ledger.gen.BuildConfig
import moe.nea.ledger.modules.AuctionHouseDetection
import moe.nea.ledger.modules.BankDetection
@@ -14,8 +17,11 @@ 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.DragonEyePlacementDetection
+import moe.nea.ledger.modules.`DragonSacrificeDetection`
import moe.nea.ledger.modules.DungeonChestDetection
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.KatDetection
@@ -23,9 +29,12 @@ import moe.nea.ledger.modules.KuudraChestDetection
import moe.nea.ledger.modules.MineshaftCorpseDetection
import moe.nea.ledger.modules.MinionDetection
import moe.nea.ledger.modules.NpcDetection
+import moe.nea.ledger.modules.UpdateChecker
import moe.nea.ledger.modules.VisitorDetection
import moe.nea.ledger.utils.ErrorUtil
+import moe.nea.ledger.utils.MinecraftExecutor
import moe.nea.ledger.utils.di.DI
+import moe.nea.ledger.utils.di.DIProvider
import moe.nea.ledger.utils.network.RequestUtil
import net.minecraft.client.Minecraft
import net.minecraft.command.ICommand
@@ -82,6 +91,9 @@ class Ledger {
val logger = LogManager.getLogger("MoneyLedger")
val managedConfig = ManagedConfig.create(File("config/money-ledger/config.json"), LedgerConfig::class.java) {
checkExpose = false
+ customProcessor<UpdateUiMarker> { option, ann ->
+ UpdateUi(option)
+ }
}
val gson = Gson()
private val tickQueue = ConcurrentLinkedQueue<Runnable>()
@@ -100,31 +112,38 @@ class Ledger {
di.registerSingleton(this)
di.registerSingleton(Minecraft.getMinecraft())
di.registerSingleton(gson)
+ di.register(LedgerConfig::class.java, DIProvider { managedConfig.instance })
di.registerInjectableClasses(
AuctionHouseDetection::class.java,
BankDetection::class.java,
BazaarDetection::class.java,
BazaarOrderDetection::class.java,
- DebugDataCommand::class.java,
BitsDetection::class.java,
BitsShopDetection::class.java,
ConfigCommand::class.java,
Database::class.java,
+ DebugDataCommand::class.java,
+ DragonEyePlacementDetection::class.java,
+ DragonSacrificeDetection::class.java,
DungeonChestDetection::class.java,
ErrorUtil::class.java,
ExternalDataProvider::class.java,
+ EyedropsDetection::class.java,
+ ForgeDetection::class.java,
+ GambleDetection::class.java,
ItemIdProvider::class.java,
KatDetection::class.java,
KuudraChestDetection::class.java,
LedgerLogger::class.java,
LogChatCommand::class.java,
- MinionDetection::class.java,
+ MinecraftExecutor::class.java,
MineshaftCorpseDetection::class.java,
- ForgeDetection::class.java,
+ MinionDetection::class.java,
NpcDetection::class.java,
- GambleDetection::class.java,
QueryCommand::class.java,
RequestUtil::class.java,
+ TriggerCommand::class.java,
+ UpdateChecker::class.java,
VisitorDetection::class.java,
)
val errorUtil = di.provide<ErrorUtil>()
@@ -148,6 +167,7 @@ class Ledger {
fun worldSwitchEvent(event: EntityJoinWorldEvent) {
if (event.entity == Minecraft.getMinecraft().thePlayer) {
lastJoin = System.currentTimeMillis()
+ MinecraftForge.EVENT_BUS.post(WorldSwitchEvent())
}
}
diff --git a/src/main/kotlin/moe/nea/ledger/TransactionType.kt b/src/main/kotlin/moe/nea/ledger/TransactionType.kt
index f6bbe6a..00feebb 100644
--- a/src/main/kotlin/moe/nea/ledger/TransactionType.kt
+++ b/src/main/kotlin/moe/nea/ledger/TransactionType.kt
@@ -13,11 +13,13 @@ enum class TransactionType {
BAZAAR_SELL_ORDER,
BITS_PURSE_STATUS,
BOOSTER_COOKIE_ATE,
+ CAPSAICIN_EYEDROPS_USED,
COMMUNITY_SHOP_BUY,
CORPSE_DESECRATED,
- FORGED,
DIE_ROLLED,
+ DRACONIC_SACRIFICE,
DUNGEON_CHEST_OPEN,
+ FORGED,
KAT_TIMESKIP,
KAT_UPGRADE,
KISMET_REROLL,
@@ -25,4 +27,5 @@ enum class TransactionType {
NPC_BUY,
NPC_SELL,
VISITOR_BARGAIN,
+ WYRM_EVOKED,
} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt b/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt
new file mode 100644
index 0000000..c97627d
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/TriggerCommand.kt
@@ -0,0 +1,34 @@
+package moe.nea.ledger
+
+import moe.nea.ledger.events.TriggerEvent
+import net.minecraft.command.CommandBase
+import net.minecraft.command.ICommandSender
+import net.minecraft.event.ClickEvent
+import net.minecraft.util.ChatComponentText
+import net.minecraftforge.common.MinecraftForge
+
+class TriggerCommand : CommandBase() {
+ fun getTriggerCommandLine(trigger: String): ClickEvent {
+ return ClickEvent(ClickEvent.Action.RUN_COMMAND, "/${commandName} $trigger")
+ }
+
+ override fun getCommandName(): String {
+ return "__ledgertrigger"
+ }
+
+ override fun getCommandUsage(sender: ICommandSender?): String {
+ return ""
+ }
+
+ override fun processCommand(sender: ICommandSender, args: Array<out String>) {
+ val event = TriggerEvent(args.joinToString(" "))
+ MinecraftForge.EVENT_BUS.post(event)
+ if (!event.isCanceled)
+ sender.addChatMessage(ChatComponentText("§cCould not find the given trigger. This is an internal command for ledger."))
+ }
+
+ override fun canCommandSenderUseCommand(sender: ICommandSender?): Boolean {
+ return true
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt b/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt
index 367f1e2..91ee5c1 100644
--- a/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt
+++ b/src/main/kotlin/moe/nea/ledger/config/LedgerConfig.kt
@@ -20,6 +20,10 @@ class LedgerConfig : Config() {
return DescriptionRendereringBehaviour.EXPAND_PANEL
}
+ @Category(name = "Ledger", desc = "")
+ @JvmField
+ val main = MainOptions()
+
@Category(name = "Synchronization", desc = "")
@JvmField
val synchronization = SynchronizationOptions()
diff --git a/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt b/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt
new file mode 100644
index 0000000..1efa970
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/config/MainOptions.kt
@@ -0,0 +1,27 @@
+package moe.nea.ledger.config
+
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption
+
+class MainOptions {
+ @ConfigOption(name = "Marker for Update UI", desc = "_")
+ @JvmField
+ @UpdateUiMarker
+ @Transient
+ var testOption = Unit
+
+ @ConfigOption(name = "Check for Updates", desc = "Automatically check for updates on startup")
+ @ConfigEditorDropdown
+ @JvmField
+ var updateCheck = UpdateCheckBehaviour.SEMI
+
+ enum class UpdateCheckBehaviour(val label: String) {
+ SEMI("Semi-Automatic"),
+ FULL("Full-Automatic"),
+ NONE("Don't check");
+
+ override fun toString(): String {
+ return label
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt b/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt
new file mode 100644
index 0000000..86ccbf7
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/config/UpdateUi.kt
@@ -0,0 +1,17 @@
+package moe.nea.ledger.config
+
+import io.github.notenoughupdates.moulconfig.gui.GuiComponent
+import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
+import io.github.notenoughupdates.moulconfig.gui.editors.ComponentEditor
+import io.github.notenoughupdates.moulconfig.processor.ProcessedOption
+import moe.nea.ledger.Ledger
+
+class UpdateUi(option: ProcessedOption) : ComponentEditor(option) {
+ val delegate by lazy {// TODO
+ TextComponent("Hier könnte ihre werbung stehen")
+ }
+
+ override fun getDelegate(): GuiComponent {
+ return delegate
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt b/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt
new file mode 100644
index 0000000..7a0466a
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/config/UpdateUiMarker.kt
@@ -0,0 +1,6 @@
+package moe.nea.ledger.config
+
+@Retention(AnnotationRetention.RUNTIME)
+@Target(AnnotationTarget.FIELD)
+annotation class UpdateUiMarker {
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt b/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt
new file mode 100644
index 0000000..3751f43
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/events/TriggerEvent.kt
@@ -0,0 +1,7 @@
+package moe.nea.ledger.events
+
+import net.minecraftforge.fml.common.eventhandler.Cancelable
+import net.minecraftforge.fml.common.eventhandler.Event
+
+@Cancelable
+data class TriggerEvent(val action: String) : Event()
diff --git a/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt b/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt
new file mode 100644
index 0000000..22a97f7
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/events/WorldSwitchEvent.kt
@@ -0,0 +1,6 @@
+package moe.nea.ledger.events
+
+import net.minecraftforge.fml.common.eventhandler.Event
+
+class WorldSwitchEvent : Event() {
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt
new file mode 100644
index 0000000..b9f70c4
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt
@@ -0,0 +1,46 @@
+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.ChatReceived
+import moe.nea.ledger.events.WorldSwitchEvent
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DragonEyePlacementDetection {
+ val eyePlaced = "☬ You placed a Summoning Eye!( Brace yourselves!)? \\(./.\\)".toPattern()
+//☬ You placed a Summoning Eye! Brace yourselves! (8/8)
+ var eyeCount = 0
+
+ @SubscribeEvent
+ fun onWorldSwap(event: WorldSwitchEvent) {
+ eyeCount = 0
+ }
+
+ @SubscribeEvent
+ fun onRetrieveEye(event: ChatReceived) {
+ if (event.message == "You recovered a Summoning Eye!") {
+ eyeCount--
+ }
+ eyePlaced.useMatcher(event.message) {
+ eyeCount++
+ }
+ if (event.message == "Your Sleeping Eyes have been awoken by the magic of the Dragon. They are now Remnants of the Eye!") {
+ logger.logEntry(LedgerEntry(
+ TransactionType.WYRM_EVOKED,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(ItemId.SUMMONING_EYE, eyeCount)
+ )
+ ))
+ eyeCount = 0
+ }
+ }
+
+ @Inject
+ lateinit var logger: LedgerLogger
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt
new file mode 100644
index 0000000..20934d2
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt
@@ -0,0 +1,71 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.DebouncedValue
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+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.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 net.minecraftforge.fml.common.gameevent.TickEvent
+import kotlin.time.Duration.Companion.seconds
+
+class DragonSacrificeDetection {
+ //SACRIFICE! You turned Holy Dragon Boots into 30 Dragon Essence!
+ //BONUS LOOT! You also received 17x Holy Dragon Fragment from your sacrifice!
+ @Inject
+ lateinit var itemIdProvider: ItemIdProvider
+
+ @Inject
+ lateinit var logger: LedgerLogger
+
+ val sacrificePattern =
+ "SACRIFICE! You turned (?<sacrifice>.*) into (?<amount>$SHORT_NUMBER_PATTERN) Dragon Essence!".toPattern()
+ val bonusLootPattern = "BONUS LOOT! You also received (?<bonus>.*) from your sacrifice!".toPattern()
+
+ var lastSacrifice: DebouncedValue<LedgerEntry> = DebouncedValue.farFuture()
+
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ sacrificePattern.useMatcher(event.message) {
+ val sacrifice = itemIdProvider.findForName(group("sacrifice")) ?: return
+ val lootEssence = parseShortNumber(group("amount"))
+ consume(lastSacrifice.replace())
+ lastSacrifice = DebouncedValue(LedgerEntry(
+ TransactionType.DRACONIC_SACRIFICE,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(sacrifice, 1),
+ ItemChange.gain(ItemId.DRAGON_ESSENCE, lootEssence)
+ )
+ ))
+ }
+ bonusLootPattern.useMatcher(event.message) {
+ val bonusItem = itemIdProvider.findStackableItemByName(
+ group("bonus"), true
+ ) ?: return
+ lastSacrifice.replace()?.let {
+ consume(
+ it.copy(items = it.items + ItemChange.unpairGain(bonusItem))
+ )
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent) {
+ consume(lastSacrifice.consume(4.seconds))
+ }
+
+ fun consume(entry: LedgerEntry?) {
+ if (entry != null)
+ logger.logEntry(entry)
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt
new file mode 100644
index 0000000..2b1a8cd
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt
@@ -0,0 +1,34 @@
+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.ChatReceived
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class EyedropsDetection {
+
+ val capsaicinEyedropsUsed = "You applied the eyedrops on the minion and ran out!".toPattern()
+
+ @Inject
+ lateinit var logger: LedgerLogger
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ capsaicinEyedropsUsed.useMatcher(event.message) {
+ logger.logEntry(
+ LedgerEntry(
+ TransactionType.CAPSAICIN_EYEDROPS_USED,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(ItemId.CAP_EYEDROPS, 1)
+ )
+ )
+ )
+ }
+ }
+} \ 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
index c17cdc8..95b8aa5 100644
--- a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt
+++ b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt
@@ -10,6 +10,7 @@ import moe.nea.ledger.TransactionType
import moe.nea.ledger.asIterable
import moe.nea.ledger.events.BeforeGuiAction
import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.events.ExtraSupplyIdEvent
import moe.nea.ledger.getDisplayNameU
import moe.nea.ledger.getInternalId
import moe.nea.ledger.getLore
@@ -46,6 +47,11 @@ class NpcDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemId
}
}
+ @SubscribeEvent
+ fun addChocolate(event: ExtraSupplyIdEvent) {
+ event.store("Chocolate", ItemId("SKYBLOCK_CHOCOLATE"))
+ }
+
@Inject
lateinit var errorUtil: ErrorUtil
diff --git a/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt b/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt
new file mode 100644
index 0000000..0d89ca1
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt
@@ -0,0 +1,167 @@
+package moe.nea.ledger.modules
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonPrimitive
+import moe.nea.ledger.DevUtil
+import moe.nea.ledger.LedgerLogger
+import moe.nea.ledger.TriggerCommand
+import moe.nea.ledger.config.LedgerConfig
+import moe.nea.ledger.config.MainOptions
+import moe.nea.ledger.events.RegistrationFinishedEvent
+import moe.nea.ledger.events.TriggerEvent
+import moe.nea.ledger.gen.BuildConfig
+import moe.nea.ledger.utils.ErrorUtil
+import moe.nea.ledger.utils.MinecraftExecutor
+import moe.nea.ledger.utils.di.Inject
+import moe.nea.ledger.utils.network.RequestUtil
+import moe.nea.libautoupdate.CurrentVersion
+import moe.nea.libautoupdate.GithubReleaseUpdateData
+import moe.nea.libautoupdate.GithubReleaseUpdateSource
+import moe.nea.libautoupdate.PotentialUpdate
+import moe.nea.libautoupdate.UpdateContext
+import moe.nea.libautoupdate.UpdateData
+import moe.nea.libautoupdate.UpdateTarget
+import moe.nea.libautoupdate.UpdateUtils
+import net.minecraft.util.ChatComponentText
+import net.minecraft.util.ChatStyle
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.concurrent.CompletableFuture
+
+class UpdateChecker @Inject constructor(
+ val errorUtil: ErrorUtil,
+ val requestUtil: RequestUtil,
+) {
+
+ @Inject
+ lateinit var minecraftExecutor: MinecraftExecutor
+
+ val updater = UpdateContext(
+ NightlyAwareGithubUpdateSource("nea89o", "LocalTransactionLedger"),
+ if (DevUtil.isDevEnv) UpdateTarget { listOf() }
+ else UpdateTarget.deleteAndSaveInTheSameFolder(UpdateChecker::class.java),
+ object : CurrentVersion {
+ override fun display(): String {
+ return BuildConfig.VERSION
+ }
+
+ override fun isOlderThan(element: JsonElement?): Boolean {
+ if (element !is JsonPrimitive || !element.isString) return true
+ val newHash = element.asString // TODO: change once i support non nightly update streams
+ val length = minOf(newHash.length, BuildConfig.GIT_COMMIT.length)
+ if (newHash.substring(0, length).equals(BuildConfig.GIT_COMMIT.substring(0, length), ignoreCase = true))
+ return false
+ return true
+ }
+
+
+ override fun toString(): String {
+ return "{gitversion:${BuildConfig.GIT_COMMIT}, version:${BuildConfig.FULL_VERSION}}"
+ }
+ },
+ "ledger"
+ )
+
+ class NightlyAwareGithubUpdateSource(owner: String, repository: String) :
+ GithubReleaseUpdateSource(owner, repository) {
+ override fun selectUpdate(updateStream: String, releases: List<GithubRelease>): UpdateData? {
+ if (updateStream == "nightly") {
+ return findAsset(releases.find { it.tagName == "nightly" })
+ }
+ return super.selectUpdate(updateStream, releases.filter { it.tagName != "nightly" })
+ }
+
+ val releaseRegex = "commit: `(?<hash>[a-f0-9]+)`".toPattern()
+
+ override fun findAsset(release: GithubRelease?): UpdateData? {
+ val update = super.findAsset(release) as GithubReleaseUpdateData? ?: return null
+ return GithubReleaseUpdateData(
+ update.versionName,
+ releaseRegex.matcher(update.releaseDescription)
+ .takeIf { it.find() }
+ ?.run { group("hash") }
+ ?.let(::JsonPrimitive)
+ ?: update.versionNumber,
+ update.sha256,
+ update.download,
+ update.releaseDescription,
+ update.targetCommittish,
+ update.createdAt,
+ update.publishedAt,
+ update.htmlUrl
+ )
+ }
+ }
+
+ init {
+ UpdateUtils.patchConnection {
+ this.requestUtil.enhanceConnection(it)
+ }
+ }
+
+ var latestUpdate: PotentialUpdate? = null
+ var hasNotified = false
+
+ @SubscribeEvent
+ fun onStartup(event: RegistrationFinishedEvent) {
+ if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.NONE) return
+ launchUpdateCheck()
+ }
+
+ fun launchUpdateCheck() {
+ errorUtil.listenToFuture(
+ updater.checkUpdate("nightly")
+ .thenAcceptAsync(
+ {
+ latestUpdate = it
+ informAboutUpdates(it)
+ }, minecraftExecutor)
+ )
+ }
+
+ @Inject
+ lateinit var config: LedgerConfig
+
+ @Inject
+ lateinit var triggerCommand: TriggerCommand
+
+ val installTrigger = "execute-download"
+
+ @Inject
+ lateinit var logger: LedgerLogger
+ fun informAboutUpdates(potentialUpdate: PotentialUpdate) {
+ if (hasNotified) return
+ hasNotified = true
+ if (!potentialUpdate.isUpdateAvailable) return
+ logger.printOut(
+ ChatComponentText("§aThere is a new update for LocalTransactionLedger. Click here to automatically download and install it.")
+ .setChatStyle(ChatStyle().setChatClickEvent(triggerCommand.getTriggerCommandLine(installTrigger))))
+ if (config.main.updateCheck == MainOptions.UpdateCheckBehaviour.FULL) {
+ downloadUpdate()
+ }
+ }
+
+ var updateFuture: CompletableFuture<Void>? = null
+
+ fun downloadUpdate() {
+ val l = latestUpdate ?: return
+ if (updateFuture != null) return
+ // TODO: inject into findAsset to overwrite the tag id with the commit id
+ logger.printOut("§aTrying to download ledger update ${l.update.versionName}")
+ updateFuture =
+ latestUpdate?.launchUpdate()
+ ?.thenAcceptAsync(
+ {
+ logger.printOut("§aLedger update downloaded. It will automatically apply after your next restart.")
+ }, minecraftExecutor)
+ ?.let(errorUtil::listenToFuture)
+ }
+
+ @SubscribeEvent
+ fun onTrigger(event: TriggerEvent) {
+ if (event.action == installTrigger) {
+ event.isCanceled = true
+ downloadUpdate()
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt b/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
index 344c8bc..e0c83f9 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/ErrorUtil.kt
@@ -4,6 +4,7 @@ import moe.nea.ledger.utils.di.Inject
import moe.nea.ledger.utils.telemetry.ContextValue
import moe.nea.ledger.utils.telemetry.EventRecorder
import moe.nea.ledger.utils.telemetry.Span
+import java.util.concurrent.CompletableFuture
class ErrorUtil {
@@ -27,6 +28,14 @@ class ErrorUtil {
return getOrNull()
}
+ fun <T : CompletableFuture<*>> listenToFuture(t: T): T {
+ t.handle { ignored, exception ->
+ if (exception != null)
+ report(exception, "Uncaught exception in completable future")
+ }
+ return t
+ }
+
inline fun <T> catch(
vararg pairs: Pair<String, ContextValue>,
crossinline function: () -> T
diff --git a/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt b/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt
new file mode 100644
index 0000000..affd86c
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/utils/MinecraftExecutor.kt
@@ -0,0 +1,10 @@
+package moe.nea.ledger.utils
+
+import net.minecraft.client.Minecraft
+import java.util.concurrent.Executor
+
+class MinecraftExecutor : Executor {
+ override fun execute(command: Runnable) {
+ Minecraft.getMinecraft().addScheduledTask(command)
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt b/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
index 133637a..a9061d7 100644
--- a/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
+++ b/src/main/kotlin/moe/nea/ledger/utils/di/DI.kt
@@ -9,25 +9,32 @@ class DI {
private fun formatInjectionStack() =
injectionStack.joinToString(" -> ")
+ fun <T : Any> getProvider(type: Class<T>): BaseDIProvider<T, *> {
+ val provider = providers[type] as BaseDIProvider<T, *>?
+ ?: error("Could not find provider for type $type")
+ return provider
+ }
+
private fun <T : Any, C> internalProvide(type: Class<T>, element: AnnotatedElement? = null): T {
- val provider = providers[type] as BaseDIProvider<T, C>
- 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: ${formatInjectionStack()} -> $type")
- }
- injectionStack.push(type)
- val value = try {
- provider.provideWithContext(this, context)
+ try {
+ val provider = getProvider(type) as BaseDIProvider<T, C>
+ 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: ${formatInjectionStack()} -> $type")
+ }
+ injectionStack.push(type)
+ val value =
+ provider.provideWithContext(this, context)
+ val cycleCheckCookie = injectionStack.pop()
+ require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" }
+ values[key] = value
+ return value
} catch (ex: Exception) {
throw RuntimeException("Could not create instance for type $type (in stack ${formatInjectionStack()})", ex)
}
- val cycleCheckCookie = injectionStack.pop()
- require(cycleCheckCookie == type) { "Unbalanced stack cookie: $cycleCheckCookie != $type" }
- values[key] = value
- return value
}
fun <T : Any> provide(type: Class<T>, element: AnnotatedElement? = null): T {