UNPKG

@screeps/engine

Version:

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

583 lines (492 loc) 22.9 kB
'use strict'; var q = require('q'), _ = require('lodash'), utils = require('../../utils'), driver = utils.getDriver(), C = driver.constants; module.exports = function ({ orders, userIntents, usersById, gameTime, roomObjectsByType, bulkObjects, bulkUsers, bulkTransactions, bulkUsersMoney, bulkUsersResources, bulkMarketOrders, bulkMarketIntershardOrders }) { var terminals = roomObjectsByType.terminal; var terminalsByRoom = _.indexBy(terminals, 'room'); function executeTransfer(fromTerminal, toTerminal, resourceType, amount, transferFeeTerminal, additionalFields) { additionalFields = additionalFields || {}; if (!fromTerminal || !toTerminal || !transferFeeTerminal) { return false; } if (fromTerminal.user && (!fromTerminal.store || !fromTerminal.store[resourceType] || fromTerminal.store[resourceType] < amount)) { return false; } if (toTerminal.user) { var targetResourceTotal = utils.calcResources(toTerminal), freeSpace = Math.max(0, toTerminal.storeCapacity - targetResourceTotal); amount = Math.min(amount, freeSpace); } if (!(amount > 0)) { return; } var range = utils.calcRoomsDistance(fromTerminal.room, toTerminal.room, true); var transferCost = utils.calcTerminalEnergyCost(amount, range); var effect = _.find(transferFeeTerminal.effects, { power: C.PWR_OPERATE_TERMINAL }); if (effect && effect.endTime > gameTime) { transferCost = Math.ceil(transferCost * C.POWER_INFO[C.PWR_OPERATE_TERMINAL].effect[effect.level - 1]); } if (transferFeeTerminal === fromTerminal && (resourceType != C.RESOURCE_ENERGY && fromTerminal.store.energy < transferCost || resourceType == C.RESOURCE_ENERGY && fromTerminal.store.energy < amount + transferCost) || transferFeeTerminal === toTerminal && toTerminal.store.energy < transferCost) { return false; } if (toTerminal.user) { toTerminal.store = toTerminal.store || {}; toTerminal.store[resourceType] = (toTerminal.store[resourceType] || 0) + amount; bulkObjects.update(toTerminal, { store: { [resourceType]: toTerminal.store[resourceType] } }); } bulkObjects.update(fromTerminal, { store: { [resourceType]: fromTerminal.store[resourceType] - amount } }); bulkObjects.update(transferFeeTerminal, { store: { energy: transferFeeTerminal.store.energy - transferCost } }); bulkTransactions.insert(_.extend({ time: +gameTime, sender: fromTerminal.user ? "" + fromTerminal.user : undefined, recipient: toTerminal.user ? "" + toTerminal.user : undefined, resourceType: resourceType, amount: amount, from: fromTerminal.room, to: toTerminal.room }, additionalFields)); return true; } _.filter(terminals, i => !!i.send).forEach(terminal => { var intent = terminal.send; bulkObjects.update(terminal, { send: null }); if (terminal.cooldownTime > gameTime) { return; } if (!terminalsByRoom[intent.targetRoomName] || !terminalsByRoom[intent.targetRoomName].user) { return; } var cooldown = C.TERMINAL_COOLDOWN; var effect = _.find(terminal.effects, { power: C.PWR_OPERATE_TERMINAL }); if (effect && effect.endTime > gameTime) { cooldown = Math.round(cooldown * C.POWER_INFO[C.PWR_OPERATE_TERMINAL].effect[effect.level - 1]); } if (executeTransfer(terminal, terminalsByRoom[intent.targetRoomName], intent.resourceType, intent.amount, terminal, { description: intent.description ? intent.description.replace(/</g, '&lt;') : undefined })) { bulkObjects.update(terminal, { cooldownTime: gameTime + cooldown }); } }); var ordersById = _.indexBy(orders, '_id'), terminalDeals = [], directDeals = []; const nowTimestamp = new Date().getTime(); if (userIntents) { userIntents.forEach(iUserIntents => { var user = usersById[iUserIntents.user]; if (iUserIntents.intents.createOrder) { iUserIntents.intents.createOrder.forEach(intent => { if (!intent.price || !intent.totalAmount) { return; } if (!_.contains(C.RESOURCES_ALL, intent.resourceType) && !_.contains(C.INTERSHARD_RESOURCES, intent.resourceType)) { return; } if (!_.contains(C.INTERSHARD_RESOURCES, intent.resourceType) && (!terminalsByRoom[intent.roomName] || terminalsByRoom[intent.roomName].user != iUserIntents.user)) { return; } if (intent.price <= 0 || intent.totalAmount <= 0) { return; } var fee = Math.ceil(intent.price * intent.totalAmount * C.MARKET_FEE); if (user.money < fee) { return; } bulkUsers.inc(user, 'money', -fee); const order = _.extend({ createdTimestamp: nowTimestamp, user: iUserIntents.user, active: false, type: intent.type == C.ORDER_SELL ? C.ORDER_SELL : C.ORDER_BUY, amount: 0, remainingAmount: intent.totalAmount }, intent); let bulk = bulkMarketIntershardOrders; if (!_.contains(C.INTERSHARD_RESOURCES, intent.resourceType)) { bulk = bulkMarketOrders; order.created = gameTime; } bulk.insert(order); intent.price /= 1000; bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: iUserIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { order: intent } }); }); } if (iUserIntents.intents.changeOrderPrice) { iUserIntents.intents.changeOrderPrice.forEach(intent => { const order = ordersById[intent.orderId]; if (!order || order.user != iUserIntents.user) { return; } if (!intent.newPrice || intent.newPrice <= 0) { return; } if (intent.newPrice != order.price) { order._skip = true; } if (intent.newPrice > order.price) { var fee = Math.ceil((intent.newPrice - order.price) * order.remainingAmount * C.MARKET_FEE); if (user.money < fee) { return; } bulkUsers.inc(user, 'money', -fee); bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: iUserIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { changeOrderPrice: { orderId: intent.orderId, oldPrice: order.price / 1000, newPrice: intent.newPrice / 1000 } } }); } const bulk = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) ? bulkMarketIntershardOrders : bulkMarketOrders; bulk.inc(order, 'price', intent.newPrice - order.price); }); } if (iUserIntents.intents.extendOrder) { iUserIntents.intents.extendOrder.forEach(intent => { const order = ordersById[intent.orderId]; if (!order || order.user != iUserIntents.user) { return; } if (!intent.addAmount || intent.addAmount <= 0) { return; } var fee = Math.ceil(order.price * intent.addAmount * C.MARKET_FEE); if (user.money < fee) { return; } bulkUsers.inc(user, 'money', -fee); bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: iUserIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { extendOrder: { orderId: intent.orderId, addAmount: intent.addAmount } } }); const bulk = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) ? bulkMarketIntershardOrders : bulkMarketOrders; bulk.inc(order, 'remainingAmount', intent.addAmount); bulk.inc(order, 'totalAmount', intent.addAmount); }); } if (iUserIntents.intents.cancelOrder) { iUserIntents.intents.cancelOrder.forEach(intent => { if (ordersById[intent.orderId] && ordersById[intent.orderId].user == iUserIntents.user) { ordersById[intent.orderId].remainingAmount = 0; ordersById[intent.orderId]._cancelled = true; } }); } if (iUserIntents.intents.deal) { iUserIntents.intents.deal.forEach(intent => { intent.user = iUserIntents.user; if (!ordersById[intent.orderId] || ordersById[intent.orderId]._skip) { return; } if (intent.amount <= 0) { return; } if (_.contains(C.INTERSHARD_RESOURCES, ordersById[intent.orderId].resourceType)) { directDeals.push(intent); return; } if (!terminalsByRoom[intent.targetRoomName] || terminalsByRoom[intent.targetRoomName].user != iUserIntents.user) { return; } terminalDeals.push(intent); }); } }); } terminalDeals.sort((a, b) => utils.calcRoomsDistance(a.targetRoomName, ordersById[a.orderId].roomName, true) - utils.calcRoomsDistance(b.targetRoomName, ordersById[b.orderId].roomName, true)); terminalDeals.forEach(deal => { var order = ordersById[deal.orderId], orderTerminal = terminalsByRoom[order.roomName], targetTerminal = terminalsByRoom[deal.targetRoomName], buyer, seller; if (!orderTerminal || !targetTerminal) { return; } if (targetTerminal.cooldownTime > gameTime) { return; } orderTerminal.store = orderTerminal.store || {}; targetTerminal.store = targetTerminal.store || {}; if (order.type == C.ORDER_SELL) { buyer = targetTerminal; seller = orderTerminal; } else { seller = targetTerminal; buyer = orderTerminal; } var amount = Math.min(deal.amount, order.remainingAmount); if (seller.user) { amount = Math.min(amount, seller.store[order.resourceType] || 0); } if (buyer.user) { var targetResourceTotal = utils.calcResources(buyer), targetFreeSpace = Math.max(0, buyer.storeCapacity - targetResourceTotal); amount = Math.min(amount, targetFreeSpace); } if (!(amount > 0)) { return; } var dealCost = amount * order.price; if (buyer.user) { dealCost = Math.min(dealCost, usersById[buyer.user].money || 0); amount = Math.floor(dealCost / order.price); dealCost = amount * order.price; if (!amount) { return; } } if (executeTransfer(seller, buyer, order.resourceType, amount, targetTerminal, { order: { id: "" + order._id, type: order.type, price: order.price / 1000 } })) { if (seller.user) { bulkUsers.inc(usersById[seller.user], 'money', dealCost); bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: seller.user, type: 'market.sell', balance: usersById[seller.user].money / 1000, change: dealCost / 1000, market: { resourceType: order.resourceType, roomName: order.roomName, targetRoomName: deal.targetRoomName, price: order.price / 1000, npc: !buyer.user, owner: order.user, dealer: deal.user, amount } }); } if (buyer.user) { bulkUsers.inc(usersById[buyer.user], 'money', -dealCost); bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: buyer.user, type: 'market.buy', balance: usersById[buyer.user].money / 1000, change: -dealCost / 1000, market: { resourceType: order.resourceType, roomName: order.roomName, targetRoomName: deal.targetRoomName, price: order.price / 1000, npc: !seller.user, owner: order.user, dealer: deal.user, amount } }); } bulkMarketOrders.update(order, { amount: order.amount - amount, remainingAmount: order.remainingAmount - amount }); let cooldown = C.TERMINAL_COOLDOWN; const effect = _.find(targetTerminal.effects, { power: C.PWR_OPERATE_TERMINAL }); if (effect && effect.endTime > gameTime) { cooldown = Math.round(cooldown * C.POWER_INFO[C.PWR_OPERATE_TERMINAL].effect[effect.level - 1]); } bulkObjects.update(targetTerminal, { cooldownTime: gameTime + cooldown }); } }); directDeals = _.shuffle(directDeals); directDeals.forEach(deal => { var order = ordersById[deal.orderId], buyer, seller; if (order.type == C.ORDER_SELL) { buyer = usersById[deal.user]; seller = usersById[order.user]; } else { seller = usersById[deal.user]; buyer = usersById[order.user]; } if (!seller || !buyer) { return; } seller.resources = seller.resources || {}; buyer.resources = buyer.resources || {}; var amount = Math.min(deal.amount, order.amount, order.remainingAmount, seller.resources[order.resourceType] || 0); if (!amount || amount < 0) { return; } var dealCost = amount * order.price; if (buyer.user && (!buyer.money || buyer.money < dealCost)) { return; } bulkUsers.inc(seller, 'money', dealCost); bulkUsers.inc(seller, 'resources.' + order.resourceType, -amount); seller.resources[order.resourceType] -= amount; bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: "" + seller._id, type: 'market.sell', balance: seller.money / 1000, change: dealCost / 1000, market: { resourceType: order.resourceType, price: order.price / 1000, amount } }); bulkUsersResources.insert({ date: new Date(), resourceType: order.resourceType, user: "" + seller._id, change: -amount, balance: seller.resources[order.resourceType], marketOrderId: "" + order._id, market: { orderId: "" + order._id, anotherUser: "" + buyer._id } }); bulkUsers.inc(buyer, 'money', -dealCost); bulkUsers.inc(buyer, 'resources.' + order.resourceType, amount); buyer.money -= dealCost; bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: "" + buyer._id, type: 'market.buy', balance: buyer.money / 1000, change: -dealCost / 1000, market: { resourceType: order.resourceType, price: order.price / 1000, amount } }); const bulk = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) ? bulkMarketIntershardOrders : bulkMarketOrders; bulk.inc(order, 'amount', -amount); bulk.inc(order, 'remainingAmount', -amount); bulkUsersResources.insert({ date: new Date(), resourceType: order.resourceType, user: "" + buyer._id, change: amount, balance: buyer.resources[order.resourceType], market: { orderId: "" + order._id, anotherUser: "" + seller._id } }); }); if (orders) { orders.forEach(order => { const bulk = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) ? bulkMarketIntershardOrders : bulkMarketOrders; if (order._cancelled) { bulk.remove(order._id); return; } if (order.user && nowTimestamp - order.createdTimestamp > C.MARKET_ORDER_LIFE_TIME) { const remainingFee = order.remainingAmount * order.price * C.MARKET_FEE; if (remainingFee > 0) { const user = usersById[order.user]; bulkUsers.inc(user, 'money', remainingFee); bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: user._id.toString(), type: 'market.fee', balance: user.money / 1000, change: remainingFee / 1000, market: { order: { orderId: order._id.toString(), type: order.type, resourceType: order.resourceType, price: order.price / 1000, remainingAmount: order.remainingAmount, roomName: order.roomName } } }); } bulk.remove(order._id); return; } if (!order.user) { return; } var terminal = terminalsByRoom[order.roomName]; if (order.type == C.ORDER_SELL) { var availableResourceAmount = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) ? (usersById[order.user].resources || {})[order.resourceType] || 0 : terminal && terminal.user == order.user ? terminal.store[order.resourceType] || 0 : 0; availableResourceAmount = Math.min(availableResourceAmount, order.remainingAmount); if (order.active) { if (!availableResourceAmount || availableResourceAmount < 0) { bulk.update(order, { active: false, amount: 0 }); return; } if (order.amount != availableResourceAmount) { bulk.update(order, { amount: availableResourceAmount }); } } else { if (availableResourceAmount > 0) { bulk.update(order, { active: true, amount: availableResourceAmount }); } } } if (order.type == C.ORDER_BUY) { var user = usersById[order.user], userMoney = user.money || 0; var isOwner = _.contains(C.INTERSHARD_RESOURCES, order.resourceType) || !!terminal && terminal.user == order.user; var newAmount = Math.min(Math.floor(userMoney / order.price), order.remainingAmount); if (terminal && terminal.user) { var targetResourceTotal = utils.calcResources(terminal), targetFreeSpace = Math.max(0, terminal.storeCapacity - targetResourceTotal); newAmount = Math.min(newAmount, targetFreeSpace); } var newActive = isOwner && newAmount > 0; if (order.amount != newAmount || order.active != newActive) { bulk.update(order, { amount: newAmount, active: newActive }); } } }); } return () => q.all([bulkUsers.execute(), bulkMarketOrders.execute(), bulkMarketIntershardOrders.execute(), bulkUsersMoney.execute(), bulkTransactions.execute(), bulkUsersResources.execute(), driver.clearMarketIntents()]); }; //# sourceMappingURL=../../sourcemaps/processor/global-intents/market.js.map