events#on JavaScript Examples

The following examples show how to use events#on. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: update.js    From aresrpg with MIT License 6 votes vote down vote up
function unload_signal({ events, x, z }) {
  const controller = new AbortController()

  aiter(on(events, Context.CHUNK_UNLOADED))
    .filter(([chunk]) => chunk.x === x && chunk.z === z)
    .take(0) // TODO: should be 1, seems to be a iterator-helper bug
    .toArray()
    .then(() => controller.abort())

  return controller.signal
}
Example #2
Source File: path.js    From aresrpg with MIT License 6 votes vote down vote up
export function path_end(mobs) {
  for (const mob of mobs) {
    const state = aiter(on(mob.events, Mob.STATE)).map(([state]) => state)

    const end = path_to_end(state)

    aiter(end).reduce((last_time, time) => {
      if (last_time !== time) {
        log.debug({ at: time }, 'Path Ended')
        mob.dispatch(MobAction.PATH_ENDED, null, time)
      }
      return time
    })
  }
}
Example #3
Source File: spawn.js    From aresrpg with MIT License 6 votes vote down vote up
function despawn_signal({ events, entity_id }) {
  const controller = new AbortController()

  aiter(on(events, Context.MOB_DESPAWNED))
    .filter(([id]) => id === entity_id)
    .take(0) // TODO: should be 1, seems to be a iterator-helper bug
    .toArray()
    .then(() => controller.abort())

  return controller.signal
}
Example #4
Source File: position.js    From aresrpg with MIT License 5 votes vote down vote up
/** @param {import('../context.js').InitialWorldWithMobs} world */
export function register(world) {
  const mobs_positions = new EventEmitter()

  for (const mob of world.mobs.all) {
    const state = aiter(on(mob.events, Mob.STATE)).map(([state]) => state)

    const positions = path_to_positions(state)

    aiter(positions).reduce((last_position, { position, target }) => {
      if (last_position !== position) {
        const chunk_x = chunk_position(position.x)
        const chunk_z = chunk_position(position.z)

        const event = {
          mob,
          position,
          last_position,
          target,
          x: chunk_x,
          z: chunk_z,
        }

        mobs_positions.emit(chunk_index(chunk_x, chunk_z), event)
        mobs_positions.emit('*', event)

        if (last_position != null && !same_chunk(position, last_position)) {
          const last_chunk_x = chunk_position(last_position.x)
          const last_chunk_z = chunk_position(last_position.z)

          mobs_positions.emit(chunk_index(last_chunk_x, last_chunk_z), event)
        }
      }
      return position
    }, null)
  }

  return {
    ...world,
    mobs: {
      ...world.mobs,
      positions: mobs_positions,
    },
  }
}
Example #5
Source File: position.js    From aresrpg with MIT License 5 votes vote down vote up
export default function observe_world({ mobs }) {
  const actions = new PassThrough({ objectMode: true })

  mobs.positions.on('*', payload =>
    actions.write({ type: 'mob_position', payload })
  )

  /** @type {import('../context.js').Observer} */
  function observe({ events }) {
    events.on(Context.CHUNK_LOADED, ({ x, z, signal }) => {
      actions.write({
        type: 'client_chunk_loaded',
        payload: { events, x, z, signal },
      })
    })

    events.on(Context.CHUNK_UNLOADED, ({ x, z }) =>
      actions.write({
        type: 'client_chunk_unloaded',
        payload: { events, x, z },
      })
    )
  }

  aiter(actions).reduce(
    (mobs_by_chunk, { type, payload: { x, z, ...payload } }) => {
      const mobs = mobs_by_chunk.get(chunk_index(x, z)) ?? []

      if (type === 'client_chunk_loaded')
        payload.events.emit(Context.CHUNK_LOADED_WITH_MOBS, {
          mobs,
          x,
          z,
          signal: payload.signal,
        })
      else if (type === 'client_chunk_unloaded')
        payload.events.emit(Context.CHUNK_UNLOADED_WITH_MOBS, { mobs, x, z })
      else if (type === 'mob_position') {
        const { mob, last_position, position } = payload

        if (last_position == null)
          return new Map([
            ...mobs_by_chunk.entries(),
            [chunk_index(x, z), [...mobs, { mob, position }]],
          ])
        else if (!same_chunk(last_position, position)) {
          const last_x = chunk_position(last_position.x)
          const last_z = chunk_position(last_position.z)
          const last_mobs = mobs_by_chunk
            .get(chunk_index(last_x, last_z))
            .filter(({ mob: { entity_id } }) => entity_id !== mob.entity_id)

          return new Map([
            ...mobs_by_chunk.entries(),
            [chunk_index(last_x, last_z), last_mobs],
            [chunk_index(x, z), [...mobs, { mob, position }]],
          ])
        }
      } else throw new Error(`unknown type: ${type}`)

      return mobs_by_chunk
    },
    new Map()
  )

  return {
    observe,
  }
}
Example #6
Source File: context.js    From aresrpg with MIT License 4 votes vote down vote up
/**
 * The following code handle the pipeline, it works as following
 *
 * state = initial_state
 * on packets + on actions
 *   |> transform_action
 *   |> (state = reduce_state(state))
 *
 * @param {import('minecraft-protocol').Client} client
 */
export async function create_context(client) {
  log.info(
    {
      username: client.username,
      uuid: client.uuid,
      id: client.id,
    },
    'Client connected'
  )

  const controller = new AbortController()
  const actions = new PassThrough({ objectMode: true })

  client.once('end', () => {
    log.info(
      { username: client.username, uuid: client.uuid },
      'Client disconnected'
    )

    actions.end()
    controller.abort()
  })

  client.on('error', error => log.error(error, 'Client error'))

  const packets = aiter(
    abortable(on(client, 'packet', { signal: controller.signal }))
  ).map(([payload, { name }]) => ({
    type: `packet/${name}`,
    payload,
  }))

  const save_state = state => {
    log.info(
      { username: client.username, uuid: client.uuid },
      'Saving to database'
    )
    Database.push({
      key: client.uuid.toLowerCase(),
      value: saved_state(state),
    })
  }

  /** @type {NodeJS.EventEmitter} */
  const events = new EventEmitter()
  const player_state = await Database.pull(client.uuid.toLowerCase())

  aiter(combineAsyncIterators(actions[Symbol.asyncIterator](), packets))
    .map(transform_action)
    .reduce(
      (last_state, action) => {
        const state = reduce_state(last_state, action)
        events.emit(Context.STATE, state)
        return state
      },
      // default nickname is the client username, and is overriden by the loaded player state
      {
        ...initial_state,
        nickname: client.username,
        ...player_state,
        last_connection_time: Date.now(),
      }
    )
    .then(state => ({
      ...state,
      last_disconnection_time: Date.now(),
    }))
    .then(save_state)
    .catch(error => {
      // TODO: what to do here if we can't save the client ?
      log.error(error, 'State error')
    })

  const get_state = last_event_value(events, Context.STATE)

  return {
    client,
    world,
    events,
    signal: controller.signal,
    get_state,
    inside_view: inside_view(get_state),
    dispatch(type, payload) {
      actions.write({ type, payload })
    },
  }
}