Rename Setting to PluginData for compatiblity issue

This commit is contained in:
Him188 2020-08-22 01:36:22 +08:00
parent 938db950e6
commit 0016a04d5e
42 changed files with 851 additions and 841 deletions

View File

@ -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");
}
}

View File

@ -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 }

View File

@ -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" +
"\"\"\""

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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()
}
}
}

View File

@ -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

View File

@ -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<*>)
}

View File

@ -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

View File

@ -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.

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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].
*/

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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> {

View File

@ -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<*>)

View File

@ -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
}
}

View File

@ -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))
}
}
}

View File

@ -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

View File

@ -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 ////

View File

@ -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) {

View File

@ -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."
)
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 +

View File

@ -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 =

View File

@ -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

View 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)
}

View File

@ -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

View 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")

View File

@ -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)

View File

@ -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

View File

@ -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)
}
*/

View File

@ -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.

View File

@ -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
}
}

View File

@ -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

View File

@ -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()
)

View File

@ -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()

View File

@ -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) }