mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Compare commits
59
Commits
v2.0.0-alpha.12
...
1.2.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c1630ff0 | ||
|
|
ed9966f65c | ||
|
|
248d9fcd3e | ||
|
|
1c0db7e58a | ||
|
|
3d8cfef8bc | ||
|
|
8803a737a5 | ||
|
|
320677194e | ||
|
|
1bdf23d960 | ||
|
|
7569c4a878 | ||
|
|
8ec7ec86d6 | ||
|
|
5fe98a0d60 | ||
|
|
136bd24399 | ||
|
|
c380fe269f | ||
|
|
909e7d5ad6 | ||
|
|
76cca59807 | ||
|
|
3e7d4a8648 | ||
|
|
b0eb188e22 | ||
|
|
e70cfe7abd | ||
|
|
a9a3fb3fce | ||
|
|
2b21bb36b6 | ||
|
|
e1771ecb38 | ||
|
|
70f3ad5ebd | ||
|
|
28b00ba5f6 | ||
|
|
9d885d5f91 | ||
|
|
d810b5646d | ||
|
|
10414eb4a6 | ||
|
|
3647ac7fc8 | ||
|
|
e75d9e967e | ||
|
|
25eb5e5d76 | ||
|
|
3c5b108e4b | ||
|
|
999c1b6c7a | ||
|
|
61b1b1a106 | ||
|
|
8b4ec58459 | ||
|
|
6e26d32fed | ||
|
|
74d9ec38dc | ||
|
|
dbef2bd2b3 | ||
|
|
e0b521d2fe | ||
|
|
9a032aed2e | ||
|
|
72b56a00c7 | ||
|
|
c25bf23286 | ||
|
|
9e983aff84 | ||
|
|
8973dcc6dd | ||
|
|
475128c8c4 | ||
|
|
0cdb5d0ce4 | ||
|
|
075d54f708 | ||
|
|
62f3a99feb | ||
|
|
3b1ed1a667 | ||
|
|
6a6274e85c | ||
|
|
a9cc743106 | ||
|
|
2a7758f60d | ||
|
|
7e2ca58a59 | ||
|
|
718d4ed659 | ||
|
|
6b090cddbe | ||
|
|
56c023f643 | ||
|
|
78de7236c5 | ||
|
|
97c7a69c7a | ||
|
|
0c45e57732 | ||
|
|
88eff17dfd | ||
|
|
e1374d501e |
No files matched your search
-10
@@ -1,10 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "9"
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y curl git tar
|
||||
- curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64
|
||||
- tar -xvf factorio.tar.gz
|
||||
script:
|
||||
- npm run cover
|
||||
@@ -0,0 +1,71 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 1.2.4
|
||||
-------------
|
||||
|
||||
- Removed broken remote combinator signaling.
|
||||
- Fixed research sync endlessly updating already researched technologies
|
||||
- Removed obsolete item/fluid statistics from clusterioMod
|
||||
- Removed per mod upload logging when config.uploadModsToMaster disabled
|
||||
- Fixed bcrypt failing to install on windows due to the new version not
|
||||
having Windows binaries.
|
||||
|
||||
|
||||
Version 1.2.3
|
||||
-------------
|
||||
|
||||
- Disabled uploading mods to the master server by default as this is mostly
|
||||
just a waste of bandwidth with the mod portal being integrated into the
|
||||
game now.
|
||||
- Fixed researchSync breaking with heavily modded games due to the tech tree
|
||||
exceeding the default 100kb limit on JSON payloads.
|
||||
- Fixed the command inventoryImport sends referencing player instead of
|
||||
character, and trying to count the non-existant quick bar slots.
|
||||
- Fixed inventoryImport never receiving the script output after the default
|
||||
mode for reading script output changed to tail mode.
|
||||
- Fixed desync caused by the mods and loaded mods arrays in Hotpatch getting
|
||||
out of sync when plugins update their scenario mod code. To fix existing
|
||||
games you will need copy lib/scenarios/Hotpatch/hotpatch/mod-tools.lua
|
||||
over the existing mod-tools.lua in the save's hotpatch folder.
|
||||
- Updated the Windows install instructions.
|
||||
|
||||
|
||||
Version 1.2.2
|
||||
-------------
|
||||
|
||||
- Fixed possible crash with modded technologies named the same as a built-in
|
||||
Object prototype property in researchSync.
|
||||
- Fixed progress of a current infinite tech carrying over to the next one
|
||||
when researching it and another node completes it in researchSync.
|
||||
- Fixed progress of a previous infinite tech from another node being applied
|
||||
to the current one in researchSync.
|
||||
- Fixed crash in researchSync when modded technologies are present only on some
|
||||
nodes.
|
||||
- Fixed install failing due to bcrypt version less than 3 not being supported
|
||||
on node v10.
|
||||
- Reordered install instructions to avoid problem with npm creating files owned
|
||||
by root in the home directory.
|
||||
- Swapped curl out with wget in the install instructions as the latter comes
|
||||
pre-installed on Ubuntu.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Node.js versions below 10 are no longer supported.
|
||||
|
||||
|
||||
Version 1.2.1
|
||||
-------------
|
||||
|
||||
- Updated node-factorio-api to v0.3.8 to fix mod downloads randomly breaking
|
||||
([#229][#229].)
|
||||
- Fixed SIGINT being sent twice to Factorio server when interrupted by CTRL+C
|
||||
on Linux ([#217][#217].)
|
||||
- Fixed package.json incorrectly reporting the license as ISC.
|
||||
|
||||
[#217]: https://github.com/clusterio/factorioClusterio/issues/217
|
||||
[#229]: https://github.com/clusterio/factorioClusterio/issues/229
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Added authentication to the socket.io server running on the master.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:9.11.2
|
||||
FROM node:10
|
||||
RUN apt-get update && apt install git curl tar -y
|
||||
RUN mkdir factorioClusterio
|
||||
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
Discord for development/support/play: https://discord.gg/5XuDkje
|
||||
|
||||
# ABANDONED VERSION
|
||||
|
||||
**The 1.2.x version of Clusterio has been abandonded.
|
||||
Please go to the [main branch of the project](https://github.com/clusterio/clusterio) and continue from there.
|
||||
The information below is out of date and kept for archival purposes only.**
|
||||
|
||||
### Ways to support me/the project:
|
||||
|
||||
* Contribute with code/documentation. Always nice to move the project forward
|
||||
@@ -88,19 +94,21 @@ NodeJS does not support EOL ubuntu releases. Make sure you are on the most recen
|
||||
|
||||
Master and all slaves:
|
||||
|
||||
sudo curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
|
||||
sudo apt install -y nodejs python-dev git wget curl tar build-essential
|
||||
sudo npm install pm2 -g
|
||||
git clone https://github.com/Danielv123/factorioClusterio.git
|
||||
wget -qO - https://deb.nodesource.com/setup_10.x | sudo -E bash -
|
||||
sudo apt install -y nodejs python-dev git build-essential
|
||||
git clone -b 1.2.x https://github.com/Danielv123/factorioClusterio.git
|
||||
cd factorioClusterio
|
||||
curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64
|
||||
wget -O factorio.tar.gz https://www.factorio.com/get-download/latest/headless/linux64
|
||||
tar -xf factorio.tar.gz
|
||||
npm install --only=production
|
||||
cp config.json.dist config.json
|
||||
node ./lib/npmPostinstall.js
|
||||
|
||||
downloads and installs nodejs, git and clusterio. To specify a version, change "latest" in the link to a version number like 0.14.21.
|
||||
|
||||
downloads and installs nodejs, pm2, git and clusterio. To specify a version, change "latest" in the link to a version number like 0.14.21.
|
||||
Optional step (if you want to use pm2):
|
||||
|
||||
sudo npm install pm2 -g
|
||||
|
||||
Now you need to edit the `config.json` file. If you skip this step nothing will work.
|
||||
Pretty much all the blank fields should be filled in, except on the master where a few can be omitted.
|
||||
@@ -113,11 +121,12 @@ Pretty much all the blank fields should be filled in, except on the master where
|
||||
|
||||
**Master**
|
||||
|
||||
node master.js
|
||||
|
||||
Or with pm2 (it's recommened to run it without pm2 first):
|
||||
|
||||
pm2 start master.js --name master
|
||||
|
||||
OR
|
||||
|
||||
node master.js
|
||||
|
||||
**Server Host**
|
||||
|
||||
@@ -159,7 +168,7 @@ Game Client = The people connecting to the server
|
||||
|
||||
**Requirements**
|
||||
|
||||
download and install nodeJS 8 or 9 from http://nodejs.org
|
||||
download and install nodeJS 10 from http://nodejs.org
|
||||
|
||||
download and install git from https://git-scm.com/
|
||||
|
||||
@@ -167,28 +176,47 @@ reboot when you are done, then proceed to the next steps. *reboots matter*
|
||||
|
||||
**Master**
|
||||
|
||||
1. download and run https://puu.sh/toFHl/01eebbb333.bat
|
||||
1. Open PowerShell or Command prompt in the directory you want to install to and run the following commands.
|
||||
|
||||
2. Copy config.json.dist to config.json
|
||||
git clone -b 1.2.x https://github.com/clusterio/factorioClusterio
|
||||
cd factorioClusterio
|
||||
npm install --only=production
|
||||
copy config.json.dist config.json
|
||||
|
||||
3. Follow the instructions in the bat file
|
||||
3. Some of the instructions are outdated. If you get stuck somewhere, look at the Ubuntu section.
|
||||
2. Obtain Factorio by either of these two methods:
|
||||
|
||||
4. Run `node client.js manage shared mods add clusterio`
|
||||
- Via the stand alone version on from their website
|
||||
|
||||
5. type `node master.js` to start the server
|
||||
1. Download the MS Windows (64-bit zip package) from https://www.factorio.com/download
|
||||
|
||||
2. Open the zip file and drag the folder called "Factorio_0.17.x" into the factorioClusterio folder
|
||||
|
||||
3. Rename the folder to "factorio"
|
||||
|
||||
- Via steam installation
|
||||
|
||||
1. Locate the game files by right clicking the game in steam, selecting properties, then Local Files, then Browse local files.
|
||||
|
||||
2. Go to the parent folder of the folder that Steam opened and copy the Factorio folder into the factorioClusterio folder
|
||||
|
||||
3. Rename the folder to "factorio"
|
||||
|
||||
|
||||
3. Open `config.json` with a text editor and configure as desired.
|
||||
|
||||
4. Run `node master.js` to generate the athentication token into secret-api-token.txt
|
||||
|
||||
5. Run `node master.js` again to start the master server.
|
||||
|
||||
**Server Host**
|
||||
|
||||
1. download and run https://puu.sh/toFHl/01eebbb333.bat
|
||||
1. Do step 1 and 2 of the Master section above *OR* use the same folder that was created in that section.
|
||||
|
||||
2. Copy config.json.dist to config.json
|
||||
2. Open `config.json` with a text editor and configure as desired. You will need to set masterAuthToken to string found in secret-api-token.txt on the master server.
|
||||
|
||||
3. Follow the instructions given.
|
||||
3. Optionally run the command `node client.js manage shared mods add clusterio` to add the clusterio mod (needed for item teleports.)
|
||||
|
||||
3.5 Some of the instructions are outdated. If you get stuck somewhere, look at the Ubuntu section.
|
||||
|
||||
4. Type `node client.js start [instancename]` to create a new instance.
|
||||
4. Run `node client.js start [instancename]` to create a new instance. Repeat it again to start the instance.
|
||||
|
||||
To connect to a master server running on a remote machine, open config.json with your favourite text editor (notepad++). You can also set it up to use the official server browser.
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ if(!config.masterAuthToken || typeof config.masterAuthToken !== "string"){
|
||||
console.error("Master server now needs an access token for write operations. As clusterio slaves depends \
|
||||
upon this, please add your token to config.json in the field named masterAuthToken. \
|
||||
You can retrieve your auth token from the master in secret-api-token.txt after running it once.");
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
const needleOptionsWithTokenAuthHeader = {
|
||||
compressed:true,
|
||||
@@ -297,7 +299,6 @@ if (!command || command == "help" || command == "--help") {
|
||||
fs.mkdirSync(instancedirectory + "/saves/");
|
||||
fs.writeFileSync(instancedirectory + "/script-output/output.txt", "");
|
||||
fs.writeFileSync(instancedirectory + "/script-output/orders.txt", "");
|
||||
fs.writeFileSync(instancedirectory + "/script-output/txbuffer.txt", "");
|
||||
fs.mkdirSync(instancedirectory + "/mods/");
|
||||
fs.mkdirSync(instancedirectory + "/instanceMods/");
|
||||
fs.mkdirSync(instancedirectory + "/scenarios/");
|
||||
@@ -359,7 +360,8 @@ write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n
|
||||
'--rcon-port', Number(process.env.RCONPORT) || instconf.clientPort,
|
||||
'--rcon-password', instconf.clientPassword,
|
||||
], {
|
||||
'stdio': ['pipe', 'pipe', 'pipe']
|
||||
'stdio': ['pipe', 'pipe', 'pipe'],
|
||||
'detached': process.platform === "linux",
|
||||
}
|
||||
);
|
||||
factorio.stdout.on("data", data => {
|
||||
@@ -471,7 +473,8 @@ write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n
|
||||
'--server-settings', instancedirectory + '/server-settings.json',
|
||||
'--port', args.port || Number(process.env.FACTORIOPORT) || instanceconfig.factorioPort
|
||||
], {
|
||||
'stdio': ['pipe', 'pipe', 'pipe']
|
||||
'stdio': ['pipe', 'pipe', 'pipe'],
|
||||
'detached': process.platform === "linux",
|
||||
}
|
||||
);
|
||||
|
||||
@@ -570,7 +573,10 @@ async function instanceManagement(instanceconfig) {
|
||||
console.log("Started instanceManagement();");
|
||||
|
||||
/* Open websocket connection to master */
|
||||
var socket = ioClient("http://"+config.masterIP+":"+config.masterPort);
|
||||
var socket = ioClient("http://"+config.masterIP+":"+config.masterPort+"?token="+config.masterAuthToken);
|
||||
socket.on("error", err => {
|
||||
console.log("SOCKET | Error: ", err);
|
||||
});
|
||||
socket.on("hello", data => {
|
||||
console.log("SOCKET | registering slave!");
|
||||
socket.emit("registerSlave", {
|
||||
@@ -710,7 +716,7 @@ async function instanceManagement(instanceconfig) {
|
||||
console.error(err);
|
||||
} else if (response && response.body) {
|
||||
// In the future we might be interested in whether or not we actually manage to send it, but honestly I don't care.
|
||||
if(response.body !== "ok") {
|
||||
if (typeof response.body !== "string" || response.body.toLowerCase() !== "ok") {
|
||||
console.log("Got no \"ok\" while registering our precense with master "+config.masterIP+" at " + payload.time);
|
||||
console.log(response.body);
|
||||
}
|
||||
@@ -731,19 +737,19 @@ async function instanceManagement(instanceconfig) {
|
||||
|
||||
setTimeout(function(){hashMods(instance, uploadMods)}, 5000);
|
||||
function uploadMods(modHashes) {
|
||||
// [{modName:string,hash:string}, ... ]
|
||||
for(let i=0;i<modHashes.length;i++){
|
||||
let payload = {
|
||||
modName: modHashes[i].modName,
|
||||
hash: modHashes[i].hash,
|
||||
};
|
||||
needle.post(config.masterIP + ":" + config.masterPort + '/api/checkMod', payload, needleOptionsWithTokenAuthHeader, function (err, response, body) {
|
||||
if(err) console.error("Unable to contact master server /api/checkMod! Please check your config.json.");
|
||||
if(response && body && body == "found") {
|
||||
console.log("master has mod "+modHashes[i].modName);
|
||||
} else if (response && body && typeof body == "string") {
|
||||
let mod = response.body;
|
||||
if(config.uploadModsToMaster){
|
||||
if (config.uploadModsToMaster) {
|
||||
// [{modName:string,hash:string}, ... ]
|
||||
for(let i=0;i<modHashes.length;i++){
|
||||
let payload = {
|
||||
modName: modHashes[i].modName,
|
||||
hash: modHashes[i].hash,
|
||||
};
|
||||
needle.post(config.masterIP + ":" + config.masterPort + '/api/checkMod', payload, needleOptionsWithTokenAuthHeader, function (err, response, body) {
|
||||
if(err) console.error("Unable to contact master server /api/checkMod! Please check your config.json.");
|
||||
if(response && body && body == "found") {
|
||||
console.log("master has mod "+modHashes[i].modName);
|
||||
} else if (response && body && typeof body == "string") {
|
||||
let mod = response.body;
|
||||
console.log("Sending mod: " + mod);
|
||||
// Send mods master says it wants
|
||||
// response.body is a string which is a modName.zip
|
||||
@@ -760,11 +766,11 @@ async function instanceManagement(instanceconfig) {
|
||||
});
|
||||
var form = req.form();
|
||||
form.append('file', fs.createReadStream(config.instanceDirectory+"/"+instance+"/mods/"+mod));
|
||||
} else {
|
||||
console.log("Not sending mod: " + mod + " to master because config.uploadModsToMaster is not enabled")
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("Bypassing sending mods to master because config.uploadModsToMaster is not enabled")
|
||||
}
|
||||
}
|
||||
} // END OF INSTANCE START ---------------------------------------------------------------------
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
"masterPort": 8080,
|
||||
|
||||
"__commentsslPort": "HTTPS port for the master to listen to. Set to 0 or an empty string to not listen for HTTPS.",
|
||||
"sslPort": 443,
|
||||
"sslPort": 0,
|
||||
"sslCert": "database/certificates/cert.crt",
|
||||
"sslPrivKey": "database/certificates/cert.key",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"__comment6.5": "When the master is undersupplied, we have 3 different modes to 'fairly' distribute items. First to ask gets all, dole division using weird formula or experimental neural net tries to make stuff as fair as possible. Feel free to play around.",
|
||||
"disableFairItemDistribution": false,
|
||||
"useNeuralNetDoleDivider": true,
|
||||
"uploadModsToMaster": true,
|
||||
"uploadModsToMaster": false,
|
||||
"msBetweenCommands": 10,
|
||||
"allowRemoteCommandExecution": true,
|
||||
"enableCrossServerShout": true,
|
||||
|
||||
@@ -461,8 +461,9 @@ install_mod = function(mod_name, mod_version, mod_code, mod_files)
|
||||
local index = find_installed_mod(mod_name)
|
||||
local mod = {}
|
||||
if index then
|
||||
-- TODO: notify about installing over top of existing mod
|
||||
mod = global.mods[index]
|
||||
debug_log('attempt to install mod that is already installed, reinstalling: ' .. mod_name)
|
||||
mod = table.remove(global.mods, index)
|
||||
table.insert(global.mods, mod)
|
||||
else
|
||||
--next free index
|
||||
table.insert(global.mods, mod)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
Clusterio master server. Facilitates communication between slaves through
|
||||
a webserver, storing data related to slaves like production graphs and
|
||||
combinator signals.
|
||||
a webserver, storing data related to slaves like production graphs.
|
||||
|
||||
@module clusterioMaster
|
||||
@author Danielv123
|
||||
@@ -83,7 +82,9 @@ const fileUpload = require('express-fileupload');
|
||||
var app = express();
|
||||
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.json({
|
||||
limit: '10mb',
|
||||
}));
|
||||
app.use(bodyParser.urlencoded({
|
||||
parameterLimit: 100000,
|
||||
limit: '10mb',
|
||||
@@ -328,6 +329,7 @@ app.post("/api/getID", authenticate.middleware, function(req,res) {
|
||||
}
|
||||
// console.log("Slave registered: " + slaves[req.body.unique].mac + " : " + slaves[req.body.unique].serverPort+" at " + slaves[req.body.unique].publicIP + " with name " + slaves[req.body.unique].instanceName);
|
||||
}
|
||||
res.sendStatus(200);
|
||||
});
|
||||
/**
|
||||
POST Allows you to add metadata related to slaves for other tools, like owner data, descriptions or statistics.
|
||||
@@ -874,14 +876,6 @@ class wsSlave {
|
||||
prometheusWsUsageCounter.labels('heartbeat', this.instanceID).inc();
|
||||
this.lastBeat = Date.now();
|
||||
});
|
||||
this.socket.on("combinatorSignal", circuitFrameWithMeta => {
|
||||
prometheusWsUsageCounter.labels('combinatorSignal', this.instanceID).inc();
|
||||
if(circuitFrameWithMeta && typeof circuitFrameWithMeta == "object"){
|
||||
Object.keys(wsSlaves).forEach(instanceID => {
|
||||
wsSlaves[instanceID].socket.emit("processCombinatorSignal", circuitFrameWithMeta);
|
||||
});
|
||||
}
|
||||
});
|
||||
// handle command return values
|
||||
this.commandsWaitingForReturn = {};
|
||||
this.socket.on("runCommandReturnValue", resp => {
|
||||
@@ -921,6 +915,18 @@ class wsSlave {
|
||||
}
|
||||
}
|
||||
|
||||
io.use((socket, next) => {
|
||||
let token = socket.handshake.query.token;
|
||||
authenticate.check(token).then((result) => {
|
||||
if (result.ok) {
|
||||
next();
|
||||
} else {
|
||||
console.error(`SOCKET | authentication failed for ${socket.handshake.address}`);
|
||||
next(new Error(result.msg));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
io.on('connection', function (socket) {
|
||||
// cleanup dead sockets from disconnected people
|
||||
let terminatedConnections = 0;
|
||||
|
||||
+7
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clusterio",
|
||||
"version": "1.7.0",
|
||||
"version": "1.2.4",
|
||||
"description": "Interserver communication for factorio",
|
||||
"repository": "https://github.com/Danielv123/factorioClusterio",
|
||||
"main": "master.js",
|
||||
@@ -15,12 +15,15 @@
|
||||
"factorio"
|
||||
],
|
||||
"author": "Danielv123",
|
||||
"license": "ISC",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"dependencies": {
|
||||
"as-table": "^1.0.31",
|
||||
"averaged-timeseries": "^1.4.0",
|
||||
"base64url": "^3.0.0",
|
||||
"bcrypt": "^2.0.1",
|
||||
"bcrypt": "=3.0.6",
|
||||
"bcrypt-promise": "^2.0.0",
|
||||
"body-parser": "^1.18.2",
|
||||
"cli": "^1.0.1",
|
||||
@@ -46,7 +49,7 @@
|
||||
"moment": "^2.22.2",
|
||||
"ncp": "^2.0.0",
|
||||
"needle": "^2.2.4",
|
||||
"node-factorio-api": "^0.3.5",
|
||||
"node-factorio-api": "^0.3.8",
|
||||
"node-forge": "^0.7.1",
|
||||
"prom-client": "^10.2.3",
|
||||
"rcon-client-fork": "^3.2.0",
|
||||
|
||||
@@ -35,61 +35,6 @@ module.exports = class remoteCommands {
|
||||
}
|
||||
})().catch(e => console.log(e));
|
||||
|
||||
// flow/production statistics ------------------------------------------------------------
|
||||
var oldFlowStats = false;
|
||||
var oldTimestamp;
|
||||
var oldFlowStats;
|
||||
setInterval(function(){
|
||||
fs.readFile(instancedirectory + "/script-output/flows.txt", {encoding: "utf8"}, function(err, data) {
|
||||
if(!err && data) {
|
||||
let timestamp = Date.now();
|
||||
data = data.split("\n");
|
||||
let flowStats = [];
|
||||
for(let i = 0; i < data.length; i++) {
|
||||
// try catch to remove any invalid json
|
||||
try{
|
||||
flowStats[flowStats.length] = JSON.parse(data[i]);
|
||||
} catch (e) {
|
||||
// console.log(" invalid json: " + i);
|
||||
// some lines of JSON are invalid but don't worry, we just filter em out
|
||||
}
|
||||
}
|
||||
// fluids
|
||||
let flowStat1 = flowStats[flowStats.length-1].flows.player.input_counts
|
||||
// items
|
||||
let flowStat2 = flowStats[flowStats.length-2].flows.player.input_counts
|
||||
// merge fluid and item flows
|
||||
let totalFlows = {};
|
||||
for(let key in flowStat1) totalFlows[key] = flowStat1[key];
|
||||
for(let key in flowStat2) totalFlows[key] = flowStat2[key];
|
||||
if(oldFlowStats && totalFlows && oldTimestamp) {
|
||||
let payload = objectOps.deepclone(totalFlows);
|
||||
// change from total reported to per time unit
|
||||
for(let key in oldFlowStats) {
|
||||
// get production per minute
|
||||
payload[key] = Math.floor((payload[key] - oldFlowStats[key])/(timestamp - oldTimestamp)*60000);
|
||||
if(payload[key] < 0) {
|
||||
payload[key] = 0;
|
||||
}
|
||||
}
|
||||
for(let key in payload) {
|
||||
if(payload[key] == '0') {
|
||||
delete payload[key];
|
||||
}
|
||||
}
|
||||
console.log("Recorded flows, copper plate since last time: " + payload["copper-plate"]);
|
||||
needle.post(that.config.masterIP + ":" + that.config.masterPort + '/api/logStats', {timestamp: timestamp, instanceID: that.config.unique, data: payload}, needleOptionsWithTokenAuthHeader, function (err, response, body) {
|
||||
// we did it, keep going
|
||||
});
|
||||
}
|
||||
oldTimestamp = timestamp;
|
||||
oldFlowStats = totalFlows;
|
||||
fs.writeFileSync(instancedirectory + "/script-output/flows.txt", "");
|
||||
}
|
||||
});
|
||||
// we don't need to update stats quickly as that could be expensive
|
||||
}, 60000*5);
|
||||
|
||||
// provide items --------------------------------------------------------------
|
||||
// trigger when something happens to output.txt
|
||||
fs.watch(instancedirectory + "/script-output/output.txt", function (eventType, filename) {
|
||||
@@ -232,56 +177,6 @@ module.exports = class remoteCommands {
|
||||
setTimeout(function(){
|
||||
messageInterface("/silent-command remote.call('clusterio','setWorldID',"+that.config.unique+")")
|
||||
}, 20000);
|
||||
/* REMOTE SIGNALLING
|
||||
* send any signals the slave has been told to send
|
||||
* Fetch combinator signals from the server
|
||||
*/
|
||||
this.socket.on("processCombinatorSignal", circuitFrameWithMeta => {
|
||||
if(circuitFrameWithMeta && typeof circuitFrameWithMeta == "object" && circuitFrameWithMeta.frame && Array.isArray(circuitFrameWithMeta.frame)){
|
||||
messageInterface("/silent-command remote.call('clusterio', 'receiveFrame', '"+JSON.stringify(circuitFrameWithMeta.frame)+"')");
|
||||
}
|
||||
});
|
||||
// get outbound frames from file and send to master
|
||||
// get array of lines in file, each line should correspond to a JSON encoded frame
|
||||
let signals = fs.readFileSync(instancedirectory + "/script-output/txbuffer.txt", "utf8").split("\n");
|
||||
// if we actually got anything from the file, proceed and reset file
|
||||
let readingTxBufferSoon = false;
|
||||
let txBufferClearCounter = 0;
|
||||
fs.watch(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (eventType, filename) => {
|
||||
if(!readingTxBufferSoon){ // use a 100ms delay to avoid messing with rapid sequential writes from factorio (I think that might be a problem maybe?)
|
||||
readingTxBufferSoon = true;
|
||||
setTimeout(()=>{
|
||||
txBufferClearCounter++;
|
||||
fs.readFile(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (err, signals) => {
|
||||
signals = signals.split("\n");
|
||||
if (signals[0]) {
|
||||
//if(txBufferClearCounter > 500){
|
||||
fs.writeFileSync(instancedirectory + "/script-output/txbuffer.txt", "");
|
||||
// txBufferClearCounter = 0;
|
||||
//}
|
||||
|
||||
// loop through all our frames
|
||||
for (let i = 0; i < signals.length; i++) {
|
||||
if (signals[i] && objectOps.isJSON(signals[i])) {
|
||||
// signals[i] is a JSON array called a "frame" of signals. We timestamp it for storage on master
|
||||
// then we unpack and RCON in this.frame to the game later.
|
||||
let framepart = JSON.parse(signals[i]);
|
||||
let doneframe = {
|
||||
time: Date.now(),
|
||||
frame: framepart, // thats our array of objects(single signals);
|
||||
}
|
||||
// send to master using socket.io, opened at the top of instanceManagement()
|
||||
this.socket.emit("combinatorSignal", doneframe);
|
||||
} else {
|
||||
// console.log("Invalid jsony: "+typeof signals[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
readingTxBufferSoon = false;
|
||||
},100);
|
||||
}
|
||||
});
|
||||
}
|
||||
async getCommand(file){
|
||||
this.commandCache = this.commandCache || {};
|
||||
|
||||
@@ -17,4 +17,5 @@ module.exports = {
|
||||
description: "Clusterio plugin to fill character logistic slots with master imports",
|
||||
// We'll send everything in this file to your stdin. Beware.
|
||||
scriptOutputFileSubscription: "inventoryImports.txt",
|
||||
fileReadDelay: 100,
|
||||
}
|
||||
@@ -141,7 +141,10 @@ function pollInventories(outputFile){
|
||||
//console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)')
|
||||
// console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)')
|
||||
//return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)'
|
||||
return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}local z={}for j,k in pairs(e)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for j,k in pairs(f)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(z)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0)'
|
||||
//return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}local z={}for j,k in pairs(e)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for j,k in pairs(f)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(z)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0)'
|
||||
|
||||
// Warning: This command is not in sync with lua commands.lua
|
||||
return `/silent-command local a='{"players":{'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..','else b=true end;local e=game.players[c].get_inventory(defines.inventory.character_main).get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'"'..c..'":{"inventory":{'local i=false;for j,k in pairs(e)do if i then a=a..','else i=true end;a=a..'"'..j..'":'..k end;a=a..'},"requestSlots":{'i=false;for j,k in pairs(g)do if i then a=a..','else i=true end;a=a..'"'..k['name']..'":'..k['count']end;a=a..'}}'end end;a=a.."}}"game.write_file("${outputFile}",a,true,0)`
|
||||
/*
|
||||
needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {instanceID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) {
|
||||
// success?
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
-- WARNING: The commands in this file is not in sync with interfacingFunctions.js
|
||||
-- Lua cheatsheet
|
||||
|
||||
/c game.write_file("test.txt",serpent.block(game.players["Danielv123"].get_quickbar().get_contents()))
|
||||
|
||||
@@ -11,15 +11,22 @@ local function updateTech()
|
||||
end
|
||||
end
|
||||
|
||||
if game.forces['player'].technologies['{tech_name}'] then
|
||||
local force = game.forces['player']
|
||||
local tech = force.technologies['{tech_name}']
|
||||
if tech then
|
||||
if 1 == {tech_infinite} then
|
||||
if (game.forces['player'].technologies['{tech_name}'].level ~= level) then
|
||||
game.forces['player'].technologies['{tech_name}'].level = {tech_level}
|
||||
if (tech.level ~= level) then
|
||||
tech.level = {tech_level}
|
||||
if tech == force.current_research then
|
||||
force.research_progress = {tech_progress}
|
||||
else
|
||||
force.set_saved_technology_progress(tech.name, {tech_progress});
|
||||
end
|
||||
updateTech()
|
||||
end
|
||||
else
|
||||
if not game.forces['player'].technologies['{tech_name}'].researched then
|
||||
game.forces['player'].technologies['{tech_name}'].researched = true
|
||||
if not tech.researched then
|
||||
tech.researched = true
|
||||
updateTech()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -169,21 +169,24 @@ class ResearchSync {
|
||||
})
|
||||
}
|
||||
|
||||
clear_contribution_to_researched_techs() {
|
||||
// if between updates tech was researched
|
||||
if (!this.prev_research)
|
||||
return
|
||||
for (let [name, research] of Object.entries(this.research)) {
|
||||
let researched
|
||||
if (research.infinite)
|
||||
researched = this.prev_research[name].level < research.level
|
||||
else
|
||||
researched = this.prev_research[name].researched < research.researched
|
||||
clear_contribution_to_researched_techs() {
|
||||
for (let [name, tech] of Object.entries(this.research)) {
|
||||
if (!Object.hasOwnProperty.call(this.prev_research, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (researched)
|
||||
research.contribution = 0
|
||||
}
|
||||
}
|
||||
let researched;
|
||||
if (tech.infinite) {
|
||||
researched = this.prev_research[name].level < tech.level;
|
||||
} else {
|
||||
researched = this.prev_research[name].researched < tech.researched;
|
||||
}
|
||||
|
||||
if (researched) {
|
||||
tech.contribution = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_cluster_techs(slavesData) {
|
||||
let cluster_techs = {}
|
||||
@@ -193,7 +196,7 @@ class ResearchSync {
|
||||
if (isNaN(node_tech.researched) || isNaN(node_tech.level) || isNaN(node_tech.infinite))
|
||||
continue
|
||||
|
||||
if (cluster_techs[name]) {
|
||||
if (Object.hasOwnProperty.call(cluster_techs, name)) {
|
||||
if (cluster_techs[name].infinite === 1 && cluster_techs[name].level < node_tech.level) {
|
||||
cluster_techs[name].level = node_tech.level
|
||||
} else if (node_tech.researched > cluster_techs[name].researched) {
|
||||
@@ -208,65 +211,90 @@ class ResearchSync {
|
||||
}
|
||||
|
||||
recount_cluster_research_progress(slaves_data, cluster_researches) {
|
||||
for (let [name, research] of Object.entries(cluster_researches))
|
||||
research.progress = this.research[name].contribution
|
||||
for (let [name, tech] of Object.entries(cluster_researches)) {
|
||||
if (Object.hasOwnProperty.call(this.research, name)) {
|
||||
tech.progress = this.research[name].contribution
|
||||
} else {
|
||||
tech.progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (let slave_data of slaves_data) {
|
||||
for (let [name, research] of Object.entries(slave_data.meta.research)) {
|
||||
if (!cluster_researches[name])
|
||||
continue
|
||||
if (isNaN(cluster_researches[name].progress)
|
||||
|| isNaN(research.contribution)
|
||||
|| isNaN(research.level)
|
||||
|| isNaN(cluster_researches[name].level))
|
||||
continue
|
||||
if (cluster_researches[name].level === research.level)
|
||||
cluster_researches[name].progress += research.contribution
|
||||
for (let [name, tech] of Object.entries(slave_data.meta.research)) {
|
||||
if (!Object.hasOwnProperty.call(cluster_researches, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
isNaN(cluster_researches[name].progress)
|
||||
|| isNaN(tech.contribution)
|
||||
|| isNaN(tech.level)
|
||||
|| isNaN(cluster_researches[name].level)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cluster_researches[name].level === tech.level) {
|
||||
cluster_researches[name].progress += tech.contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let [name, research] of Object.entries(cluster_researches)) {
|
||||
if (research.progress > 1) {
|
||||
research.progress = null
|
||||
research.researched = 1
|
||||
for (let [name, tech] of Object.entries(cluster_researches)) {
|
||||
if (Object.hasOwnProperty.call(this.research, name) && tech.progress > 1) {
|
||||
tech.progress = null
|
||||
tech.researched = 1
|
||||
this.research[name].contribution = 0
|
||||
research.contribution = 0
|
||||
if (this.research[name].level >= research.level)
|
||||
research.level = this.research[name].level + 1
|
||||
tech.contribution = 0
|
||||
if (this.research[name].level >= tech.level)
|
||||
tech.level = this.research[name].level + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter_researched_techs(cluster_researches) {
|
||||
let local_researches = this.research
|
||||
let result = {};
|
||||
for (let key in local_researches) {
|
||||
if (!cluster_researches[key])
|
||||
continue
|
||||
if (isNaN(cluster_researches[key].researched) || isNaN(cluster_researches[key].level))
|
||||
for (let [name, tech] of Object.entries(this.research)) {
|
||||
if (!Object.hasOwnProperty.call(cluster_researches, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNaN(cluster_researches[name].researched) || isNaN(cluster_researches[name].level))
|
||||
continue
|
||||
|
||||
let researched
|
||||
if (local_researches[key].infinite)
|
||||
researched = local_researches[key].level < cluster_researches[key].level
|
||||
else
|
||||
researched = local_researches[key].researched < cluster_researches[key].researched
|
||||
if (tech.infinite) {
|
||||
researched = tech.level < cluster_researches[name].level;
|
||||
} else {
|
||||
researched = tech.researched < cluster_researches[name].researched;
|
||||
}
|
||||
|
||||
if (researched)
|
||||
result[key] = cluster_researches[key]
|
||||
result[name] = cluster_researches[name]
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
filter_updated_techs(cluster_techs, to_research) {
|
||||
let local_techs = this.research
|
||||
let result = {}
|
||||
for (let name in local_techs) {
|
||||
if (name in to_research || !cluster_techs[name])
|
||||
continue
|
||||
for (let [name, tech] of Object.entries(this.research)) {
|
||||
if (Object.hasOwnProperty.call(to_research, name) || !Object.hasOwnProperty.call(cluster_techs, name)) {
|
||||
continue;
|
||||
}
|
||||
if (isNaN(cluster_techs[name].progress))
|
||||
continue
|
||||
if (local_techs[name].progress < cluster_techs[name].progress)
|
||||
|
||||
// Ignore researched techs
|
||||
if (!tech.infinite && tech.researched) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not update progress for infinite techs with a local level that's higher.
|
||||
if (tech.infinite && tech.level > cluster_techs[name].level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tech.progress < cluster_techs[name].progress)
|
||||
result[name] = cluster_techs[name]
|
||||
}
|
||||
return result
|
||||
@@ -274,8 +302,9 @@ class ResearchSync {
|
||||
|
||||
research_technologies(to_research) {
|
||||
for (let name of Object.keys(to_research))
|
||||
if (!this.research[name])
|
||||
delete to_research[name]
|
||||
if (!Object.hasOwnProperty.call(this.research, name)) {
|
||||
delete to_research[name];
|
||||
}
|
||||
|
||||
const notify = Object.keys(to_research).length === 1
|
||||
for (let [name, tech] of Object.entries(to_research)) {
|
||||
@@ -283,9 +312,9 @@ class ResearchSync {
|
||||
this.research[name].progress = null
|
||||
let command = this.functions.enableResearch;
|
||||
command = command.replace(/{tech_name}/g, name);
|
||||
command = command.replace(/{tech_researched}/g, tech.researched);
|
||||
command = command.replace(/{tech_level}/g, tech.level);
|
||||
command = command.replace(/{tech_infinite}/g, tech.infinite);
|
||||
command = command.replace(/{tech_progress}/g, tech.progress)
|
||||
command = command.replace(/{notify}/g, notify);
|
||||
this.messageInterface(command);
|
||||
let log_message = tech.infinite
|
||||
@@ -299,8 +328,9 @@ class ResearchSync {
|
||||
|
||||
update_technologies_progress(to_update) {
|
||||
for (let [name, tech] of Object.entries(to_update)) {
|
||||
if (!this.research[name])
|
||||
continue
|
||||
if (!Object.hasOwnProperty.call(this.research, name)) {
|
||||
continue;
|
||||
}
|
||||
let progress = this.research[name].progress
|
||||
if (progress === null)
|
||||
progress = 'nil'
|
||||
@@ -317,11 +347,10 @@ class ResearchSync {
|
||||
}
|
||||
|
||||
print_own_contribution() {
|
||||
if (!this.prev_research)
|
||||
return
|
||||
for (let [name, tech] of Object.entries(this.research)) {
|
||||
if (!this.prev_research[name])
|
||||
continue
|
||||
if (!Object.hasOwnProperty.call(this.prev_research, name)) {
|
||||
continue;
|
||||
}
|
||||
let diff = this.research[name].contribution - this.prev_research[name].contribution
|
||||
if (Math.abs(diff) > Number.EPSILON * 1000)
|
||||
this.log(`Own research ${name}: ${this.research[name].progress} += ${diff}`)
|
||||
@@ -346,8 +375,10 @@ class ResearchSync {
|
||||
|
||||
if (isNaN(level) || isNaN(researched))
|
||||
return
|
||||
this.prev_research[name] = this.research[name]
|
||||
if (!this.prev_research[name]) {
|
||||
|
||||
if (Object.hasOwnProperty.call(this.research, name)) {
|
||||
this.prev_research[name] = this.research[name];
|
||||
} else {
|
||||
this.prev_research[name] = {
|
||||
researched: null,
|
||||
level: null,
|
||||
|
||||
Reference in new issue
Block a user