Compare commits

..
29 Commits
Author SHA1 Message Date
Hornwitser 999c1b6c7a Release 1.2.2
Mark version 1.2.2 for release.
2019-10-13 14:53:31 +02:00
Hornwitser 61b1b1a106 Merge pull request #245 from Hornwitser/ease-installation
Ease installation
2019-10-13 14:48:30 +02:00
Hornwitser 8b4ec58459 Exit if masterAuthToken is missing
Exit client.js with a non-zero exit code if it's detected that the
masterAuthToken is missing in the config.
2019-10-12 23:39:40 +02:00
Hornwitser 6e26d32fed Recommend running without pm2 first
Recommend running master server without pm2 first as this makes
discovering any potential errors much simpler.
2019-10-12 23:36:43 +02:00
Hornwitser 74d9ec38dc Disable listening on SSL port by default
Set sslPort to 0 in the default config as this doesn't work properly in
1.2.x and also require elevated privileges to liste on.
2019-10-12 23:31:18 +02:00
Hornwitser dbef2bd2b3 Add -q option to wget
Avoid very confusing output of wget with it showing the progress for the
download at the same time as sudo asks for password input.
2019-10-12 23:25:20 +02:00
Arvids Godjuks e0b521d2fe Merge pull request #244 from Hornwitser/fix-node-install
Fix installation and instructions
2019-10-12 22:38:25 +02:00
Hornwitser 9a032aed2e Backport use Node.js version 10
Update instructions, Dockerfile and Travis CI to use Node.js 10.
Specify that Node.js version 10 or greater is needed in the engines
section of package.json (note that this is only advisory.)  And document
this as a breaking change in CHANGELOG.md.

This is a backport of 69a1ff7.
2019-10-12 22:15:53 +02:00
Hornwitser 72b56a00c7 Swap curl with wget in install instructions
Swap out curl with wget in the install instructions as wget is
pre-installed on Ubuntu.
2019-10-12 22:15:53 +02:00
Hornwitser c25bf23286 Reorder install instructions
Reorder the install pm2 step to be after the npm install step in the
instructions.  This is necessary to avoid npm creating some files in the
home directory owned as root and then failing the npm install step
because of this.
2019-10-12 22:15:53 +02:00
Hornwitser 9e983aff84 Reword development warning
Reword the warning at the start of the readme to be less intimidating,
as this is the correct version of the project that users are expected to
install.
2019-10-12 22:15:53 +02:00
Hornwitser 8973dcc6dd Fix bcrypt install failing on node v10
Update bcrypt to version 3.0.6, as versions below 3 are not supported on
node v10, and versions prior to 3.0.6 are not supported on node v12.
2019-10-12 22:15:53 +02:00
Arvids Godjuks 475128c8c4 Merge pull request #243 from Hornwitser/fix-researchsync
Try to fix researchSync
2019-10-12 21:48:09 +02:00
Hornwitser 0cdb5d0ce4 Fix crash in recount_cluster_research_progress
Check that the relevant research is present in the research mapping
before accessing it in recount_cluster_research_progress.
2019-10-12 17:07:08 +02:00
Hornwitser 075d54f708 Filter out lower levels when syncing research
Fix the next level of an infinite research getting updated with the
progress of the previous level from other nodes in the cluster by
filtering out infinite researches with a lower level than the local
level.
2019-10-12 17:07:08 +02:00
Hornwitser 62f3a99feb Set progress when completing infinite researches
Set the progress for the next level of an infinite research when
unlocking it via a script.  This is necessary when the research is
actively being researched  as Factorio will carry over the progress for
the current level into the next one when set via a script.

It's possible for the new progress to be non-zero if the next level was
on the research queue.
2019-10-12 17:07:08 +02:00
Hornwitser 3b1ed1a667 Rename research to tech
Use tech as the loop variable for researches as this is consistent with
the rest of the researchSync module.
2019-10-12 17:07:08 +02:00
Hornwitser 6a6274e85c Fix incorrect property checks in researchSync
Fix naive checks for properties being set of the form obj[key] into the
proper form Object.hasOwnProperty.call(obj, key).  The reason for this
is that if the key has a value of a built-in property like 'constructor'
then a check of the form obj[key] will always return true even if that
property hasn't been set on the object.
2019-10-12 17:07:07 +02:00
Arvids Godjuks a9cc743106 Merge pull request #240 from Hornwitser/release-1.2.1
Release 1.2.1
2019-09-20 14:18:52 +02:00
Hornwitser 2a7758f60d Release version 1.2.1
This release incorporates some minor bugfixes and a security fix for
an unauthenticated api in the master server.  See CHANGELOG.md for
details.
2019-09-18 18:20:00 +02:00
Hornwitser 7e2ca58a59 Fix license in package.json
Correct license entry being ISC instead of MIT in package.json.
2019-09-18 18:17:38 +02:00
Arvids Godjuks 718d4ed659 Merge pull request #239 from Hornwitser/fix-sigterm
Spawn factorio server deteached in client.js
2019-09-18 18:14:13 +02:00
Hornwitser 6b090cddbe Spawn factorio server deteached in client.js
Sending ^C via the terminal on Linux may cause SIGINT to be sent twice
to the factorio process, causing it to exit without completing the
saving.  This happens because the shell sends the interupt to all
members of the process group for the job and this includes both the
node client process and the factorio server process.

Avoid this situation by passing detached to spawn when the platform is
Linux, which causes the process to be created with its own process group
and therefore not targeted by a SIGINT from the shell.

This unfortunately does not work on Windows as we can't send SIGINT or
^C to a process there.  Doing subprocces.kill on Windows terminates the
process similarly to how SIGKILL works on Linux.
2019-09-18 18:11:49 +02:00
Arvids Godjuks 56c023f643 Merge pull request #232 from Hornwitser/update-node-factroio-api
Update node-factorio-api
2019-09-12 17:19:07 +02:00
Hornwitser 78de7236c5 Update node-factorio-api to 0.3.8
Fixes #229

Note that the versions 0.3.6 and 0.3.7 had issues with an old
mod_cache.json being present.
2019-09-12 16:52:44 +02:00
Arvids Godjuks 97c7a69c7a Merge pull request #230 from Hornwitser/auth-1.2.x
Add authentication to socket.io server
2019-09-11 15:19:18 +02:00
Hornwitser 0c45e57732 Add authentication to socket.io server
Authenticate connections made to the socket.io server via a token query
parameter that's validated in a similar manner to the x-access-token for
HTTP requests.
2019-09-10 23:51:48 +02:00
Arvids Godjuks 88eff17dfd Updated README.md 2019-09-04 16:20:50 +02:00
Daniel e1374d501e Use correct version in package.json 2019-08-30 18:06:42 +02:00
10 changed files with 191 additions and 85 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
language: node_js
node_js:
- "9"
- 10
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y curl git tar
+41
View File
@@ -0,0 +1,41 @@
Changelog
=========
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
View File
@@ -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
+20 -10
View File
@@ -8,6 +8,13 @@
Discord for development/support/play: https://discord.gg/5XuDkje
## Important notice
This is the stable branch of the project. The
[master branch][http://github.com/clusterio/factorioClusterio/tree/master]
is undergoing significant changes that breaks compatibility to plugins and
existing installations and is not recommended for use.
### Ways to support me/the project:
* Contribute with code/documentation. Always nice to move the project forward
@@ -88,19 +95,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 +122,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 +169,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/
+10 -3
View File
@@ -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,
@@ -359,7 +361,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 +474,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 +574,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", {
+1 -1
View File
@@ -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",
+12
View File
@@ -921,6 +921,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
View File
@@ -1,6 +1,6 @@
{
"name": "clusterio",
"version": "1.7.0",
"version": "1.2.2",
"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",
+12 -5
View File
@@ -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
+86 -60
View File
@@ -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,85 @@ 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)
// 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 +297,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 +307,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 +323,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 +342,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 +370,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,