Add Kotlin CultMesh facade
This commit is contained in:
parent
0d3c653f96
commit
5201dd6ce6
3 changed files with 98 additions and 6 deletions
|
|
@ -224,6 +224,11 @@ Current progress:
|
|||
media, sensor, tensor, and byte streams: stream declaration, clock metadata,
|
||||
preferred body transports, consumer negotiation, copy-budget classification,
|
||||
max in-flight frame pressure, and latest-frame cursors.
|
||||
- Kotlin now has a branded `CultMesh` facade matching the C#/TypeScript/Python
|
||||
entrypoint pattern for local nodes, Verse catalogs, peer catalogs, authority
|
||||
lease catalogs, stream catalogs, and RUDP client/server construction. The
|
||||
facade delegates to the same owners as the top-level helpers instead of
|
||||
creating a second authority path.
|
||||
- The remaining parity work is to deepen Python's server-side use of its port
|
||||
and move each runtime's existing TCP/LiteNetLib/WebSocket bodies behind those
|
||||
ports, then broaden RUDP interop into reconnect policy and the wider
|
||||
|
|
|
|||
|
|
@ -6,6 +6,16 @@ It provides typed MessagePack document codecs, a tiny WebSocket CultNet lane,
|
|||
a single-peer CultNet RUDP socket transport, stream catalog negotiation, an
|
||||
in-memory CultCache, and the first Eve dashboard/sensor document contracts.
|
||||
|
||||
The branded entrypoint mirrors C#/TypeScript/Python:
|
||||
|
||||
```kotlin
|
||||
val node = CultMesh.startNode()
|
||||
val verses = CultMesh.createVerseCatalog()
|
||||
val peers = CultMesh.createPeerCatalog()
|
||||
val leases = CultMesh.createAuthorityLeaseCatalog()
|
||||
val streams = CultMesh.createStreamCatalog()
|
||||
```
|
||||
|
||||
## CultCache Happy Path
|
||||
|
||||
Kotlin keeps the cache small but typed: document definitions wrap codecs,
|
||||
|
|
@ -130,12 +140,12 @@ write client/server code without hand-binding sockets or repeating channel
|
|||
strings:
|
||||
|
||||
```kotlin
|
||||
cultNetRudpServer(
|
||||
CultMesh.createRudpServer(
|
||||
runtimeId = "kotlin-server",
|
||||
connectionId = 0x10203040,
|
||||
tuning = CultNetRudpSocketTuning(maxFragmentBytes = 1024),
|
||||
).use { server ->
|
||||
cultNetRudpClient(
|
||||
CultMesh.createRudpClient(
|
||||
runtimeId = "kotlin-client",
|
||||
connectionId = 0x10203040,
|
||||
remoteHost = "127.0.0.1",
|
||||
|
|
@ -225,7 +235,7 @@ val peer = CultMeshPeerCard(
|
|||
authorityLeaseId = "lease:kotlin-peer",
|
||||
)
|
||||
|
||||
val leases = createAuthorityLeaseCatalog()
|
||||
val leases = CultMesh.createAuthorityLeaseCatalog()
|
||||
leases.upsert(
|
||||
CultMeshAuthorityLease(
|
||||
leaseId = "lease:kotlin-peer",
|
||||
|
|
@ -250,7 +260,7 @@ stream identity, clock metadata, body transport negotiation, and latest-frame
|
|||
cursors; it does not own the frame bytes themselves.
|
||||
|
||||
```kotlin
|
||||
val streams = createStreamCatalog()
|
||||
val streams = CultMesh.createStreamCatalog()
|
||||
streams.declare(
|
||||
CultMeshStreamDescriptor(
|
||||
streamId = "mimir:camera",
|
||||
|
|
|
|||
|
|
@ -268,6 +268,52 @@ class CultMeshNode(
|
|||
fun applyShardLogResponse(response: CultNetMessage): List<Any?> = cache.applyShardLogResponse(response)
|
||||
}
|
||||
|
||||
object CultMesh {
|
||||
fun createNode(cache: CultCache = CultCache()): CultMeshNode = CultMeshNode(cache)
|
||||
|
||||
fun startNode(cache: CultCache = CultCache()): CultMeshNode = createNode(cache)
|
||||
|
||||
fun createVerseCatalog(): CultMeshVerseCatalog = CultMeshVerseCatalog()
|
||||
|
||||
fun createPeerCatalog(): CultMeshPeerCatalog = CultMeshPeerCatalog()
|
||||
|
||||
fun createAuthorityLeaseCatalog(): CultMeshAuthorityLeaseCatalog = CultMeshAuthorityLeaseCatalog()
|
||||
|
||||
fun createStreamCatalog(): CultMeshStreamCatalog = CultMeshStreamCatalog()
|
||||
|
||||
fun createRudpServer(
|
||||
runtimeId: String,
|
||||
connectionId: Long,
|
||||
bindHost: String = "127.0.0.1",
|
||||
bindPort: Int = 0,
|
||||
tuning: CultNetRudpSocketTuning = CultNetRudpSocketTuning(),
|
||||
): CultNetRudpSocketTransportConnection = cultNetRudpServer(
|
||||
runtimeId = runtimeId,
|
||||
connectionId = connectionId,
|
||||
bindHost = bindHost,
|
||||
bindPort = bindPort,
|
||||
tuning = tuning,
|
||||
)
|
||||
|
||||
fun createRudpClient(
|
||||
runtimeId: String,
|
||||
connectionId: Long,
|
||||
remoteHost: String,
|
||||
remotePort: Int,
|
||||
bindHost: String = "127.0.0.1",
|
||||
bindPort: Int = 0,
|
||||
tuning: CultNetRudpSocketTuning = CultNetRudpSocketTuning(),
|
||||
): CultNetRudpSocketTransportConnection = cultNetRudpClient(
|
||||
runtimeId = runtimeId,
|
||||
connectionId = connectionId,
|
||||
remoteHost = remoteHost,
|
||||
remotePort = remotePort,
|
||||
bindHost = bindHost,
|
||||
bindPort = bindPort,
|
||||
tuning = tuning,
|
||||
)
|
||||
}
|
||||
|
||||
data class CultNetFrame(val opcode: Int, val payload: ByteArray)
|
||||
|
||||
data class CultNetTransportStats(
|
||||
|
|
@ -2236,6 +2282,7 @@ private fun nowMs(): Long = Instant.now().toEpochMilli()
|
|||
fun main(args: Array<String>) {
|
||||
if (args.isEmpty()) {
|
||||
cultCacheErgonomicsCoverTypedDocumentsAndGlobals()
|
||||
cultMeshFacadeRoutesErgonomicEntrypoints()
|
||||
cultCacheRawSnapshotsRoundTripThroughCultNetMessages()
|
||||
cultNetSchemaMessagesUseMessagePackMaps()
|
||||
cultNetSchemaCatalogsRoundTripDescriptors()
|
||||
|
|
@ -2294,6 +2341,36 @@ private fun cultCacheErgonomicsCoverTypedDocumentsAndGlobals() {
|
|||
check(node.forget(notes, "node-note"))
|
||||
}
|
||||
|
||||
private fun cultMeshFacadeRoutesErgonomicEntrypoints() {
|
||||
val notes = stringDocument("kotlin.facade_note", "kotlin.facade_note.v1")
|
||||
val cache = CultCache()
|
||||
cache.register(notes)
|
||||
val node = CultMesh.startNode(cache)
|
||||
node.remember(notes, "note:facade", "facade")
|
||||
check(node.require(notes, "note:facade") == "facade")
|
||||
|
||||
val verses = CultMesh.createVerseCatalog()
|
||||
verses.upsert(
|
||||
CultMeshVerseDescriptor(
|
||||
verseId = "facade",
|
||||
displayName = "Facade Verse",
|
||||
authorityModel = "federated",
|
||||
compatibility = CultMeshVerseCompatibility("cultmesh.v0", "rules"),
|
||||
),
|
||||
)
|
||||
check(verses.get("facade")?.displayName == "Facade Verse")
|
||||
|
||||
val peers = CultMesh.createPeerCatalog()
|
||||
peers.upsert(CultMeshPeerCard("facade-peer", "facade", listOf("rudp://127.0.0.1:4100"), roles = listOf("schema")))
|
||||
check(peers.find("facade", "schema").single().peerId == "facade-peer")
|
||||
|
||||
val leases = CultMesh.createAuthorityLeaseCatalog()
|
||||
check(leases.leases.isEmpty())
|
||||
|
||||
val streams = CultMesh.createStreamCatalog()
|
||||
check(streams.streams.isEmpty())
|
||||
}
|
||||
|
||||
private fun cultCacheRawSnapshotsRoundTripThroughCultNetMessages() {
|
||||
val notes = stringDocument("kotlin.note", "kotlin.note.v1")
|
||||
val settings = stringDocument("kotlin.settings", "kotlin.settings.v1", global = true)
|
||||
|
|
@ -3036,12 +3113,12 @@ private fun rudpSessionFragmentsAndReassemblesReliableOrderedPayloads() {
|
|||
|
||||
private fun rudpSocketTransportErgonomicFactoriesCarrySchemaFrames() {
|
||||
val connectionId = 0x10203042L
|
||||
cultNetRudpServer(
|
||||
CultMesh.createRudpServer(
|
||||
runtimeId = "kotlin-rudp-sugar-server",
|
||||
connectionId = connectionId,
|
||||
tuning = CultNetRudpSocketTuning(resendDelayMs = 25, maxFragmentBytes = 8, maxPendingReliablePackets = 16),
|
||||
).use { server ->
|
||||
cultNetRudpClient(
|
||||
CultMesh.createRudpClient(
|
||||
runtimeId = "kotlin-rudp-sugar-client",
|
||||
connectionId = connectionId,
|
||||
remoteHost = "127.0.0.1",
|
||||
|
|
|
|||
Loading…
Reference in a new issue