Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.caesarealabs.rpc4k.runtime.user.components
import com.benasher44.uuid.uuid4
import com.caesarealabs.logging.LoggingFactory
import com.caesarealabs.logging.PrintLoggingFactory
import com.caesarealabs.rpc4k.runtime.api.*
import com.caesarealabs.rpc4k.runtime.implementation.RpcResult
import com.caesarealabs.rpc4k.runtime.user.Rpc4kIndex
import com.caesarealabs.rpc4k.runtime.user.startRpc
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.serialization.KSerializer
import kotlin.time.Duration
// A global map used to store active MemoryMulticallServer by their 'port'
private val memoryServerRegistry = mutableMapOf()
/**
* Simple implementation of a [DedicatedServer] that handles everything in-memory without any need for HTTP and such
* @param port While this doesn't actually open a port on the network, this number is used as an identifier for the server,
* so that clients may connect to this server specifically and not other servers running in the same process.
* @param emulatedLatency If specified, server will wait this much time when responding. This allows emulating the latency of a real server
*/
public class MemoryDedicatedServer(
private val port: Int,
private val emulatedLatency: Duration? = null
) : DedicatedServer {
private val connections = mutableMapOf()
private var config: ServerConfig? = null
private val scope = CoroutineScope(Dispatchers.Default)
private suspend fun emulateLatency() {
if (emulatedLatency != null) delay(emulatedLatency)
}
private fun getConfig() =
config ?: error("Attempt to respond with server that has not been started with start()")
/**
* Emulates the handling of a request by a server.
* Will run the processing in its own coroutine.
*/
internal suspend fun respond(rpcRequest: ByteArray): RpcResult = scope.async {
emulateLatency()
RpcServerUtils.routeCall(rpcRequest, getConfig())
}.await()
/**
* Emulates the handling of a subscription / unsubscription by a server
*/
internal suspend fun acceptEventMessage(message: ByteArray, session: MemoryEventClient) {
emulateLatency()
getConfig().acceptEventSubscription(message, session.connection)
}
internal fun connect(client: MemoryEventClient) {
connections[client.connection] = client
}
/**
* Currently unused
*/
internal fun disconnect(client: MemoryEventClient) {
connections.remove(client.connection)
}
override fun start(config: ServerConfig, wait: Boolean) {
this.config = config
memoryServerRegistry[port] = this
}
override fun stop() {
memoryServerRegistry.remove(port)
}
override suspend fun send(connection: EventConnection, bytes: ByteArray): Boolean {
emulateLatency()
val session = connections[connection] ?: return false
session.handleMessage(S2CEventMessage.fromByteArray(bytes))
return true
}
}
/**
* Simple implementation of a [RpcClient] that handles everything in-memory without any need for HTTP and such
* @param port While this doesn't actually open a port on the network, this number is used as an identifier for the server,
* so that clients may connect to this server specifically and not other servers running in the same process.
*/
public class MemoryRpcClient(private val port: Int) : RpcClient {
override suspend fun send(
rpc: Rpc,
format: SerializationFormat,
serializers: List>
): ByteArray {
val data = rpc.toByteArray(format, serializers)
val server = memoryServerRegistry[port] ?: error("Cannot find started memory server at port $port")
when (val response = server.respond(data)) {
// The last 2 parameters don't mean much here
is RpcResult.Error -> throw RpcResponseException(
response.message,
rpc,
format,
this,
response.message,
0
)
is RpcResult.Success -> return response.bytes
}
}
override val events: EventClient = MemoryEventClient(port)
}
internal class MemoryEventClient(private val port: Int) : AbstractEventClient() {
// Used as an identifier for the connection to the server
internal val connection = EventConnection(uuid4().toString())
private var connected = false
override suspend fun send(message: ByteArray) {
val server = memoryServerRegistry[port] ?: error("Cannot find started memory server at port $port")
if (!connected) server.connect(this)
server.acceptEventMessage(message, this)
}
}
public fun Rpc4kIndex<*, C, *>.memoryClient(
port: Int,
format: SerializationFormat = JsonFormat()
): C {
return createNetworkClient(MemoryRpcClient(port), format)
}
/**
* Configure and start an RPC server with one call from the [Rpc4kIndex]
*/
public fun Rpc4kIndex.startMemory(
format: SerializationFormat = JsonFormat(),
eventManager: EventManager = MemoryEventManager(),
wait: Boolean = true,
port: Int = PortPool.get(),
logging: LoggingFactory = PrintLoggingFactory,
emulatedLatency: Duration? = null,
service: (I) -> S
): TypedServerConfig = MemoryDedicatedServer(port, emulatedLatency)
.startRpc(this, format, eventManager, logging, wait, service)