screepsmod-admin-utils
Version:
## This is a Collection of utilities for Screeps Private Server admins
238 lines (216 loc) • 9.89 kB
JavaScript
const path = require('path')
const axios = require('axios')
const fs = require('fs').promises
const log = (...args) => console.log('[ImportMap]', ...args)
const logResult = (...args) => {
log(...args)
return args.join(' ')
}
const logStart = async (config, mapId) => {
if (!config.mongo) {
throw new Error('screepsmod-mongo required for map imports')
}
log(`Importing map: ${mapId}`)
}
const logFinish = () => {
return logResult('Map imported! Restart the server and use system.resumeSimulation() to unpause ticks')
}
const loadRooms = async (config, mapId, rooms) => {
const { common: { dbCollections, storage: { db, env, pubsub } } } = config
const cliMap = require('@screeps/backend/lib/cli/map')
const mapDir = path.resolve(process.env.ASSET_DIR, 'map')
const mapZoomDir = path.resolve(process.env.ASSET_DIR, 'map/zoom2')
await fs.mkdir(mapDir, { recursive: true })
await fs.mkdir(mapZoomDir, { recursive: true })
log('Init DB')
// We want a fully empty db for this
await env.set(env.keys.MAIN_LOOP_PAUSED, '1') // Just to make sure
await Promise.all(dbCollections.map(col => db[col].removeWhere({})))
await env.flushall()
await Promise.all([
env.set(env.keys.MAIN_LOOP_PAUSED, '1'),
env.set(env.keys.GAMETIME, '1'),
env.set(env.keys.ACCESSIBLE_ROOMS, '[]'),
env.set(env.keys.MAP_URL, mapId),
db.users.update({ _id: '2' }, {
$set: {
_id: '2',
username: 'Invader',
usernameLower: 'invader',
cpu: 100,
cpuAvailable: 10000,
gcl: 13966610.2,
active: 0,
badge: {
type: {
path1: 'm 60.493413,13.745781 -1.122536,7.527255 -23.302365,-6.118884 -24.097204,26.333431 6.412507,0.949878 -5.161481,19.706217 26.301441,24.114728 1.116562,-7.546193 23.350173,6.122868 24.097202,-26.318478 -6.462307,-0.95785 5.16845,-19.699243 z m -1.58271,10.611118 -0.270923,1.821013 C 57.330986,25.69819 55.969864,25.331543 54.570958,25.072546 Z m -8.952409,4.554029 c 11.653612,0 21.055294,9.408134 21.055294,21.069735 0,11.661603 -9.401682,21.068738 -21.055294,21.068738 -11.65361,0 -21.055297,-9.407135 -21.055297,-21.068738 0,-11.661601 9.401687,-21.069735 21.055297,-21.069735 z M 26.634018,40.123069 c -0.262324,0.618965 -0.494865,1.252967 -0.708185,1.895768 l -0.0508,-0.104656 -0.194228,-0.417627 c 0.261245,-0.385697 0.631962,-0.909531 0.953211,-1.373485 z m 47.391601,17.714764 0.115539,0.237219 0.214148,0.462479 c -0.380159,0.55986 -0.886342,1.281124 -1.3835,1.988466 0.400298,-0.870957 0.752837,-1.767746 1.053813,-2.688164 z M 41.364458,73.812322 c 0.694434,0.251619 1.40261,0.471895 2.123558,0.662817 l -2.303841,0.558165 z',
path2: 'm 60.857962,24.035953 -6.397566,1.055531 c 6.084137,1.084905 11.78633,4.394548 15.786244,9.746957 5.741405,7.682749 6.465607,17.544704 2.736121,25.67958 1.511089,-2.147013 2.622575,-3.851337 2.622575,-3.851337 l 1.628526,0.241209 c 0.726895,-2.869027 1.004942,-5.843252 0.811775,-8.806053 l 1.185288,-8.634615 -3.768025,-3.072898 -2.908435,-3.21842 c -0.0103,-0.01383 -0.01958,-0.02805 -0.02988,-0.04186 -3.118009,-4.172293 -7.17889,-7.228662 -11.666624,-9.098091 z M 50.001124,37.965163 A 12.020784,12.029027 0 0 0 37.979913,49.994617 12.020784,12.029027 0 0 0 50.001124,62.024074 12.020784,12.029027 0 0 0 62.022337,49.994617 12.020784,12.029027 0 0 0 50.001124,37.965163 Z M 27.019485,39.55693 c -1.481686,2.114179 -2.5658,3.779575 -2.5658,3.779575 l -1.647451,-0.244197 c -0.69707,2.775045 -0.977606,5.64628 -0.81476,8.511019 l -1.22015,8.890775 3.768021,3.072896 3.422394,3.786551 c 2.921501,3.715734 6.608397,6.499915 10.668588,8.29872 l 5.050921,-1.223973 C 38.324728,73.038607 33.383805,69.887984 29.806406,65.100956 28.655972,63.561522 27.71377,61.932905 26.961715,60.249903 L 24.8272,48.359991 c 0.194234,-3.030146 0.935183,-6.015406 2.192285,-8.803061 z'
},
color1: '#735252',
color2: '#390305',
color3: '#ff0d39',
flip: false
}
}
}, { upsert: true }),
db.users.update({ _id: '3' }, { $set: { _id: '3', username: 'Source Keeper', usernameLower: 'source keeper', cpu: 100, cpuAvailable: 10000, gcl: 13966610.2, active: 0 } }, { upsert: true }),
db.users.update({ username: 'Screeps' }, { username: 'Screeps', usernameLower: 'screeps', gcl: 0, cpi: 0, active: false, cpuAvailable: 0, badge: { type: 12, color1: '#999999', color2: '#999999', color3: '#999999', flip: false, param: 26 } }, { upsert: true }),
env.set(env.keys.DATABASE_VERSION, '8')
])
// await upgradeDB()
log('Clear Map Assets')
// Clear map assets
const mapAssetFiles = [
...(await fs.readdir(mapDir)).map(f => path.join(mapDir, f)),
...(await fs.readdir(mapZoomDir)).map(f => path.join(mapZoomDir, f))
].filter(f => f.endsWith('png'))
await Promise.all(mapAssetFiles.map(f => fs.unlink(f)))
log('Insert Rooms')
const roomsBulk = []
const terrainBulk = []
const objectsBulk = []
rooms.forEach(
({
terrain,
room,
objects,
status = 'out of bounds',
bus,
openTime,
sourceKeepers,
novice,
respawnArea,
depositType
}) => {
roomsBulk.push({
op: 'insert',
data: {
_id: room,
name: room,
status,
bus,
openTime,
sourceKeepers,
novice,
respawnArea,
depositType
}
})
terrainBulk.push({ op: 'insert', data: { room, terrain } })
objects.forEach(o => {
o.room = room
objectsBulk.push({ op: 'insert', data: o })
})
}
)
await Promise.all([
db.rooms.bulk(roomsBulk),
db['rooms.terrain'].bulk(terrainBulk),
db['rooms.objects'].bulk(objectsBulk)
])
log('Updating Room Image Assets')
await Promise.all(rooms.map(({ room }) => cliMap.updateRoomImageAssets(room)))
log('Updating Accessible Rooms')
const accessibleRoomList = rooms.filter(r => r.status === 'normal' && (!r.openTime || r.openTime < Date.now())).map(r => r.room)
await env.set(env.keys.ACCESSIBLE_ROOMS, JSON.stringify(accessibleRoomList))
log('Updating Terrain Data')
await cliMap.updateTerrainData()
if (config.utils) {
log('Spawning NPC Terminals')
await config.utils.addNPCTerminals()
}
log('Restart Runners')
pubsub.publish(pubsub.keys.RUNTIME_RESTART, '1')
log('Reloading config.yml')
await config.utils.reloadConfig()
log('Done')
}
const getMapFromUrl = async (urlOrMapId) => {
let url = urlOrMapId
if (urlOrMapId.startsWith('random')) {
const [, size] = urlOrMapId.split('_')
const [width, height] = size.split('x').map(v => +v || 1)
const { data } = await axios.get('https://maps.screepspl.us/maps/index.json')
const maps = Object.values(data).filter(m => +m.width === width && +m.height === height)
if (!maps.length) {
throw new Error(`Random map with size ${size} requested, but no maps match requirements`)
}
url = maps[Math.floor(Math.random() * maps.length)].id
}
if (!url.startsWith('http')) {
url = `https://maps.screepspl.us/maps/map-${url}.json`
}
if (url !== urlOrMapId) {
log(`Importing map from: ${url}`)
}
const { data: { rooms } } = await axios.get(url)
return rooms
}
const getMapFromFile = async (filePath) => {
const data = await fs.readFile(filePath, { encoding: 'utf8' })
const { rooms } = JSON.parse(data)
return rooms
}
const exportMap = async (config) => {
const { common: { storage: { env, db } } } = config
if (!config.mongo) {
throw new Error('screepsmod-mongo required for map imports')
}
log('Exporting map')
// We want to pause the server just in case
const wasPaused = await env.get(env.keys.MAIN_LOOP_PAUSED)
if (!wasPaused) {
await env.set(env.keys.MAIN_LOOP_PAUSED, '1')
}
const roomNames = (await db.rooms.find({}, { _id: true })).map(r => r._id)
const shard = await env.get(env.keys.SHARD_NAME)
const date = new Date()
const desc = `${shard}:${date.getUTCFullYear()}-${date.getUTCMonth()}`
let count = 0
const allowedObjTypes = ['controller', 'source', 'mineral', 'extractor', 'keeperLair', 'deposit', 'portal']
const objectOrTypeSpec = allowedObjTypes.map(t => ({ type: t }))
const rooms = await Promise.all(roomNames.map(async (roomName) => {
const roomData = await db.rooms.findOne({ _id: roomName }, { projection: { _id: false } })
const objects = await db['rooms.objects'].find({ room: roomName, $or: objectOrTypeSpec })
const terrain = await db['rooms.terrain'].findOne({ room: roomName })
const room = {
room: roomName,
terrain: terrain.terrain,
objects: objects
}
Object.assign(room, roomData)
count++
return room
}))
const fileName = path.join(process.env.ASSET_DIR, `mapExport-${Number(date)}.json`)
await fs.writeFile(fileName, JSON.stringify({ description: desc, rooms }))
if (!wasPaused) {
await env.set(env.keys.MAIN_LOOP_PAUSED, '0')
}
return logResult(`Exported ${count} rooms to ${fileName}`)
}
module.exports = (config) => {
Object.assign(config.utils, {
async importMap (urlOrMapId) {
logStart(config, urlOrMapId)
const rooms = await getMapFromUrl(urlOrMapId)
await loadRooms(config, urlOrMapId, rooms)
return logFinish()
},
async importMapFile (filePath) {
logStart(config, filePath)
const rooms = await getMapFromFile(filePath)
await loadRooms(config, filePath, rooms)
return logFinish()
},
async exportMap () {
return exportMap(config)
}
})
config.utils.importMap._help =
'importMap(urlOrMapId) - import a map from maps.screepspl.us'
config.utils.importMapFile._help =
'importMapFile(filePath) - import a map from a json file'
config.utils.exportMap._help =
'exportMap() - export the map to a json file in the assets directory'
}