mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-28 12:30:12 +02:00
feat: unaryCall event
- feat data class builder util
This commit is contained in:
parent
8eeafc59b6
commit
1f7f270766
@ -5,6 +5,7 @@ import me.rhunk.snapenhance.common.scripting.bindings.BindingSide
|
||||
import me.rhunk.snapenhance.common.scripting.ktx.contextScope
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
@ -32,13 +33,13 @@ class Networking : AbstractBinding("networking", BindingSide.COMMON) {
|
||||
fun removeHeader(name: String) = requestBuilder.removeHeader(name).let { this }
|
||||
|
||||
@JSFunction
|
||||
fun method(method: String, body: String) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body)).let { this }
|
||||
fun method(method: String, body: String) = requestBuilder.method(method, body.toRequestBody(null)).let { this }
|
||||
|
||||
@JSFunction
|
||||
fun method(method: String, body: java.io.InputStream) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body.readBytes())).let { this }
|
||||
fun method(method: String, body: java.io.InputStream) = requestBuilder.method(method, body.readBytes().toRequestBody(null)).let { this }
|
||||
|
||||
@JSFunction
|
||||
fun method(method: String, body: ByteArray) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body)).let { this }
|
||||
fun method(method: String, body: ByteArray) = requestBuilder.method(method, body.toRequestBody(null)).let { this }
|
||||
}
|
||||
|
||||
inner class ResponseWrapper(
|
||||
|
@ -88,4 +88,6 @@ class ProtoEditor(
|
||||
}
|
||||
|
||||
fun toByteArray() = buffer
|
||||
|
||||
override fun toString() = ProtoReader(buffer).toString()
|
||||
}
|
@ -9,6 +9,7 @@ import me.rhunk.snapenhance.core.ModContext
|
||||
import me.rhunk.snapenhance.core.event.events.impl.*
|
||||
import me.rhunk.snapenhance.core.manager.Manager
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.Hooker
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import me.rhunk.snapenhance.core.util.hook.hookConstructor
|
||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
||||
@ -17,6 +18,7 @@ import me.rhunk.snapenhance.core.wrapper.impl.Message
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.MessageContent
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.MessageDestinations
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class EventDispatcher(
|
||||
private val context: ModContext
|
||||
@ -156,6 +158,67 @@ class EventDispatcher(
|
||||
)
|
||||
}
|
||||
|
||||
context.classCache.unifiedGrpcService.hook("unaryCall", HookStage.BEFORE) { param ->
|
||||
val uri = param.arg<String>(0)
|
||||
val buffer = param.argNullable<ByteBuffer>(1)?.run {
|
||||
val array = ByteArray(limit())
|
||||
position(0)
|
||||
get(array)
|
||||
rewind()
|
||||
array
|
||||
} ?: return@hook
|
||||
val unaryEventHandler = param.argNullable<Any>(3) ?: return@hook
|
||||
|
||||
val event = context.event.post(
|
||||
UnaryCallEvent(
|
||||
uri = uri,
|
||||
buffer = buffer
|
||||
).apply {
|
||||
adapter = param
|
||||
}
|
||||
) ?: return@hook
|
||||
|
||||
if (event.canceled) {
|
||||
param.setResult(null)
|
||||
return@hook
|
||||
}
|
||||
|
||||
if (!event.buffer.contentEquals(buffer)) {
|
||||
param.setArg(1, ByteBuffer.wrap(event.buffer))
|
||||
}
|
||||
|
||||
if (event.callbacks.size == 0) {
|
||||
return@hook
|
||||
}
|
||||
|
||||
Hooker.ephemeralHookObjectMethod(unaryEventHandler::class.java, unaryEventHandler, "onEvent", HookStage.BEFORE) { methodParam ->
|
||||
val byteBuffer = methodParam.argNullable<ByteBuffer>(0) ?: return@ephemeralHookObjectMethod
|
||||
val array = byteBuffer.run {
|
||||
val array = ByteArray(limit())
|
||||
position(0)
|
||||
get(array)
|
||||
rewind()
|
||||
array
|
||||
}
|
||||
|
||||
val responseUnaryCallEvent = UnaryCallEvent(
|
||||
uri = uri,
|
||||
buffer = array
|
||||
)
|
||||
|
||||
event.callbacks.forEach { callback ->
|
||||
callback(responseUnaryCallEvent)
|
||||
}
|
||||
|
||||
if (responseUnaryCallEvent.canceled) {
|
||||
param.setResult(null)
|
||||
return@ephemeralHookObjectMethod
|
||||
}
|
||||
|
||||
methodParam.setArg(0, ByteBuffer.wrap(event.buffer))
|
||||
}
|
||||
}
|
||||
|
||||
hookViewBinder()
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package me.rhunk.snapenhance.core.event.events.impl
|
||||
|
||||
import me.rhunk.snapenhance.core.event.events.AbstractHookEvent
|
||||
|
||||
class UnaryCallEvent(
|
||||
val uri: String,
|
||||
var buffer: ByteArray
|
||||
): AbstractHookEvent() {
|
||||
val callbacks = mutableListOf<(UnaryCallEvent) -> Unit>()
|
||||
|
||||
fun addResponseCallback(responseCallback: UnaryCallEvent.() -> Unit) {
|
||||
callbacks.add(responseCallback)
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package me.rhunk.snapenhance.core.util
|
||||
|
||||
|
||||
fun Any?.dataBuilder(dataClassBuilder: DataClassBuilder.() -> Unit): Any? {
|
||||
return DataClassBuilder(
|
||||
when (this) {
|
||||
is Class<*> -> CallbackBuilder.createEmptyObject(
|
||||
this.constructors.firstOrNull() ?: return null
|
||||
) ?: return null
|
||||
else -> this
|
||||
} ?: return null
|
||||
).apply(dataClassBuilder).build()
|
||||
}
|
||||
|
||||
// Util for building/editing data classes
|
||||
class DataClassBuilder(
|
||||
private val instance: Any,
|
||||
) {
|
||||
fun set(fieldName: String, value: Any?) {
|
||||
val field = instance::class.java.declaredFields.firstOrNull { it.name == fieldName } ?: return
|
||||
val fieldType = field.type
|
||||
field.isAccessible = true
|
||||
|
||||
when {
|
||||
fieldType.isEnum -> {
|
||||
val enumValue = fieldType.enumConstants.firstOrNull { it.toString() == value } ?: return
|
||||
field.set(instance, enumValue)
|
||||
}
|
||||
fieldType.isPrimitive -> {
|
||||
when (fieldType) {
|
||||
Boolean::class.javaPrimitiveType -> field.setBoolean(instance, value as Boolean)
|
||||
Byte::class.javaPrimitiveType -> field.setByte(instance, value as Byte)
|
||||
Char::class.javaPrimitiveType -> field.setChar(instance, value as Char)
|
||||
Short::class.javaPrimitiveType -> field.setShort(instance, value as Short)
|
||||
Int::class.javaPrimitiveType -> field.setInt(instance, value as Int)
|
||||
Long::class.javaPrimitiveType -> field.setLong(instance, value as Long)
|
||||
Float::class.javaPrimitiveType -> field.setFloat(instance, value as Float)
|
||||
Double::class.javaPrimitiveType -> field.setDouble(instance, value as Double)
|
||||
}
|
||||
}
|
||||
else -> field.set(instance, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun set(vararg fields: Pair<String, Any?>) = fields.forEach { set(it.first, it.second) }
|
||||
|
||||
fun from(fieldName: String, new: Boolean = false, callback: DataClassBuilder.() -> Unit) {
|
||||
val field = instance::class.java.declaredFields.firstOrNull { it.name == fieldName } ?: return
|
||||
field.isAccessible = true
|
||||
|
||||
val lazyInstance by lazy { CallbackBuilder.createEmptyObject(field.type.constructors.firstOrNull() ?: return@lazy null) ?: return@lazy null }
|
||||
val builderInstance = if (new) lazyInstance else {
|
||||
field.get(instance).takeIf { it != null } ?: lazyInstance
|
||||
}
|
||||
|
||||
DataClassBuilder(builderInstance ?: return).apply(callback)
|
||||
|
||||
field.set(instance, builderInstance)
|
||||
}
|
||||
|
||||
fun <T> cast(type: Class<T>, callback: T.() -> Unit) {
|
||||
type.cast(instance)?.let { callback(it) }
|
||||
}
|
||||
|
||||
fun build() = instance
|
||||
}
|
@ -23,10 +23,10 @@ class OperaPageViewControllerMapper : AbstractClassMapper() {
|
||||
|
||||
val layerListField = clazz.fields.first { it.type == "Ljava/util/ArrayList;" }
|
||||
|
||||
val onDisplayStateChange = clazz.methods.first {
|
||||
if (it.returnType != "V" || it.parameterTypes.size != 1) return@first false
|
||||
val firstParameterType = getClass(it.parameterTypes[0]) ?: return@first false
|
||||
if (firstParameterType.type == clazz.type || !firstParameterType.isAbstract()) return@first false
|
||||
val onDisplayStateChange = clazz.methods.firstOrNull {
|
||||
if (it.returnType != "V" || it.parameterTypes.size != 1) return@firstOrNull false
|
||||
val firstParameterType = getClass(it.parameterTypes[0]) ?: return@firstOrNull false
|
||||
if (firstParameterType.type == clazz.type || !firstParameterType.isAbstract()) return@firstOrNull false
|
||||
//check if the class contains a field with the enumViewStateClass type
|
||||
firstParameterType.fields.any { field ->
|
||||
field.type == viewStateField.type
|
||||
@ -44,7 +44,7 @@ class OperaPageViewControllerMapper : AbstractClassMapper() {
|
||||
"class" to clazz.getClassName(),
|
||||
"viewStateField" to viewStateField.name,
|
||||
"layerListField" to layerListField.name,
|
||||
"onDisplayStateChange" to onDisplayStateChange.name,
|
||||
"onDisplayStateChange" to onDisplayStateChange?.name,
|
||||
"onDisplayStateChangeGesture" to onDisplayStateChangeGesture.name
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user