aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/moe')
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemChange.kt1
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt25
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemUtil.kt22
-rw-r--r--src/main/kotlin/moe/nea/ledger/Ledger.kt12
-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/modules/MineshaftCorpseDetection.kt6
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt52
-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
16 files changed, 413 insertions, 23 deletions
diff --git a/src/main/kotlin/moe/nea/ledger/ItemChange.kt b/src/main/kotlin/moe/nea/ledger/ItemChange.kt
index 186a4e3..fda709c 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemChange.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemChange.kt
@@ -59,6 +59,7 @@ data class ItemChange(
}
fun unpairGain(pair: Pair<ItemId, Double>) = unpair(ChangeDirection.GAINED, pair)
+ fun unpairLose(pair: Pair<ItemId, Double>) = unpair(ChangeDirection.LOST, pair)
fun gain(itemId: ItemId, amount: Number): ItemChange {
return ItemChange(itemId, amount.toDouble(), ChangeDirection.GAINED)
diff --git a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
index 30610e9..7fe0206 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
@@ -29,16 +29,24 @@ class ItemIdProvider {
private val knownNames = mutableMapOf<String, ItemId>()
+ fun createLookupTagFromDisplayName(itemName: String): String {
+ return itemName.unformattedString().trim().lowercase()
+ }
+
+ fun saveKnownItem(itemName: String, itemId: ItemId) {
+ knownNames[createLookupTagFromDisplayName(itemName)] = itemId
+ }
+
@SubscribeEvent
fun onDataLoaded(event: ExternalDataProvider.DataLoaded) {
event.provider.itemNames.forEach { (itemId, itemName) ->
- knownNames[itemName.unformattedString().trim()] = ItemId(itemId)
+ saveKnownItem(itemName, ItemId(itemId))
}
}
@SubscribeEvent
fun onRegistrationFinished(event: RegistrationFinishedEvent) {
- MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(knownNames::put))
+ MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(::saveKnownItem))
}
@SubscribeEvent(priority = EventPriority.HIGH)
@@ -63,7 +71,7 @@ class ItemIdProvider {
name = name.trim()
val id = stack.getInternalId()
if (id != null && name.isNotBlank()) {
- knownNames[name] = id
+ saveKnownItem(name, id)
}
}
@@ -84,7 +92,7 @@ class ItemIdProvider {
// TODO: make use of colour
fun findForName(name: String, fallbackToGenerated: Boolean = true): ItemId? {
- var id = knownNames[name]
+ var id = knownNames[createLookupTagFromDisplayName(name)]
if (id == null && fallbackToGenerated) {
id = generateName(name)
}
@@ -98,6 +106,7 @@ class ItemIdProvider {
private val coinRegex = "(?<amount>$SHORT_NUMBER_PATTERN) Coins?".toPattern()
private val stackedItemRegex = "(?<name>.*) x(?<count>$SHORT_NUMBER_PATTERN)".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()
@@ -153,6 +162,14 @@ class ItemIdProvider {
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/ItemUtil.kt b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
index 949f58a..a3d8162 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
@@ -1,5 +1,6 @@
package moe.nea.ledger
+import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@@ -37,7 +38,10 @@ class PetInfo {
fun ItemStack.getPetId(): String? {
val petInfoStr = getExtraAttributes().getString("petInfo")
- val petInfo = runCatching { Ledger.gson.fromJson(petInfoStr, PetInfo::class.java) }.getOrNull() // TODO: error reporting to sentry
+ val petInfo = runCatching {
+ Ledger.gson.fromJson(petInfoStr,
+ PetInfo::class.java)
+ }.getOrNull() // TODO: error reporting to sentry
if (petInfo?.type == null || petInfo.tier == null) return null
return petInfo.type + ";" + rarityToIndex(petInfo.tier ?: "")
}
@@ -62,6 +66,22 @@ fun ItemStack.getLore(): List<String> {
}
+fun IInventory.asIterable(): Iterable<ItemStack?> = object : Iterable<ItemStack?> {
+ override fun iterator(): Iterator<ItemStack?> {
+ return object : Iterator<ItemStack?> {
+ var i = 0
+ override fun hasNext(): Boolean {
+ return i < this@asIterable.sizeInventory
+ }
+
+ override fun next(): ItemStack? {
+ if (!hasNext()) throw NoSuchElementException("$i is out of range for inventory ${this@asIterable}")
+ return this@asIterable.getStackInSlot(i++)
+ }
+ }
+ }
+}
+
fun ItemStack.getDisplayNameU(): String {
val nbt = this.tagCompound ?: NBTTagCompound()
val extraAttributes = nbt.getCompoundTag("display")
diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt
index bb69621..72bd32f 100644
--- a/src/main/kotlin/moe/nea/ledger/Ledger.kt
+++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt
@@ -3,6 +3,8 @@ 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
@@ -23,9 +25,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 +87,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,6 +108,7 @@ 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,
@@ -123,6 +132,9 @@ class Ledger {
ForgeDetection::class.java,
NpcDetection::class.java,
GambleDetection::class.java,
+ MinecraftExecutor::class.java,
+ UpdateChecker::class.java,
+ TriggerCommand::class.java,
QueryCommand::class.java,
RequestUtil::class.java,
VisitorDetection::class.java,
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/modules/MineshaftCorpseDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt
index 049ea39..60b06ae 100644
--- a/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt
+++ b/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt
@@ -39,7 +39,6 @@ class MineshaftCorpseDetection : BorderedTextTracker() {
}
override fun onBorderedTextFinished(enclosed: List<ChatReceived>) {
- // TODO: test this once profile swapping has been re-enabled
val rewards = enclosed.asSequence()
.dropWhile { it.message != " REWARDS" }
.drop(1)
@@ -68,7 +67,10 @@ class MineshaftCorpseDetection : BorderedTextTracker() {
}
val corpseNameToKey = mapOf(
- "LAPIS" to ItemId.NIL
+ "LAPIS" to ItemId.NIL,
+ "VANGUARD" to ItemId("SKELETON_KEY"),
+ "UMBER" to ItemId("UMBER_KEY"),
+ "TUNGSTEN" to ItemId("TUNGSTEN_KEY"),
)
@Inject
diff --git a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt
index 5906562..95b8aa5 100644
--- a/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt
+++ b/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt
@@ -7,9 +7,17 @@ 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.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
import moe.nea.ledger.parseShortNumber
+import moe.nea.ledger.unformattedString
import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.ErrorUtil
import moe.nea.ledger.utils.di.Inject
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.regex.Pattern
@@ -21,7 +29,49 @@ class NpcDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemId
val npcSellPattern =
Pattern.compile("You sold (?<what>.*) (x(?<count>$SHORT_NUMBER_PATTERN) )?for (?<coins>$SHORT_NUMBER_PATTERN) Coins?!")
- // TODO: IMPROVE BUYING FROM NPC TO INCLUDE ITEMS OTHER THAN COINS (KUUDRA KEYS ARE CHEAP)
+ // You bought InfiniDirt™ Wand!
+ // You bought Prismapump x4!
+ val npcBuyWithItemPattern =
+ "You bought (?<what>.*?)!".toPattern()
+ var storedPurchases = mutableMapOf<String, List<ItemChange>>()
+
+ @SubscribeEvent
+ fun onClick(event: BeforeGuiAction) {
+ (event.chestSlots?.lowerChestInventory?.asIterable() ?: listOf())
+ .filterNotNull().forEach {
+ val name = it.getDisplayNameU().unformattedString()
+ val id = it.getInternalId() ?: return@forEach
+ val count = it.stackSize
+ val cost = ids.findCostItemsFromSpan(it.getLore())
+ storedPurchases[name] = listOf(ItemChange.gain(id, count)) + cost.map { ItemChange.unpairLose(it) }
+ }
+ }
+
+ @SubscribeEvent
+ fun addChocolate(event: ExtraSupplyIdEvent) {
+ event.store("Chocolate", ItemId("SKYBLOCK_CHOCOLATE"))
+ }
+
+ @Inject
+ lateinit var errorUtil: ErrorUtil
+
+ @SubscribeEvent
+ fun onBarteredItemBought(event: ChatReceived) {
+ npcBuyWithItemPattern.useMatcher(event.message) {
+ val changes = storedPurchases[group("what")]
+ if (changes == null) {
+ errorUtil.reportAdHoc("Item bought for items without associated cost")
+ }
+ storedPurchases.clear()
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.NPC_BUY,
+ event.timestamp,
+ changes ?: listOf()
+ )
+ )
+ }
+ }
@SubscribeEvent
fun onNpcBuy(event: ChatReceived) {
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 {