@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
JavaScript
'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, '<') : 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