package net.taehui.twilight.system import com.fasterxml.jackson.databind.ObjectMapper import net.taehui.twilight.JSON import net.taehui.twilight.Logger import net.taehui.twilight.TwilightComponent import net.taehui.twilight.Utility import org.apache.hc.client5.http.classic.methods.HttpGet import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler import org.apache.hc.client5.http.impl.classic.HttpClients import org.jsoup.Jsoup import java.io.IOException import java.nio.file.Files object AbilitySystem : Logger { class DefaultAbility { var abilityFiles = emptyArray<AbilityFile>() var abilityWww = emptyArray<String>() var abilityMap = emptyMap<String, Double>() class AbilityFile { var fileName = "" var abilityClassVariety = AbilityClassSystem.AbilityClassVariety.ABILITY_CLASS_5K } } class AbilityHashMap { val hashMap128 = mutableMapOf<String, Double>() val hashMap256 = mutableMapOf<String, Double>() fun wipe() { hashMap128.clear() hashMap256.clear() } } private val abilityHashMap = arrayOf(AbilityHashMap(), AbilityHashMap(), AbilityHashMap()) var isLoading = false fun loadAbility() { isLoading = true logFuture { try { val jm = ObjectMapper() val defaultAbility = jm.readValue( TwilightComponent.ABILITY_ENTRY_PATH.resolve("Default.json").toFile(), DefaultAbility::class.java ) if (Configure.mode.getAbility) { defaultAbility.abilityWww.forEach { target -> try { val o = Jsoup.connect(target).get() var dataValue = o.selectXpath("/html/head/meta[@name='bmstable']") if (dataValue.isEmpty()) { dataValue = o.selectXpath("/html/body/meta[@name='bmstable']") } if (dataValue.isEmpty()) { dataValue = o.selectXpath("/html/head/body/meta[@name='bmstable']") } if (!dataValue.isEmpty()) { val www = dataValue[0].attr("content") HttpClients.createDefault().use { fun doModifyDataValue(target: String, dataValue: String): String { return if (!Utility.isValidWww(dataValue)) { if (target.substring(target.lastIndexOf('/')) .contains(".") || target.endsWith("/") ) { "$target/../$dataValue" } else { "$target/$dataValue" } } else { dataValue } } val text = it.execute( HttpGet(doModifyDataValue(target, www)), BasicHttpClientResponseHandler() ) val abilityTable = text.byteInputStream().use { src -> ObjectMapper().readValue(src, JSON.BMSTable::class.java) } Files.writeString( TwilightComponent.ABILITY_ENTRY_PATH.resolve("#" + abilityTable.name + ".json"), text ) Files.writeString( TwilightComponent.ABILITY_ENTRY_PATH.resolve(abilityTable.name + ".json"), it.execute( HttpGet(doModifyDataValue(target, abilityTable.data_url)), BasicHttpClientResponseHandler() ) ) logInfo("Saved Ability (${abilityTable.name})") } } } catch (e: IOException) { logFault(e) } } } try { val abilityMap = defaultAbility.abilityMap abilityHashMap[AbilityClassSystem.AbilityClassVariety.ABILITY_CLASS_5K.ordinal].wipe() abilityHashMap[AbilityClassSystem.AbilityClassVariety.ABILITY_CLASS_7K.ordinal].wipe() Files.list(TwilightComponent.ABILITY_ENTRY_PATH).use { abilityFilePaths -> abilityFilePaths.filter { it.fileName.toString().startsWith("#") }.sorted( Comparator.comparingInt { abilityFilePath -> defaultAbility.abilityFiles.indexOfFirst { it.fileName == abilityFilePath.fileName.toString() } } ).forEach { abilityFilePath -> val abilityClassVariety = defaultAbility.abilityFiles.find { it.fileName == abilityFilePath.fileName.toString() }?.abilityClassVariety if (abilityClassVariety != null) { val abilityHashMap = abilityHashMap[abilityClassVariety.ordinal] val abilityTable = jm.readValue(abilityFilePath.toFile(), JSON.BMSTable::class.java) jm.readValue( TwilightComponent.ABILITY_ENTRY_PATH.resolve(abilityTable.name + ".json") .toFile(), Array<JSON.BMSTableData>::class.java ).forEach { val ability = abilityMap[abilityTable.symbol + it.level] ?: 0.0 if (ability > 0.0) { if (it.md5.isNotEmpty()) { abilityHashMap.hashMap128.putIfAbsent(it.md5, ability) } if (it.sha256.isNotEmpty()) { abilityHashMap.hashMap256.putIfAbsent(it.sha256, ability) } } } logInfo("Loaded Ability (${abilityTable.name})") } } } } catch (e: IOException) { logFault(e) } try { DB.learnAbility() logInfo("Learned Ability") } catch (e: Throwable) { logFault(e) } } finally { isLoading = false } } } fun getAbility( abilityClassVariety: AbilityClassSystem.AbilityClassVariety, noteID128: String, noteID256: String ): Double { val abilityHashMap = abilityHashMap[abilityClassVariety.ordinal] return abilityHashMap.hashMap128.getOrDefault(noteID128, abilityHashMap.hashMap256.getOrDefault(noteID256, 0.0)) } }