diff --git a/src/main/kotlin/net/taehui/twilight/qwilight/QwilightAvatar.kt b/src/main/kotlin/net/taehui/twilight/qwilight/QwilightAvatar.kt index 0cf1120..d6db2fb 100644 --- a/src/main/kotlin/net/taehui/twilight/qwilight/QwilightAvatar.kt +++ b/src/main/kotlin/net/taehui/twilight/qwilight/QwilightAvatar.kt @@ -843,7 +843,11 @@ it, targetComputing.noteID, isPaused, - inputFlags + inputFlags, + comment.audioMultipliersList.map { it.audioMultiplier } + .plus(audioMultiplier).min(), + comment.audioMultipliersList.map { it.audioMultiplier } + .plus(audioMultiplier).max(), ) if (commentID != null) { val commentEntryPath = TwilightComponent.COMMENT_ENTRY_PATH.resolve(noteID512) diff --git a/src/main/kotlin/net/taehui/twilight/system/DB.kt b/src/main/kotlin/net/taehui/twilight/system/DB.kt index ff0421c..0ce1329 100644 --- a/src/main/kotlin/net/taehui/twilight/system/DB.kt +++ b/src/main/kotlin/net/taehui/twilight/system/DB.kt @@ -1,13 +1,17 @@ package net.taehui.twilight.system +import CommentKt +import CommentOuterClass import com.fasterxml.jackson.annotation.JsonIgnore import net.taehui.twilight.* import net.taehui.twilight.BundleIO.BundleVariety import net.taehui.twilight.Component import net.taehui.twilight.qwilight.QwilightAvatar +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream import org.apache.commons.dbcp2.BasicDataSource import org.apache.commons.io.FileUtils import org.apache.commons.io.FilenameUtils +import org.apache.commons.io.IOUtils import java.io.IOException import java.nio.file.Files import java.nio.file.Path @@ -18,8 +22,10 @@ import java.time.format.DateTimeFormatter import java.util.* import java.util.concurrent.CompletableFuture +import java.util.concurrent.atomic.AtomicInteger import java.util.stream.Stream import kotlin.collections.ArrayList +import kotlin.io.path.inputStream import kotlin.math.pow object DB : Logger { @@ -87,7 +93,9 @@ Comment_ID CHAR(128) NOT NULL, Is_Max BOOLEAN, Is_Paused BOOLEAN, - Input_Flags INTEGER, + Input_Flags INTEGER NOT NULL, + Lowest_Audio_Multiplier REAL, + Highest_Audio_Multiplier REAL, FOREIGN KEY (Avatar) REFERENCES tn_avatar(Avatar_ID) ON UPDATE CASCADE ON DELETE CASCADE, KEY (Date), KEY (Note_ID), @@ -98,21 +106,6 @@ KEY (Stand), KEY (Comment_ID), KEY (Is_Max), - CHECK (Multiplier >= 0.0), - CHECK (Auto_Mode IN (0, 1)), - CHECK (Note_Salt_Mode IN (0, 1, 2, 4, 11, 13)), - CHECK (Audio_Multiplier >= 0.5 AND Audio_Multiplier <= 2.0), - CHECK (Faint_Note_Mode IN (0, 1, 2, 3)), - CHECK (Judgment_Mode IN (0, 1, 2, 3, 4)), - CHECK (Hit_Points_Mode IN (0, 1, 2, 3, 4, 5)), - CHECK (Note_Mobility_Mode IN (0, 1, 3, 4, 5)), - CHECK (Long_Note_Mode IN (0, 1)), - CHECK (Input_Favor_Mode IN (0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)), - CHECK (Note_Modify_Mode IN (0, 1)), - CHECK (Lowest_Judgment_Condition_Mode IN (0, 1)), - CHECK (Stand >= 0 AND Stand <= 1000000), - CHECK (Band >= 0), - CHECK (Point >= 0.0 AND Point <= 1.0), CHECK (Input_Flags >= 0 AND Input_Flags <= 15) ) COLLATE=utf8mb4_general_ci ENGINE=InnoDB """.trimIndent() @@ -492,14 +485,16 @@ } } - fun wipeNotes() { - pool.connection.use { - it.prepareStatement( - """ + fun wipeNotes(): CompletableFuture { + return logFuture { + pool.connection.use { + it.prepareStatement( + """ DELETE FROM tw_note """.trimIndent() - ).use { dbStatement -> dbStatement.execute() } + ).use { dbStatement -> dbStatement.execute() } + } } } @@ -638,6 +633,76 @@ } } + fun learnComments(futureLearnCommentsStatus: AtomicInteger): CompletableFuture { + return logFuture { + pool.connection.use { + Files.newDirectoryStream(TwilightComponent.COMMENT_ENTRY_PATH).use { commentEntryPaths -> + commentEntryPaths.forEach { commentEntryPath -> + Files.list(commentEntryPath).use { commentFilePaths -> + commentFilePaths.forEach { commentFilePath -> + val commentID = FilenameUtils.removeExtension(commentFilePath.fileName.toString()) + it.prepareStatement( + """ + SELECT Audio_Multiplier + FROM tw_comment + WHERE Comment_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setString(1, commentID) + dbStatement.executeQuery().use { rows -> + if (rows.next()) { + val audioMultiplier = rows.getDouble("Audio_Multiplier") + val comment = + XZCompressorInputStream( + Files.readAllBytes(commentFilePath).inputStream() + ).use { + CommentOuterClass.Comment.parseFrom(it) + } + if (comment.audioMultipliersCount > 0) { + it.prepareStatement( + """ + UPDATE tw_comment + SET Lowest_Audio_Multiplier = ?, Highest_Audio_Multiplier = ? + WHERE Comment_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setDouble( + 1, + comment.audioMultipliersList.map { it.audioMultiplier } + .plus(audioMultiplier).min() + ) + dbStatement.setDouble( + 2, + comment.audioMultipliersList.map { it.audioMultiplier } + .plus(audioMultiplier).max() + ) + dbStatement.setString(3, commentID) + dbStatement.executeUpdate() + } + } else { + it.prepareStatement( + """ + UPDATE tw_comment + SET Lowest_Audio_Multiplier = Audio_Multiplier, Highest_Audio_Multiplier = Audio_Multiplier + WHERE Comment_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setString(1, commentID) + dbStatement.executeUpdate() + } + } + } + } + } + futureLearnCommentsStatus.incrementAndGet() + } + } + } + } + } + } + } + fun getWipeBundles(): CompletableFuture> { return logValueFuture { data class BundleID(val avatarID: String, val bundleName: String) @@ -1677,7 +1742,9 @@ avatarID: String, noteID: String, isPaused: Boolean, - inputFlags: Int + inputFlags: Int, + lowestAudioMultiplier: Double, + highestAudioMultiplier: Double ): String? { var commentID: String? = null pool.connection.use { @@ -1716,7 +1783,7 @@ it.prepareStatement( """ INSERT INTO tw_comment - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """.trimIndent() ).use { dbStatement -> dbStatement.setTimestamp(1, Timestamp(System.currentTimeMillis())) @@ -1743,6 +1810,8 @@ dbStatement.setBoolean(22, commentID != null) dbStatement.setBoolean(23, isPaused) dbStatement.setInt(24, inputFlags) + dbStatement.setDouble(25, lowestAudioMultiplier) + dbStatement.setDouble(26, highestAudioMultiplier) dbStatement.execute() } it.commit() diff --git a/src/main/kotlin/net/taehui/twilight/system/IO.kt b/src/main/kotlin/net/taehui/twilight/system/IO.kt index 214a319..a334645 100644 --- a/src/main/kotlin/net/taehui/twilight/system/IO.kt +++ b/src/main/kotlin/net/taehui/twilight/system/IO.kt @@ -16,6 +16,8 @@ object IO : Logger { private val futureLearnNotesStatus = AtomicInteger() private var futureLearnNotes: CompletableFuture? = null + private val futureLearnCommentsStatus = AtomicInteger() + private var futureLearnComments: CompletableFuture? = null private var futureWipeNotes: Collection? = null private var futurePMSNotes: Collection? = null private var futureWipeComments: Collection? = null @@ -184,6 +186,22 @@ "comment" -> { if (w.size > 1) { when (w[1]) { + "learn" -> { + if (futureLearnComments?.isDone != false) { + futureLearnComments = + DB.learnComments(futureLearnCommentsStatus).thenAccept { + logInfo( + "Learned ${futureLearnCommentsStatus.get()} Comments" + ) + } + } else { + logInfo( + "Learning ${futureLearnCommentsStatus.get()} Comments" + ) + } + continue + } + "wipe" -> { DB.getWipeComments().thenAccept { if (it.isNotEmpty()) { @@ -195,6 +213,8 @@ } } } + + logInfo("comment [learn] Learn Comments") logInfo("comment [wipe] Clean Comment Files") } @@ -202,57 +222,43 @@ if (w.size > 1) { when (w[1]) { "learn" -> { - fun learnNoteFile(noteFilePath: Path): Boolean { - return try { - val noteFileData = Files.readAllBytes(noteFilePath) - BaseCompiler.handleCompile(noteFileData, -1).forEach { - DB.setNote( - it.noteID, - Utility.getID128(noteFileData), - Utility.getID256(noteFileData), - it + if (futureLearnNotes?.isDone != false) { + futureLearnNotes = DB.wipeNotes().thenAccept { + try { + Files.newDirectoryStream( + TwilightComponent.NOTE_ENTRY_PATH + ).use { noteEntryPaths -> + StreamSupport.stream(noteEntryPaths.spliterator(), true) + .forEach { noteFilePath -> + try { + val noteFileData = Files.readAllBytes(noteFilePath) + BaseCompiler.handleCompile(noteFileData, -1) + .forEach { + DB.setNote( + it.noteID, + Utility.getID128(noteFileData), + Utility.getID256(noteFileData), + it + ) + } + futureLearnNotesStatus.incrementAndGet() + } catch (e: Throwable) { + logFault(e) + } + } + } + } finally { + logInfo( + "Learned ${futureLearnNotesStatus.get()} Notes" ) + futureLearnNotesStatus.set(0) + NoteFilesSystem.loadNoteFiles() } - true - } catch (e: Throwable) { - logFault(e) - false - } - } - - if (w.size > 2) { - if (learnNoteFile(Path.of(w[2]))) { - logInfo("Learned Note File") - NoteFilesSystem.loadNoteFiles() } } else { - if (futureLearnNotes?.isDone != false) { - futureLearnNotes = logFuture { - DB.wipeNotes() - try { - Files.newDirectoryStream( - TwilightComponent.NOTE_ENTRY_PATH - ).use { - StreamSupport.stream(it.spliterator(), true) - .forEach { noteFilePath -> - if (learnNoteFile(noteFilePath)) { - futureLearnNotesStatus.incrementAndGet() - } - } - } - } finally { - logInfo( - "Learned ${futureLearnNotesStatus.get()} Note Files" - ) - futureLearnNotesStatus.set(0) - NoteFilesSystem.loadNoteFiles() - } - } - } else { - logInfo( - "Learning ${futureLearnNotesStatus.get()} Note Files" - ) - } + logInfo( + "Learning ${futureLearnNotesStatus.get()} Notes" + ) } continue } @@ -304,7 +310,7 @@ } } } - logInfo("note [learn] Learn Notes") + logInfo("note [learn] Learn Notes") logInfo("note [ban] Ban Note") logInfo("note [allow] Allow Note") logInfo("note [wipe] Clean or Wipe Note File")