This ADR describes the outline of the "scene runtime" for Decentraland, it includes a minimum set of required environment functions to run a scene, including the formalization of the RPC protocol to load other parts i.e. the Rendering engine (Renderer from now on).
Decentraland Explorers (defined in ADR-102 are often compared with operative systems that run programs. A scene is a deployable JavaScript program that controls a set of entities in-world, the user-interface, and also may add functionality to the Explorer. Those programs run in a sandboxed environment exposing a set of functions to enable the scene to communicate with other components like the Rendering engine.
      The deployed scenes MUST comply with the Scene schema defined in
      ADR-51. And the format used to represent the deployment is the one
      used in the content servers as defined in ADR-80. Entities can be
      loaded as scenes if their metadata matches the scene.json schema. For the sake of
      simplicity in this specification, we are assuming a minimum scene.json in the
      shape of {"main": "bin/scene.js"} to illustrate how to load
      and run the code.
    
bin/scene.js and run it. The mechanism to resolve files based on deployed
        entities is explained in detail in ADR-79.
      stateDiagram
    [*] --> CreateRuntime(Entity)
    CreateRuntime(Entity) --> FetchCode(Entity)
    RequireModules --> Eval(code)
    GetInitialState --> .OnStart()
    state RuntimeSandbox {
      FetchCode(Entity) --> Eval(code)
    .OnStart()
    Eval(code) --> .OnStart()
      .OnStart() --> .OnUpdate(dt)
      state MainLoop {
        .OnUpdate(dt) --> .OnUpdate(dt)
      }
    }
    .OnUpdate(dt) --> [*]
    
      The runtime for the SDK7 is compatible with
      CommonJS's require to load
      RPC modules. This is so to enable a wide variety of bundlers to create compatible Decentraland
      scenes.
    
The exposed RPC modules are defined in the protocol repository.
TODO: define and document naming conventions about code generation for modules
// `require` instantiates a proxy to a RPC module. Every exposed function
// of the module returns a promise.
// require must fail immediately if the moduleName is invalid or unknown,
// and it must return a Module or Proxy synchronously
function require(moduleName: string): Module
// Commonjs-compatible modules
const exports: Object
const module: {
  readonly exports: typeof exports
}
// extra functions
function fetch(requestInit: Request): Promise<Response>
function fetch(url: string, requestInit: Request): Promise<Response>
class WebSocket {}
function setImmediate(fn: Function): void
    TODO: Document fetch and WebSocket adaptations for Decentraland Scenes
      The scenes synchronize with the renderer via the EngineApi.crdtSendToRenderer RPC
      using the CRDT protocol defined in ADR-117. The renderer will keep
      a local copy of all the entities and components required for rendering. Those components are
      in their majority serialized using protobuf as defined in ADR-123.
    
      The EngineApi.crdtSendToRenderer response includes a list of CRDT messages to be
      applied in the local scene, that is used to send information back from the renderer like the
      position of the player.
    
sequenceDiagram
  participant S as Scene
  participant K as Runtime
  participant R as Renderer
  S->>S: Load the code of the scene and execute it
  S->>K: require("~system/EngineApi")
  activate K
  K-->>R: Create scene ID=1
  K-->>S: EngineApi
  deactivate K
  loop function onUpdate(deltaTime: number)
    S-->>S: engine.update(deltaTime)
    S->>R: crdtSendToRenderer(stateChanges)
    activate R
    R-->>R: Apply patches to the engine owned entities
    R-->>R: Execute queries
    R-->>S: CRDT changes (if any)
    deactivate R
    S-->>S: Apply patches to the scene owned entities
  end
    
      The scene can hook up to certain events by adding functions to the
      module.exports variable. The functions that can be registered are:
    
onStart(): Promise<void> | void is the first function to be called in a
        scene. It is recommended that all side-effects related to the initialization of a scene are
        performed inside the onStart function.
      onUpdate(deltaTime: number): Promise<void> | void is called every frame.
        It is in charge of the scene itself to run the frame and send/receive changes to the
        renderer
      // The following example only illustrates an hypothetic scenario,
// since it is a low-level API and it shouldn't be used this way
let rotation = 0
export async function onUpdate(deltaTimeSeconds: number) {
  const speed = 0.001
  rotation += deltaTimeSeconds * speed
  updateEntityRotation(rotation)
  await sendUpdatesToRenderer()
}
    💡 Since the runtime is compatible with CommonJS, the event handler functions can be exported as
export function ...and skip themodule.exports = ...for convenience.
const engineApi = require("~system/EngineApi")
// this is a lamport timestamp, required by the CRDT rules
let timestamp = 0
const position = Vector3.Zero()
const scale = Vector3.One()
const rotation = Quaternion.Identity()
// entities are now numbers
const entityId = 1234
// component numbers, defined in .proto files
const transformId = 1
const rendererMeshId = 2
const transform = Transform.serialize({ position, rotation, scale })
const mesh = RendererMesh.serialize({ box: {} })
// now we are sending the component messages from the LWW-ElementSet
// this sets the transform & meshRenderer for the entity
const messagesBackFromRenderer = await engineApi.crdtSendToRenderer([
  CRDT.PutMessage(entityId, transformId, transform, timestamp++),
  CRDT.PutMessage(entityId, rendererMeshId, mesh, timestamp++)
])
module.exports.onUpdate = function (deltaTime: number) {
  const transformId = 1
  position.x += deltaTime
  const transform = Transform.serialize({ position, rotation, scale })
  // now we are sending the component messages from the LWW-ElementSet
  // this sets the transform & meshRenderer for the entity
  const messagesBackFromRenderer = await engineApi.crdtSendToRenderer([
    CRDT.PutMessage(entityId, transformId, transform, timestamp++)
  ])
}