Skip to content

Commit 73d491f

Browse files
committed
Add cog#unload, pass ObjectStorage to classes for persistent storage (needs testing)
1 parent 0d2d1ed commit 73d491f

File tree

4 files changed

+87
-5
lines changed

4 files changed

+87
-5
lines changed

src/main/kotlin/me/devoxin/flight/api/entities/Cog.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,11 @@ interface Cog {
2626
*/
2727
fun localCheck(ctx: Context, command: CommandFunction): Boolean = true
2828

29+
30+
/**
31+
* Invoked when this Cog gets unloaded, usually through [CommandRegistry.unload].
32+
* This can be used as a last-ditch attempt to clean up, or shut down any resources.
33+
*/
34+
fun unload(): Unit = Unit
35+
2936
}

src/main/kotlin/me/devoxin/flight/api/entities/CommandRegistry.kt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData
99
import net.dv8tion.jda.api.interactions.commands.build.Commands
1010
import net.dv8tion.jda.api.interactions.commands.build.OptionData
1111
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData
12+
import org.slf4j.LoggerFactory
1213

1314
class CommandRegistry : HashMap<String, CommandFunction>() {
15+
val objectStorage = ObjectStorage()
16+
1417
fun toDiscordCommands(): List<CommandData> {
1518
val commands = mutableListOf<CommandData>()
1619

@@ -77,7 +80,7 @@ class CommandRegistry : HashMap<String, CommandFunction>() {
7780
}
7881

7982
fun findCogByName(name: String): Cog? {
80-
return this.values.firstOrNull { it.cog::class.simpleName == name }?.cog
83+
return this.values.firstOrNull { it.cog.name() == name || it.cog::class.simpleName == name }?.cog
8184
}
8285

8386
fun findCommandsByCog(cog: Cog): List<CommandFunction> {
@@ -86,12 +89,15 @@ class CommandRegistry : HashMap<String, CommandFunction>() {
8689

8790
fun unload(commandFunction: CommandFunction) {
8891
this.values.remove(commandFunction)
92+
doUnload(commandFunction.cog)
8993
}
9094

9195
fun unload(cog: Cog) {
9296
val commands = this.values.filter { it.cog == cog }
9397
this.values.removeAll(commands)
9498

99+
commands.map(CommandFunction::cog).let(::doUnload)
100+
95101
val jar = commands.firstOrNull { it.jar != null }?.jar
96102
?: return // No commands loaded from jar, thus no classloader to close.
97103

@@ -107,21 +113,23 @@ class CommandRegistry : HashMap<String, CommandFunction>() {
107113
val commands = this.values.filter { it.jar == jar }
108114
this.values.removeAll(commands)
109115

116+
commands.map(CommandFunction::cog).let(::doUnload)
117+
110118
jar.close()
111119
}
112120

113121
fun register(packageName: String) {
114122
val indexer = Indexer(packageName)
115123

116-
for (cog in indexer.getCogs()) {
124+
for (cog in indexer.getCogs(objectStorage)) {
117125
register(cog, indexer)
118126
}
119127
}
120128

121129
fun register(jarPath: String, packageName: String) {
122130
val indexer = Indexer(packageName, jarPath)
123131

124-
for (cog in indexer.getCogs()) {
132+
for (cog in indexer.getCogs(objectStorage)) {
125133
register(cog, indexer)
126134
}
127135
}
@@ -140,4 +148,24 @@ class CommandRegistry : HashMap<String, CommandFunction>() {
140148
this[cmd.name] = cmd
141149
}
142150
}
151+
152+
private fun doUnload(cogs: Iterable<Cog>) {
153+
val uniqueCogs = cogs.distinctBy(Cog::name)
154+
155+
for (cog in uniqueCogs) {
156+
doUnload(cog)
157+
}
158+
}
159+
160+
private fun doUnload(cog: Cog) {
161+
try {
162+
cog.unload()
163+
} catch (t: Throwable) {
164+
log.error("An error occurred whilst unloading cog \"{}\"", cog.name() ?: cog::class.java.simpleName, t)
165+
}
166+
}
167+
168+
companion object {
169+
private val log = LoggerFactory.getLogger(CommandRegistry::class.java)
170+
}
143171
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package me.devoxin.flight.api.entities
2+
3+
import java.util.concurrent.ConcurrentHashMap
4+
5+
class ObjectStorage {
6+
private val map = ConcurrentHashMap<String, Any>()
7+
8+
val size: Int
9+
get() = map.size
10+
11+
operator fun set(key: String, value: Any) {
12+
map[key] = value
13+
}
14+
15+
operator fun get(key: String): Any? {
16+
return map[key]
17+
}
18+
19+
fun <T> get(key: String, cls: Class<T>): T? {
20+
val value = map[key]
21+
22+
if (cls.isInstance(value)) {
23+
return cls.cast(value)
24+
}
25+
26+
return null
27+
}
28+
29+
@Suppress("UNCHECKED_CAST")
30+
fun <T : Any> computeIfAbsent(key: String, initializer: (String) -> T): T {
31+
return map.computeIfAbsent(key, initializer) as T
32+
}
33+
34+
fun clear() {
35+
map.clear()
36+
}
37+
}

src/main/kotlin/me/devoxin/flight/internal/utils/Indexer.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import me.devoxin.flight.api.annotations.*
77
import me.devoxin.flight.internal.arguments.Argument
88
import me.devoxin.flight.internal.entities.Jar
99
import me.devoxin.flight.api.entities.Cog
10+
import me.devoxin.flight.api.entities.ObjectStorage
1011
import org.reflections.Reflections
1112
import org.reflections.scanners.MethodParameterNamesScanner
1213
import org.reflections.scanners.Scanners
1314
import org.slf4j.LoggerFactory
1415
import java.io.File
16+
import java.lang.reflect.Constructor
1517
import java.lang.reflect.Modifier
1618
import java.net.URL
1719
import java.net.URLClassLoader
@@ -47,13 +49,13 @@ class Indexer {
4749
reflections = Reflections(packageName, this.classLoader, MethodParameterNamesScanner(), Scanners.SubTypes)
4850
}
4951

50-
fun getCogs(): List<Cog> {
52+
fun getCogs(objectStorage: ObjectStorage): List<Cog> {
5153
val cogs = reflections.getSubTypesOf(Cog::class.java)
5254
log.debug("Discovered ${cogs.size} cogs in $packageName")
5355

5456
return cogs
5557
.filter { !Modifier.isAbstract(it.modifiers) && !it.isInterface && Cog::class.java.isAssignableFrom(it) }
56-
.map { it.getDeclaredConstructor().newInstance() }
58+
.map { construct(it, objectStorage) }
5759
}
5860

5961
fun getCommands(cog: Cog): List<KFunction<*>> {
@@ -147,6 +149,14 @@ class Indexer {
147149
return arguments
148150
}
149151

152+
private fun construct(cls: Class<out Cog>, objectStorage: ObjectStorage): Cog {
153+
return try {
154+
cls.getDeclaredConstructor(ObjectStorage::class.java).newInstance(objectStorage)
155+
} catch (t: NoSuchMethodException) {
156+
cls.getDeclaredConstructor().newInstance()
157+
}
158+
}
159+
150160
companion object {
151161
private val log = LoggerFactory.getLogger(Indexer::class.java)
152162
}

0 commit comments

Comments
 (0)