codecombat/headless_client.coffee

205 lines
5.7 KiB
CoffeeScript

###
This file will simulate games on node.js by emulating the browser environment.
At some point, most of the code can be merged with Simulator.coffee
###
bowerComponentsPath = "./bower_components/"
headlessClientPath = "./headless_client/"
# SETTINGS
options =
workerCode: require headlessClientPath + 'worker_world'
debug: false # Enable logging of ajax calls mainly
testing: false # Instead of simulating 'real' games, use the same one over and over again. Good for leak hunting.
testFile: require headlessClientPath + 'test.js'
leakTest: false # Install callback that tries to find leaks automatically
exitOnLeak: false # Exit if leak is found. Only useful if leaktest is set to true, obviously.
heapdump: false # Dumps the whole heap after every pass. The heap dumps can then be viewed in Chrome browser.
headlessClient: true
options.heapdump = require('heapdump') if options.heapdump
server = if options.testing then "http://127.0.0.1:3000" else "http://codecombat.com"
# Disabled modules
disable = [
'lib/AudioPlayer'
'locale/locale'
'../locale/locale'
]
# Start of the actual code. Setting up the enivronment to match the environment of the browser
# the path used for the loader. __dirname is module dependent.
path = __dirname
m = require 'module'
request = require 'request'
originalLoader = m._load
unhook = () ->
m._load = originalLoader
hook = () ->
m._load = hookedLoader
JASON = require 'jason'
# Global emulated stuff
GLOBAL.window = GLOBAL
GLOBAL.document = location: pathname: "headless_client"
GLOBAL.console.debug = console.log
GLOBAL.Worker = require('webworker-threads').Worker
Worker::removeEventListener = (what) ->
if what is 'message'
@onmessage = -> #This webworker api has only one event listener at a time.
GLOBAL.tv4 = require('tv4').tv4
GLOBAL.marked = setOptions: ->
store = {}
GLOBAL.localStorage =
getItem: (key) => store[key]
setItem: (key, s) => store[key] = s
removeItem: (key) => delete store[key]
# Hook node.js require. See https://github.com/mfncooper/mockery/blob/master/mockery.js
# The signature of this function *must* match that of Node's Module._load,
# since it will replace that.
# (Why is there no easier way?)
hookedLoader = (request, parent, isMain) ->
if request in disable or ~request.indexOf('templates')
console.log 'Ignored ' + request if options.debug
return class fake
else if '/' in request and not (request[0] is '.') or request is 'application'
request = path + '/app/' + request
else if request is 'underscore'
request = 'lodash'
console.log "loading " + request if options.debug
originalLoader request, parent, isMain
#jQuery wrapped for compatibility purposes. Poorly.
GLOBAL.$ = GLOBAL.jQuery = (input) ->
console.log 'Ignored jQuery: ' + input if options.debug
append: (input)-> exports: ()->
cookies = request.jar()
$.ajax = (options) ->
responded = false
url = options.url
if url.indexOf('http')
url = '/' + url unless url[0] is '/'
url = server + url
data = options.data
#if (typeof data) is 'object'
#console.warn JSON.stringify data
#data = JSON.stringify data
console.log "Requesting: " + JSON.stringify options if options.debug
console.log "URL: " + url if options.debug
request
url: url
jar: cookies
json: options.parse
method: options.type
body: data
, (error, response, body) ->
console.log "HTTP Request:" + JSON.stringify options if options.debug and not error
if responded
console.log "\t↳Already returned before." if options.debug
return
if (error)
console.warn "\t↳Returned: error: #{error}"
options.error(error) if options.error?
else
console.log "\t↳Returned: statusCode #{response.statusCode}: #{if options.parse then JSON.stringify body else body}" if options.debug
options.success(body, response, status: response.statusCode) if options.success?
statusCode = response.statusCode if response?
options.complete(status: statusCode) if options.complete?
responded = true
$.extend = (deep, into, from) ->
copy = _.clone(from, deep);
if into
_.assign into, copy
copy = into
copy
$.isArray = (object) ->
_.isArray object
$.isPlainObject = (object) ->
_.isPlainObject object
do (setupLodash = this) ->
GLOBAL._ = require 'lodash'
_.str = require 'underscore.string'
_.string = _.str
_.mixin _.str.exports()
# load Backbone. Needs hooked loader to reroute underscore to lodash.
hook()
GLOBAL.Backbone = require bowerComponentsPath + 'backbone/backbone'
unhook()
Backbone.$ = $
require bowerComponentsPath + 'validated-backbone-mediator/backbone-mediator'
# Instead of mediator, dummy might be faster yet suffice?
#Mediator = class Mediator
# publish: (id, object) ->
# console.Log "Published #{id}: #{object}"
# @subscribe: () ->
# @unsubscribe: () ->
GLOBAL.Aether = require 'aether'
# Set up new loader.
hook()
login = require './login.coffee' #should contain an object containing they keys 'username' and 'password'
#Login user and start the code.
$.ajax
url: '/auth/login'
type: "POST"
data: login
parse: true
error: (error) -> "Bad Error. Can't connect to server or something. " + error
success: (response) ->
console.log "User: " + response
GLOBAL.window.userObject = response # JSON.parse response
User = require 'models/User'
World = require 'lib/world/world'
LevelLoader = require 'lib/LevelLoader'
GoalManager = require 'lib/world/GoalManager'
SuperModel = require 'models/SuperModel'
log = require 'winston'
CocoClass = require 'lib/CocoClass'
Simulator = require 'lib/simulator/Simulator'
sim = new Simulator options
sim.fetchAndSimulateTask()