package moe.nea.ledger import com.google.gson.Gson import com.google.gson.JsonArray import moe.nea.ledger.database.DBItemEntry import moe.nea.ledger.database.DBLogEntry import moe.nea.ledger.database.Database import moe.nea.ledger.events.ChatReceived import moe.nea.ledger.utils.di.Inject import net.minecraft.client.Minecraft import net.minecraft.util.ChatComponentText import net.minecraft.util.IChatComponent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent import java.io.File import java.text.SimpleDateFormat import java.util.Date import java.util.UUID class LedgerLogger { fun printOut(text: String) = printOut(ChatComponentText(text)) fun printOut(comp: IChatComponent) { Minecraft.getMinecraft().ingameGUI?.chatGUI?.printChatMessage(comp) } val profileIdPattern = "Profile ID: (?<profile>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})".toPattern() var currentProfile: UUID? = null var shouldLog by Ledger.managedConfig.instance.debug::logEntries @Inject lateinit var database: Database @SubscribeEvent fun onProfileSwitch(event: ChatReceived) { profileIdPattern.useMatcher(event.message) { currentProfile = UUID.fromString(group("profile")) } } fun printToChat(entry: LedgerEntry) { val items = entry.items.joinToString("\n§e") { " - ${it.direction} ${it.count}x ${it.itemId}" } printOut( """ §e================= TRANSACTION START §eTYPE: §a${entry.transactionType} §eTIMESTAMP: §a${entry.timestamp} §e%s §ePROFILE: §a${currentProfile} §e================= TRANSACTION END """.trimIndent().replace("%s", items) ) } val entries = JsonArray() var hasRecentlyMerged = false var lastMergeTime = System.currentTimeMillis() fun doMerge() { val allFiles = folder.listFiles()?.toList() ?: emptyList() val mergedJson = allFiles .filter { it.name != "merged.json" && it.extension == "json" } .sortedDescending() .map { it.readText().trim().removePrefix("[").removeSuffix("]") } .joinToString(",", "[", "]") folder.resolve("merged.json").writeText(mergedJson) hasRecentlyMerged = true } init { Runtime.getRuntime().addShutdownHook(Thread { doMerge() }) } @SubscribeEvent fun onTick(event: ClientTickEvent) { if (!hasRecentlyMerged && (System.currentTimeMillis() - lastMergeTime) > 60_000L) { lastMergeTime = System.currentTimeMillis() doMerge() } } fun logEntry(entry: LedgerEntry) { if (shouldLog) printToChat(entry) Ledger.logger.info("Logging entry of type ${entry.transactionType}") val transactionId = UUIDUtil.createULIDAt(entry.timestamp) DBLogEntry.insert(database.connection) { it[DBLogEntry.profileId] = currentProfile ?: UUIDUtil.NIL_UUID it[DBLogEntry.playerId] = UUIDUtil.getPlayerUUID() it[DBLogEntry.type] = entry.transactionType it[DBLogEntry.transactionId] = transactionId } entry.items.forEach { change -> DBItemEntry.insert(database.connection) { it[DBItemEntry.transactionId] = transactionId it[DBItemEntry.mode] = change.direction it[DBItemEntry.size] = change.count it[DBItemEntry.itemId] = change.itemId } } entries.add(entry.intoJson(currentProfile)) commit() } fun commit() { try { hasRecentlyMerged = false file.writeText(gson.toJson(entries)) } catch (ex: Exception) { Ledger.logger.error("Could not save file", ex) } } val gson = Gson() val folder = Ledger.dataFolder val file: File = run { val date = SimpleDateFormat("yyyy.MM.dd").format(Date()) generateSequence(0) { it + 1 } .map { if (it == 0) folder.resolve("$date.json") else folder.resolve("$date-$it.json") } .filter { !it.exists() } .first() } }