Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SDK7 implement ADR-245 (player components) #5724

Draft
wants to merge 5 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 123 additions & 3 deletions browser-interface/packages/shared/apis/host/EngineAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,119 @@ import type { EventData, ManyEntityAction } from 'shared/protocol/decentraland/k
import { EngineApiServiceDefinition } from 'shared/protocol/decentraland/kernel/apis/engine_api.gen'
import type { PortContext } from './context'

import { avatarSdk7Ecs, avatarSdk7MessageObservable } from './runtime7/avatar'
import { DeleteComponent } from './runtime7/serialization/crdt/deleteComponent'
import { ReadWriteByteBuffer } from './runtime7/serialization/ByteBuffer'
import { PBPointerEventsResult, Sdk7ComponentIds } from './runtime7/avatar/ecs'
import { buildAvatarTransformMessage } from './runtime7/serialization/transform'
import { AppendValueOperation } from './runtime7/serialization/crdt/appendValue'
import { InputAction, PointerEventType } from 'shared/protocol/decentraland/sdk/components/common/input_action.gen'

function getParcelNumber(x: number, z: number) {
return z * 100e8 + x
}

export function registerEngineApiServiceServerImplementation(port: RpcServerPort<PortContext>) {
codegen.registerService(
port,
EngineApiServiceDefinition,
async (): Promise<RpcServerModule<EngineApiServiceDefinition, PortContext>> => {
async (port, ctx): Promise<RpcServerModule<EngineApiServiceDefinition, PortContext>> => {
let sdk7AvatarUpdates: Uint8Array[] = []

if (ctx.sdk7) {
const tempReusableBuffer = new ReadWriteByteBuffer()
const parcels: Set<number> = new Set()
if (!ctx.sceneData.isGlobalScene) {
ctx.sceneData.entity.pointers.forEach((pointer) => {
const [x, z] = pointer.split(',').map((n) => parseInt(n, 10))
parcels.add(getParcelNumber(x, z))
})
}

const [baseX, baseZ] = ctx.sceneData.entity.metadata?.scene?.base?.split(',').map((n) => parseInt(n, 10)) ?? [
0, 0
]
const offset = { x: baseX * 16.0, y: 0, z: baseZ * 16.0 }

sdk7AvatarUpdates = avatarSdk7Ecs.getState()

avatarSdk7MessageObservable.on('BinaryMessage', (message) => {
sdk7AvatarUpdates.push(message)
})

avatarSdk7MessageObservable.on('RemoveAvatar', (message) => {
sdk7AvatarUpdates.push(message.data)
ctx.avatarEntityInsideScene.delete(message.entity)
})

avatarSdk7MessageObservable.on('ChangePosition', (message) => {
const isInsideScene =
ctx.sceneData.isGlobalScene || parcels.has(getParcelNumber(message.parcel.x, message.parcel.z))
const wasInsideScene = ctx.avatarEntityInsideScene.get(message.entity) || false
if (isInsideScene) {
sdk7AvatarUpdates.push(buildAvatarTransformMessage(message.entity, message.ts, message.data, offset))

if (!wasInsideScene) {
ctx.avatarEntityInsideScene.set(message.entity, true)
}
} else if (wasInsideScene) {
ctx.avatarEntityInsideScene.set(message.entity, false)

tempReusableBuffer.resetBuffer()
DeleteComponent.write(message.entity, Sdk7ComponentIds.TRANSFORM, message.ts, tempReusableBuffer)
sdk7AvatarUpdates.push(tempReusableBuffer.toCopiedBinary())
}
})

ctx.subscribedEvents.add('playerClicked')
}

const crdtReusableBuffer = new ReadWriteByteBuffer()
let localTimestamp = 0
function getPlayerClickedEvents(): Uint8Array[] {
const msgs: Uint8Array[] = []
const playerClickedEvents = ctx.events.filter((value) => value.generic?.eventId === 'playerClicked')
for (const event of playerClickedEvents) {
event.generic?.eventData
const { userId, ray } = JSON.parse(event.generic!.eventData)
const { origin, direction, distance } = ray

localTimestamp++
const entityId = avatarSdk7Ecs.ensureAvatarEntityId(userId)

{
crdtReusableBuffer.resetBuffer()

const writer = PBPointerEventsResult.encode({
button: InputAction.IA_POINTER,
hit: {
position: origin,
globalOrigin: origin,
direction,
normalHit: undefined,
length: distance,
entityId: entityId
},
state: PointerEventType.PET_DOWN,
timestamp: localTimestamp,
tickNumber: ctx.tickNumber
})
const buffer = new Uint8Array(writer.finish(), 0, writer.len)
AppendValueOperation.write(
entityId,
localTimestamp,
Sdk7ComponentIds.POINTER_EVENTS_RESULT,
buffer,
crdtReusableBuffer
)

const messageData = crdtReusableBuffer.toCopiedBinary()
msgs.push(messageData)
}
}
return msgs
}

return {
async sendBatch(_req: ManyEntityAction, ctx) {
// TODO: (2023/01/06) `sendBatch` is still used by sdk7 scenes to retreive
Expand All @@ -21,7 +129,7 @@ export function registerEngineApiServiceServerImplementation(port: RpcServerPort
if (events.length) {
ctx.events = []
}

ctx.sendBatchCalled = true
return { events }
},

Expand All @@ -45,7 +153,19 @@ export function registerEngineApiServiceServerImplementation(port: RpcServerPort
payload: req.data
})

return { data: [ret.payload] }
const avatarPointerEvents = getPlayerClickedEvents()
const data: Uint8Array[] = [ret.payload, ...avatarPointerEvents, ...sdk7AvatarUpdates]

sdk7AvatarUpdates = []
ctx.tickNumber++

// If the sendBatch is not being called after 10 ticks, clean the events her (after getting data from playerClickedEvents)
// Corner case: if the player click other player in this windows of 10 ticks, it'll receive a bunch of times
if (!ctx.sendBatchCalled && ctx.tickNumber > 10) {
ctx.events = []
}

return { data }
},

// @deprecated
Expand Down
7 changes: 4 additions & 3 deletions browser-interface/packages/shared/apis/host/Testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { RpcServerPort } from '@dcl/rpc/dist/types'
import { TestingServiceDefinition } from 'shared/protocol/decentraland/kernel/apis/testing.gen'
import type { PortContextService } from './context'

declare var __DCL_TESTING_EXTENSION__: any
declare let __DCL_TESTING_EXTENSION__: any

export function registerTestingServiceServerImplementation(port: RpcServerPort<PortContextService<'logger'>>) {
codegen.registerService(port, TestingServiceDefinition, async () => ({
Expand All @@ -16,8 +16,9 @@ export function registerTestingServiceServerImplementation(port: RpcServerPort<P
return {}
},
async setCameraTransform(transform) {
if (typeof __DCL_TESTING_EXTENSION__ !== 'undefined') return __DCL_TESTING_EXTENSION__.setCameraTransform(transform)
if (typeof __DCL_TESTING_EXTENSION__ !== 'undefined')
return __DCL_TESTING_EXTENSION__.setCameraTransform(transform)
return {}
}
}))
}
}
13 changes: 9 additions & 4 deletions browser-interface/packages/shared/apis/host/context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { RpcClientPort } from '@dcl/rpc'
import type { RpcClientModule } from '@dcl/rpc/dist/codegen'
import type { ILogger } from 'lib/logger'
import type { LoadableScene } from 'shared/types'
import type { PermissionItem } from 'shared/protocol/decentraland/kernel/apis/permissions.gen'
import type { EventData } from 'shared/protocol/decentraland/kernel/apis/engine_api.gen'
import type { RpcClientPort } from '@dcl/rpc'
import type { PermissionItem } from 'shared/protocol/decentraland/kernel/apis/permissions.gen'
import type { RpcSceneControllerServiceDefinition } from 'shared/protocol/decentraland/renderer/renderer_services/scene_controller.gen'
import type { RpcClientModule } from '@dcl/rpc/dist/codegen'
import { EntityAction } from 'shared/protocol/decentraland/sdk/ecs6/engine_interface_ecs6.gen'
import type { LoadableScene } from 'shared/types'
import type { Entity } from './runtime7/engine/entity'

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }

Expand All @@ -20,6 +21,10 @@ export type PortContext = {
subscribedEvents: Set<string>
events: EventData[]

tickNumber: number
sendBatchCalled: boolean
avatarEntityInsideScene: Map<Entity, boolean>

// @deprecated
sendBatch(actions: EntityAction[]): void
sendSceneEvent<K extends keyof IEvents>(id: K, event: IEvents[K]): void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This code is extracted from `js-sdk-toolchain` where is holded the SDK7 codebase. Importing the all library just for some of behavior doesn't seems to be
Loading