diff --git a/app/lib/aether_utils.coffee b/app/lib/aether_utils.coffee new file mode 100644 index 000000000..de07fdb57 --- /dev/null +++ b/app/lib/aether_utils.coffee @@ -0,0 +1,48 @@ +Aether.addGlobal 'Vector', require 'lib/world/vector' +Aether.addGlobal '_', _ + +module.exports.createAetherOptions = (options) -> + throw new Error 'Specify a function name to create an Aether instance' unless options.functionName + throw new Error 'Specify a code language to create an Aether instance' unless options.codeLanguage + + aetherOptions = + functionName: options.functionName + protectAPI: not options.skipProtectAPI + includeFlow: false + yieldConditionally: options.functionName is 'plan' + globals: ['Vector', '_'] + problems: + jshint_W040: {level: 'ignore'} + jshint_W030: {level: 'ignore'} # aether_NoEffect instead + jshint_W038: {level: 'ignore'} # eliminates hoisting problems + jshint_W091: {level: 'ignore'} # eliminates more hoisting problems + jshint_E043: {level: 'ignore'} # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things + jshint_Unknown: {level: 'ignore'} # E043 also triggers Unknown, so ignore that, too + aether_MissingThis: {level: 'error'} + #functionParameters: # TODOOOOO + executionLimit: 1 * 1000 * 1000 + language: options.codeLanguage + parameters = functionParameters[options.functionName] + unless parameters + console.warn "Unknown method #{options.functionName}: please add function parameters to lib/aether_utils.coffee." + parameters = [] + if options.functionParameters and not _.isEqual options.functionParameters, parameters + console.error "Update lib/aether_utils.coffee with the right function parameters for #{options.functionName} (had: #{parameters} but this gave #{options.functionParameters}." + parameters = options.functionParameters + aetherOptions.functionParameters = parameters.slice() + #console.log 'creating aether with options', aetherOptions + return aetherOptions + +# TODO: figure out some way of saving this info dynamically so that we don't have to hard-code it: #1329 +functionParameters = + hear: ['speaker', 'message', 'data'] + makeBid: ['tileGroupLetter'] + findCentroids: ['centroids'] + isFriend: ['name'] + evaluateBoard: ['board', 'player'] + getPossibleMoves: ['board'] + minimax_alphaBeta: ['board', 'player', 'depth', 'alpha', 'beta'] + + chooseAction: [] + plan: [] + initializeCentroids: [] diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index 0e5a6631e..01cff2057 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -3,9 +3,7 @@ CocoClass = require 'lib/CocoClass' LevelLoader = require 'lib/LevelLoader' GoalManager = require 'lib/world/GoalManager' God = require 'lib/God' - -Aether.addGlobal 'Vector', require 'lib/world/vector' -Aether.addGlobal '_', _ +{createAetherOptions} = require 'lib/aether_utils' module.exports = class Simulator extends CocoClass constructor: (@options) -> @@ -400,23 +398,7 @@ module.exports = class Simulator extends CocoClass aether.transpile '' createAether: (methodName, method, useProtectAPI, codeLanguage) -> - aetherOptions = - functionName: methodName - protectAPI: useProtectAPI - includeFlow: false - yieldConditionally: methodName is 'plan' - globals: ['Vector', '_'] - problems: - jshint_W040: {level: 'ignore'} - jshint_W030: {level: 'ignore'} # aether_NoEffect instead - aether_MissingThis: {level: 'error'} - #functionParameters: # TODOOOOO - executionLimit: 1 * 1000 * 1000 - language: codeLanguage - if methodName is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if methodName is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if methodName is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - #console.log 'creating aether with options', aetherOptions + aetherOptions = createAetherOptions functionName: methodName, codeLanguage: codeLanguage, skipProtectAPI: not useProtectAPI return new Aether aetherOptions class SimulationTask diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 4fe5fb30a..be48d2e74 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -62,7 +62,7 @@ module.exports = class ThangType extends CocoModel options buildSpriteSheet: (options) -> - return false unless @isFullyLoaded() + return false unless @isFullyLoaded() and @get 'raw' @options = @fillOptions options key = @spriteSheetKey(@options) if ss = @spriteSheets[key] then return ss diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 480159024..7fa0bbb15 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -122,7 +122,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' - format: 'javascript' + format: 'code' codeLanguage: type: 'string' @@ -165,6 +165,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' + format: 'code' submittedCodeLanguage: type: 'string' @@ -175,6 +176,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' + format: 'code' isRanking: type: 'boolean' diff --git a/app/views/play/common/LadderSubmissionView.coffee b/app/views/play/common/LadderSubmissionView.coffee index 4cf4b7e29..580afba8a 100644 --- a/app/views/play/common/LadderSubmissionView.coffee +++ b/app/views/play/common/LadderSubmissionView.coffee @@ -1,5 +1,6 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/play/common/ladder_submission' +{createAetherOptions} = require 'lib/aether_utils' module.exports = class LadderSubmissionView extends CocoView className: 'ladder-submission-view' @@ -72,29 +73,15 @@ module.exports = class LadderSubmissionView extends CocoView transpileSession: -> submittedCode = @session.get('code') - language = @session.get('codeLanguage') or 'javascript' - @session.set('submittedCodeLanguage', language) + codeLanguage = @session.get('codeLanguage') or 'javascript' + @session.set('submittedCodeLanguage', codeLanguage) @session.save() # TODO: maybe actually use a callback to make sure this works? transpiledCode = {} for thang, spells of submittedCode transpiledCode[thang] = {} for spellID, spell of spells unless _.contains(@session.get('teamSpells')[@session.get('team')], thang + '/' + spellID) then continue - #DRY this - aetherOptions = - problems: {} - language: language - functionName: spellID - functionParameters: [] - yieldConditionally: spellID is 'plan' - globals: ['Vector', '_'] - protectAPI: true - includeFlow: false - executionLimit: 1 * 1000 * 1000 - if spellID is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if spellID is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if spellID is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - + aetherOptions = createAetherOptions functionName: spellID, codeLanguage: codeLanguage aether = new Aether aetherOptions transpiledCode[thang][spellID] = aether.transpile spell transpiledCode diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index ff2dc39e6..5d56e131d 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -1,9 +1,7 @@ SpellView = require './SpellView' SpellListTabEntryView = require './SpellListTabEntryView' {me} = require 'lib/auth' - -Aether.addGlobal 'Vector', require 'lib/world/vector' -Aether.addGlobal '_', _ +{createAetherOptions} = require 'lib/aether_utils' module.exports = class Spell loaded: false @@ -125,25 +123,8 @@ module.exports = class Spell createAether: (thang) -> aceConfig = me.get('aceConfig') ? {} writable = @permissions.readwrite.length > 0 - aetherOptions = - problems: - jshint_W040: {level: 'ignore'} - jshint_W030: {level: 'ignore'} # aether_NoEffect instead - jshint_W038: {level: 'ignore'} # eliminates hoisting problems - jshint_W091: {level: 'ignore'} # eliminates more hoisting problems - jshint_E043: {level: 'ignore'} # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things - jshint_Unknown: {level: 'ignore'} # E043 also triggers Unknown, so ignore that, too - aether_MissingThis: {level: 'error'} - language: @language - functionName: @name - functionParameters: @parameters - yieldConditionally: thang.plan? - globals: ['Vector', '_'] - # TODO: Gridmancer doesn't currently work with protectAPI, so hack it off - protectAPI: not (@skipProtectAPI or window.currentView?.level.get('name').match('Gridmancer')) and writable # If anyone can write to this method, we must protect it. - includeFlow: false - executionLimit: 1 * 1000 * 1000 - #console.log 'creating aether with options', aetherOptions + skipProtectAPI = @skipProtectAPI or not writable + aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI aether = new Aether aetherOptions workerMessage = function: 'createAether' diff --git a/scripts/transpile.coffee b/scripts/transpile.coffee index 5c39b414b..53e66e43a 100644 --- a/scripts/transpile.coffee +++ b/scripts/transpile.coffee @@ -8,9 +8,8 @@ async = require 'async' serverSetup = require '../server_setup' Level = require '../server/levels/Level' LevelSession = require '../server/levels/sessions/LevelSession' +{createAetherOptions} = require '../app/lib/aether_utils' -Aether.addGlobal 'Vector', require '../app/lib/world/vector' -Aether.addGlobal '_', _ i = 0 transpileLevelSession = (sessionID, cb) -> query = LevelSession.findOne('_id': sessionID).select('team teamSpells submittedCode submittedCodeLanguage').lean() @@ -27,23 +26,9 @@ transpileLevelSession = (sessionID, cb) -> transpiledCode[thang] = {} for spellID, spell of spells spellName = thang + '/' + spellID - - if session.teamSpells and not (spellName in session.teamSpells[session.team]) then continue + continue if session.teamSpells and not (spellName in session.teamSpells[session.team]) #console.log "Transpiling spell #{spellName}" - aetherOptions = - problems: {} - language: session.submittedCodeLanguage - functionName: spellID - functionParameters: [] - yieldConditionally: spellID is 'plan' - globals: ['Vector', '_'] - protectAPI: true - includeFlow: false - executionLimit: 1 * 1000 * 1000 - if spellID is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if spellID is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if spellID is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - + aetherOptions = createAetherOptions functionName: spellID, codeLanguage: session.submittedCodeLanguage aether = new Aether aetherOptions transpiledCode[thang][spellID] = aether.transpile spell conditions =