mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-23 13:50:12 +08:00
Rename Setting to PluginData for compatiblity issue
This commit is contained in:
parent
938db950e6
commit
0016a04d5e
@ -1,176 +0,0 @@
|
||||
package net.mamoe.n;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.mamoe.mirai.console.command.*;
|
||||
import net.mamoe.mirai.console.plugin.Config;
|
||||
import net.mamoe.mirai.console.plugin.ConfigSection;
|
||||
import net.mamoe.mirai.console.plugin.ConfigSectionFactory;
|
||||
import net.mamoe.mirai.console.plugin.PluginBase;
|
||||
import net.mamoe.mirai.console.util.Utils;
|
||||
import net.mamoe.mirai.message.GroupMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jsoup.Jsoup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class PingMyMCServerMain extends PluginBase {
|
||||
|
||||
private String defaultServerName;
|
||||
private ConfigSection serverMap;
|
||||
private Config setting;
|
||||
private String API;
|
||||
private String responseTemplate;
|
||||
|
||||
public void onLoad(){
|
||||
super.onLoad();
|
||||
|
||||
this.setting = this.loadConfig("setting.yml");
|
||||
|
||||
this.setting.setIfAbsent("API","https://api-mping.loliboy.com/ping/{address}/{port}");
|
||||
this.setting.setIfAbsent("ServerList", ConfigSectionFactory.create());
|
||||
this.setting.setIfAbsent("DefaultServerName","");
|
||||
this.setting.setIfAbsent("ResponseTemplate","Ping {serverName}: \nGame: {game}, {version}\nName: {fullName}\nPlayer: {currentPlayers}/{maxPlayers}\nConnected: {connected}\nIP: {address}:{port}");
|
||||
|
||||
|
||||
this.API = this.setting.getString("API");
|
||||
this.defaultServerName = this.setting.getString("DefaultServerName");
|
||||
this.serverMap = this.setting.getConfigSection("ServerList");
|
||||
this.responseTemplate = this.setting.getString("ResponseTemplate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
super.onDisable();
|
||||
this.setting.set("ServerList",serverMap);
|
||||
this.setting.set("DefaultServerName",defaultServerName);
|
||||
this.setting.save();
|
||||
}
|
||||
|
||||
public void onEnable(){
|
||||
this.getEventListener().subscribeAlways(GroupMessage.class, (GroupMessage event) -> {
|
||||
|
||||
String messageInString = event.getMessage().toString();
|
||||
|
||||
if(!messageInString.contains("ping ")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String serverName = messageInString.replace("ping ", "").toLowerCase().trim();
|
||||
|
||||
if(!this.serverMap.containsKey(serverName)) {
|
||||
serverName = this.defaultServerName;
|
||||
}
|
||||
|
||||
if(!this.serverMap.containsKey(serverName)){
|
||||
event.getSubject().sendMessage("Bot管理员没有设置任何可ping的服务器, 请使用/mcserver 来增加");
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigSection serverInfo = this.serverMap.getConfigSection(serverName);
|
||||
|
||||
final String serverName_ = serverName;
|
||||
|
||||
getScheduler().async(() -> {
|
||||
event.getSubject().sendMessage("正在获取中..");
|
||||
try {
|
||||
String response = Utils.tryNTimes(2, () ->
|
||||
Jsoup.connect(API
|
||||
.replace("{address}", serverInfo.getString("address"))
|
||||
.replace("{port}", serverInfo.getString("port"))
|
||||
).ignoreContentType(true).timeout(8000).execute().body()
|
||||
);
|
||||
JsonObject resObj = JsonParser.parseString(response).getAsJsonObject();
|
||||
JsonObject addressObj = resObj.get("rinfo").getAsJsonObject();
|
||||
event.getSubject().sendMessage(this.responseTemplate
|
||||
.replace("{connected}",resObj.get("connected").getAsString())
|
||||
.replace("{currentPlayers}",resObj.get("currentPlayers").getAsString())
|
||||
.replace("{maxPlayers}",resObj.get("maxPlayers").getAsString())
|
||||
.replace("{serverName}",serverName_)
|
||||
.replace("{fullName}",resObj.get("cleanName").getAsString())
|
||||
.replace("{game}",resObj.get("game").getAsString())
|
||||
.replace("{version}",resObj.get("version").getAsString())
|
||||
.replace("{address}",addressObj.get("address").getAsString())
|
||||
.replace("{port}",addressObj.get("port").getAsString())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
event.getSubject().sendMessage("获取失败.." + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
JCommandManager.getInstance().register(this, new BlockingCommand(
|
||||
"mcserver", new ArrayList<>(),"管理可以ping的MC服务器","/mcserver add/remove"
|
||||
) {
|
||||
@Override
|
||||
public boolean onCommandBlocking(@NotNull CommandSender commandSender, @NotNull List<String> list) {
|
||||
if(list.size() < 1){
|
||||
return false;
|
||||
}
|
||||
switch (list.get(0)){
|
||||
case "add":
|
||||
if(list.size() < 4){
|
||||
commandSender.sendMessageBlocking("/mcserver add 服务器名字 IP 端口");
|
||||
return true;
|
||||
}
|
||||
String serverName = list.get(1);
|
||||
|
||||
String IP = list.get(2);
|
||||
|
||||
int port = -1;
|
||||
try {
|
||||
port = Integer.parseInt(list.get(3));
|
||||
}catch (Exception e){
|
||||
commandSender.sendMessageBlocking("无法识别端口号");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(port < 0 || port > 65535){
|
||||
commandSender.sendMessageBlocking("无法识别端口号[0-65535]");
|
||||
return true;
|
||||
}
|
||||
if(IP.contains(":")){
|
||||
commandSender.sendMessageBlocking("IP中不应包含端口");
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigSection data = ConfigSectionFactory.create();
|
||||
|
||||
data.set("address",IP);
|
||||
data.set("port",port);
|
||||
|
||||
if(serverMap.size() == 0){
|
||||
defaultServerName = serverName;
|
||||
}
|
||||
|
||||
serverMap.put(serverName.toLowerCase(),data);
|
||||
commandSender.sendMessageBlocking("设置成功, 发送ping " + serverName + " 即可");
|
||||
|
||||
break;
|
||||
case "remove":
|
||||
if(list.size() < 2){
|
||||
commandSender.sendMessageBlocking("/mcserver remove 服务器名字");
|
||||
return true;
|
||||
}
|
||||
String serverNameToRemove = list.get(1).toLowerCase();
|
||||
if(serverMap.containsKey(serverNameToRemove)){
|
||||
serverMap.remove(serverNameToRemove);
|
||||
commandSender.sendMessageBlocking("移除成功");
|
||||
}else{
|
||||
commandSender.sendMessageBlocking("没有找到" + list.get(1) + "的数据");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
this.getLogger().info("PingMyMCServer Enabled");
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("PRE_RELEASE_CLASS", "ClassName", "RedundantVisibilityModifier")
|
||||
@file:Suppress("PRE_RELEASE_CLASS", "ClassName", "RedundantVisibilityModifier", "KDocUnresolvedReference")
|
||||
|
||||
package net.mamoe.mirai.console.codegen
|
||||
|
||||
@ -15,7 +15,7 @@ import kotlin.reflect.full.functions
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
internal object ValueSettingCodegen {
|
||||
internal object ValuePluginDataCodegen {
|
||||
/**
|
||||
* The interface
|
||||
*/
|
||||
@ -37,7 +37,7 @@ internal object ValueSettingCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
object BuiltInSerializerConstantsPrimitivesCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke {
|
||||
object BuiltInSerializerConstantsPrimitivesCodegen : RegionCodegen("_PluginData.value.kt"), DefaultInvoke {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = super.startIndependently()
|
||||
override val defaultInvokeArgs: List<KtType> = KtPrimitives + KtString
|
||||
@ -99,7 +99,7 @@ internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName}
|
||||
|
||||
}
|
||||
|
||||
object Setting_value_PrimitivesImplCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke {
|
||||
object PluginData_value_PrimitivesImplCodegen : RegionCodegen("_PluginData.value.kt"), DefaultInvoke {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = super.startIndependently()
|
||||
override val defaultInvokeArgs: List<KtType> = KtPrimitives + KtString
|
||||
@ -107,12 +107,12 @@ internal abstract class ${ktType.standardName}ValueImpl : ${ktType.standardName}
|
||||
override fun StringBuilder.apply(ktType: KtType) {
|
||||
appendKCode(
|
||||
"""
|
||||
internal fun Setting.valueImpl(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> {
|
||||
internal fun PluginData.valueImpl(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> {
|
||||
return object : ${ktType.standardName}ValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${ktType.standardName}> {
|
||||
internal fun PluginData.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${ktType.standardName}> {
|
||||
return object : ${ktType.standardName}ValueImpl() {
|
||||
override fun onChanged() = this@${ktType.lowerCaseName}ValueImpl.onValueChanged(this)
|
||||
}
|
||||
@ -122,7 +122,7 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${
|
||||
}
|
||||
}
|
||||
|
||||
object Setting_valueImplPrimitiveCodegen : RegionCodegen("_Setting.value.kt"), DefaultInvoke {
|
||||
object PluginData_valueImplPrimitiveCodegen : RegionCodegen("_PluginData.value.kt"), DefaultInvoke {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = super.startIndependently()
|
||||
override val defaultInvokeArgs: List<KtType> = KtPrimitives + KtString
|
||||
@ -136,7 +136,7 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${
|
||||
}
|
||||
}
|
||||
|
||||
object Setting_value_primitivesCodegen : RegionCodegen("Setting.kt"), DefaultInvoke {
|
||||
object PluginData_value_primitivesCodegen : RegionCodegen("PluginData.kt"), DefaultInvoke {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = super.startIndependently()
|
||||
override val defaultInvokeArgs: List<KtType> = KtPrimitives + KtString
|
||||
@ -145,9 +145,13 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${
|
||||
@Suppress("unused")
|
||||
appendKCode(
|
||||
"""
|
||||
public fun Setting.value(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> = valueImpl(default)
|
||||
/**
|
||||
* 创建一个 [${ktType.standardName}] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: ${ktType.standardName}): SerializerAwareValue<${ktType.standardName}> = valueImpl(default)
|
||||
"""
|
||||
)
|
||||
appendLine()
|
||||
}
|
||||
|
||||
}
|
||||
@ -158,7 +162,7 @@ internal fun Setting.${ktType.lowerCaseName}ValueImpl(): SerializerAwareValue<${
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
ValueSettingCodegen::class.nestedClasses
|
||||
ValuePluginDataCodegen::class.nestedClasses
|
||||
.filter { it.isSubclassOf(RegionCodegen::class) }
|
||||
.associateWith { kClass -> kClass.functions.find { it.name == "main" && it.hasAnnotation<JvmStatic>() } }
|
||||
.filter { it.value != null }
|
||||
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.codegen.old
|
||||
|
||||
/**
|
||||
* used to generate Java Setting
|
||||
* used to generate Java PluginData
|
||||
*/
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ internal val J_EXTRA = listOf(
|
||||
|
||||
fun JClazz.getTemplate(): String = """
|
||||
@NotNull default Value<${this.packageName}> $funName(${this.primitiveName} defaultValue){
|
||||
return _SettingKt.value(this,defaultValue);
|
||||
return _PluginDataKt.value(this,defaultValue);
|
||||
}
|
||||
"""
|
||||
|
||||
@ -61,7 +61,7 @@ fun main() {
|
||||
appendLine()
|
||||
appendLine(
|
||||
"/**\n" +
|
||||
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
|
||||
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JPluginDataCodegen.kt\n" +
|
||||
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
|
||||
" */\n" +
|
||||
"\"\"\""
|
||||
|
@ -18,7 +18,7 @@ import java.io.File
|
||||
fun main() {
|
||||
println(File("").absolutePath) // default project base dir
|
||||
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt").apply {
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/_PluginData.kt").apply {
|
||||
createNewFile()
|
||||
}.writeText(buildString {
|
||||
appendLine(COPYRIGHT)
|
||||
@ -39,20 +39,20 @@ fun main() {
|
||||
|
||||
private val DO_NOT_MODIFY = """
|
||||
/**
|
||||
* !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt
|
||||
* !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.PluginDataValueUseSiteCodegen.kt
|
||||
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
||||
*/
|
||||
""".trimIndent()
|
||||
|
||||
private val PACKAGE = """
|
||||
package net.mamoe.mirai.console.setting
|
||||
package net.mamoe.mirai.console.data
|
||||
""".trimIndent()
|
||||
|
||||
internal val FILE_SUPPRESS = """
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused")
|
||||
""".trimIndent()
|
||||
private val IMPORTS = """
|
||||
import net.mamoe.mirai.console.setting.internal.valueImpl
|
||||
import net.mamoe.mirai.console.data.internal.valueImpl
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
""".trimIndent()
|
||||
|
||||
@ -104,7 +104,7 @@ fun genAllValueUseSite(): String = buildString {
|
||||
appendln(
|
||||
"""
|
||||
@JvmName("valueMutable")
|
||||
fun Setting.value(default: Mutable${collectionName}<${number}>): Mutable${number}${collectionName}Value = valueImpl(default)
|
||||
fun PluginData.value(default: Mutable${collectionName}<${number}>): Mutable${number}${collectionName}Value = valueImpl(default)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
@ -114,30 +114,30 @@ fun genAllValueUseSite(): String = buildString {
|
||||
appendLine()
|
||||
appendln(
|
||||
"""
|
||||
fun <T : Setting> Setting.value(default: T): Value<T> {
|
||||
fun <T : PluginData> PluginData.value(default: T): Value<T> {
|
||||
require(this::class != default::class) {
|
||||
"Recursive nesting is prohibited"
|
||||
}
|
||||
return valueImpl(default).also {
|
||||
if (default is Setting.NestedSetting) {
|
||||
if (default is PluginData.NestedPluginData) {
|
||||
default.attachedValue = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T : Setting> Setting.value(default: T, crossinline initializer: T.() -> Unit): Value<T> =
|
||||
inline fun <T : PluginData> PluginData.value(default: T, crossinline initializer: T.() -> Unit): Value<T> =
|
||||
value(default).also { it.value.apply(initializer) }
|
||||
|
||||
inline fun <reified T : Setting> Setting.value(default: List<T>): SettingListValue<T> = valueImpl(default)
|
||||
inline fun <reified T : PluginData> PluginData.value(default: List<T>): PluginDataListValue<T> = valueImpl(default)
|
||||
|
||||
@JvmName("valueMutable")
|
||||
inline fun <reified T : Setting> Setting.value(default: MutableList<T>): MutableSettingListValue<T> = valueImpl(default)
|
||||
inline fun <reified T : PluginData> PluginData.value(default: MutableList<T>): MutablePluginDataListValue<T> = valueImpl(default)
|
||||
|
||||
|
||||
inline fun <reified T : Setting> Setting.value(default: Set<T>): SettingSetValue<T> = valueImpl(default)
|
||||
inline fun <reified T : PluginData> PluginData.value(default: Set<T>): PluginDataSetValue<T> = valueImpl(default)
|
||||
|
||||
@JvmName("valueMutable")
|
||||
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
|
||||
inline fun <reified T : PluginData> PluginData.value(default: MutableSet<T>): MutablePluginDataSetValue<T> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个只引用对象而不跟踪其属性的值.
|
||||
@ -147,7 +147,7 @@ fun genAllValueUseSite(): String = buildString {
|
||||
@DangerousReferenceOnlyValue
|
||||
@JvmName("valueDynamic")
|
||||
@LowPriorityInOverloadResolution
|
||||
inline fun <reified T : Any> Setting.value(default: T): Value<T> = valueImpl(default)
|
||||
inline fun <reified T : Any> PluginData.value(default: T): Value<T> = valueImpl(default)
|
||||
|
||||
@RequiresOptIn(
|
||||
""${'"'}
|
||||
@ -164,6 +164,6 @@ fun genAllValueUseSite(): String = buildString {
|
||||
|
||||
fun genValueUseSite(kotlinTypeName: String, miraiValueName: String): String =
|
||||
"""
|
||||
fun Setting.value(default: $kotlinTypeName): ${miraiValueName}Value = valueImpl(default)
|
||||
fun PluginData.value(default: $kotlinTypeName): ${miraiValueName}Value = valueImpl(default)
|
||||
""".trimIndent()
|
||||
|
||||
|
@ -18,7 +18,7 @@ import java.io.File
|
||||
fun main() {
|
||||
println(File("").absolutePath) // default project base dir
|
||||
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt").apply {
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/internal/_ValueImpl.kt").apply {
|
||||
createNewFile()
|
||||
}.writeText(buildString {
|
||||
appendLine(COPYRIGHT)
|
||||
@ -43,13 +43,13 @@ private val DO_NOT_MODIFY = """
|
||||
""".trimIndent()
|
||||
|
||||
private val PACKAGE = """
|
||||
package net.mamoe.mirai.console.setting.internal
|
||||
package net.mamoe.mirai.console.data.internal
|
||||
""".trimIndent()
|
||||
|
||||
private val IMPORTS = """
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.*
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
import net.mamoe.mirai.console.data.*
|
||||
""".trimIndent()
|
||||
|
||||
fun genAllValueImpl(): String = buildString {
|
||||
@ -121,7 +121,7 @@ fun genAllValueImpl(): String = buildString {
|
||||
appendln(
|
||||
"""
|
||||
@JvmName("valueImplMutable${number}${collectionName}")
|
||||
internal fun Setting.valueImpl(
|
||||
internal fun PluginData.valueImpl(
|
||||
default: Mutable${collectionName}<${number}>
|
||||
): Mutable${number}${collectionName}Value {
|
||||
var internalValue: Mutable${collectionName}<${number}> = default
|
||||
@ -166,8 +166,8 @@ fun genAllValueImpl(): String = buildString {
|
||||
|
||||
appendln(
|
||||
"""
|
||||
internal fun <T : Setting> Setting.valueImpl(default: T): Value<T> {
|
||||
return object : SettingValue<T>() {
|
||||
internal fun <T : PluginData> PluginData.valueImpl(default: T): Value<T> {
|
||||
return object : PluginDataValue<T>() {
|
||||
private var internalValue: T = default
|
||||
override var value: T
|
||||
get() = internalValue
|
||||
@ -187,7 +187,7 @@ fun genAllValueImpl(): String = buildString {
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: T) {
|
||||
internalValue.updaterSerializer.serialize(encoder, SettingSerializerMark)
|
||||
internalValue.updaterSerializer.serialize(encoder, PluginDataSerializerMark)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,20 +203,20 @@ fun genPrimitiveValueImpl(
|
||||
isArray: Boolean
|
||||
): String =
|
||||
"""
|
||||
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||
internal fun PluginData.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||
return object : ${miraiValueName}Value() {
|
||||
private var internalValue: $kotlinTypeName = default
|
||||
override var value: $kotlinTypeName
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
${
|
||||
if (isArray) """
|
||||
if (isArray) """
|
||||
if (!new.contentEquals(internalValue)) {
|
||||
internalValue = new
|
||||
onElementChanged(this)
|
||||
}
|
||||
""".trim()
|
||||
else """
|
||||
else """
|
||||
if (new != internalValue) {
|
||||
internalValue = new
|
||||
onElementChanged(this)
|
||||
@ -238,7 +238,7 @@ fun genCollectionValueImpl(
|
||||
isArray: Boolean
|
||||
): String =
|
||||
"""
|
||||
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||
internal fun PluginData.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||
var internalValue: $kotlinTypeName = default
|
||||
val delegt = dynamic$collectionName { internalValue }
|
||||
return object : ${miraiValueName}Value(), $kotlinTypeName by delegt {
|
||||
@ -246,13 +246,13 @@ fun genCollectionValueImpl(
|
||||
get() = internalValue
|
||||
set(new) {
|
||||
${
|
||||
if (isArray) """
|
||||
if (isArray) """
|
||||
if (!new.contentEquals(internalValue)) {
|
||||
internalValue = new
|
||||
onElementChanged(this)
|
||||
}
|
||||
""".trim()
|
||||
else """
|
||||
else """
|
||||
if (new != internalValue) {
|
||||
internalValue = new
|
||||
onElementChanged(this)
|
||||
|
@ -17,7 +17,7 @@ import java.io.File
|
||||
fun main() {
|
||||
println(File("").absolutePath) // default project base dir
|
||||
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt").apply {
|
||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/_Value.kt").apply {
|
||||
createNewFile()
|
||||
}.writeText(genPublicApi())
|
||||
}
|
||||
@ -64,7 +64,7 @@ fun genPublicApi() = buildString {
|
||||
appendLine()
|
||||
appendln(
|
||||
"""
|
||||
package net.mamoe.mirai.console.setting
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
@ -85,15 +85,15 @@ fun genPublicApi() = buildString {
|
||||
|
||||
appendln(
|
||||
"""
|
||||
sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
||||
sealed class Value<T : Any> : ReadWriteProperty<PluginData, T> {
|
||||
abstract var value: T
|
||||
|
||||
/**
|
||||
* 用于更新 [value] 的序列化器
|
||||
*/
|
||||
abstract val serializer: KSerializer<T>
|
||||
override fun getValue(thisRef: Setting, property: KProperty<*>): T = value
|
||||
override fun setValue(thisRef: Setting, property: KProperty<*>, value: T) {
|
||||
override fun getValue(thisRef: PluginData, property: KProperty<*>): T = value
|
||||
override fun setValue(thisRef: PluginData, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
||||
// SETTING
|
||||
appendln(
|
||||
"""
|
||||
abstract class Setting${collectionName}Value<T: Setting> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T>
|
||||
abstract class PluginData${collectionName}Value<T: PluginData> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T>
|
||||
"""
|
||||
)
|
||||
appendLine()
|
||||
@ -222,7 +222,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
||||
|
||||
appendln(
|
||||
"""
|
||||
abstract class SettingValue<T : Setting> internal constructor() : Value<T>()
|
||||
abstract class PluginDataValue<T : PluginData> internal constructor() : Value<T>()
|
||||
"""
|
||||
)
|
||||
|
||||
@ -250,7 +250,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
||||
// SETTING
|
||||
appendln(
|
||||
"""
|
||||
abstract class MutableSetting${collectionName}Value<T: Setting> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T>
|
||||
abstract class MutablePluginData${collectionName}Value<T: PluginData> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T>
|
||||
"""
|
||||
)
|
||||
appendLine()
|
||||
|
@ -14,6 +14,7 @@ package net.mamoe.mirai.console
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
@ -21,6 +22,7 @@ import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -72,7 +74,24 @@ public interface MiraiConsole : CoroutineScope {
|
||||
*/ // MiraiConsole.INSTANCE.getJob()
|
||||
public val job: Job
|
||||
get() = MiraiConsole.coroutineContext[Job]
|
||||
?: error("Internal error: Job not found in MiraiConsole.coroutineContext")
|
||||
?: throw IllegalMiraiConsoleImplementationError("Internal error: Job not found in MiraiConsole.coroutineContext")
|
||||
|
||||
/**
|
||||
* 添加一个 [Bot] 实例到全局 Bot 列表, 但不登录.
|
||||
*
|
||||
* 调用 [Bot.login] 可登录.
|
||||
*
|
||||
* @see Bot.botInstances 获取现有 [Bot] 实例列表
|
||||
*/
|
||||
// don't static
|
||||
@ConsoleExperimentalAPI("This is a low-level API and might be removed in the future.")
|
||||
public fun addBot(id: Long, password: String, configuration: BotConfiguration.() -> Unit = {}): Bot =
|
||||
Bot(id, password) {
|
||||
fileBasedDeviceInfo()
|
||||
this.loginSolver = frontEnd.createLoginSolver()
|
||||
redirectNetworkLogToDirectory()
|
||||
configuration()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,10 @@ import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
@ -75,8 +75,8 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
|
||||
public val consoleCommandSender: ConsoleCommandSender
|
||||
|
||||
public val settingStorageForJarPluginLoader: SettingStorage
|
||||
public val settingStorageForBuiltIns: SettingStorage
|
||||
public val dataStorageForJarPluginLoader: PluginDataStorage
|
||||
public val dataStorageForBuiltIns: PluginDataStorage
|
||||
|
||||
public companion object {
|
||||
internal lateinit var instance: MiraiConsoleImplementation
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.console.data.PluginData.ValueNode
|
||||
import net.mamoe.mirai.console.internal.data.PluginDataImpl
|
||||
import net.mamoe.mirai.console.internal.data.serialName
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* [PluginData] 的默认实现. 支持使用 `by value()` 等委托方法创建 [Value] 并跟踪其改动.
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
||||
/**
|
||||
* 添加了追踪的 [ValueNode] 列表, 即通过 `by value` 初始化的属性列表.
|
||||
*
|
||||
* 他们的修改会被跟踪, 并触发 [onValueChanged].
|
||||
*
|
||||
* @see provideDelegate
|
||||
*/
|
||||
public override val valueNodes: MutableList<ValueNode<*>> = mutableListOf()
|
||||
|
||||
/**
|
||||
* 使用 `by` 时自动调用此方法, 添加对 [Value] 的值修改的跟踪.
|
||||
*
|
||||
* 将会创建一个 [ValueNode] 并添加到 [valueNodes]
|
||||
*/
|
||||
public final override operator fun <T> SerializerAwareValue<T>.provideDelegate(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>
|
||||
): SerializerAwareValue<T> = apply { valueNodes.add(ValueNode(property.serialName, this, this.serializer)) }
|
||||
|
||||
/**
|
||||
* 值更新序列化器. 仅供内部使用.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public final override val updaterSerializer: KSerializer<Unit>
|
||||
get() = super.updaterSerializer
|
||||
|
||||
/**
|
||||
* 当所属于这个 [PluginData] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
||||
*/
|
||||
public abstract override fun onValueChanged(value: Value<*>)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
/**
|
||||
* 一个插件的配置数据, 用于和用户交互.
|
||||
*
|
||||
* 用户可通过 mirai-console 前端修改这些配置, 修改会自动写入这个对象中.
|
||||
*/
|
||||
public interface PluginConfig : PluginData
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.console.internal.data.createInstanceSmart
|
||||
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||
import net.mamoe.mirai.console.internal.data.valueFromKTypeImpl
|
||||
import net.mamoe.mirai.console.internal.data.valueImpl
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.loadPluginData
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
|
||||
|
||||
/**
|
||||
* 序列化之后的名称.
|
||||
*
|
||||
* 例:
|
||||
* ```
|
||||
* @SerialName("accounts")
|
||||
* object AccountPluginData : PluginData by ... {
|
||||
* @SerialName("info")
|
||||
* val map: Map<String, String> by value("a" to "b")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 将被保存为配置 (YAML 作为示例):
|
||||
* ```yaml
|
||||
* accounts:
|
||||
* info:
|
||||
* a: b
|
||||
* ```
|
||||
*/
|
||||
public typealias SerialName = kotlinx.serialization.SerialName
|
||||
|
||||
/**
|
||||
* 一个插件内部的, 对用户隐藏的数据对象. 可包含对多个 [Value] 的值变更的跟踪.
|
||||
*
|
||||
* [PluginData] 不涉及有关数据的存储, 而是只维护数据结构: [属性节点列表][valueNodes].
|
||||
*
|
||||
* 有关存储方案, 请查看 [PluginDataStorage]
|
||||
*
|
||||
* 在 [JvmPlugin] 的典型实现方式:
|
||||
* ```
|
||||
* object PluginMain : KotlinPlugin()
|
||||
*
|
||||
* object AccountPluginData : PluginData by PluginMain.loadPluginData() {
|
||||
* val map: Map<String, String> by value("a" to "b")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see JvmPlugin.loadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
||||
* @see PluginDataStorage [PluginData] 存储仓库
|
||||
*/
|
||||
public interface PluginData {
|
||||
/**
|
||||
* 添加了追踪的 [ValueNode] 列表 (即使用 `by value()` 委托的属性), 即通过 `by value` 初始化的属性列表.
|
||||
*
|
||||
* 他们的修改会被跟踪, 并触发 [onValueChanged].
|
||||
*
|
||||
* @see provideDelegate
|
||||
*/
|
||||
public val valueNodes: MutableList<ValueNode<*>>
|
||||
|
||||
/**
|
||||
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
||||
*/
|
||||
public data class ValueNode<T>(
|
||||
/**
|
||||
* 节点名称.
|
||||
*
|
||||
* 如果属性带有 [SerialName], 则使用 [kotlinx.serialization.SerialName.value],
|
||||
* 否则使用 [属性名称][KProperty.name]
|
||||
*/
|
||||
val serialName: String,
|
||||
/**
|
||||
* 属性值代理
|
||||
*/
|
||||
val value: Value<T>,
|
||||
/**
|
||||
* 属性值更新器
|
||||
*
|
||||
* @suppress 注意, 这是实验性 API.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
val updaterSerializer: KSerializer<Unit>
|
||||
)
|
||||
|
||||
/**
|
||||
* 使用 `by value()` 时自动调用此方法, 添加对 [Value] 的值修改的跟踪, 并创建 [ValueNode] 加入 [valueNodes]
|
||||
*/
|
||||
public operator fun <T> SerializerAwareValue<T>.provideDelegate(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>
|
||||
): SerializerAwareValue<T>
|
||||
|
||||
/**
|
||||
* 值更新序列化器. 仅供内部使用
|
||||
*
|
||||
* @suppress 注意, 这是实验性 API.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val updaterSerializer: KSerializer<Unit>
|
||||
|
||||
/**
|
||||
* 当所属于这个 [PluginData] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
||||
*/
|
||||
public fun onValueChanged(value: Value<*>)
|
||||
|
||||
/**
|
||||
* 当这个 [PluginData] 被放入一个 [PluginDataStorage] 时调用
|
||||
*/
|
||||
public fun setStorage(storage: PluginDataStorage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于支持属性委托
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline operator fun <T> Value<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
|
||||
|
||||
/**
|
||||
* 用于支持属性委托
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline operator fun <T> Value<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
//// region PluginData_value_primitives CODEGEN ////
|
||||
|
||||
/**
|
||||
* 创建一个 [Byte] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Short] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Short): SerializerAwareValue<Short> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Int] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Int): SerializerAwareValue<Int> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Long] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Long): SerializerAwareValue<Long> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Float] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Float): SerializerAwareValue<Float> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Double] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Double): SerializerAwareValue<Double> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Char] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Char): SerializerAwareValue<Char> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [Boolean] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(default)
|
||||
|
||||
/**
|
||||
* 创建一个 [String] 类型的 [Value], 并设置初始值为 [default]
|
||||
*/
|
||||
public fun PluginData.value(default: String): SerializerAwareValue<String> = valueImpl(default)
|
||||
|
||||
//// endregion PluginData_value_primitives CODEGEN ////
|
||||
|
||||
|
||||
/**
|
||||
* 通过具体化类型创建一个 [SerializerAwareValue], 并设置初始值.
|
||||
*
|
||||
* @param T 具体化参数类型 T. 仅支持:
|
||||
* - 基础数据类型
|
||||
* - 标准库集合类型 ([List], [Map], [Set])
|
||||
* - 标准库数据类型 ([Map.Entry], [Pair], [Triple])
|
||||
* - 和使用 [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) 的 [Serializable] 标记的
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@LowPriorityInOverloadResolution
|
||||
public inline fun <reified T> PluginData.value(default: T): SerializerAwareValue<T> =
|
||||
valueFromKType(typeOf0<T>(), default)
|
||||
|
||||
/**
|
||||
* 通过具体化类型创建一个 [SerializerAwareValue].
|
||||
* @see valueFromKType 查看更多实现信息
|
||||
*/
|
||||
@LowPriorityInOverloadResolution
|
||||
public inline fun <reified T> PluginData.value(): SerializerAwareValue<T> =
|
||||
value(T::class.run { objectInstance ?: createInstanceSmart() } as T)
|
||||
|
||||
/**
|
||||
* 通过一个特定的 [KType] 创建 [Value], 并设置初始值.
|
||||
*
|
||||
* 对于 [List], [Map], [Set] 等标准库类型, 这个函数会尝试构造 [LinkedHashMap] 等相关类型.
|
||||
* 而对于自定义数据类型, 本函数只会反射获取 [objectInstance][KClass.objectInstance] 或使用无参构造器构造实例.
|
||||
*
|
||||
* @param T 具体化参数类型 T. 仅支持:
|
||||
* - 基础数据类型
|
||||
* - 标准库集合类型 ([List], [Map], [Set])
|
||||
* - 标准库数据类型 ([Map.Entry], [Pair], [Triple])
|
||||
* - 和使用 [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) 的 [Serializable] 标记的
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ConsoleExperimentalAPI
|
||||
public fun <T> PluginData.valueFromKType(type: KType, default: T): SerializerAwareValue<T> =
|
||||
(valueFromKTypeImpl(type) as SerializerAwareValue<Any?>).apply { this.value = default } as SerializerAwareValue<T>
|
||||
|
||||
// TODO: 2020/6/24 Introduce class TypeToken for compound types for Java.
|
@ -0,0 +1,84 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST", "unused")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
|
||||
import net.mamoe.mirai.console.internal.data.AutoSavePluginData
|
||||
import net.mamoe.mirai.console.internal.data.cast
|
||||
import net.mamoe.mirai.console.internal.data.newPluginDataInstanceUsingReflection
|
||||
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
/**
|
||||
* 可以持有相关 [PluginData] 实例的对象, 作为 [PluginData] 实例的拥有者.
|
||||
*
|
||||
* @see PluginDataStorage.load
|
||||
* @see PluginDataStorage.store
|
||||
*
|
||||
* @see AutoSavePluginDataHolder 自动保存
|
||||
*/
|
||||
public interface PluginDataHolder {
|
||||
/**
|
||||
* 保存时使用的分类名
|
||||
*/
|
||||
public val name: String
|
||||
|
||||
/**
|
||||
* 创建一个 [PluginData] 实例.
|
||||
*
|
||||
* @see Companion.newPluginDataInstance
|
||||
* @see KClass.createType
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun <T : PluginData> newPluginDataInstance(type: KType): T =
|
||||
newPluginDataInstanceUsingReflection<PluginData>(type) as T
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [PluginData] 实例.
|
||||
*
|
||||
* @see PluginDataHolder.newPluginDataInstance
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : PluginData> PluginDataHolder.newPluginDataInstance(): T {
|
||||
return this.newPluginDataInstance(typeOf0<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以持有相关 [AutoSavePluginData] 的对象.
|
||||
*
|
||||
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
*/
|
||||
public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
|
||||
/**
|
||||
* [AutoSavePluginData] 每次自动保存时间间隔
|
||||
*
|
||||
* - 区间的左端点为最小间隔, 一个 [Value] 被修改后, 若此时间段后无其他修改, 将触发自动保存; 若有, 将重新开始计时.
|
||||
* - 区间的右端点为最大间隔, 一个 [Value] 被修改后, 最多不超过这个时间段后就会被保存.
|
||||
*
|
||||
* 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job],
|
||||
* 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||
*
|
||||
* @see LongRange Java 用户使用 [LongRange] 的构造器创建
|
||||
* @see Long.rangeTo Kotlin 用户使用 [Long.rangeTo] 创建, 如 `3000..50000`
|
||||
*/
|
||||
public val autoSaveIntervalMillis: LongRange
|
||||
|
||||
/**
|
||||
* 仅支持确切的 [PluginData] 类型
|
||||
*/
|
||||
@JvmDefault
|
||||
public override fun <T : PluginData> newPluginDataInstance(type: KType): T {
|
||||
val classifier = type.classifier?.cast<KClass<PluginData>>()
|
||||
require(classifier != null && classifier.java == PluginData::class.java) {
|
||||
"Cannot create PluginData instance. AutoSavePluginDataHolder supports only PluginData type."
|
||||
}
|
||||
return AutoSavePluginData(this, classifier) as T // T is always PluginData
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST", "unused")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import net.mamoe.mirai.console.internal.data.MemoryPluginDataStorageImpl
|
||||
import net.mamoe.mirai.console.internal.data.MultiFilePluginDataStorageImpl
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* [PluginData] 存储仓库.
|
||||
*
|
||||
* 此为较低层的 API, 一般插件开发者不会接触.
|
||||
*
|
||||
* [JarPluginLoader] 实现一个 [PluginDataStorage], 用于管理所有 [JvmPlugin] 的 [PluginData] 实例.
|
||||
*
|
||||
* @see PluginDataHolder
|
||||
* @see JarPluginLoader.dataStorage
|
||||
*/
|
||||
public interface PluginDataStorage {
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
public fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T
|
||||
|
||||
/**
|
||||
* 保存一个实例
|
||||
*/
|
||||
public fun store(holder: PluginDataHolder, pluginData: PluginData)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: KClass<T>): T =
|
||||
this.load(holder, dataClass.java)
|
||||
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : PluginData> PluginDataStorage.load(holder: PluginDataHolder): T =
|
||||
this.load(holder, T::class)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在内存存储所有 [PluginData] 实例的 [PluginDataStorage]. 在内存数据丢失后相关 [PluginData] 实例也会丢失.
|
||||
* @see PluginDataStorage
|
||||
*/
|
||||
public interface MemoryPluginDataStorage : PluginDataStorage, Map<Class<out PluginData>, PluginData> {
|
||||
/**
|
||||
* 当任一 [PluginData] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
||||
*/
|
||||
public fun interface OnChangedCallback {
|
||||
public fun onChanged(storage: MemoryPluginDataStorage, value: Value<*>)
|
||||
|
||||
/**
|
||||
* 无任何操作的 [OnChangedCallback]
|
||||
* @see OnChangedCallback
|
||||
*/
|
||||
public object NoOp : OnChangedCallback {
|
||||
public override fun onChanged(storage: MemoryPluginDataStorage, value: Value<*>) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [MemoryPluginDataStorage] 实例.
|
||||
*
|
||||
* @param onChanged 当任一 [PluginData] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
// @JvmOverloads
|
||||
public operator fun invoke(onChanged: OnChangedCallback = OnChangedCallback.NoOp): MemoryPluginDataStorage =
|
||||
MemoryPluginDataStorageImpl(onChanged)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用多个文件存储 [PluginData] 实例的 [PluginDataStorage].
|
||||
*/
|
||||
public interface MultiFilePluginDataStorage : PluginDataStorage {
|
||||
/**
|
||||
* 存放 [PluginData] 的目录.
|
||||
*/
|
||||
public val directory: File
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [MultiFilePluginDataStorage] 实例.
|
||||
*
|
||||
* @see directory 存放 [PluginData] 的目录.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
public operator fun invoke(directory: File): MultiFilePluginDataStorage =
|
||||
MultiFilePluginDataStorageImpl(directory)
|
||||
}
|
||||
}
|
@ -9,23 +9,24 @@
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused", "NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.setting
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.BinaryFormat
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.StringFormat
|
||||
import net.mamoe.mirai.console.internal.setting.map
|
||||
import net.mamoe.mirai.console.internal.setting.setValueBySerializer
|
||||
import net.mamoe.mirai.console.internal.data.map
|
||||
import net.mamoe.mirai.console.internal.data.setValueBySerializer
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* 表示一个可被观测的, 不可变的值包装.
|
||||
* 表示一个值代理.
|
||||
*
|
||||
* [Value.value] 可以像 Kotlin 的 `var` 一样被修改, 然而它也可能被用户修改, 如通过 UI 前端.
|
||||
* [Value.value] 可以像 Kotlin 的 `var` 一样被修改, 然而它也可能被用户修改, 如通过 UI 前端, 或通过自动重载.
|
||||
*
|
||||
* 一些常用的基础类型实现由代码生成创建特性的优化.
|
||||
*
|
||||
* @see PluginData 容纳 [Value] 的数据对象
|
||||
*
|
||||
* @see PrimitiveValue 基础数据类型实现
|
||||
* @see CompositeValue 复合数据类型实现
|
||||
*/
|
||||
@ -39,7 +40,7 @@ public interface Value<T> {
|
||||
public class SerializableValue<T>(
|
||||
private val delegate: Value<T>,
|
||||
/**
|
||||
* The serializer used to update and dump [delegate]
|
||||
* 用于更新和保存 [delegate] 的序列化器
|
||||
*/
|
||||
public override val serializer: KSerializer<Unit>
|
||||
) : Value<T> by delegate, SerializerAwareValue<T> {
|
||||
@ -63,7 +64,7 @@ public class SerializableValue<T>(
|
||||
* 带有显式 [序列化器][serializer] 的 [Value].
|
||||
*
|
||||
* @see SerializableValue 简单实现
|
||||
* @see Setting.value 创建一个这样的 [SerializerAwareValue]
|
||||
* @see PluginData.value 创建一个这样的 [SerializerAwareValue]
|
||||
*/
|
||||
public interface SerializerAwareValue<T> : Value<T> {
|
||||
/**
|
||||
@ -109,20 +110,6 @@ public interface SerializerAwareValue<T> : Value<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于支持属性委托
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline operator fun <T> Value<T>.getValue(mySetting: Any?, property: KProperty<*>): T = value
|
||||
|
||||
/**
|
||||
* 用于支持属性委托
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline operator fun <T> Value<T>.setValue(mySetting: Any?, property: KProperty<*>, value: T) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础数据类型 [Value]
|
||||
*
|
||||
@ -144,42 +131,34 @@ public interface PrimitiveValue<T> : Value<T>
|
||||
* 表示一个不可空 [Byte] [Value].
|
||||
*/
|
||||
public interface ByteValue : PrimitiveValue<Byte>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Short] [Value].
|
||||
*/
|
||||
public interface ShortValue : PrimitiveValue<Short>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Int] [Value].
|
||||
*/
|
||||
public interface IntValue : PrimitiveValue<Int>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Long] [Value].
|
||||
*/
|
||||
public interface LongValue : PrimitiveValue<Long>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Float] [Value].
|
||||
*/
|
||||
public interface FloatValue : PrimitiveValue<Float>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Double] [Value].
|
||||
*/
|
||||
public interface DoubleValue : PrimitiveValue<Double>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Char] [Value].
|
||||
*/
|
||||
public interface CharValue : PrimitiveValue<Char>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [Boolean] [Value].
|
||||
*/
|
||||
public interface BooleanValue : PrimitiveValue<Boolean>
|
||||
|
||||
/**
|
||||
* 表示一个不可空 [String] [Value].
|
||||
*/
|
@ -22,13 +22,13 @@ import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.utils.ConsoleBuiltInSettingStorage
|
||||
import net.mamoe.mirai.console.internal.utils.ConsoleBuiltInPluginDataStorage
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
@ -58,8 +58,8 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
|
||||
override val consoleCommandSender: ConsoleCommandSender get() = instance.consoleCommandSender
|
||||
|
||||
override val settingStorageForJarPluginLoader: SettingStorage get() = instance.settingStorageForJarPluginLoader
|
||||
override val settingStorageForBuiltIns: SettingStorage get() = instance.settingStorageForBuiltIns
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage get() = instance.dataStorageForJarPluginLoader
|
||||
override val dataStorageForBuiltIns: PluginDataStorage get() = instance.dataStorageForBuiltIns
|
||||
|
||||
init {
|
||||
DefaultLogger = { identity -> this.newLogger(identity) }
|
||||
@ -91,7 +91,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
|
||||
mainLogger.info { "mirai-console started successfully." }
|
||||
|
||||
ConsoleBuiltInSettingStorage // init
|
||||
ConsoleBuiltInPluginDataStorage // init
|
||||
// Only for initialize
|
||||
}
|
||||
}
|
@ -10,8 +10,10 @@
|
||||
package net.mamoe.mirai.console.internal.command
|
||||
|
||||
import net.mamoe.mirai.console.command.*
|
||||
import net.mamoe.mirai.console.command.description.CommandArgumentParserException
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.nameCardOrNick
|
||||
|
||||
|
||||
internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
|
||||
@ -61,9 +63,6 @@ internal inline fun <T : Any> Collection<T>.fuzzySearch(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
this.forEach {
|
||||
@ -90,9 +89,6 @@ internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
||||
target: String,
|
||||
index: (T) -> String
|
||||
): T? {
|
||||
if (this.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var potential: T? = null
|
||||
var rate = 0.0
|
||||
var collide = 0
|
||||
@ -115,9 +111,7 @@ internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
||||
|
||||
|
||||
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
|
||||
return this.members.fuzzySearchOnly(nameCardTarget) {
|
||||
it.nameCard
|
||||
}
|
||||
return this.members.fuzzySearch(nameCardTarget) { it.nameCardOrNick }
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +153,10 @@ internal suspend inline fun CommandSender.executeCommandInternal(
|
||||
kotlin.runCatching {
|
||||
command.onCommand(this, args)
|
||||
}.onFailure {
|
||||
throw CommandExecutionException(command, commandName, it)
|
||||
catchExecutionException(it)
|
||||
if (it !is CommandArgumentParserException) {
|
||||
throw CommandExecutionException(command, commandName, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
import net.mamoe.mirai.console.data.*
|
||||
|
||||
|
||||
// type inference bug
|
||||
internal fun <T> Setting.createCompositeSetValueImpl(tToValue: (T) -> Value<T>): CompositeSetValueImpl<T> {
|
||||
internal fun <T> PluginData.createCompositeSetValueImpl(tToValue: (T) -> Value<T>): CompositeSetValueImpl<T> {
|
||||
return object : CompositeSetValueImpl<T>(tToValue) {
|
||||
override fun onChanged() {
|
||||
this@createCompositeSetValueImpl.onValueChanged(this)
|
||||
@ -62,7 +62,7 @@ internal abstract class CompositeSetValueImpl<T>(
|
||||
|
||||
|
||||
// type inference bug
|
||||
internal fun <T> Setting.createCompositeListValueImpl(tToValue: (T) -> Value<T>): CompositeListValueImpl<T> {
|
||||
internal fun <T> PluginData.createCompositeListValueImpl(tToValue: (T) -> Value<T>): CompositeListValueImpl<T> {
|
||||
return object : CompositeListValueImpl<T>(tToValue) {
|
||||
override fun onChanged() {
|
||||
this@createCompositeListValueImpl.onValueChanged(this)
|
||||
@ -108,7 +108,7 @@ internal abstract class CompositeListValueImpl<T>(
|
||||
}
|
||||
|
||||
// workaround to a type inference bug
|
||||
internal fun <K, V> Setting.createCompositeMapValueImpl(
|
||||
internal fun <K, V> PluginData.createCompositeMapValueImpl(
|
||||
kToValue: (K) -> Value<K>,
|
||||
vToValue: (V) -> Value<V>
|
||||
): CompositeMapValueImpl<K, V> {
|
@ -9,13 +9,13 @@
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.console.setting.SerializableValue.Companion.serializableValueWith
|
||||
import net.mamoe.mirai.console.setting.SerializerAwareValue
|
||||
import net.mamoe.mirai.console.setting.Setting
|
||||
import net.mamoe.mirai.console.setting.valueFromKType
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.SerializableValue.Companion.serializableValueWith
|
||||
import net.mamoe.mirai.console.data.SerializerAwareValue
|
||||
import net.mamoe.mirai.console.data.valueFromKType
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createInstance as createInstanceKotlin
|
||||
@ -30,7 +30,7 @@ internal inline fun <reified T> typeOf0(): KType = kotlin.reflect.typeOf<T>()
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UnsafeCall", "SMARTCAST_IMPOSSIBLE", "UNCHECKED_CAST")
|
||||
internal fun Setting.valueFromKTypeImpl(type: KType): SerializerAwareValue<*> {
|
||||
internal fun PluginData.valueFromKTypeImpl(type: KType): SerializerAwareValue<*> {
|
||||
val classifier = type.classifier
|
||||
require(classifier is KClass<*>)
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS")
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -19,9 +19,9 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.CompositeDecoder
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import net.mamoe.mirai.console.setting.AbstractSetting.ValueNode
|
||||
import net.mamoe.mirai.console.setting.Setting
|
||||
import net.mamoe.mirai.console.setting.Value
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.PluginData.ValueNode
|
||||
import net.mamoe.mirai.console.data.Value
|
||||
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
@ -29,17 +29,17 @@ import kotlin.reflect.full.findAnnotation
|
||||
internal val KProperty<*>.serialName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
||||
|
||||
/**
|
||||
* Internal implementation for [Setting] including:
|
||||
* Internal implementation for [PluginData] including:
|
||||
* - Reflection on Kotlin properties and Java fields
|
||||
* - Auto-saving
|
||||
*/
|
||||
internal abstract class SettingImpl {
|
||||
internal abstract class PluginDataImpl {
|
||||
internal fun findNodeInstance(name: String): ValueNode<*>? = valueNodes.firstOrNull { it.serialName == name }
|
||||
|
||||
internal abstract val valueNodes: MutableList<ValueNode<*>>
|
||||
|
||||
internal open val updaterSerializer: KSerializer<Unit> = object : KSerializer<Unit> {
|
||||
override val descriptor: SerialDescriptor get() = settingUpdaterSerializerDescriptor
|
||||
override val descriptor: SerialDescriptor get() = dataUpdaterSerializerDescriptor
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun deserialize(decoder: Decoder) {
|
||||
@ -96,7 +96,7 @@ internal abstract class SettingImpl {
|
||||
with(encoder.beginCollection(descriptor, valueNodes.size)) {
|
||||
var index = 0
|
||||
|
||||
// val vSerializer = settingUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
||||
// val vSerializer = dataUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
||||
valueNodes.forEach { (serialName, _, valueSerializer) ->
|
||||
encodeSerializableElement(descriptor, index++, String.serializer(), serialName)
|
||||
encodeSerializableElement(descriptor, index++, valueSerializer, Unit)
|
||||
@ -113,8 +113,8 @@ internal abstract class SettingImpl {
|
||||
abstract fun onValueChanged(value: Value<*>)
|
||||
|
||||
companion object {
|
||||
private val settingUpdaterSerializerTypeArguments = arrayOf(String.serializer(), YamlNullableDynamicSerializer)
|
||||
private val settingUpdaterSerializerDescriptor =
|
||||
MapSerializer(settingUpdaterSerializerTypeArguments[0], settingUpdaterSerializerTypeArguments[1]).descriptor
|
||||
private val dataUpdaterSerializerTypeArguments = arrayOf(String.serializer(), YamlNullableDynamicSerializer)
|
||||
private val dataUpdaterSerializerDescriptor =
|
||||
MapSerializer(dataUpdaterSerializerTypeArguments[0], dataUpdaterSerializerTypeArguments[1]).descriptor
|
||||
}
|
||||
}
|
@ -7,14 +7,14 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
||||
import net.mamoe.mirai.console.plugin.jvm.loadSetting
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
import net.mamoe.mirai.console.plugin.jvm.loadPluginData
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
@ -26,21 +26,21 @@ import kotlin.reflect.full.findAnnotation
|
||||
|
||||
|
||||
/**
|
||||
* 链接自动保存的 [Setting].
|
||||
* 链接自动保存的 [PluginData].
|
||||
* 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存
|
||||
*
|
||||
* 若 [AutoSaveSettingHolder.coroutineContext] 含有 [Job], 则 [AutoSaveSetting] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||
* 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||
*
|
||||
* @see loadSetting
|
||||
* @see loadPluginData
|
||||
*/
|
||||
internal open class AutoSaveSetting(
|
||||
private val owner: AutoSaveSettingHolder,
|
||||
internal val originSettingClass: KClass<out Setting>
|
||||
internal open class AutoSavePluginData(
|
||||
private val owner: AutoSavePluginDataHolder,
|
||||
internal val originPluginDataClass: KClass<out PluginData>
|
||||
) :
|
||||
AbstractSetting() {
|
||||
private lateinit var storage: SettingStorage
|
||||
AbstractPluginData() {
|
||||
private lateinit var storage: PluginDataStorage
|
||||
|
||||
override fun setStorage(storage: SettingStorage) {
|
||||
override fun setStorage(storage: PluginDataStorage) {
|
||||
check(!this::storage.isInitialized) { "storage is already initialized" }
|
||||
this.storage = storage
|
||||
}
|
||||
@ -82,71 +82,71 @@ internal open class AutoSaveSetting(
|
||||
private fun doSave() = storage.store(owner, this)
|
||||
}
|
||||
|
||||
internal class MemorySettingStorageImpl(
|
||||
private val onChanged: MemorySettingStorage.OnChangedCallback
|
||||
) : SettingStorage, MemorySettingStorage,
|
||||
MutableMap<Class<out Setting>, Setting> by mutableMapOf() {
|
||||
internal class MemoryPluginDataStorageImpl(
|
||||
private val onChanged: MemoryPluginDataStorage.OnChangedCallback
|
||||
) : PluginDataStorage, MemoryPluginDataStorage,
|
||||
MutableMap<Class<out PluginData>, PluginData> by mutableMapOf() {
|
||||
|
||||
internal inner class MemorySettingImpl : AbstractSetting() {
|
||||
internal inner class MemoryPluginDataImpl : AbstractPluginData() {
|
||||
@ConsoleInternalAPI
|
||||
override fun onValueChanged(value: Value<*>) {
|
||||
onChanged.onChanged(this@MemorySettingStorageImpl, value)
|
||||
onChanged.onChanged(this@MemoryPluginDataStorageImpl, value)
|
||||
}
|
||||
|
||||
override fun setStorage(storage: SettingStorage) {
|
||||
check(storage is MemorySettingStorageImpl) { "storage is not MemorySettingStorageImpl" }
|
||||
override fun setStorage(storage: PluginDataStorage) {
|
||||
check(storage is MemoryPluginDataStorageImpl) { "storage is not MemoryPluginDataStorageImpl" }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T = (synchronized(this) {
|
||||
this.getOrPut(settingClass) {
|
||||
settingClass.kotlin.run {
|
||||
override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T = (synchronized(this) {
|
||||
this.getOrPut(dataClass) {
|
||||
dataClass.kotlin.run {
|
||||
objectInstance ?: createInstanceOrNull() ?: kotlin.run {
|
||||
if (settingClass != Setting::class.java) {
|
||||
if (dataClass != PluginData::class.java) {
|
||||
throw IllegalArgumentException(
|
||||
"Cannot create Setting instance. Make sure settingClass is Setting::class.java or a Kotlin's object, " +
|
||||
"Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " +
|
||||
"or has a constructor which either has no parameters or all parameters of which are optional"
|
||||
)
|
||||
}
|
||||
MemorySettingImpl()
|
||||
MemoryPluginDataImpl()
|
||||
}
|
||||
}
|
||||
}
|
||||
} as T).also { it.setStorage(this) }
|
||||
|
||||
override fun store(holder: SettingHolder, setting: Setting) {
|
||||
override fun store(holder: PluginDataHolder, pluginData: PluginData) {
|
||||
synchronized(this) {
|
||||
this[setting::class.java] = setting
|
||||
this[pluginData::class.java] = pluginData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("RedundantVisibilityModifier") // might be public in the future
|
||||
internal open class MultiFileSettingStorageImpl(
|
||||
internal open class MultiFilePluginDataStorageImpl(
|
||||
public final override val directory: File
|
||||
) : SettingStorage, MultiFileSettingStorage {
|
||||
) : PluginDataStorage, MultiFilePluginDataStorage {
|
||||
init {
|
||||
directory.mkdir()
|
||||
}
|
||||
|
||||
public override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T =
|
||||
with(settingClass.kotlin) {
|
||||
public override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T =
|
||||
with(dataClass.kotlin) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run {
|
||||
require(settingClass == Setting::class.java) {
|
||||
"Cannot create Setting instance. Make sure settingClass is Setting::class.java or a Kotlin's object, " +
|
||||
require(dataClass == PluginData::class.java) {
|
||||
"Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " +
|
||||
"or has a constructor which either has no parameters or all parameters of which are optional"
|
||||
}
|
||||
if (holder is AutoSaveSettingHolder) {
|
||||
AutoSaveSetting(holder, this) as T?
|
||||
if (holder is AutoSavePluginDataHolder) {
|
||||
AutoSavePluginData(holder, this) as T?
|
||||
} else null
|
||||
} ?: throw IllegalArgumentException(
|
||||
"Cannot create Setting instance. Make sure 'holder' is a AutoSaveSettingHolder, " +
|
||||
"or 'setting' is an object or has a constructor which either has no parameters or all parameters of which are optional"
|
||||
"Cannot create PluginData instance. Make sure 'holder' is a AutoSavePluginDataHolder, " +
|
||||
"or 'data' is an object or has a constructor which either has no parameters or all parameters of which are optional"
|
||||
)
|
||||
|
||||
val file = getSettingFile(holder, this)
|
||||
val file = getPluginDataFile(holder, this)
|
||||
file.createNewFile()
|
||||
check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" }
|
||||
val text = file.readText()
|
||||
@ -156,29 +156,32 @@ internal open class MultiFileSettingStorageImpl(
|
||||
instance
|
||||
}.also { it.setStorage(this) }
|
||||
|
||||
protected open fun getSettingFile(holder: SettingHolder, clazz: KClass<*>): File = with(clazz) {
|
||||
protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) {
|
||||
val name = findASerialName()
|
||||
|
||||
val dir = File(directory, holder.name)
|
||||
if (dir.isFile) {
|
||||
error("Target directory ${dir.path} for holder $holder is occupied by a file therefore setting $qualifiedNameOrTip can't be saved.")
|
||||
error("Target directory ${dir.path} for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.")
|
||||
}
|
||||
dir.mkdir()
|
||||
|
||||
val file = File(directory, name)
|
||||
if (file.isDirectory) {
|
||||
error("Target file $file is occupied by a directory therefore setting $qualifiedNameOrTip can't be saved.")
|
||||
error("Target file $file is occupied by a directory therefore data $qualifiedNameOrTip can't be saved.")
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public override fun store(holder: SettingHolder, setting: Setting) {
|
||||
public override fun store(holder: PluginDataHolder, pluginData: PluginData) {
|
||||
val file =
|
||||
getSettingFile(holder, if (setting is AutoSaveSetting) setting.originSettingClass else setting::class)
|
||||
getPluginDataFile(
|
||||
holder,
|
||||
if (pluginData is AutoSavePluginData) pluginData.originPluginDataClass else pluginData::class
|
||||
)
|
||||
|
||||
if (file.exists() && file.isFile && file.canRead()) {
|
||||
file.writeText(Yaml.default.encodeToString(setting.updaterSerializer, Unit))
|
||||
file.writeText(Yaml.default.encodeToString(pluginData.updaterSerializer, Unit))
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import kotlinx.serialization.KSerializer
|
@ -7,11 +7,11 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import net.mamoe.mirai.console.setting.SerializerAwareValue
|
||||
import net.mamoe.mirai.console.setting.Setting
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.SerializerAwareValue
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
@ -49,9 +49,9 @@ internal object BuiltInSerializerConstants {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <T : Any> Setting.valueImplPrimitive(kClass: KClass<T>): SerializerAwareValue<T>? {
|
||||
internal fun <T : Any> PluginData.valueImplPrimitive(kClass: KClass<T>): SerializerAwareValue<T>? {
|
||||
return when (kClass) {
|
||||
//// region Setting_valueImplPrimitive CODEGEN ////
|
||||
//// region PluginData_valueImplPrimitive CODEGEN ////
|
||||
|
||||
Byte::class -> byteValueImpl()
|
||||
Short::class -> shortValueImpl()
|
||||
@ -63,103 +63,120 @@ internal fun <T : Any> Setting.valueImplPrimitive(kClass: KClass<T>): Serializer
|
||||
Boolean::class -> booleanValueImpl()
|
||||
String::class -> stringValueImpl()
|
||||
|
||||
//// endregion Setting_valueImplPrimitive CODEGEN ////
|
||||
//// endregion PluginData_valueImplPrimitive CODEGEN ////
|
||||
else -> error("Internal error: unexpected type passed: ${kClass.qualifiedName}")
|
||||
} as SerializerAwareValue<T>?
|
||||
}
|
||||
|
||||
|
||||
//// region Setting_value_PrimitivesImpl CODEGEN ////
|
||||
//// region PluginData_value_PrimitivesImpl CODEGEN ////
|
||||
|
||||
internal fun Setting.valueImpl(default: Byte): SerializerAwareValue<Byte> {
|
||||
internal fun PluginData.valueImpl(default: Byte): SerializerAwareValue<Byte> {
|
||||
return object : ByteValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.byteValueImpl(): SerializerAwareValue<Byte> {
|
||||
|
||||
internal fun PluginData.byteValueImpl(): SerializerAwareValue<Byte> {
|
||||
return object : ByteValueImpl() {
|
||||
override fun onChanged() = this@byteValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Short): SerializerAwareValue<Short> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Short): SerializerAwareValue<Short> {
|
||||
return object : ShortValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.shortValueImpl(): SerializerAwareValue<Short> {
|
||||
|
||||
internal fun PluginData.shortValueImpl(): SerializerAwareValue<Short> {
|
||||
return object : ShortValueImpl() {
|
||||
override fun onChanged() = this@shortValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Int): SerializerAwareValue<Int> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Int): SerializerAwareValue<Int> {
|
||||
return object : IntValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.intValueImpl(): SerializerAwareValue<Int> {
|
||||
|
||||
internal fun PluginData.intValueImpl(): SerializerAwareValue<Int> {
|
||||
return object : IntValueImpl() {
|
||||
override fun onChanged() = this@intValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Long): SerializerAwareValue<Long> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Long): SerializerAwareValue<Long> {
|
||||
return object : LongValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.longValueImpl(): SerializerAwareValue<Long> {
|
||||
|
||||
internal fun PluginData.longValueImpl(): SerializerAwareValue<Long> {
|
||||
return object : LongValueImpl() {
|
||||
override fun onChanged() = this@longValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Float): SerializerAwareValue<Float> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Float): SerializerAwareValue<Float> {
|
||||
return object : FloatValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.floatValueImpl(): SerializerAwareValue<Float> {
|
||||
|
||||
internal fun PluginData.floatValueImpl(): SerializerAwareValue<Float> {
|
||||
return object : FloatValueImpl() {
|
||||
override fun onChanged() = this@floatValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Double): SerializerAwareValue<Double> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Double): SerializerAwareValue<Double> {
|
||||
return object : DoubleValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.doubleValueImpl(): SerializerAwareValue<Double> {
|
||||
|
||||
internal fun PluginData.doubleValueImpl(): SerializerAwareValue<Double> {
|
||||
return object : DoubleValueImpl() {
|
||||
override fun onChanged() = this@doubleValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Char): SerializerAwareValue<Char> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Char): SerializerAwareValue<Char> {
|
||||
return object : CharValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.charValueImpl(): SerializerAwareValue<Char> {
|
||||
|
||||
internal fun PluginData.charValueImpl(): SerializerAwareValue<Char> {
|
||||
return object : CharValueImpl() {
|
||||
override fun onChanged() = this@charValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: Boolean): SerializerAwareValue<Boolean> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: Boolean): SerializerAwareValue<Boolean> {
|
||||
return object : BooleanValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.booleanValueImpl(): SerializerAwareValue<Boolean> {
|
||||
|
||||
internal fun PluginData.booleanValueImpl(): SerializerAwareValue<Boolean> {
|
||||
return object : BooleanValueImpl() {
|
||||
override fun onChanged() = this@booleanValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.valueImpl(default: String): SerializerAwareValue<String> {
|
||||
|
||||
internal fun PluginData.valueImpl(default: String): SerializerAwareValue<String> {
|
||||
return object : StringValueImpl(default) {
|
||||
override fun onChanged() = this@valueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
internal fun Setting.stringValueImpl(): SerializerAwareValue<String> {
|
||||
|
||||
internal fun PluginData.stringValueImpl(): SerializerAwareValue<String> {
|
||||
return object : StringValueImpl() {
|
||||
override fun onChanged() = this@stringValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
|
||||
//// endregion Setting_value_PrimitivesImpl CODEGEN ////
|
||||
//// endregion PluginData_value_PrimitivesImpl CODEGEN ////
|
@ -7,14 +7,14 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
import net.mamoe.mirai.console.data.*
|
||||
|
||||
/**
|
||||
* The super class to all ValueImpl s
|
||||
@ -98,7 +98,6 @@ internal abstract class ShortValueImpl : ShortValue, SerializerAwareValue<Short>
|
||||
else value.hashCode() * 31
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class IntValueImpl : IntValue, SerializerAwareValue<Int>, KSerializer<Unit>, AbstractValueImpl<Int> {
|
||||
constructor()
|
||||
constructor(default: Int) {
|
@ -7,10 +7,10 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.setting.Setting
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
@ -29,16 +29,16 @@ internal inline fun <reified T : Any> KType.asKClass(): KClass<out T> {
|
||||
return clazz
|
||||
}
|
||||
|
||||
internal inline fun <reified T : Setting> newSettingInstanceUsingReflection(type: KType): T {
|
||||
internal inline fun <reified T : PluginData> newPluginDataInstanceUsingReflection(type: KType): T {
|
||||
val classifier = type.asKClass<T>()
|
||||
|
||||
return with(classifier) {
|
||||
objectInstance
|
||||
?: createInstanceOrNull()
|
||||
?: throw IllegalArgumentException(
|
||||
"Cannot create Setting instance. " +
|
||||
"SettingHolder supports Settings implemented as an object " +
|
||||
"or the ones with a constructor which either has no parameters or all parameters of which are optional, by default newSettingInstance implementation."
|
||||
"Cannot create PluginData instance. " +
|
||||
"PluginDataHolder supports PluginDatas implemented as an object " +
|
||||
"or the ones with a constructor which either has no parameters or all parameters of which are optional, by default newPluginDataInstance implementation."
|
||||
)
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
@file:Suppress("DuplicatedCode")
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.InternalSerializationApi
|
||||
import kotlinx.serialization.serializer
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.InternalSerializationApi
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.setting
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.console.internal.plugin
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginLoadException
|
||||
@ -18,7 +19,6 @@ import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescriptionImpl
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
@ -35,8 +35,8 @@ internal object JarPluginLoaderImpl :
|
||||
private val logger: MiraiLogger = MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
override val settingStorage: SettingStorage
|
||||
get() = MiraiConsoleImplementationBridge.settingStorageForJarPluginLoader
|
||||
override val dataStorage: PluginDataStorage
|
||||
get() = MiraiConsoleImplementationBridge.dataStorageForJarPluginLoader
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
MiraiConsole.coroutineContext +
|
||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.safeLoader
|
||||
import net.mamoe.mirai.console.util.ResourceContainer.Companion.asResourceContainer
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
@ -65,17 +66,36 @@ internal abstract class JvmPluginInternal(
|
||||
|
||||
internal fun internalOnDisable() {
|
||||
firstRun = false
|
||||
this.onDisable()
|
||||
this.cancel(CancellationException("plugin disabled"))
|
||||
kotlin.runCatching {
|
||||
onLoad()
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
cancel(CancellationException("plugin disabled"))
|
||||
},
|
||||
onFailure = {
|
||||
cancel(CancellationException("Exception while enabling plugin", it))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
internal fun internalOnLoad() {
|
||||
this.onLoad()
|
||||
@Throws(Throwable::class)
|
||||
internal fun internalOnLoad() { // propagate exceptions
|
||||
onLoad()
|
||||
}
|
||||
|
||||
internal fun internalOnEnable() {
|
||||
internal fun internalOnEnable(): Boolean {
|
||||
if (!firstRun) refreshCoroutineContext()
|
||||
this.onEnable()
|
||||
kotlin.runCatching {
|
||||
onEnable()
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
return true
|
||||
},
|
||||
onFailure = {
|
||||
cancel(CancellationException("Exception while enabling plugin", it))
|
||||
return false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -85,18 +105,25 @@ internal abstract class JvmPluginInternal(
|
||||
// for future use
|
||||
@Suppress("PropertyName")
|
||||
@JvmField
|
||||
internal var _intrinsicCoroutineContext: CoroutineContext =
|
||||
EmptyCoroutineContext
|
||||
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
|
||||
@JvmField
|
||||
internal val coroutineContextInitializer = {
|
||||
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
||||
.plus(parentCoroutineContext)
|
||||
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
|
||||
.plus(SupervisorJob(parentCoroutineContext[Job]))
|
||||
.plus(_intrinsicCoroutineContext)
|
||||
}
|
||||
|
||||
private fun refreshCoroutineContext(): CoroutineContext {
|
||||
return coroutineContextInitializer().also { _coroutineContext = it }
|
||||
return coroutineContextInitializer().also { _coroutineContext = it }.also {
|
||||
job.invokeOnCompletion { e ->
|
||||
if (e != null) {
|
||||
logger.error(e)
|
||||
safeLoader.disable(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val contextUpdateLock: ReentrantLock =
|
||||
|
@ -15,7 +15,7 @@ import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.internal.setting.cast
|
||||
import net.mamoe.mirai.console.internal.data.cast
|
||||
import net.mamoe.mirai.console.plugin.*
|
||||
import net.mamoe.mirai.utils.info
|
||||
import java.io.File
|
||||
|
@ -16,9 +16,9 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.setting.*
|
||||
import net.mamoe.mirai.console.setting.SettingStorage.Companion.load
|
||||
import net.mamoe.mirai.console.util.BotManager
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
@ -29,7 +29,7 @@ internal object BotManagerImpl : BotManager {
|
||||
/**
|
||||
* 判断此用户是否为 console 管理员
|
||||
*/
|
||||
override val User.isManager: Boolean get() = this.id in this.bot.managers
|
||||
override val User.isManager: Boolean get() = this.id in ManagersConfig[this.bot]
|
||||
|
||||
override fun Bot.removeManager(id: Long): Boolean {
|
||||
return ManagersConfig[this].remove(id)
|
||||
@ -43,7 +43,7 @@ internal object BotManagerImpl : BotManager {
|
||||
}
|
||||
}
|
||||
|
||||
internal object ManagersConfig : Setting by ConsoleBuiltInSettingStorage.load() {
|
||||
internal object ManagersConfig : PluginData by ConsoleBuiltInPluginDataStorage.load() {
|
||||
private val managers: MutableMap<Long, MutableSet<Long>> by value()
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
||||
@ -54,14 +54,14 @@ internal fun CoroutineContext.overrideWithSupervisorJob(): CoroutineContext = th
|
||||
internal fun CoroutineScope.childScope(context: CoroutineContext = EmptyCoroutineContext): CoroutineScope =
|
||||
CoroutineScope(this.coroutineContext.overrideWithSupervisorJob() + context)
|
||||
|
||||
internal object ConsoleBuiltInSettingHolder : AutoSaveSettingHolder,
|
||||
internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by MiraiConsole.childScope() {
|
||||
override val autoSaveIntervalMillis: LongRange = 30.minutesToMillis..60.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInSettingStorage :
|
||||
SettingStorage by MiraiConsoleImplementationBridge.settingStorageForJarPluginLoader {
|
||||
internal object ConsoleBuiltInPluginDataStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns {
|
||||
|
||||
inline fun <reified T : Setting> load(): T = load(ConsoleBuiltInSettingHolder)
|
||||
inline fun <reified T : PluginData> load(): T = load(ConsoleBuiltInPluginDataHolder)
|
||||
}
|
@ -14,8 +14,8 @@ import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.encodeToString
|
||||
import net.mamoe.mirai.console.internal.setting.SemverAsStringSerializerIvy
|
||||
import net.mamoe.mirai.console.internal.setting.map
|
||||
import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerIvy
|
||||
import net.mamoe.mirai.console.internal.data.map
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||
import java.io.File
|
||||
|
@ -10,9 +10,9 @@
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.plugin.JarPluginLoaderImpl
|
||||
import net.mamoe.mirai.console.plugin.FilePluginLoader
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
/**
|
||||
@ -20,10 +20,10 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
*/
|
||||
public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
|
||||
/**
|
||||
* [JvmPlugin.loadSetting] 默认使用的实例
|
||||
* [JvmPlugin.loadPluginData] 默认使用的实例
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val settingStorage: SettingStorage
|
||||
public val dataStorage: PluginDataStorage
|
||||
|
||||
public companion object INSTANCE : JarPluginLoader by JarPluginLoaderImpl {
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
|
@ -12,10 +12,10 @@
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
||||
import net.mamoe.mirai.console.setting.AutoSaveSettingHolder
|
||||
import net.mamoe.mirai.console.setting.Setting
|
||||
import net.mamoe.mirai.console.util.ResourceContainer
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import kotlin.reflect.KClass
|
||||
@ -36,7 +36,7 @@ import kotlin.reflect.KClass
|
||||
* @see ResourceContainer 支持资源获取 (如 Jar 中的资源文件)
|
||||
*/
|
||||
public interface JvmPlugin : Plugin, CoroutineScope,
|
||||
PluginFileExtensions, ResourceContainer, AutoSaveSettingHolder {
|
||||
PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder {
|
||||
/** 日志 */
|
||||
public val logger: MiraiLogger
|
||||
|
||||
@ -49,10 +49,10 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
||||
get() = JarPluginLoader
|
||||
|
||||
/**
|
||||
* 从 [JarPluginLoader.settingStorage] 获取一个 [Setting] 实例
|
||||
* 从 [JarPluginLoader.dataStorage] 获取一个 [PluginData] 实例
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun <T : Setting> loadSetting(clazz: Class<T>): T = loader.settingStorage.load(this, clazz)
|
||||
public fun <T : PluginData> loadPluginData(clazz: Class<T>): T = loader.dataStorage.load(this, clazz)
|
||||
|
||||
/**
|
||||
* 在插件被加载时调用. 只会被调用一次.
|
||||
@ -77,7 +77,12 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun <T : Setting> JvmPlugin.loadSetting(clazz: KClass<T>): T = this.loadSetting(clazz.java)
|
||||
public inline fun <T : PluginData> JvmPlugin.loadPluginData(clazz: KClass<T>): T = this.loadPluginData(clazz.java)
|
||||
|
||||
/**
|
||||
* 读取一个插件数据.
|
||||
*
|
||||
* 插件数据
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : Setting> JvmPlugin.loadSetting(): T = this.loadSetting(T::class)
|
||||
public inline fun <reified T : PluginData> JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class)
|
@ -13,7 +13,7 @@ import com.vdurmont.semver4j.Semver
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.console.internal.setting.SemverAsStringSerializerLoose
|
||||
import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerLoose
|
||||
import net.mamoe.mirai.console.plugin.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.PluginKind
|
||||
|
@ -48,7 +48,7 @@ public abstract class KotlinMemoryPlugin @JvmOverloads constructor(
|
||||
|
||||
public object MyPlugin : KotlinPlugin()
|
||||
|
||||
public object AccountSetting : Setting by MyPlugin.getSetting() {
|
||||
public object AccountPluginData : PluginData by MyPlugin.getPluginData() {
|
||||
public val s by value(1)
|
||||
}
|
||||
*/
|
@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_SUPER_CLASS")
|
||||
|
||||
package net.mamoe.mirai.console.setting
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.console.internal.setting.*
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.loadSetting
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
|
||||
|
||||
/**
|
||||
* 序列化之后的名称.
|
||||
*
|
||||
* 例:
|
||||
* ```
|
||||
* @SerialName("accounts")
|
||||
* object AccountSettings : Setting by ... {
|
||||
* @SerialName("info")
|
||||
* val map: Map<String, String> by value("a" to "b")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 将被保存为配置 (YAML 作为示例):
|
||||
* ```yaml
|
||||
* accounts:
|
||||
* info:
|
||||
* a: b
|
||||
* ```
|
||||
*/
|
||||
public typealias SerialName = kotlinx.serialization.SerialName
|
||||
|
||||
/**
|
||||
* [Setting] 的默认实现. 支持使用 `by value()` 等委托方法创建 [Value] 并跟踪其改动.
|
||||
*
|
||||
* @see Setting
|
||||
*/
|
||||
public abstract class AbstractSetting : Setting, SettingImpl() {
|
||||
/**
|
||||
* 添加了追踪的 [ValueNode] 列表, 即通过 `by value` 初始化的属性列表.
|
||||
*
|
||||
* 他们的修改会被跟踪, 并触发 [onValueChanged].
|
||||
*
|
||||
* @see provideDelegate
|
||||
*/
|
||||
public override val valueNodes: MutableList<ValueNode<*>> = mutableListOf()
|
||||
|
||||
/**
|
||||
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性.
|
||||
*/
|
||||
public data class ValueNode<T>(
|
||||
val serialName: String,
|
||||
val value: Value<T>,
|
||||
@ConsoleExperimentalAPI
|
||||
val updaterSerializer: KSerializer<Unit>
|
||||
)
|
||||
|
||||
/**
|
||||
* 使用 `by` 时自动调用此方法, 添加对 [Value] 的值修改的跟踪.
|
||||
*
|
||||
* 将会创建一个 [ValueNode] 并添加到 [valueNodes]
|
||||
*/
|
||||
public final override operator fun <T> SerializerAwareValue<T>.provideDelegate(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>
|
||||
): SerializerAwareValue<T> {
|
||||
val name = property.serialName
|
||||
valueNodes.add(ValueNode(name, this, this.serializer))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 值更新序列化器. 仅供内部使用.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public final override val updaterSerializer: KSerializer<Unit>
|
||||
get() = super.updaterSerializer
|
||||
|
||||
/**
|
||||
* 当所属于这个 [Setting] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
||||
*/
|
||||
public abstract override fun onValueChanged(value: Value<*>)
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个配置对象. 可包含对多个 [Value] 的值变更的跟踪.
|
||||
*
|
||||
* 在 [JvmPlugin] 的典型实现方式:
|
||||
* ```
|
||||
* object PluginMain : KotlinPlugin()
|
||||
*
|
||||
* object AccountSettings : Setting by PluginMain.loadSetting() {
|
||||
* val map: Map<String, String> by value("a" to "b")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see JvmPlugin.loadSetting 通过 [JvmPlugin] 获取指定 [Setting] 实例.
|
||||
*/
|
||||
public interface Setting : ExperimentalSettingExtensions {
|
||||
/**
|
||||
* 使用 `by` 时自动调用此方法, 添加对 [Value] 的值修改的跟踪.
|
||||
*/
|
||||
public operator fun <T> SerializerAwareValue<T>.provideDelegate(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>
|
||||
): SerializerAwareValue<T>
|
||||
|
||||
/**
|
||||
* 值更新序列化器. 仅供内部使用
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val updaterSerializer: KSerializer<Unit>
|
||||
|
||||
/**
|
||||
* 当所属于这个 [Setting] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
||||
*/
|
||||
public fun onValueChanged(value: Value<*>)
|
||||
|
||||
/**
|
||||
* 当这个 [Setting] 被放入一个 [SettingStorage] 时调用
|
||||
*/
|
||||
public fun setStorage(storage: SettingStorage)
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI("")
|
||||
public interface ExperimentalSettingExtensions {
|
||||
public fun <E, V, K> MutableMap<E, V>.shadowMap(
|
||||
eToK: (E) -> K,
|
||||
kToE: (K) -> E
|
||||
): MutableMap<K, V> {
|
||||
return this.shadowMap(
|
||||
kTransform = eToK,
|
||||
kTransformBack = kToE,
|
||||
vTransform = { it },
|
||||
vTransformBack = { it }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//// region Setting_value_primitives CODEGEN ////
|
||||
|
||||
public fun Setting.value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default)
|
||||
public fun Setting.value(default: Short): SerializerAwareValue<Short> = valueImpl(default)
|
||||
public fun Setting.value(default: Int): SerializerAwareValue<Int> = valueImpl(default)
|
||||
public fun Setting.value(default: Long): SerializerAwareValue<Long> = valueImpl(default)
|
||||
public fun Setting.value(default: Float): SerializerAwareValue<Float> = valueImpl(default)
|
||||
public fun Setting.value(default: Double): SerializerAwareValue<Double> = valueImpl(default)
|
||||
public fun Setting.value(default: Char): SerializerAwareValue<Char> = valueImpl(default)
|
||||
public fun Setting.value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(default)
|
||||
public fun Setting.value(default: String): SerializerAwareValue<String> = valueImpl(default)
|
||||
|
||||
//// endregion Setting_value_primitives CODEGEN ////
|
||||
|
||||
|
||||
/**
|
||||
* 通过具体化类型创建一个 [SerializerAwareValue], 并设置初始值.
|
||||
*
|
||||
* @param T 具体化参数类型 T. 仅支持:
|
||||
* - 基础数据类型
|
||||
* - 标准库集合类型 ([List], [Map], [Set])
|
||||
* - 标准库数据类型 ([Map.Entry], [Pair], [Triple])
|
||||
* - 和使用 [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) 的 [Serializable] 标记的
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@LowPriorityInOverloadResolution
|
||||
public inline fun <reified T> Setting.value(default: T): SerializerAwareValue<T> = valueFromKType(typeOf0<T>(), default)
|
||||
|
||||
/**
|
||||
* 通过具体化类型创建一个 [SerializerAwareValue].
|
||||
* @see valueFromKType 查看更多实现信息
|
||||
*/
|
||||
@LowPriorityInOverloadResolution
|
||||
public inline fun <reified T> Setting.value(): SerializerAwareValue<T> =
|
||||
value(T::class.run { objectInstance ?: createInstanceSmart() } as T)
|
||||
|
||||
/**
|
||||
* 通过一个特定的 [KType] 创建 [Value], 并设置初始值.
|
||||
*
|
||||
* 对于 [List], [Map], [Set] 等标准库类型, 这个函数会尝试构造 [LinkedHashMap] 等相关类型.
|
||||
* 而对于自定义数据类型, 本函数只会反射获取 [objectInstance][KClass.objectInstance] 或使用无参构造器构造实例.
|
||||
*
|
||||
* @param T 具体化参数类型 T. 仅支持:
|
||||
* - 基础数据类型
|
||||
* - 标准库集合类型 ([List], [Map], [Set])
|
||||
* - 标准库数据类型 ([Map.Entry], [Pair], [Triple])
|
||||
* - 和使用 [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) 的 [Serializable] 标记的
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ConsoleExperimentalAPI
|
||||
public fun <T> Setting.valueFromKType(type: KType, default: T): SerializerAwareValue<T> =
|
||||
(valueFromKTypeImpl(type) as SerializerAwareValue<Any?>).apply { this.value = default } as SerializerAwareValue<T>
|
||||
|
||||
// TODO: 2020/6/24 Introduce class TypeToken for compound types for Java.
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST", "unused")
|
||||
|
||||
package net.mamoe.mirai.console.setting
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.internal.setting.*
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.setting.SettingStorage.Companion.load
|
||||
import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
/**
|
||||
* [Setting] 存储容器.
|
||||
*
|
||||
* 此为较低层的 API, 一般插件开发者不会接触.
|
||||
*
|
||||
* [JarPluginLoader] 实现一个 [SettingStorage], 用于管理所有 [JvmPlugin] 的 [Setting] 实例.
|
||||
*
|
||||
* @see SettingHolder
|
||||
* @see JarPluginLoader.settingStorage
|
||||
*/
|
||||
public interface SettingStorage {
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [SettingStorage]][Setting.setStorage]
|
||||
*/
|
||||
public fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T
|
||||
|
||||
/**
|
||||
* 保存一个实例
|
||||
*/
|
||||
public fun store(holder: SettingHolder, setting: Setting)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [SettingStorage]][Setting.setStorage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun <T : Setting> SettingStorage.load(holder: SettingHolder, settingClass: KClass<T>): T =
|
||||
this.load(holder, settingClass.java)
|
||||
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [SettingStorage]][Setting.setStorage]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : Setting> SettingStorage.load(holder: SettingHolder): T =
|
||||
this.load(holder, T::class)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在内存存储所有 [Setting] 实例的 [SettingStorage]. 在内存数据丢失后相关 [Setting] 实例也会丢失.
|
||||
*/
|
||||
public interface MemorySettingStorage : SettingStorage, Map<Class<out Setting>, Setting> {
|
||||
/**
|
||||
* 当任一 [Setting] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
||||
*/
|
||||
public fun interface OnChangedCallback {
|
||||
public fun onChanged(storage: MemorySettingStorage, value: Value<*>)
|
||||
|
||||
/**
|
||||
* 无任何操作的 [OnChangedCallback]
|
||||
* @see OnChangedCallback
|
||||
*/
|
||||
public object NoOp : OnChangedCallback {
|
||||
public override fun onChanged(storage: MemorySettingStorage, value: Value<*>) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [MemorySettingStorage] 实例.
|
||||
*
|
||||
* @param onChanged 当任一 [Setting] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
// @JvmOverloads
|
||||
public operator fun invoke(onChanged: OnChangedCallback = OnChangedCallback.NoOp): MemorySettingStorage =
|
||||
MemorySettingStorageImpl(onChanged)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用多个文件存储 [Setting] 实例的 [SettingStorage].
|
||||
*/
|
||||
public interface MultiFileSettingStorage : SettingStorage {
|
||||
/**
|
||||
* 存放 [Setting] 的目录.
|
||||
*/
|
||||
public val directory: File
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [MultiFileSettingStorage] 实例.
|
||||
*
|
||||
* @see directory 存放 [Setting] 的目录.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
public operator fun invoke(directory: File): MultiFileSettingStorage = MultiFileSettingStorageImpl(directory)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以持有相关 [Setting] 实例的对象, 作为 [Setting] 实例的拥有者.
|
||||
*
|
||||
* @see SettingStorage.load
|
||||
* @see SettingStorage.store
|
||||
*
|
||||
* @see AutoSaveSettingHolder 自动保存
|
||||
*/
|
||||
public interface SettingHolder {
|
||||
/**
|
||||
* 保存时使用的分类名
|
||||
*/
|
||||
public val name: String
|
||||
|
||||
/**
|
||||
* 创建一个 [Setting] 实例.
|
||||
*
|
||||
* @see Companion.newSettingInstance
|
||||
* @see KClass.createType
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun <T : Setting> newSettingInstance(type: KType): T =
|
||||
newSettingInstanceUsingReflection<Setting>(type) as T
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [Setting] 实例.
|
||||
*
|
||||
* @see SettingHolder.newSettingInstance
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : Setting> SettingHolder.newSettingInstance(): T {
|
||||
return this.newSettingInstance(typeOf0<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以持有相关 [AutoSaveSetting] 的对象.
|
||||
*
|
||||
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
*/
|
||||
public interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
|
||||
/**
|
||||
* [AutoSaveSetting] 每次自动保存时间间隔
|
||||
*
|
||||
* - 区间的左端点为最小间隔, 一个 [Value] 被修改后, 若此时间段后无其他修改, 将触发自动保存; 若有, 将重新开始计时.
|
||||
* - 区间的右端点为最大间隔, 一个 [Value] 被修改后, 最多不超过这个时间段后就会被保存.
|
||||
*
|
||||
* 若 [AutoSaveSettingHolder.coroutineContext] 含有 [Job],
|
||||
* 则 [AutoSaveSetting] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||
*
|
||||
* @see LongRange Java 用户使用 [LongRange] 的构造器创建
|
||||
* @see Long.rangeTo Kotlin 用户使用 [Long.rangeTo] 创建, 如 `3000..50000`
|
||||
*/
|
||||
public val autoSaveIntervalMillis: LongRange
|
||||
|
||||
/**
|
||||
* 仅支持确切的 [Setting] 类型
|
||||
*/
|
||||
@JvmDefault
|
||||
public override fun <T : Setting> newSettingInstance(type: KType): T {
|
||||
val classifier = type.classifier?.cast<KClass<Setting>>()
|
||||
require(classifier != null && classifier.java == Setting::class.java) {
|
||||
"Cannot create Setting instance. AutoSaveSettingHolder supports only Setting type."
|
||||
}
|
||||
return AutoSaveSetting(this, classifier) as T // T is always Setting
|
||||
}
|
||||
}
|
@ -17,11 +17,11 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.MemoryPluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.plugin.DeferredPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.setting.MemorySettingStorage
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
@ -51,8 +51,8 @@ fun initTestEnvironment() {
|
||||
override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() {
|
||||
override suspend fun sendMessage(message: Message) = println(message)
|
||||
}
|
||||
override val settingStorageForJarPluginLoader: SettingStorage get() = MemorySettingStorage()
|
||||
override val settingStorageForBuiltIns: SettingStorage get() = MemorySettingStorage()
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val coroutineContext: CoroutineContext = SupervisorJob()
|
||||
}.start()
|
||||
CommandManager
|
||||
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.setting
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
@ -16,9 +16,9 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertSame
|
||||
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
internal class SettingTest {
|
||||
internal class PluginDataTest {
|
||||
|
||||
class MySetting : AbstractSetting() {
|
||||
class MyPluginData : AbstractPluginData() {
|
||||
var int by value(1)
|
||||
val map by value<MutableMap<String, String>>()
|
||||
val map2 by value<MutableMap<String, MutableMap<String, String>>>()
|
||||
@ -28,7 +28,7 @@ internal class SettingTest {
|
||||
|
||||
}
|
||||
|
||||
override fun setStorage(storage: SettingStorage) {
|
||||
override fun setStorage(storage: PluginDataStorage) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,45 +37,45 @@ internal class SettingTest {
|
||||
|
||||
@Test
|
||||
fun testStringify() {
|
||||
val setting = MySetting()
|
||||
val data = MyPluginData()
|
||||
|
||||
var string = json.encodeToString(setting.updaterSerializer, Unit)
|
||||
var string = json.encodeToString(data.updaterSerializer, Unit)
|
||||
assertEquals("""{"int":1,"map":{},"map2":{}}""", string)
|
||||
|
||||
setting.int = 2
|
||||
data.int = 2
|
||||
|
||||
string = json.encodeToString(setting.updaterSerializer, Unit)
|
||||
string = json.encodeToString(data.updaterSerializer, Unit)
|
||||
assertEquals("""{"int":2,"map":{},"map2":{}}""", string)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseUpdate() {
|
||||
val setting = MySetting()
|
||||
val data = MyPluginData()
|
||||
|
||||
assertEquals(1, setting.int)
|
||||
assertEquals(1, data.int)
|
||||
|
||||
json.decodeFromString(
|
||||
setting.updaterSerializer, """
|
||||
data.updaterSerializer, """
|
||||
{"int":3,"map":{},"map2":{}}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertEquals(3, setting.int)
|
||||
assertEquals(3, data.int)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedParseUpdate() {
|
||||
val setting = MySetting()
|
||||
val data = MyPluginData()
|
||||
|
||||
fun delegation() = setting.map
|
||||
fun delegation() = data.map
|
||||
|
||||
val refBefore = setting.map
|
||||
val refBefore = data.map
|
||||
fun reference() = refBefore
|
||||
|
||||
assertEquals(mutableMapOf(), delegation()) // delegation
|
||||
|
||||
json.decodeFromString(
|
||||
setting.updaterSerializer, """
|
||||
data.updaterSerializer, """
|
||||
{"int":1,"map":{"t":"test"},"map2":{}}
|
||||
""".trimIndent()
|
||||
)
|
||||
@ -88,17 +88,17 @@ internal class SettingTest {
|
||||
|
||||
@Test
|
||||
fun testDeepNestedParseUpdate() {
|
||||
val setting = MySetting()
|
||||
val data = MyPluginData()
|
||||
|
||||
fun delegation() = setting.map2
|
||||
fun delegation() = data.map2
|
||||
|
||||
val refBefore = setting.map2
|
||||
val refBefore = data.map2
|
||||
fun reference() = refBefore
|
||||
|
||||
assertEquals(mutableMapOf(), delegation()) // delegation
|
||||
|
||||
json.decodeFromString(
|
||||
setting.updaterSerializer, """
|
||||
data.updaterSerializer, """
|
||||
{"int":1,"map":{},"map2":{"t":{"f":"test"}}}
|
||||
""".trimIndent()
|
||||
)
|
||||
@ -111,19 +111,19 @@ internal class SettingTest {
|
||||
|
||||
@Test
|
||||
fun testDeepNestedTrackingParseUpdate() {
|
||||
val setting = MySetting()
|
||||
val data = MyPluginData()
|
||||
|
||||
setting.map2["t"] = mutableMapOf()
|
||||
data.map2["t"] = mutableMapOf()
|
||||
|
||||
fun delegation() = setting.map2["t"]!!
|
||||
fun delegation() = data.map2["t"]!!
|
||||
|
||||
val refBefore = setting.map2["t"]!!
|
||||
val refBefore = data.map2["t"]!!
|
||||
fun reference() = refBefore
|
||||
|
||||
assertEquals(mutableMapOf(), delegation()) // delegation
|
||||
|
||||
json.decodeFromString(
|
||||
setting.updaterSerializer, """
|
||||
data.updaterSerializer, """
|
||||
{"int":1,"map":{},"map2":{"t":{"f":"test"}}}
|
||||
""".trimIndent()
|
||||
)
|
@ -28,11 +28,11 @@ import net.mamoe.mirai.console.ConsoleFrontEndImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsoleFrontEnd
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.plugin.DeferredPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.setting.MultiFileSettingStorage
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
@ -53,8 +53,8 @@ class MiraiConsoleImplementationPure
|
||||
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure,
|
||||
override val mainLogger: MiraiLogger = frontEnd.loggerFor("main"),
|
||||
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
|
||||
override val settingStorageForJarPluginLoader: SettingStorage = MultiFileSettingStorage(File(rootDir, "data")),
|
||||
override val settingStorageForBuiltIns: SettingStorage = MultiFileSettingStorage(File(rootDir, "data"))
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(File(rootDir, "data")),
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(File(rootDir, "data"))
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) {
|
||||
init {
|
||||
rootDir.mkdir()
|
||||
|
@ -20,9 +20,18 @@
|
||||
|
||||
package net.mamoe.mirai.console.pure
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.alsoLogin
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
||||
import net.mamoe.mirai.console.command.CommandPermission
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.command.SimpleCommand
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.nameCardOrNick
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.content
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
@ -35,9 +44,24 @@ object MiraiConsolePureLoader {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>?) {
|
||||
startup()
|
||||
YellowCommand.register()
|
||||
runBlocking { MiraiConsole.addBot(1994701021, "Asd123456789asd").alsoLogin() }
|
||||
}
|
||||
}
|
||||
|
||||
object YellowCommand : SimpleCommand(
|
||||
net.mamoe.mirai.console.command.ConsoleCommandOwner, "睡", "sleep",
|
||||
prefixOptional = true,
|
||||
description = "睡一个人",
|
||||
permission = CommandPermission.Any
|
||||
) {
|
||||
@Handler
|
||||
suspend fun CommandSender.handle(target: Member) {
|
||||
target.mute(1)
|
||||
|
||||
sendMessage("${this.name} 睡了 ${target.nameCardOrNick}")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun startup() {
|
||||
DefaultLogger = { MiraiConsoleFrontEndPure.loggerFor(it) }
|
||||
|
Loading…
Reference in New Issue
Block a user