<commit_before>import Manager from 'sk/core/Manager'

import ResourceEvent from 'sk/events/ResourceEvent'
import ProgressEvent from 'sk/events/ProgressEvent'
import ErrorEvent from 'sk/events/ErrorEvent'

import audioMetadataSchema from 'sk/config/audioMetadataSchema'
import audioSpriteMetadataSchema from 'sk/config/audioSpriteMetadataSchema'

import * as middlewares from 'sk/middlewares'
import * as utils from 'sk/utils'


const CODECS = {
  'ogg'   : 'ogg',
  'ogv'   : 'ogg',
  'oga'   : 'ogg',
  'ogx'   : 'ogg',
  'ogm'   : 'ogg',
  'spx'   : 'ogg',
  'opus'  : 'opus',
  'm4a'   : 'm4a',
  'mp4'   : 'm4a',
  'm4p'   : 'm4a',
  'm4b'   : 'm4a',
  'm4r'   : 'm4a',
  'm4v'   : 'm4a',
  'mp3'   : 'mp3',
  'wav'   : 'wav',
  'wave'  : 'wav',
  'webm'  : 'webm',
  'ac3'   : 'dolby',
}

/**
 * Resources manager handle the loading and caching of the game assets.
 */
export default class ResourcesManager extends Manager {

  /**
   * @param {Game} game - The game instance.
   */
  constructor(game) {
    super(game)

    this._loader = null
    this._resources = {}
    
    this._stackSize = 0
  }

  /**
   * Bash path that will be used for all resources during loading.
   * @type {String}
   */
  get basePath() { return this._loader.baseUrl }

  /**
   * Maximum number of concurrent loading process.
   * @type {Number}
   */
  get maxConcurrency() { return this._loader._queue.concurrency }


  /**
   * Manager setup. Called internally by the engine. Do not call it manually.
   */
  setup() {
    utils.profiling.begin('resources')
    this._setupLoader()
    this._setupMiddlewares()
    this._setupEvents()
    utils.profiling.end('resources')
  }

  /**
   * Setup the PIXI loader.
   */
  _setupLoader() {
    let config = this.game.config
    let basePath = config.resources.basePath || ''
    let maxConcurrency = config.resources.maxConcurrency
    this._loader = new PIXI.loaders.Loader(basePath, maxConcurrency)

    // We have to force PIXI loader to load audio data as an arraybuffer, in 
    // XHR, so we need to register manually the audio extensions. See
    // https://github.com/englercj/resource-loader/issues/31 for more info. 
    let Resource = PIXI.loaders.Resource
    let audioExtensions = ['ogg', 'opus', 'mp3', 'wav', 'm4a', 'webm', 'ac3']
    for (let i=0; i<audioExtensions.length; i++) {
      let ext = audioExtensions[i]
      Resource.setExtensionLoadType(ext, Resource.LOAD_TYPE.XHR);
      Resource.setExtensionXhrType(ext, Resource.XHR_RESPONSE_TYPE.BUFFER);
    }
  }

  /**
   * Setup the loader middlewares.
   */
  _setupMiddlewares() {
    // remove the current middlewares from the pixi loader. We don't change 
    // the Load._pixiMiddleware variable to avoid any conflit with non-skald 
    // pixi game running in the same page.
    this._loader._afterMiddleware = []

    this._loader.use(middlewares.textureMiddleware(this.game))
    this._loader.use(middlewares.jsonMiddleware(this.game))
    this._loader.use(middlewares.rawMiddleware(this.game))
    this._loader.use(middlewares.audioMiddleware(this.game))
    this._loader.use(middlewares.audioSpriteMiddleware(this.game))
    this._loader.use(middlewares.spriteSheetMiddleware(this.game))
    this._loader.use(middlewares.bitmapFontMiddleware(this.game))
  }

  /**
   * Configure the PIXI loader events.
   */
  _setupEvents() {
    this._loader.on('progress', (l, r)=>this._onProgress(l, r))
    this._loader.on('error', (e, l, r)=>this._onError(e, l, r))
    this._loader.on('load', (l, r)=>this._onLoad(l, r))
    this._loader.on('complete', ()=>this._onComplete())
  }

  /**
   * Handle progress event.
   */
  _onProgress(loader, resource) {
    let total = this._stackSize
    let loaded = parseInt(this._stackSize*loader.progress/100)
    
    this.game.events.dispatch(
      new ProgressEvent('resources.progress', loaded, total)
    )
  }
  
  /**
   * Handle error event.
   */
  _onError(error, loader, resource) {
    this.game.events.dispatch(
      new ErrorEvent(
        'resources.error',
        `Could not load the resource "${resource.name}" from "${resource.url}".`
      )
    )

    this.game.log.warn(
      `Could not load the resource "${resource.name}" from "${resource.url}".`
    )
  }
  
  /**
   * Handle load event.
   */
  _onLoad(loader, resource) {
    let r = this._resources[resource.name] || {}
    this.game.events.dispatch(
      new ResourceEvent(
        'resources.load',
        resource.name,
        resource.url,
        r.resource,
        r.metadata
      )
    )
  }
  
  /**
   * Handle complete event.
   */
  _onComplete(event) {
    this.game.events.dispatch('resources.complete')
    this._stackSize = 0
    this._loader.reset()
  }

  /**
   * Automatically extract the audio URL, considering the browser support. 
   * Check the CODEC constant (in this file) to see the priority order.
   */
  _getAudioUrl(url) {
    if (Array.isArray(url)) {
      let extensions = Object.keys(CODECS)
      let device = this.game.device

      for (let i=0; i<url.length; i++) {
        let file = url[i].toLowerCase()

        let extension = extensions.find(e => file.endsWith(e))
        if (extension && device[CODECS[extension]]) {<commit_msg>Resource manager using event pool<commit_after>import Manager from 'sk/core/Manager'

import ResourceEvent from 'sk/events/ResourceEvent'
import ProgressEvent from 'sk/events/ProgressEvent'
import ErrorEvent from 'sk/events/ErrorEvent'

import audioMetadataSchema from 'sk/config/audioMetadataSchema'
import audioSpriteMetadataSchema from 'sk/config/audioSpriteMetadataSchema'

import * as middlewares from 'sk/middlewares'
import * as utils from 'sk/utils'


const CODECS = {
  'ogg'   : 'ogg',
  'ogv'   : 'ogg',
  'oga'   : 'ogg',
  'ogx'   : 'ogg',
  'ogm'   : 'ogg',
  'spx'   : 'ogg',
  'opus'  : 'opus',
  'm4a'   : 'm4a',
  'mp4'   : 'm4a',
  'm4p'   : 'm4a',
  'm4b'   : 'm4a',
  'm4r'   : 'm4a',
  'm4v'   : 'm4a',
  'mp3'   : 'mp3',
  'wav'   : 'wav',
  'wave'  : 'wav',
  'webm'  : 'webm',
  'ac3'   : 'dolby',
}

/**
 * Resources manager handle the loading and caching of the game assets.
 */
export default class ResourcesManager extends Manager {

  /**
   * @param {Game} game - The game instance.
   */
  constructor(game) {
    super(game)

    this._loader = null
    this._resources = {}
    
    this._stackSize = 0
  }

  /**
   * Bash path that will be used for all resources during loading.
   * @type {String}
   */
  get basePath() { return this._loader.baseUrl }

  /**
   * Maximum number of concurrent loading process.
   * @type {Number}
   */
  get maxConcurrency() { return this._loader._queue.concurrency }


  /**
   * Manager setup. Called internally by the engine. Do not call it manually.
   */
  setup() {
    utils.profiling.begin('resources')
    this._setupLoader()
    this._setupMiddlewares()
    this._setupEvents()
    utils.profiling.end('resources')
  }

  /**
   * Setup the PIXI loader.
   */
  _setupLoader() {
    let config = this.game.config
    let basePath = config.resources.basePath || ''
    let maxConcurrency = config.resources.maxConcurrency
    this._loader = new PIXI.loaders.Loader(basePath, maxConcurrency)

    // We have to force PIXI loader to load audio data as an arraybuffer, in 
    // XHR, so we need to register manually the audio extensions. See
    // https://github.com/englercj/resource-loader/issues/31 for more info. 
    let Resource = PIXI.loaders.Resource
    let audioExtensions = ['ogg', 'opus', 'mp3', 'wav', 'm4a', 'webm', 'ac3']
    for (let i=0; i<audioExtensions.length; i++) {
      let ext = audioExtensions[i]
      Resource.setExtensionLoadType(ext, Resource.LOAD_TYPE.XHR);
      Resource.setExtensionXhrType(ext, Resource.XHR_RESPONSE_TYPE.BUFFER);
    }
  }

  /**
   * Setup the loader middlewares.
   */
  _setupMiddlewares() {
    // remove the current middlewares from the pixi loader. We don't change 
    // the Load._pixiMiddleware variable to avoid any conflit with non-skald 
    // pixi game running in the same page.
    this._loader._afterMiddleware = []

    this._loader.use(middlewares.textureMiddleware(this.game))
    this._loader.use(middlewares.jsonMiddleware(this.game))
    this._loader.use(middlewares.rawMiddleware(this.game))
    this._loader.use(middlewares.audioMiddleware(this.game))
    this._loader.use(middlewares.audioSpriteMiddleware(this.game))
    this._loader.use(middlewares.spriteSheetMiddleware(this.game))
    this._loader.use(middlewares.bitmapFontMiddleware(this.game))
  }

  /**
   * Configure the PIXI loader events.
   */
  _setupEvents() {
    this._loader.on('progress', (l, r)=>this._onProgress(l, r))
    this._loader.on('error', (e, l, r)=>this._onError(e, l, r))
    this._loader.on('load', (l, r)=>this._onLoad(l, r))
    this._loader.on('complete', ()=>this._onComplete())
  }

  /**
   * Handle progress event.
   */
  _onProgress(loader, resource) {
    let total = this._stackSize
    let loaded = parseInt(this._stackSize*loader.progress/100)
    
    let event = this.game.pool.create(ProgressEvent)
    event._type = 'resources.progress'
    event._loaded = loaded
    event._total = total
    this.game.events.dispatch(event)
  }
  
  /**
   * Handle error event.
   */
  _onError(error, loader, resource) {
    let event = this.game.pool.create(ErrorEvent)
    event._type = 'resources.error'
    event._message = `Could not load the resource "${resource.name}" from `+
                     `"${resource.url}".`
    this.game.events.dispatch(event)

    this.game.log.warn(
      `Could not load the resource "${resource.name}" from "${resource.url}".`
    )
  }
  
  /**
   * Handle load event.
   */
  _onLoad(loader, resource) {
    let r = this._resources[resource.name] || {}
    
    let event = this.game.pool.create(ResourceEvent)
    event._type = 'resources.load'
    event._id = resource.name
    event._url = resource.url
    event._resource = r.resource
    event._metadata = r.metadata
    this.game.events.dispatch(event)
  }
  
  /**
   * Handle complete event.
   */
  _onComplete(event) {
    this.game.events.dispatch('resources.complete')
    this._stackSize = 0
    this._loader.reset()
  }

  /**
   * Automatically extract the audio URL, considering the browser support. 
   * Check the CODEC constant (in this file) to see the priority order.
   */
  _getAudioUrl(url) {
    if (Array.isArray(url)) {
      let extensions = Object.keys(CODECS)
      let device = this.game.device

      for (let i=0; i<url.length; i++) {
        let file = url[i].toLowerCase()

        let extension = extensions.find(e => file.endsWith(e))
        if (extension && device[CODECS[extension]]) {
{'fraction_non_alphanumeric': 0.13900337204945673, 'fraction_numerical': 0.005058074185088048, 'mean_word_length': 2.9239250275633957, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6330203', 'n_tokens_mistral': 3595, 'n_tokens_neox': 3529, 'n_words': 806}