mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
ExtensionSelectors and improved extensions API
This commit is contained in:
parent
e1f8125163
commit
bf4c82f1a5
@ -17,6 +17,8 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
* 表示一个扩展.
|
||||
*
|
||||
* Console 许多不容易通过
|
||||
*
|
||||
* @see ExtensionWithConfig
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface Extension
|
||||
|
@ -11,15 +11,48 @@
|
||||
|
||||
package net.mamoe.mirai.console.extension
|
||||
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public interface ExtensionPoint<T : Extension> {
|
||||
public val type: KClass<T>
|
||||
|
||||
public fun registerExtension(plugin: Plugin, extension: T)
|
||||
public fun getExtensions(): Set<ExtensionRegistry<T>>
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
@JvmSynthetic
|
||||
@ConsoleExperimentalAPI
|
||||
public inline fun <reified T : Extension> ExtensionPoint<*>.isFor(exactType: Boolean = false): Boolean {
|
||||
return if (exactType) {
|
||||
T::class == type
|
||||
} else T::class.isSubclassOf(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public interface SingletonExtensionPoint<T : SingletonExtension<*>> : ExtensionPoint<T> {
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
@ConsoleExperimentalAPI
|
||||
public fun <T : SingletonExtension<*>> SingletonExtensionPoint<T>.findSingleton(): T? {
|
||||
return SingletonExtensionSelector.selectSingleton(type, this.getExtensions())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示一个扩展点
|
||||
@ -27,26 +60,28 @@ import kotlin.reflect.KClass
|
||||
@ConsoleExperimentalAPI
|
||||
public open class AbstractExtensionPoint<T : Extension>(
|
||||
@ConsoleExperimentalAPI
|
||||
public val type: KClass<T>
|
||||
) {
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public data class ExtensionRegistry<T>(
|
||||
public val plugin: Plugin,
|
||||
public val extension: T
|
||||
)
|
||||
public override val type: KClass<T>
|
||||
) : ExtensionPoint<T> {
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
allExtensionPoints.add(this)
|
||||
}
|
||||
|
||||
private val instances: MutableSet<ExtensionRegistry<T>> = CopyOnWriteArraySet()
|
||||
|
||||
@Synchronized
|
||||
@ConsoleExperimentalAPI
|
||||
public fun registerExtension(plugin: Plugin, extension: T) {
|
||||
public override fun registerExtension(plugin: Plugin, extension: T) {
|
||||
// require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." }
|
||||
requireNotNull(extension::class.qualifiedName) { "Extension must not be an anonymous object" }
|
||||
instances.add(ExtensionRegistry(plugin, extension))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
internal fun getExtensions(): Set<ExtensionRegistry<T>> = instances
|
||||
public override fun getExtensions(): Set<ExtensionRegistry<T>> = Collections.unmodifiableSet(instances)
|
||||
|
||||
internal companion object {
|
||||
@ConsoleExperimentalAPI
|
||||
internal val allExtensionPoints: MutableList<AbstractExtensionPoint<*>> = mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.extension
|
||||
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public data class ExtensionRegistry<T>(
|
||||
public val plugin: Plugin,
|
||||
public val extension: T
|
||||
)
|
@ -5,7 +5,6 @@ package net.mamoe.mirai.console.extensions
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.FunctionExtension
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
|
||||
/**
|
||||
@ -13,7 +12,6 @@ import net.mamoe.mirai.utils.BotConfiguration
|
||||
*
|
||||
* @see MiraiConsole.addBot
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface BotConfigurationAlterer : FunctionExtension {
|
||||
|
||||
/**
|
||||
|
@ -2,16 +2,19 @@ package net.mamoe.mirai.console.extensions
|
||||
|
||||
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.SingletonExtension
|
||||
import net.mamoe.mirai.console.extension.SingletonExtensionPoint
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
|
||||
/**
|
||||
* [权限服务][PermissionService] 提供器.
|
||||
*
|
||||
* 此扩展
|
||||
* 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供
|
||||
*/
|
||||
@ExperimentalPermission
|
||||
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
|
||||
public companion object ExtensionPoint :
|
||||
AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class)
|
||||
AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class),
|
||||
SingletonExtensionPoint<PermissionServiceProvider>
|
||||
}
|
@ -3,9 +3,12 @@ package net.mamoe.mirai.console.extensions
|
||||
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.InstanceExtension
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
|
||||
/**
|
||||
* 提供扩展 [PluginLoader]
|
||||
*
|
||||
* 此扩展可由 [PluginKind.LOADER] 插件提供
|
||||
*/
|
||||
public interface PluginLoaderProvider : InstanceExtension<PluginLoader<*, *>> {
|
||||
public companion object ExtensionPoint : AbstractExtensionPoint<PluginLoaderProvider>(PluginLoaderProvider::class)
|
||||
|
@ -16,6 +16,8 @@ import net.mamoe.mirai.console.extension.FunctionExtension
|
||||
* 在 Console 启动完成后立即在主线程调用的扩展. 用于进行一些必要的延迟初始化.
|
||||
*
|
||||
* 这些扩展只会, 且一定会被调用正好一次.
|
||||
*
|
||||
* 此扩展可由所有插件提供
|
||||
*/
|
||||
public fun interface PostStartupExtension : FunctionExtension {
|
||||
/**
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.extensions
|
||||
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.extension.*
|
||||
import net.mamoe.mirai.console.internal.extensions.BuiltInSingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.utils.info
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* 用于同时拥有多个 [SingletonExtension] 时选择一个实例.
|
||||
*
|
||||
* 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器.
|
||||
*
|
||||
* 此扩展可由 [PluginKind.LOADER] 和 [PluginKind.HIGH_PRIORITY_EXTENSIONS] 插件提供
|
||||
*/
|
||||
public interface SingletonExtensionSelector : FunctionExtension {
|
||||
|
||||
public fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
): T?
|
||||
|
||||
public companion object ExtensionPoint :
|
||||
AbstractExtensionPoint<SingletonExtensionSelector>(SingletonExtensionSelector::class) {
|
||||
internal val instance: SingletonExtensionSelector by lazy {
|
||||
val instances = SingletonExtensionSelector.getExtensions()
|
||||
when {
|
||||
instances.isEmpty() -> BuiltInSingletonExtensionSelector
|
||||
instances.size == 1 -> {
|
||||
instances.single().also { (plugin, ext) ->
|
||||
MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: $ext from ${plugin.name}" }
|
||||
}.extension
|
||||
}
|
||||
else -> {
|
||||
error("Found too many SingletonExtensionSelectors: ${instances.joinToString { (p, i) -> "'$i' from '${p.name}'" }}. Check your plugins and ensure there is only one external SingletonExtensionSelectors")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
): T? =
|
||||
instance.selectSingleton(extensionType, candidates)
|
||||
}
|
||||
}
|
@ -28,14 +28,15 @@ import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.extension.useExtensions
|
||||
import net.mamoe.mirai.console.extensions.PostStartupExtension
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
|
||||
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.permission.BuiltInPermissionService
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.permission.StorablePermissionService
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
@ -135,10 +136,12 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." }
|
||||
}
|
||||
|
||||
SingletonExtensionSelector // init
|
||||
|
||||
phase `load PermissionService`@{
|
||||
mainLogger.verbose { "Loading PermissionService..." }
|
||||
PermissionService.INSTANCE.let { ps ->
|
||||
if (ps is StorablePermissionService<*>) {
|
||||
if (ps is BuiltInPermissionService) {
|
||||
ConsoleDataScope.addAndReloadConfig(ps.config)
|
||||
mainLogger.verbose { "Reloaded PermissionService settings." }
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
package net.mamoe.mirai.console.internal.extensions
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginData
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.extension.Extension
|
||||
import net.mamoe.mirai.console.extension.ExtensionRegistry
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.utils.info
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector {
|
||||
|
||||
private val config: SaveData = SaveData()
|
||||
|
||||
private class SaveData : AutoSavePluginData() {
|
||||
override val saveName: String get() = "ExtensionSelector"
|
||||
|
||||
val value: MutableMap<String, String> by value()
|
||||
}
|
||||
|
||||
override fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
): T? = when {
|
||||
candidates.isEmpty() -> null
|
||||
candidates.size == 1 -> candidates.single().extension
|
||||
else -> kotlin.run {
|
||||
val target = config.value[extensionType.qualifiedName!!]
|
||||
?: return promptForSelectionAndSave(extensionType, candidates)
|
||||
|
||||
val found = candidates.firstOrNull { it.extension::class.qualifiedName == target }?.extension
|
||||
?: return promptForSelectionAndSave(extensionType, candidates)
|
||||
|
||||
found
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Extension> promptForSelectionAndSave(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
) = promptForManualSelection(extensionType, candidates)
|
||||
.also { config.value[extensionType.qualifiedName!!] = it.extension.kClassQualifiedName!! }.extension
|
||||
|
||||
private fun <T : Any> promptForManualSelection(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
): ExtensionRegistry<T> {
|
||||
MiraiConsole.mainLogger.info { "There are multiple ${extensionType.simpleName}s, please select one:" }
|
||||
|
||||
val candidatesList = candidates.toList()
|
||||
|
||||
for ((index, candidate) in candidatesList.withIndex()) {
|
||||
MiraiConsole.mainLogger.info { "${index + 1}. '${candidate.extension}' from '${candidate.plugin.name}'" }
|
||||
}
|
||||
|
||||
MiraiConsole.mainLogger.info { "Please choose a number from 1 to ${candidatesList.count()}" }
|
||||
|
||||
val choiceStr = runBlocking { ConsoleInput.requestInput("Your choice: ") }
|
||||
|
||||
val choice = choiceStr.toIntOrNull() ?: error("Bad choice")
|
||||
|
||||
return candidatesList[choice - 1]
|
||||
}
|
||||
}
|
@ -10,8 +10,16 @@
|
||||
package net.mamoe.mirai.console.permission
|
||||
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.data.valueFromKType
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KTypeProjection
|
||||
import kotlin.reflect.KVariance
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
|
||||
@ -72,8 +80,8 @@ public object AllDenyPermissionService : PermissionService<PermissionImpl> {
|
||||
}
|
||||
|
||||
@ExperimentalPermission
|
||||
internal object BuiltInPermissionService : AbstractConcurrentPermissionService<PermissionImpl>(),
|
||||
StorablePermissionService<PermissionImpl> {
|
||||
public object BuiltInPermissionService : AbstractConcurrentPermissionService<PermissionImpl>(),
|
||||
PermissionService<PermissionImpl> {
|
||||
|
||||
@ExperimentalPermission
|
||||
override val permissionType: KClass<PermissionImpl>
|
||||
@ -87,10 +95,43 @@ internal object BuiltInPermissionService : AbstractConcurrentPermissionService<P
|
||||
override fun createPermission(id: PermissionId, description: String, base: PermissionId?): PermissionImpl =
|
||||
PermissionImpl(id, description, base)
|
||||
|
||||
override val config: StorablePermissionService.ConcurrentSaveData<PermissionImpl> =
|
||||
StorablePermissionService.ConcurrentSaveData(
|
||||
internal val config: ConcurrentSaveData<PermissionImpl> =
|
||||
ConcurrentSaveData(
|
||||
PermissionImpl::class.createType(),
|
||||
"PermissionService",
|
||||
AutoSavePluginConfig()
|
||||
)
|
||||
|
||||
@Suppress("RedundantVisibilityModifier")
|
||||
@ExperimentalPermission
|
||||
internal class ConcurrentSaveData<P : Permission> private constructor(
|
||||
permissionType: KType,
|
||||
public override val saveName: String,
|
||||
delegate: PluginConfig,
|
||||
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
|
||||
) : PluginConfig by delegate {
|
||||
public val permissions: MutableMap<PermissionId, P>
|
||||
by valueFromKType<MutableMap<PermissionId, P>>(
|
||||
MutableMap::class.createType(
|
||||
listOf(
|
||||
KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()),
|
||||
KTypeProjection(KVariance.INVARIANT, permissionType),
|
||||
)
|
||||
),
|
||||
ConcurrentHashMap()
|
||||
)
|
||||
|
||||
public val grantedPermissionMap: MutableMap<PermissionId, List<PermissibleIdentifier>>
|
||||
by value<MutableMap<PermissionId, List<PermissibleIdentifier>>>(ConcurrentHashMap())
|
||||
.withDefault { CopyOnWriteArrayList() }
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
public operator fun <P : Permission> invoke(
|
||||
permissionType: KType,
|
||||
saveName: String,
|
||||
delegate: PluginConfig,
|
||||
): ConcurrentSaveData<P> = ConcurrentSaveData(permissionType, saveName, delegate, null)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.permission
|
||||
|
||||
import net.mamoe.mirai.console.extension.SingletonExtensionPoint.Companion.findSingleton
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
@ -49,13 +50,10 @@ public interface PermissionService<P : Permission> {
|
||||
public fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P)
|
||||
|
||||
public companion object {
|
||||
private val builtIn: PermissionService<out Permission> get() = BuiltInPermissionService
|
||||
|
||||
@get:JvmName("getInstance")
|
||||
@JvmStatic
|
||||
public val INSTANCE: PermissionService<out Permission> by lazy {
|
||||
PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance ?: builtIn
|
||||
// TODO: 2020/9/4 ExtensionSelector
|
||||
PermissionServiceProvider.findSingleton()?.instance ?: BuiltInPermissionService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
package net.mamoe.mirai.console.permission
|
||||
|
||||
import net.mamoe.mirai.console.data.PluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.data.valueFromKType
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KTypeProjection
|
||||
import kotlin.reflect.KVariance
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
@ExperimentalPermission
|
||||
public interface StorablePermissionService<P : Permission> : PermissionService<P> {
|
||||
/**
|
||||
* The config to be stored
|
||||
*/
|
||||
public val config: PluginConfig
|
||||
|
||||
@ExperimentalPermission
|
||||
public class ConcurrentSaveData<P : Permission> private constructor(
|
||||
permissionType: KType,
|
||||
public override val saveName: String,
|
||||
delegate: PluginConfig,
|
||||
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
|
||||
) : PluginConfig by delegate {
|
||||
public val permissions: MutableMap<PermissionId, P>
|
||||
by valueFromKType<MutableMap<PermissionId, P>>(
|
||||
MutableMap::class.createType(
|
||||
listOf(
|
||||
KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()),
|
||||
KTypeProjection(KVariance.INVARIANT, permissionType),
|
||||
)
|
||||
),
|
||||
ConcurrentHashMap()
|
||||
)
|
||||
|
||||
public val grantedPermissionMap: MutableMap<PermissionId, List<PermissibleIdentifier>>
|
||||
by value<MutableMap<PermissionId, List<PermissibleIdentifier>>>(ConcurrentHashMap())
|
||||
.withDefault { CopyOnWriteArrayList() }
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
public operator fun <P : Permission> invoke(
|
||||
permissionType: KType,
|
||||
saveName: String,
|
||||
delegate: PluginConfig,
|
||||
): ConcurrentSaveData<P> = ConcurrentSaveData(permissionType, saveName, delegate, null)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user