UNPKG

@screeps/engine

Version:

This is a module for Screeps standalone server. See [main repository](https://github.com/screeps/screeps) for more info.

596 lines (516 loc) 26.4 kB
'use strict'; (function() { var _ = require('lodash'), utils = require('../utils'), driver = utils.getRuntimeDriver(), C = driver.constants, map = require('./map'), market = require('./market'), customPrototypes = require('./custom-prototypes'), bindFunction = Function.call.bind(Function.bind), objectCreate = Object.create, jsonStringify = JSON.stringify, jsonParse = JSON.parse; var findCacheFn = { [C.FIND_CREEPS]: (i) => !i.spawning, [C.FIND_MY_CREEPS]: (i) => !i.spawning && i.my, [C.FIND_HOSTILE_CREEPS]: (i) => !i.spawning && !i.my, [C.FIND_MY_POWER_CREEPS]: (i) => i.my, [C.FIND_HOSTILE_POWER_CREEPS]: (i) => !i.my, [C.FIND_MY_SPAWNS]: (i) => i.my === true, [C.FIND_HOSTILE_SPAWNS]: (i) => i.my === false, [C.FIND_SOURCES_ACTIVE]: (i) => i.energy > 0, [C.FIND_MY_STRUCTURES]: (i) => i.my === true, [C.FIND_HOSTILE_STRUCTURES]: (i) => i.my === false && i.owner, [C.FIND_MY_CONSTRUCTION_SITES]: (i) => i.my, [C.FIND_HOSTILE_CONSTRUCTION_SITES]: (i) => i.my === false }; function addObjectToFindCache(register, type, objectInstance, objectRaw) { if(!findCacheFn[type] || findCacheFn[type](objectInstance)) { register.findCache[type] = register.findCache[type] || {}; register.findCache[type][objectRaw.room] = register.findCache[type][objectRaw.room] || []; register.findCache[type][objectRaw.room].push(objectInstance); } } function addObjectToRegister(register, type, objectInstance, objectRaw) { register[type][objectInstance.id] = objectInstance; register.byRoom[objectRaw.room][type][objectInstance.id] = objectInstance; let index = objectRaw.x * 50 + objectRaw.y; let spatial = register.byRoom[objectRaw.room].spatial[type]; if (spatial[index] === undefined) { spatial[index] = [ objectInstance ]; } else { spatial[index].push(objectInstance); } } function makeGameObject ({runtimeData, intents, memory, getUsedCpu, globals, sandboxedFunctionWrapper, getHeapStatistics, cpuHalt}) { var customObjectsInfo = {}; if(runtimeData.customObjectPrototypes) { runtimeData.customObjectPrototypes.forEach(i => { i.opts = i.opts || {}; customObjectsInfo[i.objectType] = { name: i.name, make: customPrototypes(i.name, i.opts.parent, i.opts.properties, i.opts.prototypeExtender, !!i.opts.userOwned), findConstant: i.opts.findConstant, lookConstant: i.opts.lookConstant }; }); } var register = { _useNewPathFinder: true, _objects: {}, byRoom: {}, findCache: {}, rooms: {}, roomEventLogCache: {}, wrapFn: sandboxedFunctionWrapper || function(fn) { return fn } }; function populateRegister(reg, spatial) { _.extend(reg, { creeps: {}, structures: {}, ownedStructures: {}, spawns: {}, sources: {}, energy: {}, flags: {}, constructionSites: {}, minerals: {}, deposits: {}, tombstones: {}, nukes: {}, powerCreeps: {}, ruins: {}, customObjects: {} }); if(runtimeData.customObjectPrototypes) { runtimeData.customObjectPrototypes.forEach(i => { if(i.opts.lookConstant) { reg[i.opts.lookConstant] = {}; } }); } if(spatial) { var keys = Object.keys(reg); reg.spatial = {}; keys.forEach((i) => { reg.spatial[i] = new Array(2500); }); } } var deprecatedShown = []; register.deprecated = (msg) => { if (!_.contains(deprecatedShown, msg)) { deprecatedShown.push(msg); globals.console.log(msg); } }; register.assertTargetObject = (obj) => { if(obj && _.isPlainObject(obj) && _.isString(obj.id) && obj.id.length == 24) { throw new Error("It seems you're trying to use a serialized game object stored in Memory which is not allowed. Please use `Game.getObjectById` to retrieve a live object reference instead."); } }; populateRegister(register); var gclLevel = Math.floor(Math.pow((runtimeData.user.gcl || 0) / C.GCL_MULTIPLY, 1 / C.GCL_POW)) + 1, gclBaseProgress = Math.pow(gclLevel - 1, C.GCL_POW) * C.GCL_MULTIPLY; var gplLevel = Math.floor(Math.pow((runtimeData.user.power || 0) / C.POWER_LEVEL_MULTIPLY, 1 / C.POWER_LEVEL_POW)), gplBaseProgress = Math.pow(gplLevel, C.POWER_LEVEL_POW) * C.POWER_LEVEL_MULTIPLY; var game = { creeps: {}, powerCreeps: {}, spawns: {}, structures: {}, flags: {}, constructionSites: {}, rooms: {}, time: runtimeData.time, cpuLimit: runtimeData.cpu, cpu: { getUsed(){ return getUsedCpu(); }, tickLimit: runtimeData.cpu, limit: runtimeData.user.cpu, bucket: runtimeData.cpuBucket, getHeapStatistics: getHeapStatistics ? function() { return getHeapStatistics(); } : undefined, halt: cpuHalt, }, map: {}, gcl: { level: gclLevel, progress: (runtimeData.user.gcl || 0) - gclBaseProgress, progressTotal: Math.pow(gclLevel, C.GCL_POW) * C.GCL_MULTIPLY - gclBaseProgress }, gpl: { level: gplLevel, progress: (runtimeData.user.power || 0) - gplBaseProgress, progressTotal: Math.pow(gplLevel+1, 2) * 1000 - gplBaseProgress }, market: {}, resources: jsonParse(jsonStringify(runtimeData.user.resources||{})), getObjectById(id) { return register._objects[id] || null; }, notify(message, groupInterval) { if (intents.push('notify', {message, groupInterval}, 20)) { return C.OK; } else { return C.ERR_FULL; } } }; register.objectsByRoom = {}; register.objectsByRoomKeys = {}; _.forEach(runtimeData.roomObjects, (i, key) => { if (i.temp) { return; } register.objectsByRoom[i.room] = register.objectsByRoom[i.room] || {}; register.objectsByRoom[i.room][key] = i; register.objectsByRoomKeys[i.room] = register.objectsByRoomKeys[i.room] || []; register.objectsByRoomKeys[i.room].push(key); }); for (var i in runtimeData.rooms) { register.byRoom[i] = {}; populateRegister(register.byRoom[i], true); } require('./rooms').make(runtimeData, intents, register, globals); require('./rooms').makePos(register, globals); require('./creeps').make(runtimeData, intents, register, globals); require('./structures').make(runtimeData, intents, register, globals); require('./sources').make(runtimeData, intents, register, globals); require('./minerals').make(runtimeData, intents, register, globals); require('./deposits').make(runtimeData, intents, register, globals); require('./nukes').make(runtimeData, intents, register, globals); require('./resources').make(runtimeData, intents, register, globals); require('./flags').make(runtimeData, intents, register, globals); require('./tombstones').make(runtimeData, intents, register, globals); require('./construction-sites').make(runtimeData, intents, register, globals); require('./path-finder').make(runtimeData, intents, register, globals); require('./power-creeps').make(runtimeData, intents, register, globals); require('./ruins').make(runtimeData, intents, register, globals); require('./store').make(runtimeData, intents, register, globals); for (var i in runtimeData.rooms) { register.rooms[i] = new globals.Room(i); } for(var i in customObjectsInfo) { customObjectsInfo[i].make(runtimeData, intents, register, globals); } game.rooms = _.clone(register.rooms); var structureTypes = { rampart: globals.StructureRampart, road: globals.StructureRoad, extension: globals.StructureExtension, constructedWall: globals.StructureWall, keeperLair: globals.StructureKeeperLair, controller: globals.StructureController, link: globals.StructureLink, storage: globals.StructureStorage, tower: globals.StructureTower, observer: globals.StructureObserver, powerBank: globals.StructurePowerBank, powerSpawn: globals.StructurePowerSpawn, lab: globals.StructureLab, extractor: globals.StructureExtractor, terminal: globals.StructureTerminal, container: globals.StructureContainer, spawn: globals.StructureSpawn, nuker: globals.StructureNuker, portal: globals.StructurePortal, factory: globals.StructureFactory, invaderCore: globals.StructureInvaderCore }; var c = {}; for(var i in runtimeData.userPowerCreeps) { register.powerCreeps[i] = new globals.PowerCreep(i); game.powerCreeps[register.powerCreeps[i].name] = register.powerCreeps[i]; } for(var i in runtimeData.roomObjects) { var object = runtimeData.roomObjects[i]; if (object.temp) { continue; } c[object.type] = c[object.type] || 0; c[object.type]++; if (object.type == 'creep') { register._objects[i] = new globals.Creep(i); addObjectToRegister(register, 'creeps', register._objects[i], object); if (runtimeData.userObjects[i]) { if (game.creeps[register.creeps[i].name]) { register.creeps[i].suicide(); } else { game.creeps[register.creeps[i].name] = register.creeps[i]; } } addObjectToFindCache(register, C.FIND_CREEPS, register.creeps[i], object); addObjectToFindCache(register, C.FIND_MY_CREEPS, register.creeps[i], object); addObjectToFindCache(register, C.FIND_HOSTILE_CREEPS, register.creeps[i], object); } if (object.type == 'powerCreep') { if(register.powerCreeps[i]) { register._objects[i] = register.powerCreeps[i]; } else { register._objects[i] = new globals.PowerCreep(i); } addObjectToRegister(register, 'powerCreeps', register._objects[i], object); addObjectToFindCache(register, C.FIND_POWER_CREEPS, register.powerCreeps[i], object); addObjectToFindCache(register, C.FIND_MY_POWER_CREEPS, register.powerCreeps[i], object); addObjectToFindCache(register, C.FIND_HOSTILE_POWER_CREEPS, register.powerCreeps[i], object); } if (structureTypes[object.type]) { register._objects[i] = new structureTypes[object.type](i); addObjectToRegister(register, 'structures', register._objects[i], object); if (register._objects[i] instanceof globals.OwnedStructure) { if (runtimeData.userObjects[i]) { game.structures[register.structures[i].id] = register.structures[i]; } addObjectToRegister(register, 'ownedStructures', register._objects[i], object); } addObjectToFindCache(register, C.FIND_STRUCTURES, register.structures[i], object); addObjectToFindCache(register, C.FIND_MY_STRUCTURES, register.structures[i], object); addObjectToFindCache(register, C.FIND_HOSTILE_STRUCTURES, register.structures[i], object); if(object.type == 'spawn') { addObjectToRegister(register, 'spawns', register._objects[i], object); if (runtimeData.userObjects[i]) { game.spawns[register.spawns[i].name] = register.spawns[i]; } addObjectToFindCache(register, C.FIND_MY_SPAWNS, register.spawns[i], object); addObjectToFindCache(register, C.FIND_HOSTILE_SPAWNS, register.spawns[i], object); } } if (!object.off && (object.type == 'extension' || object.type == 'spawn') && (object.user == runtimeData.user._id)) { register.rooms[object.room].energyAvailable += object.store.energy; register.rooms[object.room].energyCapacityAvailable += object.storeCapacityResource.energy; } if (object.type == 'source') { register._objects[i] = new globals.Source(i); addObjectToRegister(register, 'sources', register._objects[i], object); addObjectToFindCache(register, C.FIND_SOURCES, register.sources[i], object); addObjectToFindCache(register, C.FIND_SOURCES_ACTIVE, register.sources[i], object); } if (object.type == 'mineral') { register._objects[i] = new globals.Mineral(i); addObjectToRegister(register, 'minerals', register._objects[i], object); addObjectToFindCache(register, C.FIND_MINERALS, register.minerals[i], object); } if(object.type == 'deposit') { register._objects[i] = new globals.Deposit(i); addObjectToRegister(register, 'deposits', register._objects[i], object) addObjectToFindCache(register, C.FIND_DEPOSITS, register.deposits[i], object) } if (object.type == 'energy') { register._objects[i] = new globals.Energy(i); addObjectToRegister(register, 'energy', register._objects[i], object); addObjectToFindCache(register, C.FIND_DROPPED_RESOURCES, register.energy[i], object); } if (object.type == 'nuke') { register._objects[i] = new globals.Nuke(i); addObjectToRegister(register, 'nukes', register._objects[i], object); addObjectToFindCache(register, C.FIND_NUKES, register._objects[i], object); } if (object.type == 'tombstone') { register._objects[i] = new globals.Tombstone(i); addObjectToRegister(register, 'tombstones', register._objects[i], object); addObjectToFindCache(register, C.FIND_TOMBSTONES, register._objects[i], object); } if(object.type == 'ruin') { register._objects[i] = new globals.Ruin(i); addObjectToRegister(register, 'ruins', register._objects[i], object); addObjectToFindCache(register, C.FIND_RUINS, register._objects[i], object) } if (object.type == 'constructionSite') { register._objects[i] = new globals.ConstructionSite(i); if (runtimeData.userObjects[i]) { game.constructionSites[register._objects[i].id] = register._objects[i]; if (!register.byRoom[runtimeData.userObjects[i].room]) { register.byRoom[runtimeData.userObjects[i].room] = {}; populateRegister(register.byRoom[runtimeData.userObjects[i].room], true); } } addObjectToRegister(register, 'constructionSites', register._objects[i], object); if(runtimeData.rooms[object.room]) { addObjectToFindCache(register, C.FIND_CONSTRUCTION_SITES, register.constructionSites[i], object); addObjectToFindCache(register, C.FIND_MY_CONSTRUCTION_SITES, register.constructionSites[i], object); addObjectToFindCache(register, C.FIND_HOSTILE_CONSTRUCTION_SITES, register.constructionSites[i], object); } } if(customObjectsInfo[object.type]) { register._objects[i] = new globals[customObjectsInfo[object.type].name](i); addObjectToRegister(register, 'customObjects', register._objects[i], object); if(customObjectsInfo[object.type].findConstant) { addObjectToFindCache(register, customObjectsInfo[object.type].findConstant, register._objects[i], object); } if(customObjectsInfo[object.type].lookConstant) { addObjectToRegister(register, customObjectsInfo[object.type].lookConstant, register._objects[i], object); } } } runtimeData.flags.forEach(flagRoomData => { var data = flagRoomData.data.split("|"); data.forEach(flagData => { if(!flagData) { return; } var info = flagData.split("~"); info[0] = info[0].replace(/\$VLINE\$/g,"|").replace(/\$TILDE\$/g,"~"); var id = 'flag_'+info[0]; var flag = register._objects[id] = new globals.Flag(info[0], info[1], info[2], flagRoomData.room, info[3], info[4]); register.flags[id] = flag; if(register.byRoom[flagRoomData.room]) { register.byRoom[flagRoomData.room].flags[id] = flag; let index = (+info[3]) * 50 + (+info[4]); let spatial = register.byRoom[flagRoomData.room].spatial.flags; if (spatial[index] === undefined) { spatial[index] = [ flag ]; } else { spatial[index].push(flag); } } game.flags[info[0]] = flag; if(!findCacheFn[C.FIND_FLAGS] || findCacheFn[C.FIND_FLAGS](flag)) { register.findCache[C.FIND_FLAGS] = register.findCache[C.FIND_FLAGS] || {}; register.findCache[C.FIND_FLAGS][flagRoomData.room] = register.findCache[C.FIND_FLAGS][flagRoomData.room] || []; register.findCache[C.FIND_FLAGS][flagRoomData.room].push(flag); } }) }); game.map = register.map = map.makeMap(runtimeData, register, globals); game.market = register.market = market.make(runtimeData, intents, register); _.extend(globals, jsonParse(jsonStringify(C))); return game; }; (function() { var runCodeCache = objectCreate(null); exports.init = function ( _globals, _codeModules, _runtimeData, _intents, _memory, _fakeConsole, _consoleCommands, _timeout, _getUsedCpu, _scriptCachedData, _sandboxedFunctionWrapper, _getHeapStatistics, _cpuHalt ) { var userId = _runtimeData.user._id; runCodeCache[userId] = runCodeCache[userId] || objectCreate(null); runCodeCache[userId].globals = _globals; runCodeCache[userId].codeModules = _codeModules; runCodeCache[userId].runtimeData = _runtimeData; runCodeCache[userId].intents = _intents; runCodeCache[userId].memory = _memory; runCodeCache[userId].fakeConsole = _fakeConsole; runCodeCache[userId].consoleCommands = _consoleCommands; runCodeCache[userId].timeout = _timeout; runCodeCache[userId].getUsedCpu = _getUsedCpu; runCodeCache[userId].scriptCachedData = _scriptCachedData; runCodeCache[userId].getHeapStatistics = _getHeapStatistics; runCodeCache[userId].cpuHalt = _cpuHalt; runCodeCache[userId].sandboxedFunctionWrapper = _sandboxedFunctionWrapper; _.extend(runCodeCache[userId].globals, { RawMemory: runCodeCache[userId].memory, console: runCodeCache[userId].fakeConsole }); if (!runCodeCache[userId].globals._) { runCodeCache[userId].globals._ = _.runInContext(); } Object.defineProperty(runCodeCache[userId].globals, 'Memory', { configurable: true, enumerable: true, get() { try { runCodeCache[userId].memory._parsed = JSON.parse(runCodeCache[userId].memory.get() || "{}"); runCodeCache[userId].memory._parsed.__proto__ = null; } catch (e) { runCodeCache[userId].memory._parsed = null; } Object.defineProperty(runCodeCache[userId].globals, 'Memory', { configurable: true, enumerable: true, value: runCodeCache[userId].memory._parsed }); return runCodeCache[userId].memory._parsed; } }); runCodeCache[userId].globals.Game = makeGameObject(runCodeCache[userId]); if (!runCodeCache[userId].globals.require || runCodeCache[userId].runtimeData.userCodeTimestamp != runCodeCache[userId].globals.require.timestamp || !_.isObject(runCodeCache[userId].globals.require.cache.main) || !_.isFunction( runCodeCache[userId].globals.require.cache.main.loop)) { runCodeCache[userId].globals.require = bindFunction(requireFn, runCodeCache[userId]); runCodeCache[userId].globals.require.cache = {lodash: runCodeCache[userId].globals._}; runCodeCache[userId].globals.require.timestamp = runCodeCache[userId].runtimeData.userCodeTimestamp; } return runCodeCache[userId]; }; exports.run = function(userId) { var mainExports = runCodeCache[userId].globals.require('main'); if(_.isObject(mainExports) && _.isFunction(mainExports.loop)) { if(runCodeCache[userId].globals.require.initGlobals) { _.forEach(runCodeCache[userId].globals.require.initGlobals, (i) => i()); } driver.evalCode({ exports: mainExports, user: runCodeCache[userId].runtimeData.user._id, timestamp: runCodeCache[userId].runtimeData.userCodeTimestamp, name: '__mainLoop', code: 'module.exports.loop();' }, runCodeCache[userId].globals, false, runCodeCache[userId].timeout); } if (runCodeCache[userId].consoleCommands) { for (var i = 0; i < runCodeCache[userId].consoleCommands.length; i++) { var result = driver.evalCode({ exports: {}, user: runCodeCache[userId].runtimeData.user._id, name: '_console' + new Date().getTime() + '_' + i, code: runCodeCache[userId].consoleCommands[i].expression }, runCodeCache[userId].globals, true); if (!runCodeCache[userId].consoleCommands[i].hidden) { runCodeCache[userId].fakeConsole.commandResult(result); } } } }; })(); function requireFn(moduleName){ moduleName = moduleName.replace(/^\.\//,''); if (!(moduleName in this.globals.require.cache)) { if (_.isUndefined(this.codeModules[moduleName])) { throw new Error(`Unknown module '${moduleName}'`); } if(_.isObject(this.codeModules[moduleName]) && this.codeModules[moduleName].binary !== undefined) { this.globals.require.cache[moduleName] = driver.bufferFromBase64(this.codeModules[moduleName].binary); } else { this.globals.require.cache[moduleName] = -1; var moduleObject = { exports: {}, user: this.runtimeData.user._id, timestamp: this.runtimeData.userCodeTimestamp, name: moduleName, code: this.codeModules[moduleName] }; try { driver.evalCode(moduleObject, this.globals, false, this.timeout, this.scriptCachedData); } catch (e) { delete this.globals.require.cache[moduleName]; throw e; } this.globals.require.cache[moduleName] = moduleObject.exports; if (moduleObject.__initGlobals) { this.globals.require.initGlobals = this.globals.require.initGlobals || {}; this.globals.require.initGlobals[moduleName] = moduleObject.__initGlobals; } } } else if (this.globals.require.cache[moduleName] === -1) { throw new Error(`Circular reference to module '${moduleName}'`) } return this.globals.require.cache[moduleName]; } })();