-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathYAMLBackend.kt
202 lines (178 loc) · 6.97 KB
/
YAMLBackend.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package cyou.untitled.bungeesafeguard.storage
import cyou.untitled.bungeesafeguard.BungeeSafeguard
import cyou.untitled.bungeesafeguard.helpers.ListChecker
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.md_5.bungee.api.ChatColor
import net.md_5.bungee.api.CommandSender
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.api.plugin.Plugin
import net.md_5.bungee.config.Configuration
import net.md_5.bungee.config.ConfigurationProvider
import net.md_5.bungee.config.YamlConfiguration
import java.io.File
import java.util.*
/**
* The backend that uses a YAML file to store the lists in human-friendly format
* (i.e., the legacy format in older versions)
*/
@Suppress("BlockingMethodInNonBlockingContext", "MemberVisibilityCanBePrivate")
open class YAMLBackend(context: Plugin, initFile: File): Backend(context) {
companion object {
/* YAML entries */
const val WHITELIST = "whitelist"
const val LAZY_WHITELIST = "lazy-whitelist"
const val BLACKLIST = "blacklist"
const val LAZY_BLACKLIST = "lazy-blacklist"
val pathTranslations = mapOf(
Pair("whitelist.main", WHITELIST),
Pair("whitelist.lazy", LAZY_WHITELIST),
Pair("blacklist.main", BLACKLIST),
Pair("blacklist.lazy", LAZY_BLACKLIST)
)
/**
* Translate the path to legacy config entry
*
* @param path the list path
*/
fun translatePath(path: Array<String>): String {
assert(path.isNotEmpty()) { "Empty path" }
val pathString = path.joinToString(".")
return pathTranslations[pathString] ?: error("Invalid path \"$pathString\"")
}
protected val yamlConfigProvider = ConfigurationProvider.getProvider(YamlConfiguration::class.java)!!
}
protected var initialized = false
var file: File = initFile
protected set
val name = "YAMLBackend-$id"
protected val lock = Mutex()
open suspend fun init(file: File, commandSender: CommandSender?) {
lock.withLock {
assert(!initialized) { "Reinitialize without first closing" }
this.file = file
FileManager.withFile(file.path, name) {
// If it does not throw, assume the file is OK
yamlConfigProvider.load(it)
}
initialized = true
commandSender?.sendMessage(TextComponent("${ChatColor.GREEN}YAMLBackend is using \"${file.path}\" as the underlying storage file"))
}
// Sanity check
val bsg = try {
BungeeSafeguard.getPlugin()
} catch (err: UninitializedPropertyAccessException) { return }
val listMgr = bsg.listMgr
ListChecker.checkLists(bsg, null, listMgr, { get(it.lazyPath) }, { it.lazyName })
ListChecker.checkLists(bsg, null, listMgr, { get(it.path) }, { it.name })
}
/**
* Init without logging and checking
*/
open suspend fun init(file: File = this.file) {
lock.withLock {
assert(!initialized) { "Reinitialize without first closing" }
FileManager.withFile(file.path, name) {
// If it does not throw, assume the file is OK
yamlConfigProvider.load(it)
}
}
}
override suspend fun init(commandSender: CommandSender?) {
init(file, commandSender)
}
override suspend fun close(commandSender: CommandSender?) {
lock.withLock {
initialized = false
}
}
override suspend fun reload(commandSender: CommandSender?) {
lock.withLock {
// Do nothing as we don't cache the data here
}
}
protected suspend fun <T>withEntryAndFile(path: Array<String>, block: suspend (String, File) -> T): T {
lock.withLock {
// First make sure the path is valid
val entry = translatePath(path)
return FileManager.withFile(file.path, name) { block(entry, it) }
}
}
protected suspend fun <T>withEntryAndConfigFile(path: Array<String>, block: suspend (String, Configuration) -> T): T {
return withEntryAndFile(path) { entry, _ ->
val conf = yamlConfigProvider.load(file)
return@withEntryAndFile block(entry, conf)
}
}
protected suspend fun <T>withConfigFile(block: suspend (Configuration) -> T): T {
lock.withLock {
return FileManager.withFile(file.path, name) {
val conf = yamlConfigProvider.load(file)
return@withFile block(conf)
}
}
}
override suspend fun add(path: Array<String>, rawRecord: String): Boolean {
return withEntryAndConfigFile(path) { entry, conf ->
val records = conf.getStringList(entry).toMutableSet()
if (records.add(rawRecord)) {
conf.set(entry, records.toTypedArray())
yamlConfigProvider.save(conf, file)
true
} else {
false
}
}
}
override suspend fun remove(path: Array<String>, rawRecord: String): Boolean {
return withEntryAndConfigFile(path) { entry, conf ->
val records = conf.getStringList(entry).toMutableSet()
if (records.remove(rawRecord)) {
conf.set(entry, records.toTypedArray())
yamlConfigProvider.save(conf, file)
true
} else {
false
}
}
}
override suspend fun has(path: Array<String>, rawRecord: String): Boolean {
return withEntryAndConfigFile(path) { entry, conf ->
conf.getStringList(entry).contains(rawRecord)
}
}
override suspend fun getSize(path: Array<String>): Int {
return get(path).size
}
override suspend fun get(path: Array<String>): Set<String> {
return withEntryAndConfigFile(path) { entry, conf ->
conf.getStringList(entry).toSet()
}
}
override suspend fun moveToListIfInLazyList(
username: String,
id: UUID,
mainPath: Array<String>,
lazyPath: Array<String>,
): Boolean {
return withConfigFile {
val lazyEntry = translatePath(lazyPath)
val mainEntry = translatePath(mainPath)
val lazyRecords = it.getStringList(lazyEntry).toMutableSet()
return@withConfigFile if (lazyRecords.remove(username)) {
it.set(lazyEntry, lazyRecords.toTypedArray())
val mainRecords = it.getStringList(mainEntry).toMutableSet()
mainRecords.add(id.toString())
it.set(mainEntry, mainRecords.toTypedArray())
yamlConfigProvider.save(it, file)
true
} else {
false
}
}
}
override suspend fun onReloadConfigFile(newConfig: File, commandSender: CommandSender?) {
// Do nothing
}
override fun toString(): String = "YAMLBackend(\"${file.path}\")"
}