mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-30 13:30:19 +02:00
feat(scripting): ipc global channels
This commit is contained in:
parent
98b3f2bfc9
commit
19a0ab8398
@ -1,19 +0,0 @@
|
|||||||
package me.rhunk.snapenhance.scripting
|
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
class IRemoteIPC : IPCInterface() {
|
|
||||||
private val listeners = ConcurrentHashMap<String, MutableSet<Listener>>()
|
|
||||||
|
|
||||||
fun removeListener(eventName: String, listener: Listener) {
|
|
||||||
listeners[eventName]?.remove(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun on(eventName: String, listener: Listener) {
|
|
||||||
listeners.getOrPut(eventName) { mutableSetOf() }.add(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun emit(eventName: String, args: Array<out String?>) {
|
|
||||||
listeners[eventName]?.toList()?.forEach { it(args) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,53 @@
|
|||||||
|
package me.rhunk.snapenhance.scripting
|
||||||
|
|
||||||
|
import android.os.DeadObjectException
|
||||||
|
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||||
|
import me.rhunk.snapenhance.core.logger.AbstractLogger
|
||||||
|
import me.rhunk.snapenhance.scripting.type.ModuleInfo
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
typealias IPCListeners = ConcurrentHashMap<String, MutableMap<String, MutableSet<IPCListener>>> // channel, eventName -> listeners
|
||||||
|
|
||||||
|
class RemoteManagerIPC(
|
||||||
|
private val moduleInfo: ModuleInfo,
|
||||||
|
private val logger: AbstractLogger,
|
||||||
|
private val ipcListeners: IPCListeners = ConcurrentHashMap(),
|
||||||
|
) : IPCInterface() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RemoteManagerIPC"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun on(eventName: String, listener: Listener) {
|
||||||
|
onBroadcast(moduleInfo.name, eventName, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun emit(eventName: String, vararg args: String?) {
|
||||||
|
emit(moduleInfo.name, eventName, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
||||||
|
ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(object: IPCListener.Stub() {
|
||||||
|
override fun onMessage(args: Array<out String?>) {
|
||||||
|
try {
|
||||||
|
listener(args)
|
||||||
|
} catch (doe: DeadObjectException) {
|
||||||
|
ipcListeners[channel]?.get(eventName)?.remove(this)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logger.error("Failed to receive message for channel: $channel, event: $eventName", t, TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun broadcast(channel: String, eventName: String, vararg args: String?) {
|
||||||
|
ipcListeners[channel]?.get(eventName)?.toList()?.forEach {
|
||||||
|
try {
|
||||||
|
it.onMessage(args)
|
||||||
|
} catch (doe: DeadObjectException) {
|
||||||
|
ipcListeners[channel]?.get(eventName)?.remove(it)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logger.error("Failed to send message for channel: $channel, event: $eventName", t, TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package me.rhunk.snapenhance.scripting
|
package me.rhunk.snapenhance.scripting
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.DeadObjectException
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import me.rhunk.snapenhance.RemoteSideContext
|
import me.rhunk.snapenhance.RemoteSideContext
|
||||||
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
import me.rhunk.snapenhance.bridge.scripting.IPCListener
|
||||||
@ -14,14 +13,10 @@ class RemoteScriptManager(
|
|||||||
private val context: RemoteSideContext,
|
private val context: RemoteSideContext,
|
||||||
) : IScripting.Stub() {
|
) : IScripting.Stub() {
|
||||||
val runtime = ScriptRuntime(context.log)
|
val runtime = ScriptRuntime(context.log)
|
||||||
val remoteIpc = IRemoteIPC()
|
|
||||||
|
|
||||||
private fun getScriptFolder()
|
|
||||||
= DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get()))
|
|
||||||
//private fun hasHotReload() = context.config.root.scripting.hotReload.get()
|
|
||||||
private val reloadListeners = mutableListOf<ReloadListener>()
|
private val reloadListeners = mutableListOf<ReloadListener>()
|
||||||
|
|
||||||
private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>()
|
private val cachedModuleInfo = mutableMapOf<String, ModuleInfo>()
|
||||||
|
private val ipcListeners = IPCListeners()
|
||||||
|
|
||||||
fun sync() {
|
fun sync() {
|
||||||
getScriptFileNames().forEach { name ->
|
getScriptFileNames().forEach { name ->
|
||||||
@ -40,8 +35,8 @@ class RemoteScriptManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
runtime.buildModuleObject = {
|
runtime.buildModuleObject = { module ->
|
||||||
putConst("ipc", this, remoteIpc)
|
putConst("ipc", this, RemoteManagerIPC(module.moduleInfo, context.log, ipcListeners))
|
||||||
}
|
}
|
||||||
|
|
||||||
sync()
|
sync()
|
||||||
@ -56,6 +51,8 @@ class RemoteScriptManager(
|
|||||||
return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null)
|
return context.androidContext.contentResolver.openInputStream(file.uri)?.use(callback) ?: callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getScriptFolder() = DocumentFile.fromTreeUri(context.androidContext, Uri.parse(context.config.root.scripting.moduleFolder.get()))
|
||||||
|
|
||||||
private fun getScriptFileNames(): List<String> {
|
private fun getScriptFileNames(): List<String> {
|
||||||
return (getScriptFolder() ?: return emptyList()).listFiles().filter { it.name?.endsWith(".js") ?: false }.map { it.name!! }
|
return (getScriptFolder() ?: return emptyList()).listFiles().filter { it.name?.endsWith(".js") ?: false }.map { it.name!! }
|
||||||
}
|
}
|
||||||
@ -78,23 +75,15 @@ class RemoteScriptManager(
|
|||||||
reloadListeners.add(listener)
|
reloadListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerIPCListener(eventName: String, listener: IPCListener) {
|
override fun registerIPCListener(channel: String, eventName: String, listener: IPCListener) {
|
||||||
remoteIpc.on(eventName, object: Listener {
|
ipcListeners.getOrPut(channel) { mutableMapOf() }.getOrPut(eventName) { mutableSetOf() }.add(listener)
|
||||||
override fun invoke(args: Array<out String?>) {
|
|
||||||
try {
|
|
||||||
listener.onMessage(args)
|
|
||||||
} catch (e: DeadObjectException) {
|
|
||||||
remoteIpc.removeListener(eventName, this)
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
context.log.error("Failed to invoke $eventName", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendIPCMessage(eventName: String, args: Array<out String>) {
|
override fun sendIPCMessage(channel: String, eventName: String, args: Array<out String>) {
|
||||||
runCatching {
|
runCatching {
|
||||||
remoteIpc.emit(eventName, args)
|
ipcListeners[channel]?.get(eventName)?.toList()?.forEach {
|
||||||
|
it.onMessage(args)
|
||||||
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.log.error("Failed to send message for $eventName", it)
|
context.log.error("Failed to send message for $eventName", it)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ interface IScripting {
|
|||||||
|
|
||||||
void registerReloadListener(ReloadListener listener);
|
void registerReloadListener(ReloadListener listener);
|
||||||
|
|
||||||
void registerIPCListener(String eventName, IPCListener listener);
|
void registerIPCListener(String channel, String eventName, IPCListener listener);
|
||||||
|
|
||||||
void sendIPCMessage(String eventName, in String[] args);
|
void sendIPCMessage(String channel, String eventName, in String[] args);
|
||||||
}
|
}
|
@ -5,8 +5,13 @@ typealias Listener = (Array<out String?>) -> Unit
|
|||||||
abstract class IPCInterface {
|
abstract class IPCInterface {
|
||||||
abstract fun on(eventName: String, listener: Listener)
|
abstract fun on(eventName: String, listener: Listener)
|
||||||
|
|
||||||
|
abstract fun onBroadcast(channel: String, eventName: String, listener: Listener)
|
||||||
|
|
||||||
abstract fun emit(eventName: String, vararg args: String?)
|
abstract fun emit(eventName: String, vararg args: String?)
|
||||||
|
abstract fun broadcast(channel: String, eventName: String, vararg args: String?)
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun emit(eventName: String) = emit(eventName, *emptyArray())
|
fun emit(eventName: String) = emit(eventName, *emptyArray())
|
||||||
|
@Suppress("unused")
|
||||||
|
fun emit(channel: String, eventName: String) = broadcast(channel, eventName, *emptyArray())
|
||||||
}
|
}
|
@ -18,7 +18,6 @@ class CoreScriptRuntime(
|
|||||||
|
|
||||||
private val scriptHookers = mutableListOf<ScriptHooker>()
|
private val scriptHookers = mutableListOf<ScriptHooker>()
|
||||||
|
|
||||||
|
|
||||||
fun connect(scriptingInterface: IScripting) {
|
fun connect(scriptingInterface: IScripting) {
|
||||||
scriptingInterface.apply {
|
scriptingInterface.apply {
|
||||||
registerReloadListener(object: ReloadListener.Stub() {
|
registerReloadListener(object: ReloadListener.Stub() {
|
||||||
@ -27,23 +26,28 @@ class CoreScriptRuntime(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcInterface = object: IPCInterface() {
|
buildModuleObject = { module ->
|
||||||
override fun on(eventName: String, listener: Listener) {
|
putConst("ipc", this, object: IPCInterface() {
|
||||||
registerIPCListener(eventName, object: IPCListener.Stub() {
|
override fun onBroadcast(channel: String, eventName: String, listener: Listener) {
|
||||||
|
registerIPCListener(channel, eventName, object: IPCListener.Stub() {
|
||||||
override fun onMessage(args: Array<out String?>) {
|
override fun onMessage(args: Array<out String?>) {
|
||||||
listener(args)
|
listener(args)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun emit(eventName: String, args: Array<out String?>) {
|
override fun on(eventName: String, listener: Listener) {
|
||||||
sendIPCMessage(eventName, args)
|
onBroadcast(module.moduleInfo.name, eventName, listener)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildModuleObject = { module ->
|
override fun emit(eventName: String, vararg args: String?) {
|
||||||
putConst("ipc", this, ipcInterface)
|
broadcast(module.moduleInfo.name, eventName, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun broadcast(channel: String, eventName: String, vararg args: String?) {
|
||||||
|
sendIPCMessage(channel, eventName, args)
|
||||||
|
}
|
||||||
|
})
|
||||||
putFunction("findClass") {
|
putFunction("findClass") {
|
||||||
val className = it?.get(0).toString()
|
val className = it?.get(0).toString()
|
||||||
classLoader.loadClass(className)
|
classLoader.loadClass(className)
|
||||||
@ -52,6 +56,9 @@ class CoreScriptRuntime(
|
|||||||
scriptHookers.add(it)
|
scriptHookers.add(it)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
scriptingInterface.enabledScripts.forEach { path ->
|
scriptingInterface.enabledScripts.forEach { path ->
|
||||||
runCatching {
|
runCatching {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user