UNPKG

@screeps/engine

Version:

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

526 lines (442 loc) 20.4 kB
'use strict'; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var q = require('q'), _ = require('lodash'), utils = require('../utils'), driver = utils.getDriver(), C = driver.constants; module.exports.execute = function (market, gameTime, terminals, bulkObjects) { var bulkUsers = driver.bulkUsersWrite(), bulkTransactions = driver.bulkTransactionsWrite(), bulkUsersMoney = driver.bulkUsersMoney(), bulkUsersResources = driver.bulkUsersResources(), bulkMarketOrders = driver.bulkMarketOrders(); 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[resourceType] || fromTerminal[resourceType] < amount)) { return false; } if (toTerminal.user) { var targetResourceTotal = utils.calcResources(toTerminal), freeSpace = Math.max(0, toTerminal.energyCapacity - 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); if (transferFeeTerminal === fromTerminal && (resourceType != C.RESOURCE_ENERGY && fromTerminal.energy < transferCost || resourceType == C.RESOURCE_ENERGY && fromTerminal.energy < amount + transferCost) || transferFeeTerminal === toTerminal && toTerminal.energy < transferCost) { return false; } if (toTerminal.user) { toTerminal[resourceType] = toTerminal[resourceType] || 0; toTerminal[resourceType] += amount; bulkObjects.update(toTerminal, _defineProperty({}, resourceType, toTerminal[resourceType])); } bulkObjects.update(fromTerminal, _defineProperty({}, resourceType, fromTerminal[resourceType] - amount)); bulkObjects.update(transferFeeTerminal, { energy: transferFeeTerminal.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, function (i) { return !!i.send; }).forEach(function (terminal) { var intent = terminal.send; bulkObjects.update(terminal, { send: null }); if (terminal.cooldownTime > gameTime) { return; } if (!terminalsByRoom[intent.targetRoomName] || !terminalsByRoom[intent.targetRoomName].user) { return; } 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 + C.TERMINAL_COOLDOWN }); } }); if (market) { var usersById = _.indexBy(market.users, '_id'), ordersById = _.indexBy(market.orders, '_id'), terminalDeals = [], directDeals = []; market.intents.forEach(function (userIntents) { var user = usersById[userIntents.user]; if (userIntents.intents.createOrder) { userIntents.intents.createOrder.forEach(function (intent) { if (!intent.price || !intent.totalAmount) { return; } if (!_.contains(C.RESOURCES_ALL, intent.resourceType) && intent.resourceType != C.SUBSCRIPTION_TOKEN) { return; } if (intent.resourceType != C.SUBSCRIPTION_TOKEN && (!terminalsByRoom[intent.roomName] || terminalsByRoom[intent.roomName].user != userIntents.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); bulkMarketOrders.insert(_.extend({ created: gameTime, user: userIntents.user, active: false, type: intent.type == C.ORDER_SELL ? C.ORDER_SELL : C.ORDER_BUY, amount: 0, remainingAmount: intent.totalAmount }, intent)); intent.price /= 1000; bulkUsersMoney.insert({ date: new Date(), tick: gameTime, user: userIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { order: intent } }); }); } if (userIntents.intents.changeOrderPrice) { userIntents.intents.changeOrderPrice.forEach(function (intent) { var order = ordersById[intent.orderId]; if (!order || order.user != userIntents.user) { return; } if (!intent.newPrice || intent.newPrice <= 0) { return; } 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: userIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { changeOrderPrice: { orderId: intent.orderId, oldPrice: order.price / 1000, newPrice: intent.newPrice / 1000 } } }); } bulkMarketOrders.update(order._id, { price: intent.newPrice }); }); } if (userIntents.intents.extendOrder) { userIntents.intents.extendOrder.forEach(function (intent) { var order = ordersById[intent.orderId]; if (!order || order.user != userIntents.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: userIntents.user, type: 'market.fee', balance: user.money / 1000, change: -fee / 1000, market: { extendOrder: { orderId: intent.orderId, addAmount: intent.addAmount } } }); bulkMarketOrders.update(order, { remainingAmount: order.remainingAmount + intent.addAmount, totalAmount: order.totalAmount + intent.addAmount }); }); } if (userIntents.intents.cancelOrder) { userIntents.intents.cancelOrder.forEach(function (intent) { if (ordersById[intent.orderId] && ordersById[intent.orderId].user == userIntents.user) { ordersById[intent.orderId].remainingAmount = 0; ordersById[intent.orderId]._cancelled = true; //console.log('Order cancelled ',JSON.stringify(ordersById[intent.orderId])); } }); } if (userIntents.intents.deal) { userIntents.intents.deal.forEach(function (intent) { intent.user = userIntents.user; if (!ordersById[intent.orderId]) { return; } if (intent.amount <= 0) { return; } if (ordersById[intent.orderId].resourceType == C.SUBSCRIPTION_TOKEN) { directDeals.push(intent); return; } if (!terminalsByRoom[intent.targetRoomName] || terminalsByRoom[intent.targetRoomName].user != userIntents.user) { return; } terminalDeals.push(intent); }); } }); terminalDeals.sort(function (a, b) { return utils.calcRoomsDistance(a.targetRoomName, ordersById[a.orderId].roomName, true) - utils.calcRoomsDistance(b.targetRoomName, ordersById[b.orderId].roomName, true); }); terminalDeals.forEach(function (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; } 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[order.resourceType] || 0); } if (buyer.user) { var targetResourceTotal = utils.calcResources(buyer), targetFreeSpace = Math.max(0, buyer.energyCapacity - 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, amount: 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, amount: amount } }); } bulkMarketOrders.update(order, { amount: order.amount - amount, remainingAmount: order.remainingAmount - amount }); bulkObjects.update(targetTerminal, { cooldownTime: gameTime + C.TERMINAL_COOLDOWN }); } }); directDeals = _.shuffle(directDeals); directDeals.forEach(function (deal) { var order = ordersById[deal.orderId], buyer, seller, userFieldName = 'subscriptionTokens'; 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; } var amount = Math.min(deal.amount, order.remainingAmount); if (seller.user) { amount = Math.min(amount, seller[userFieldName] || 0); } if (!amount) { return; } var dealCost = amount * order.price; if (buyer.user && (!buyer.money || buyer.money < dealCost)) { return; } bulkUsers.inc(seller, 'money', dealCost); bulkUsers.inc(seller, userFieldName, -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: amount } }); bulkUsersResources.insert({ date: new Date(), resourceType: order.resourceType, user: "" + seller._id, change: -amount, balance: seller[userFieldName], marketOrderId: "" + order._id, market: { orderId: "" + order._id, anotherUser: "" + buyer._id } }); bulkUsers.inc(buyer, 'money', -dealCost); bulkUsers.inc(buyer, userFieldName, amount); 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: amount } }); bulkMarketOrders.update(order, { amount: order.amount - amount, remainingAmount: order.remainingAmount - amount }); bulkUsersResources.insert({ date: new Date(), resourceType: order.resourceType, user: "" + buyer._id, change: amount, balance: buyer[userFieldName], market: { orderId: "" + order._id, anotherUser: "" + seller._id } }); }); market.orders.forEach(function (order) { if (order._cancelled) { bulkMarketOrders.remove(order._id); return; } if (!order.user) { return; } var terminal = terminalsByRoom[order.roomName]; if (order.type == C.ORDER_SELL) { var availableResourceAmount = order.resourceType == C.SUBSCRIPTION_TOKEN ? usersById[order.user].subscriptionTokens || 0 : terminal && terminal.user == order.user ? terminal[order.resourceType] || 0 : 0; availableResourceAmount = Math.min(availableResourceAmount, order.remainingAmount); if (order.active) { if (!availableResourceAmount) { bulkMarketOrders.update(order, { active: false, amount: 0 }); return; } if (order.amount != availableResourceAmount) { bulkMarketOrders.update(order, { amount: availableResourceAmount }); } } else { if (availableResourceAmount > 0) { bulkMarketOrders.update(order, { active: true, amount: availableResourceAmount }); } } } if (order.type == C.ORDER_BUY) { var user = usersById[order.user], userMoney = user.money || 0; var isOwner = order.resourceType == C.SUBSCRIPTION_TOKEN || !!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.energyCapacity - targetResourceTotal); newAmount = Math.min(newAmount, targetFreeSpace); } var newActive = isOwner && newAmount > 0; if (order.amount != newAmount || order.active != newActive) { bulkMarketOrders.update(order, { amount: newAmount, active: newActive }); } } }); } return q.all([bulkUsers.execute(), bulkMarketOrders.execute(), bulkUsersMoney.execute(), bulkTransactions.execute(), bulkUsersResources.execute(), driver.clearMarketIntents()]); }; //# sourceMappingURL=../sourcemaps/processor/market.js.map