mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f2bd2653e | ||
|
|
5bbd879f34 | ||
|
|
81aa2141b9 | ||
|
|
9c108dd4dd | ||
|
|
6415784e8a | ||
|
|
206f3db042 | ||
|
|
fb28693dec | ||
|
|
79ff52aa20 | ||
|
|
cb4616f47c | ||
|
|
bc7f3804c8 | ||
|
|
a452749caa | ||
|
|
1e8fb85122 | ||
|
|
66c0238a3d | ||
|
|
ca0281ae58 | ||
|
|
200c8dbdeb | ||
|
|
d0afcbf0ac | ||
|
|
13a069a798 | ||
|
|
0e849488e2 | ||
|
|
6264bd9888 | ||
|
|
cbd1939a74 | ||
|
|
7cf9319624 | ||
|
|
de2141ec50 | ||
|
|
0df1b8e1fd | ||
|
|
ec0243d7cf | ||
|
|
f32624586b | ||
|
|
6ea47cb6af | ||
|
|
a204163a6c | ||
|
|
f76c84c3f1 | ||
|
|
6caef9bda9 | ||
|
|
30cda4de3b | ||
|
|
a13d4c6805 | ||
|
|
56b805e19b | ||
|
|
5fd3a6b188 | ||
|
|
9d1e60aa03 | ||
|
|
35101a7e32 |
No files matched your search
+2
-4
@@ -11,8 +11,9 @@ pids
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
# Coverage directories used by tools like nyc and codecov
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
@@ -25,7 +26,6 @@ build/Release
|
||||
dist/
|
||||
|
||||
node_modules/*
|
||||
!node_modules/_app
|
||||
|
||||
Factorio*
|
||||
/factorio
|
||||
@@ -63,7 +63,5 @@ sharedPlugins/scenario_loader/scenarios
|
||||
# File used for marking plugins as disabled
|
||||
sharedPlugins/*/DISABLED
|
||||
|
||||
\.cache-require-paths\.json
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
@@ -1,45 +0,0 @@
|
||||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.13.3
|
||||
ignore: {}
|
||||
# patches apply the minimum changes required to fix a vulnerability
|
||||
patch:
|
||||
'npm:lodash:20180130':
|
||||
- html5-validator > caporal > cli-table2 > lodash:
|
||||
patched: '2018-07-03T06:07:55.532Z'
|
||||
'npm:hawk:20160119':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > request > hawk:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:http-signature:20150122':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > request > http-signature:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:mime:20170907':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > mime:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > mime:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > request > form-data > mime:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:minimatch:20160620':
|
||||
- node-factorio-api > documentation > highlight.js > gear > liftoff > findup-sync > glob > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > gear > liftoff > findup-sync > glob > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > glob > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > jslint > glob > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > jshint > cli > glob > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > jshint > minimatch:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:request:20160119':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > request:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:tunnel-agent:20170305':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > less > request > tunnel-agent:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
'npm:uglify-js:20151024':
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > handlebars > uglify-js:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
- node-factorio-api > documentation > highlight.js > gear-lib > uglify-js:
|
||||
patched: '2019-02-25T06:48:21.845Z'
|
||||
+1
-1
@@ -2,7 +2,7 @@ FROM node:9.11.2
|
||||
RUN apt-get update && apt install git curl tar -y
|
||||
RUN mkdir factorioClusterio
|
||||
|
||||
RUN git clone -b master https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install
|
||||
RUN git clone -b master https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install --only=production
|
||||
RUN cd factorioClusterio && curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 && tar -xf factorio.tar.gz
|
||||
|
||||
WORKDIR factorioClusterio
|
||||
|
||||
@@ -95,7 +95,7 @@ Master and all slaves:
|
||||
cd factorioClusterio
|
||||
curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64
|
||||
tar -xf factorio.tar.gz
|
||||
npm install
|
||||
npm install --only=production
|
||||
cp config.json.dist config.json
|
||||
node ./lib/npmPostinstall.js
|
||||
|
||||
@@ -230,18 +230,6 @@ According to [this link](https://askubuntu.com/questions/839520/open-port-443-fo
|
||||
|
||||
sudo setcap 'cap_net_bind_service=+ep' $(readlink -f $(which node))
|
||||
|
||||
### InitializationError: First argument must be an abstract-leveldown compliant store
|
||||
|
||||
do:
|
||||
|
||||
npm install linvodb3@3.25.1
|
||||
|
||||
if that does not suffice do
|
||||
|
||||
npm install levelup@1.3.9
|
||||
|
||||
If it still doesn't work, come over to our support discord linked at the top of this readme.
|
||||
|
||||
### Portforwarding doesn't work on the master server when running under WSL
|
||||
|
||||
If you follow the ubuntu guide on WSL (Windows Subsystem for Linux, Bash on Ubuntu on Windows specifically), you will find that the website works on localhost and on your local ip, but not on the global ip. This is also true when you correctly port-forwarded the correct ports. Even when routing this server through nginx in WSL, the issue persists. Then, on a hunch, I tried to run nginx from windows itself and found that this DID work. It came to me that the only usage difference between the 2 versions of nginx is that I got a Windows Firewall popup.
|
||||
@@ -283,7 +271,7 @@ node client.js list
|
||||
```
|
||||
git pull
|
||||
|
||||
npm install
|
||||
npm install --only=production
|
||||
```
|
||||
|
||||
3. Download the latest version of the factorioClusterioMod from its github repo
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const fs = require('fs-extra');
|
||||
const Tail = require('tail').Tail;
|
||||
const https = require('follow-redirects').https;
|
||||
const needle = require("needle");
|
||||
const child_process = require('child_process');
|
||||
@@ -6,19 +7,18 @@ const path = require('path');
|
||||
const syncRequest = require('sync-request');
|
||||
const request = require("request");
|
||||
const ncp = require('ncp').ncp;
|
||||
const Rcon = require('rcon-client').Rcon;
|
||||
const hashFiles = require('hash-files');
|
||||
const _ = require('underscore');
|
||||
const Rcon = require('rcon-client-fork').Rcon;
|
||||
const deepmerge = require("deepmerge");
|
||||
const getMac = require('getmac').getMac;
|
||||
const rmdirSync = require('rmdir-sync');
|
||||
const ioClient = require("socket.io-client");
|
||||
|
||||
// internal libraries
|
||||
const objectOps = require("./lib/objectOps.js");
|
||||
const fileOps = require("_app/fileOps");
|
||||
const stringUtils = require("./lib/stringUtils.js");
|
||||
const configManager = require("./lib/manager/configManager.js");
|
||||
const objectOps = require("lib/objectOps.js");
|
||||
const fileOps = require("lib/fileOps");
|
||||
const stringUtils = require("lib/stringUtils.js");
|
||||
const configManager = require("lib/manager/configManager.js");
|
||||
const hashFile = require('lib/hash').hashFile;
|
||||
|
||||
// argument parsing
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
@@ -55,6 +55,7 @@ if(!config.masterAuthToken || typeof config.masterAuthToken !== "string"){
|
||||
You can retrieve your auth token from the master in secret-api-token.txt after running it once.");
|
||||
}
|
||||
const needleOptionsWithTokenAuthHeader = {
|
||||
compressed:true,
|
||||
headers: {
|
||||
'x-access-token': config.masterAuthToken
|
||||
},
|
||||
@@ -187,7 +188,7 @@ if (!command || command == "help" || command == "--help") {
|
||||
if(tool == "mods"){
|
||||
(async function(){try{
|
||||
// do require down here to reduce master load time
|
||||
const modManager = require("./lib/manager/modManager.js")(config);
|
||||
const modManager = require("lib/manager/modManager.js")(config);
|
||||
|
||||
// allow managing mods
|
||||
if(action == "list"){
|
||||
@@ -227,7 +228,7 @@ if (!command || command == "help" || command == "--help") {
|
||||
}
|
||||
} else if(tool == "plugins"){
|
||||
(async function(){try{
|
||||
const pluginManager = require("./lib/manager/pluginManager.js")(config);
|
||||
const pluginManager = require("lib/manager/pluginManager.js")(config);
|
||||
if(action == "list"){
|
||||
await pluginManager.listPlugins();
|
||||
} else if(action == "add" || action == "install" || action == "download"){
|
||||
@@ -300,7 +301,7 @@ if (!command || command == "help" || command == "--help") {
|
||||
fs.mkdirSync(instancedirectory + "/mods/");
|
||||
fs.mkdirSync(instancedirectory + "/instanceMods/");
|
||||
fs.mkdirSync(instancedirectory + "/scenarios/");
|
||||
ncp("./lib/scenarios", path.resolve(instancedirectory, "scenarios"), err => {
|
||||
ncp("lib/scenarios", path.resolve(instancedirectory, "scenarios"), err => {
|
||||
if (err) console.error(err)
|
||||
});
|
||||
|
||||
@@ -561,8 +562,11 @@ write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n
|
||||
}
|
||||
|
||||
// ensure instancemanagement only ever runs once
|
||||
_.once(instanceManagement);
|
||||
var _instanceInitialized;
|
||||
async function instanceManagement(instanceconfig) {
|
||||
if (_instanceInitialized) return;
|
||||
_instanceInitialized = true;
|
||||
|
||||
console.log("Started instanceManagement();");
|
||||
|
||||
/* Open websocket connection to master */
|
||||
@@ -576,15 +580,12 @@ async function instanceManagement(instanceconfig) {
|
||||
setInterval(B=> socket.emit("heartbeat"), 10000);
|
||||
|
||||
// load plugins and execute onLoad event
|
||||
const pluginManager = require("./lib/manager/pluginManager.js")(config);
|
||||
const pluginManager = require("lib/manager/pluginManager.js")(config);
|
||||
let pluginsToLoad = await pluginManager.getPlugins();
|
||||
let plugins = [];
|
||||
|
||||
for(let i = 0; i < pluginsToLoad.length; i++){
|
||||
let pluginLoadStarted = Date.now();
|
||||
let log = function(message) {
|
||||
console.log("Clusterio | "+ pluginsToLoad[i].name + " | " + message);
|
||||
}
|
||||
let combinedConfig = deepmerge(instanceconfig,config,{clone:true});
|
||||
let pluginConfig = pluginsToLoad[i];
|
||||
|
||||
@@ -596,7 +597,7 @@ async function instanceManagement(instanceconfig) {
|
||||
let pluginClass = require(path.resolve(pluginConfig.pluginPath, "index.js"));
|
||||
plugins[i] = new pluginClass(combinedConfig, async function(data, callback){
|
||||
if(data && data.toString('utf8')[0] != "/") {
|
||||
log(data.toString('utf8'));
|
||||
console.log("Clusterio | "+ pluginsToLoad[i].name + " | " + data.toString('utf8'));
|
||||
return true;
|
||||
} else if (data && data.toString('utf8')[0] == "/"){
|
||||
return messageInterface(data.toString('utf8'), callback);
|
||||
@@ -622,37 +623,50 @@ async function instanceManagement(instanceconfig) {
|
||||
global.subscribedFiles[pluginConfig.scriptOutputFileSubscription] = true;
|
||||
console.log("Clusterio | Registered file subscription on "+instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription);
|
||||
|
||||
fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, fileChangeHandler);
|
||||
// run once in case a plugin wrote out information before the plugin loaded fully
|
||||
// delay, so the socket got enough time to connect
|
||||
setTimeout(()=> {
|
||||
fileChangeHandler(false, pluginConfig.scriptOutputFileSubscription);
|
||||
}, 500);
|
||||
// send file contents to plugin for processing
|
||||
function fileChangeHandler(eventType, filename) {
|
||||
if(filename != null){
|
||||
setTimeout(
|
||||
()=>{
|
||||
// get array of lines in file
|
||||
let stuff = fs.readFileSync(instancedirectory + "/script-output/" + filename, "utf8").split("\n");
|
||||
|
||||
// if you found anything, reset the file
|
||||
if (stuff[0]) {
|
||||
fs.writeFileSync(instancedirectory + "/script-output/" + filename, "");
|
||||
}
|
||||
for(let o = 0; o < stuff.length; o++) {
|
||||
if(stuff[o] && !stuff[o].includes('\u0000\u0000')) {
|
||||
try{
|
||||
plugins[i].scriptOutput(stuff[o]);
|
||||
}catch(e){console.error(e)}
|
||||
}
|
||||
}
|
||||
},
|
||||
pluginConfig.fileReadDelay || 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!pluginConfig.fileReadDelay || pluginConfig.fileReadDelay == 0) {
|
||||
// only wipe the file on restart for now, should most likely be rotated during runtime too
|
||||
fs.writeFileSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, "");
|
||||
let tail = new Tail(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription);
|
||||
tail.on("line", function (data) {
|
||||
plugins[i].scriptOutput(data);
|
||||
});
|
||||
} else {
|
||||
fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, fileChangeHandler);
|
||||
// run once in case a plugin wrote out information before the plugin loaded fully
|
||||
// delay, so the socket got enough time to connect
|
||||
setTimeout(() => {
|
||||
fileChangeHandler(false, pluginConfig.scriptOutputFileSubscription);
|
||||
}, 500);
|
||||
|
||||
// send file contents to plugin for processing
|
||||
function fileChangeHandler(eventType, filename) {
|
||||
if (filename != null) {
|
||||
setTimeout(
|
||||
() => {
|
||||
// get array of lines in file
|
||||
let stuff = fs.readFileSync(instancedirectory + "/script-output/" + filename, "utf8").split("\n");
|
||||
|
||||
// if you found anything, reset the file
|
||||
if (stuff[0]) {
|
||||
fs.writeFileSync(instancedirectory + "/script-output/" + filename, "");
|
||||
}
|
||||
for (let o = 0; o < stuff.length; o++) {
|
||||
if (stuff[o] && !stuff[o].includes('\u0000\u0000')) {
|
||||
try {
|
||||
plugins[i].scriptOutput(stuff[o]);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pluginConfig.fileReadDelay || 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Clusterio | Loaded plugin ${pluginsToLoad[i].name} in ${Date.now() - pluginLoadStarted}ms`);
|
||||
} else {
|
||||
// this plugin doesn't have a client portion. Maybe it runs on the master only?
|
||||
@@ -675,7 +689,7 @@ async function instanceManagement(instanceconfig) {
|
||||
mods:modHashes,
|
||||
instanceName: instance,
|
||||
info: instanceconfig.info,
|
||||
}
|
||||
};
|
||||
if(playerCount){
|
||||
payload.playerCount = playerCount.replace(/(\r\n\t|\n|\r\t)/gm, "");
|
||||
} else {
|
||||
@@ -722,7 +736,7 @@ async function instanceManagement(instanceconfig) {
|
||||
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") {
|
||||
@@ -761,42 +775,22 @@ function hashMods(instanceName, callback) {
|
||||
if(!callback) {
|
||||
throw new Error("ERROR in function hashMods NO CALLBACK");
|
||||
}
|
||||
function callback2(hash, modName){
|
||||
hashedMods[hashedMods.length] = {
|
||||
modName: modName,
|
||||
hash: hash,
|
||||
}
|
||||
// check if this callback has ran once for each mod
|
||||
if(hashedMods.length == instanceMods.length) {
|
||||
callback(hashedMods);
|
||||
}
|
||||
//console.log(modname);
|
||||
}
|
||||
let hashedMods = [];
|
||||
/*let mods = fs.readdirSync("./sharedMods/")*/
|
||||
let instanceMods = fs.readdirSync(config.instanceDirectory+"/"+instanceName+"/mods/");
|
||||
if(instanceMods.length == 0){
|
||||
callback({}); // there are no mods installed, return an empty object
|
||||
}
|
||||
for(let o=0;o<instanceMods.length;o++) {
|
||||
if(path.extname(instanceMods[o]) != ".zip") {
|
||||
instanceMods.splice(instanceMods.indexOf(instanceMods[o]), 1); // remove element from array, we can't hash unzipped mods
|
||||
|
||||
let modsDir = config.instanceDirectory+"/"+instanceName+"/mods/";
|
||||
function hashMod(name) {
|
||||
if (path.extname(name) != ".zip") {
|
||||
// Can't hash unzipped mods, return null that's filtered out later
|
||||
return null;
|
||||
} else {
|
||||
return hashFile(modsDir+name).then(hash => (
|
||||
{modName: name, hash: hash}
|
||||
));
|
||||
}
|
||||
}
|
||||
for(let i=0; i<instanceMods.length; i++){
|
||||
let path = config.instanceDirectory+"/"+instanceName+"/mods/"+instanceMods[i];
|
||||
let name = instanceMods[i];
|
||||
let options = {
|
||||
files:path,
|
||||
}
|
||||
// options {files:[array of paths]}
|
||||
hashFiles(options, function(error, hash) {
|
||||
// hash will be a string if no error occurred
|
||||
if(!error){
|
||||
callback2(hash, name);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let promises = fs.readdirSync(modsDir).map(hashMod);
|
||||
Promise.all(promises).then(hashes => {
|
||||
// Remove null entries from hashMod
|
||||
callback(hashes.filter(entry => entry !== null));
|
||||
});
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
toc:
|
||||
- name: Master Server API
|
||||
- clusterioMaster
|
||||
- name: Type declarations
|
||||
- itemStack
|
||||
- name: Internal modules
|
||||
description: |
|
||||
Here are some helper functions for navigation.
|
||||
- modManager
|
||||
- getFactorioLocale
|
||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -32,7 +32,7 @@ module.exports = function(config){
|
||||
}
|
||||
} else {
|
||||
return async function thisAintGonnaWork(){
|
||||
let err = new Error("getInstanceName from _app/clusterTools needs to be called with {config} to work");
|
||||
let err = new Error("getInstanceName from lib/clusterTools needs to be called with {config} to work");
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -15,9 +15,8 @@ const child_process = require("child_process");
|
||||
const path = require("path");
|
||||
|
||||
const mkdirp = require("mkdirp");
|
||||
const appRootPath = require("app-root-path");
|
||||
|
||||
const fileOps = require("_app/fileOps");
|
||||
const fileOps = require("lib/fileOps");
|
||||
|
||||
module.exports = class factorioServer {
|
||||
constructor({
|
||||
@@ -165,4 +164,4 @@ module.exports = class factorioServer {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const appRootPath = require("app-root-path");
|
||||
const parallel = require("mocha.parallel");
|
||||
const mkdirp = require("mkdirp");
|
||||
|
||||
@@ -75,7 +74,7 @@ describe("class factorioServer/index.js", ()=>{
|
||||
describe(".createMap(mapName.zip)", ()=>{
|
||||
it("creates a map file as instanceDirectory/saves/map.zip", async function(){
|
||||
this.timeout(20000); // these test often takes a lot of time in the map creation step (4500 ms on i3 dev system)
|
||||
let relativePath = __dirname.replace(appRootPath, "").substr(1);
|
||||
let relativePath = path.relative('.', __dirname);
|
||||
let server = new factorioServer({
|
||||
instanceDirectory: path.join(relativePath, "_testData_createMap"),
|
||||
instanceName: "Unit testing instance",
|
||||
@@ -93,7 +92,7 @@ describe("class factorioServer/index.js", ()=>{
|
||||
});
|
||||
it("supports custom filenames", async function(){
|
||||
this.timeout(20000);
|
||||
let relativePath = __dirname.replace(appRootPath, "").substr(1);
|
||||
let relativePath = path.relative('.', __dirname);
|
||||
let server = new factorioServer({
|
||||
instanceDirectory: path.join(relativePath, "_testData_createMap_2"),
|
||||
instanceName: "Unit testing instance",
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
var assert = require("assert");
|
||||
var fs = require("fs");
|
||||
|
||||
var fileOps = require("_app/fileOps");
|
||||
var fileOps = require("lib/fileOps");
|
||||
var generateSSLcert = require("./generateSSLcert.js")
|
||||
|
||||
describe("generateSSLcert.js(options)", ()=>{
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const fileOps = require("_app/fileOps");
|
||||
const fileOps = require("lib/fileOps");
|
||||
// const config = require("./../config");
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves to the SHA1 hash of the file given by path
|
||||
*/
|
||||
exports.hashFile = function(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let hasher = crypto.createHash('sha1')
|
||||
hasher.setEncoding('hex')
|
||||
hasher.on('finish', function() {
|
||||
resolve(hasher.read());
|
||||
});
|
||||
|
||||
let stream = fs.createReadStream(path);
|
||||
stream.on('error', function(error) {
|
||||
// the docs doesn't say anything about what's passed on error
|
||||
reject(error);
|
||||
});
|
||||
stream.pipe(hasher);
|
||||
});
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,7 +3,6 @@ Provides tools to manage mods in a clusterio install
|
||||
@module modManager
|
||||
*/
|
||||
|
||||
const rightpad = require("right-pad");
|
||||
const asTable = require("as-table").configure({delimiter: ' | '});
|
||||
const moment = require("moment");
|
||||
const syncRequest = require("sync-request");
|
||||
@@ -13,7 +12,7 @@ const factorioAPI = require("node-factorio-api");
|
||||
const sLog = require("single-line-log").stdout;
|
||||
const query = require('cli-interact').getYesNo;
|
||||
|
||||
const fileOps = require("_app/fileOps");
|
||||
const fileOps = require("lib/fileOps");
|
||||
// const config = require("./../../config");
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ function download(url, file, callback){
|
||||
}
|
||||
|
||||
var directories = [
|
||||
"database/linvodb",
|
||||
"sharedMods",
|
||||
"instances",
|
||||
];
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "lib",
|
||||
"description": "Internal library for factorioClusterio"
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ require 'hotpatch.gui'
|
||||
-- As this is part of the base mod, the developers have given permission for modders to use/adapt the assets therein, for modding Factorio
|
||||
-- Any other use would require licensing/permission from Wube
|
||||
-- If you don't want to run this, either comment this out before creating your scenario, OR uninstall freeplay at runtime
|
||||
hotpatch_tools.static_mod('freeplay', '1.0.0', [===[
|
||||
hotpatch_tools.static_mod('freeplay', '0.17.50', [===[
|
||||
local util = require("util")
|
||||
local silo_script = require("silo-script")
|
||||
|
||||
@@ -127,6 +127,7 @@ remote.add_interface("freeplay",
|
||||
global.chart_distance = tonumber(value)
|
||||
end
|
||||
})
|
||||
|
||||
]===])
|
||||
|
||||
-- Multi-file test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local hotpatch_tools = require 'hotpatch.mod-tools'
|
||||
hotpatch_tools.static_mod('hotpatch-commands', '1.0.1', [===[
|
||||
hotpatch_tools.static_mod('hotpatch-commands', '1.0.2', [===[
|
||||
--[[
|
||||
|
||||
Copyright 2018 Chrisgbk
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local hotpatch_tools = require 'hotpatch.mod-tools'
|
||||
hotpatch_tools.static_mod('hotpatch-gui', '1.0.4', [===[
|
||||
hotpatch_tools.static_mod('hotpatch-gui', '1.0.6', [===[
|
||||
--[[
|
||||
|
||||
Copyright 2018 Chrisgbk
|
||||
@@ -86,7 +86,6 @@ on_gui_click_handlers = {
|
||||
--on_gui_click_handlers['hotpatch-menu.IDE'](e)
|
||||
--do return end
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
@@ -100,23 +99,20 @@ on_gui_click_handlers = {
|
||||
end
|
||||
menu.visible = not menu.visible
|
||||
end,
|
||||
['hotpatch-menu'] = function(e)
|
||||
['hotpatch-menu'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local menu = left['hotpatch-menu']
|
||||
menu.visible = not menu.visible
|
||||
|
||||
on_gui_click_handlers[e.element.name](e)
|
||||
if e.element.name ~= 'hotpatch-menu' then
|
||||
local menu = left['hotpatch-menu']
|
||||
menu.visible = not menu.visible
|
||||
on_gui_click_handlers[e.element.name](e)
|
||||
end
|
||||
end,
|
||||
['hotpatch-menu.IDE'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
|
||||
local IDE = center['hotpatch-IDE']
|
||||
if not IDE then
|
||||
IDE = center.add{type = 'frame', name = 'hotpatch-IDE', direction = 'vertical', caption = 'Hotpatch IDE', style = mod_gui.frame_style}
|
||||
@@ -155,8 +151,6 @@ on_gui_click_handlers = {
|
||||
end,
|
||||
['hotpatch-menu.console'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local hotpatch_console = center['hotpatch-console']
|
||||
@@ -192,9 +186,6 @@ on_gui_click_handlers = {
|
||||
end,
|
||||
['hotpatch-console-run'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local hotpatch_console = center['hotpatch-console']
|
||||
@@ -252,8 +243,6 @@ on_gui_click_handlers = {
|
||||
end,
|
||||
['hotpatch-IDE-file'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local element = e.element
|
||||
@@ -296,8 +285,6 @@ end)
|
||||
on_gui_selection_state_changed_handlers = {
|
||||
['hotpatch-console-mod-selector'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local element = e.element
|
||||
@@ -316,8 +303,6 @@ on_gui_selection_state_changed_handlers = {
|
||||
end,
|
||||
['hotpatch-IDE-mod-selector'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local element = e.element
|
||||
@@ -374,4 +359,19 @@ end)
|
||||
|
||||
--]===])
|
||||
|
||||
--[===[
|
||||
-- keeping this as a reminder
|
||||
['hotpatch-menu'] = function(e)
|
||||
local player = game.players[e.player_index]
|
||||
local top = mod_gui.get_button_flow(player)
|
||||
local left = mod_gui.get_frame_flow(player)
|
||||
local center = player.gui.center
|
||||
|
||||
local menu = left['hotpatch-menu']
|
||||
menu.visible = not menu.visible
|
||||
|
||||
on_gui_click_handlers[e.element.name](e)
|
||||
end,
|
||||
--]===]
|
||||
|
||||
return true
|
||||
@@ -15,6 +15,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
log('Hotpatch runtime initializing...')
|
||||
local util = require 'util'
|
||||
|
||||
local compat_mode = false -- enable some compatibility settings, which can help some mods load
|
||||
local strict_mode = false -- causes hotpatch to hard-stop on mods doing bad things
|
||||
|
||||
local debug_levels = {['disabled'] = -1, ['severe'] = 1, ['error'] = 2, ['warning'] = 3, ['info'] = 4, ['verbose'] = 5, ['trace'] = 6}
|
||||
|
||||
-- configuration options; these should be exposed via API one day
|
||||
@@ -23,12 +26,15 @@ local debug_log_to_console_only = _ENV.debug_settings.log_to_console_only
|
||||
local debug_log_to_RCON = _ENV.debug_settings.log_to_RCON --only affects when log_to_console_only is in effect
|
||||
local debug_log_on_tick = _ENV.debug_settings.log_on_tick
|
||||
|
||||
local compat_mode = false -- enable some compatibility settings, which can help some mods load
|
||||
local strict_mode = false -- causes hotpatch to hard-stop on mods doing bad things
|
||||
|
||||
-- convenience object(rcon.print also prints to stdout when called from server console)
|
||||
local console = {name = 'Console', admin = true, print = function(...) rcon.print(...) end, color = {1,1,1,1}}
|
||||
|
||||
-- mapping of events to names for logging
|
||||
local event_names = {}
|
||||
for k,v in pairs(defines.events) do
|
||||
event_names[v] = k
|
||||
end
|
||||
|
||||
-- these represent the mods as statically packaged with the scenario
|
||||
local static_mods = {}
|
||||
-- static_mods[i] = {}
|
||||
@@ -127,12 +133,6 @@ local function static_mod(name, version, code, files)
|
||||
table.insert(static_mods, mod)
|
||||
end
|
||||
|
||||
-- mapping of events to names for logging
|
||||
local event_names = {}
|
||||
for k,v in pairs(defines.events) do
|
||||
event_names[v] = k
|
||||
end
|
||||
|
||||
-- This scenario contains an embedded locale, but it's unavailable until this file finishes loading
|
||||
-- ie: calls to log{...} will fail right now, but will work in events
|
||||
-- Rseding91 fixed it for 0.17 (https://forums.factorio.com/viewtopic.php?f=23&t=60767)
|
||||
@@ -334,7 +334,7 @@ local function debug_log(message, mod_name, stack_level)
|
||||
local class = 'hotpatch.info'
|
||||
local log_type = (mod_name and 'hotpatch.log-mod') or 'hotpatch.log'
|
||||
local severity
|
||||
if type(message) == 'table' then
|
||||
if type(message) == 'table' then
|
||||
severity = message[1]:match('.-%-([^%-]*)%.')
|
||||
if not severity then
|
||||
severity = message[1]:match('.-%-.-%-(.*)%.')
|
||||
@@ -346,7 +346,7 @@ local function debug_log(message, mod_name, stack_level)
|
||||
else
|
||||
severity = 'always'
|
||||
end
|
||||
local level = ((severity == 'always') and 0) or debug_levels[severity]
|
||||
local level = ((severity == 'always') and 0) or debug_levels[severity]
|
||||
if debug_level >= level then
|
||||
if debug_log_to_console_only then
|
||||
if debug_log_to_RCON then
|
||||
@@ -365,15 +365,15 @@ debug_log({'hotpatch-info.logging-enabled'})
|
||||
|
||||
-- track _ENV accesses
|
||||
setmetatable(_ENV, {
|
||||
__index = function(_, k)
|
||||
debug_log({'hotpatch-trace.nil-var-access', k}, nil, 3)
|
||||
return nil
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
debug_log({'hotpatch-trace.nil-var-assignment', k}, nil, 3)
|
||||
rawset(t,k,v)
|
||||
end,
|
||||
__metatable = false
|
||||
__index = function(_, k)
|
||||
debug_log({'hotpatch-trace.nil-var-access', k}, nil, 3)
|
||||
return nil
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
debug_log({'hotpatch-trace.nil-var-assignment', k}, nil, 3)
|
||||
rawset(t,k,v)
|
||||
end,
|
||||
__metatable = false
|
||||
})
|
||||
|
||||
debug_log({'hotpatch-info.metatable-installed'})
|
||||
@@ -395,7 +395,7 @@ local libraries = {
|
||||
|
||||
for k, v in pairs(libraries) do
|
||||
debug_log({'hotpatch-info.loading-library', v})
|
||||
loaded_libraries[k] = require(v)
|
||||
loaded_libraries[k] = require(v)
|
||||
end
|
||||
|
||||
debug_log({'hotpatch-info.complete', {'hotpatch-info.loading-libs'}})
|
||||
@@ -614,20 +614,20 @@ load_mod = function(installed_index)
|
||||
-- mods private package.loaded
|
||||
local loaded = {}
|
||||
-- copy the current environment
|
||||
for k,v in pairs(_ENV) do
|
||||
env[k] = v
|
||||
end
|
||||
for k,v in pairs(_ENV) do
|
||||
env[k] = v
|
||||
end
|
||||
-- copy package.loaded
|
||||
for k,v in pairs(_ENV.package.loaded) do
|
||||
loaded[k] = v
|
||||
end
|
||||
loaded[k] = v
|
||||
end
|
||||
loaded._G = env
|
||||
|
||||
-- so many ways to escape sandboxes...
|
||||
|
||||
for k,v in pairs(_ENV.package) do
|
||||
pack[k] = v
|
||||
end
|
||||
pack[k] = v
|
||||
end
|
||||
pack.loaded = loaded
|
||||
env.package = pack
|
||||
loaded.package = pack
|
||||
@@ -681,15 +681,15 @@ load_mod = function(installed_index)
|
||||
-- I blame Nexela for this
|
||||
path = path:gsub('/', '.')
|
||||
path = path:gsub('\\', '.')
|
||||
local alt_path = ''
|
||||
local alt_path = ''
|
||||
if env.package._current_path_in_package then
|
||||
alt_path = env.package._current_path_in_package .. path
|
||||
end
|
||||
if mod_obj.loaded_files[path] then
|
||||
debug_log({'hotpatch-trace.cached-load-require', path}, mod_name)
|
||||
return mod_obj.loaded_files[path]
|
||||
elseif mod_obj.loaded_files[alt_path] then
|
||||
debug_log({'hotpatch-trace.cached-load-require', alt_path}, mod_name)
|
||||
elseif mod_obj.loaded_files[alt_path] then
|
||||
debug_log({'hotpatch-trace.cached-load-require', alt_path}, mod_name)
|
||||
return mod_obj.loaded_files[alt_path]
|
||||
else
|
||||
local oldbase = env.package._current_path_in_package
|
||||
@@ -711,7 +711,7 @@ load_mod = function(installed_index)
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
file = global.mods[installed_index].files[alt_path]
|
||||
file = global.mods[installed_index].files[alt_path]
|
||||
if file then
|
||||
debug_log({'hotpatch-trace.load-require', alt_path}, mod_name)
|
||||
local code, err = load(file, '[' .. mod_name .. '] ' .. alt_path .. '.lua', 'bt', env)
|
||||
@@ -728,11 +728,11 @@ load_mod = function(installed_index)
|
||||
|
||||
debug_log({'hotpatch-trace.load-core-lib', path}, mod_name)
|
||||
env.package._current_path_in_package = oldbase
|
||||
local lib = package.loaded[path]
|
||||
if not lib then
|
||||
debug_log({'hotpatch-error.path-not-found', path}, mod_name, 3)
|
||||
local lib = package.loaded[path]
|
||||
if not lib then
|
||||
debug_log({'hotpatch-error.path-not-found', path}, mod_name, 3)
|
||||
error(path .. ' not found')
|
||||
end
|
||||
end
|
||||
return lib
|
||||
end
|
||||
end
|
||||
@@ -808,10 +808,6 @@ reset_mod = function(loaded_index)
|
||||
local install_index = find_installed_mod(mod.name)
|
||||
mod = global.mods[install_index]
|
||||
mod.global = new_global
|
||||
--local mod_global = global.mod_global[mod_name]
|
||||
--for k, v in pairs(mod_global) do
|
||||
-- mod_global[k] = nil
|
||||
--end
|
||||
|
||||
reset_mod_events(loaded_index)
|
||||
end
|
||||
@@ -833,15 +829,38 @@ reset_mod_events = function(loaded_index)
|
||||
end
|
||||
end
|
||||
|
||||
local wrap_table
|
||||
wrap_table = function(t, path)
|
||||
local mt = {
|
||||
__index = function(_, k)
|
||||
local v = rawget(t, k)
|
||||
if type(v) == 'table' then
|
||||
if not v.__self then
|
||||
local p = (path or 'global') .. '[' .. tostring(k) .. ']'
|
||||
return wrap_table(v, p)
|
||||
end
|
||||
end
|
||||
return v
|
||||
end,
|
||||
__newindex = function(_, k, v)
|
||||
local k_type = type(k)
|
||||
local wrap = ((k_type == 'string') and '"') or ''
|
||||
local p = (path or 'global') .. '[' .. wrap .. tostring(k) .. wrap .. ']'
|
||||
debug_log('WARN: Assignment to global table when none was expected: '.. p, nil, 3)
|
||||
rawset(t, k, v)
|
||||
end
|
||||
}
|
||||
return setmetatable({}, mt)
|
||||
end
|
||||
|
||||
|
||||
run_mod = function(loaded_index)
|
||||
local mod = loaded_mods[loaded_index]
|
||||
if mod then
|
||||
local mod_name = mod.name
|
||||
local old_global = table.deepcopy(mod.env.global)
|
||||
|
||||
if strict_mode then
|
||||
--mod.env.global = {}
|
||||
end
|
||||
local old_global = mod.env.global
|
||||
mod.env.global = wrap_table(old_global)
|
||||
|
||||
debug_log({'hotpatch-info.running'}, mod_name)
|
||||
|
||||
local success, result = xpcall(mod.code, debug.traceback)
|
||||
@@ -856,26 +875,8 @@ run_mod = function(loaded_index)
|
||||
return false
|
||||
end
|
||||
|
||||
if strict_mode then
|
||||
--if mod.env.global ~= {} then
|
||||
--TODO: error, mod touched global inappropriately during load
|
||||
--end
|
||||
--mod.env.global = old_global
|
||||
|
||||
end
|
||||
|
||||
-- if a mod is being loaded that already has global data, it must be restored after
|
||||
-- running control.lua, discarding any changes made
|
||||
if not table.compare(old_global, {}) then
|
||||
for k,_ in pairs(mod.env.global) do
|
||||
mod.env.global[k] = nil
|
||||
end
|
||||
for k,v in pairs(old_global) do
|
||||
mod.env.global[k] = v
|
||||
end
|
||||
--mod.env.global = old_global
|
||||
end
|
||||
old_global = nil
|
||||
mod.env.global = old_global
|
||||
old_global = nil
|
||||
|
||||
mod.running = true
|
||||
debug_log({'hotpatch-info.complete', {'hotpatch-info.running'}}, mod_name)
|
||||
@@ -1001,7 +1002,7 @@ mod_on_init = function(loaded_index)
|
||||
if mod.on_init then
|
||||
local success, result = xpcall(mod.on_init, debug.traceback)
|
||||
if not success then
|
||||
debug_log({'hotpatch-error.on-init-failed'}, mod_name)
|
||||
debug_log({'hotpatch-error.on-init-failed'}, mod_name)
|
||||
debug_log(result, mod_name)
|
||||
return false
|
||||
end
|
||||
@@ -1018,13 +1019,16 @@ mod_on_load = function(loaded_index)
|
||||
if mod then
|
||||
debug_log({'hotpatch-trace.mod-on-load'}, mod.name)
|
||||
if mod.on_load then
|
||||
--local old_global = table.deepcopy(mod.env.global)
|
||||
local old_global = mod.env.global
|
||||
mod.env.global = wrap_table(table.deepcopy(old_global))
|
||||
local success, result = xpcall(mod.on_load, debug.traceback)
|
||||
if not success then
|
||||
debug_log({'hotpatch-error.on-load-failed'}, mod_name)
|
||||
debug_log(result, mod_name)
|
||||
return false
|
||||
end
|
||||
mod.env.global = old_global
|
||||
|
||||
end
|
||||
register_mod_events(loaded_index)
|
||||
return true
|
||||
@@ -1040,7 +1044,7 @@ mod_on_configuration_changed = function(loaded_index, config)
|
||||
if mod.on_configuration_changed then
|
||||
local success, result = xpcall(mod.on_configuration_changed, debug.traceback, config)
|
||||
if not success then
|
||||
debug_log({'hotpatch-error.on-configuration-changed-failed'}, mod_name)
|
||||
debug_log({'hotpatch-error.on-configuration-changed-failed'}, mod_name)
|
||||
debug_log(result, mod_name)
|
||||
return false
|
||||
end
|
||||
@@ -1189,21 +1193,27 @@ on_load = function()
|
||||
end
|
||||
|
||||
-- run mods which loaded successfully
|
||||
local failed_mods = {}
|
||||
local failed_mods = {}
|
||||
--log('before')
|
||||
--log(serpent.block(global))
|
||||
|
||||
for i = 1, #loaded_mods do
|
||||
if run_mod(i) then
|
||||
if not mod_on_load(i) then
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
else
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
if not mod_on_load(i) then
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
else
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
end
|
||||
|
||||
--log('after')
|
||||
--log(serpent.block(global))
|
||||
|
||||
-- unload mods which failed to run
|
||||
for i = 1, #failed_mods do
|
||||
unload_mod(failed_mods[i])
|
||||
end
|
||||
|
||||
-- unload mods which failed to run
|
||||
for i = 1, #failed_mods do
|
||||
unload_mod(failed_mods[i])
|
||||
end
|
||||
|
||||
debug_log({'hotpatch-info.complete', {'hotpatch-info.loading-installed-mods'}})
|
||||
debug_log({'hotpatch-info.complete', {'hotpatch-info.on-load'}})
|
||||
@@ -1211,16 +1221,16 @@ end
|
||||
|
||||
on_configuration_changed = function(config)
|
||||
debug_log({'hotpatch-info.on-configuration-changed'})
|
||||
local failed_mods = {}
|
||||
local failed_mods = {}
|
||||
for i = 1, #loaded_mods do
|
||||
if not mod_on_configuration_changed(i, config) then
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
table.insert(failed_mods, i)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #failed_mods do
|
||||
unload_mod(failed_mods[i])
|
||||
end
|
||||
for i = 1, #failed_mods do
|
||||
unload_mod(failed_mods[i])
|
||||
end
|
||||
debug_log({'hotpatch-info.complete', {'hotpatch-info.on-configuration-changed'}})
|
||||
end
|
||||
|
||||
@@ -1324,7 +1334,7 @@ local mod_tools = setmetatable({
|
||||
static_mod = static_mod, -- (name, version, code, files)
|
||||
set_debug_level = function(level)
|
||||
--debug_level = tonumber(_ENV.debug_settings.level) or debug_levels[_ENV.debug_settings.level]
|
||||
debug_level = tonumber(level) or debug_levels[level]
|
||||
debug_level = tonumber(level) or debug_levels[level]
|
||||
end,
|
||||
},{
|
||||
__index = mod_tools_internal,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
local hotpatch_tools = require 'hotpatch.mod-tools'
|
||||
hotpatch_tools.static_mod('hotpatch-remote-interface', '1.0.2', [===[
|
||||
hotpatch_tools.static_mod('hotpatch-remote-interface', '1.0.3', [===[
|
||||
--[[
|
||||
|
||||
Copyright 2018 Chrisgbk
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Hierdie is Factorio vryspel. Jou taak is om 'n vuurpyl in die buitenste ruimte in te stuur. Doen dit deur die bou van 'n Vuurpyl Silo en van daar af 'n vuurpyl te lanseer met a satelliet aan boord. Jy sal baie gevorderde tegnologiëe moet navors om die Vuurpyl Silo te ontsluit. Begin klein, werk jou pad op met outomatisering en moenie vergeet om jouself te beskerm teen die inboorlinge nie.
|
||||
|
||||
msg-intro=Hierdie is Factorio vryspel. Jou taak is om 'n vuurpyl in die ruimte in te stuur. Doen dit deur 'n vuurpyl te bou en hom te lanseer. Jy sal baie gevorderde tegnologiëe moet navors om die Vuurpyl Silo te ontsluit. Begin klein, werk jou pad op met outomatisering en moenie vergeet om jouself te beskerm teen die inboorlinge nie.
|
||||
scenario-name=Vryspel
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=.نييلصألا ناكسلا نم كسفن ةيامح ىسنت الو ةياهنلا ىلإ لصتل كتقيرطب لمعأو ،رفصلا نم أدبتس .خيراوصلا عماوص حتف لجأ نم ةمدقتم ايجولونكت نع ثحبلل جاتحت فوس .ةيعانصلا رامقألاب خيراوصلا قالطإو خيراوصلا عماوص ءانب قيرط نع كلذب مايقلا عيطتست .ءاضفلل اخوراص قلطت نا يه كتمهم .Factorio ـل رحلا بعللا وه اذه
|
||||
|
||||
msg-intro=.بكوكلا اذه ناكس م كسفن يمحت نا ىسنت الو ,ةيكيتاموتوألا ربع كقيرط ينبتسو ,رفصلا نم أدبتس .كلت خيراوصلا قالطإ ةطحم حتف عيطتست فيكل ةروطتملا ايجولونكتلل ثاحبأب مايقلا ىلإ جاتحتس .اهنم خوراص قالطإ مث خيراوص قالطإ ةطحم ءانب قيرط نع كلذب مايقلا كنكمي ,ءاضفلا ىلإ اخوراص قلطت نا يه كتمهم ,factorioـل رحلا بعللا روط وه اذه
|
||||
scenario-name=رحلا بعللا
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Гэта рэжым свабоднай гульні Factorio. Вашая мэта - запусціць ракету ў космас. Дзеля гэтага вам трэба пабудаваць ракетную шахту і саму ракету з спадарожнікам. Вы мусіце даследваць перадавыя тэхналогіі, каб навучыцца будаваць ракетную шахту. Пачынайце з малога, працуйце над аўтаматызацыяй вытворчасці, даследуйце новыя тэхналогіі і не забывайцеся на абарону ад "тутэйшых"!
|
||||
|
||||
msg-intro=Гэта рэжым свабоднай гульні Factorio. Вашая мэта - запусціць ракету ў космас. Дзеля гэтага вам трэба пабудаваць ракетную шахту і саму ракету са спадарожнікам. Вы мусіце даследаваць перадавыя тэхналогіі, каб навучыцца будаваць ракетную шахту. Пачынайце з малога, працуйце над аўтаматызацыяй вытворчасці, даследуйце новыя тэхналогіі і не забывайцеся на абарону ад «тутэйшых».
|
||||
scenario-name=Свабодная гульня
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Това е режимът на свободна игра. Задачата ви е да изстреляте ракета в космоса. За това ще е нужно да построите ракетна шахта и изстреляте ракетата със спътник. Ще трябва да проучите развити технологии, които ще отключат ракетната шахта. Започнете с малко и постепенно автоматизирайте процеса, като не забравяте да се защитавате от местните форми на живот.
|
||||
|
||||
scenario-name=Свободна игра
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Aquest és el mode lliure del Factorio. La vostra tasca és construir un coet i enviar-lo a l'espai. Ho haureu de fer construint un centre de coets espacials i llençant un coet amb un satèl·lit. Necessitareu investigar tecnologies avançades per poder-ho fer. Aneu pas a pas, avanceu cap a l'automatització i no oblideu protegir-vos dels natius.
|
||||
|
||||
msg-intro=Aquest és el mode lliure del Factorio. La vostra tasca és construir un coet i enviar-lo a l'espai. Ho haureu de fer construint un centre de coets espacials i llençant un coet. Necessitareu investigar tecnologies avançades per poder-ho fer. Aneu pas a pas, avanceu cap a l'automatització i no oblideu protegir-vos dels natius.
|
||||
scenario-name=Mode lliure
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Toto je volná hra Factorio. Tvým úkolem je vypustit raketu do vesmíru. Postav raketové silo a vypusť raketu se satelitem. Pro odemknutí raketového sila budeš muset vyzkoumat pokročilé technologie. Začni v malém, postupuj s automatizací a nezapomeň se chránit před domorodci.
|
||||
|
||||
msg-intro=Toto je volná hra Factorio. Tvým úkolem je vypustit raketu do vesmíru. Postav raketové silo a vypusť raketu. Pro odemknutí raketového sila budeš muset vyzkoumat pokročilé technologie. Začni v malém, postupuj s automatizací a nezapomeň se chránit před domorodci.
|
||||
scenario-name=Volná hra
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Det her er Factorio fri leg. Din opgave er at affyre en raket for at udforske rummet. For at gøre det skal du bygge en raket silo og affyre en raket med en satellit i. Du bliver nød til at udforske avancerede teknologier for at at kunne bygge en raket silo. Start småt, og arbejd din vej op med hjælp af automatisering, glem ikke at beskytte dig fra de indfødte.
|
||||
|
||||
msg-intro=Dette er Factorio freeplay. Din opgave er at affyre en raket ud i rummet. Gør dette ved at konstruere en raketsilo og affyr en raket. Du bliver nød til at undersøge avancerede teknologier for at låse op for raketsiloen. Start småt og arbejd din vej op med automatisering og glem ikke at beskytte dig selv fra de indfødte.
|
||||
scenario-name=Fri lej
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Dies ist das freie Spiel von Factorio. Deine Aufgabe ist es, eine Rakete in den Weltraum zu starten. Dazu musst Du ein Raketensilo bauen und eine Rakete mit einem Satelliten starten. Du wirst erweiterte Technologien erforschen müssen, um das Raketensilo freizuschalten. Fange klein an, arbeite Dich durch Automatisierung hoch und vergiss nicht, Dich vor den Einheimischen zu schützen.
|
||||
|
||||
msg-intro=Du befindest dich im freien Spiel Modus von Factorio. Ziel ist es, eine Rakete ins Weltall zu schicken. Baue hierzu das Raketensilo um die Rakete starten zu können. Um das Raketensilo freischalten zu können wirst du fortgeschrittene Technologien erforschen müssen. Fange klein an und erforsche die Automation. Vergiss aber nicht, dich vor den Ureinwohner zu schützen.
|
||||
scenario-name=Freies Spiel
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Αυτό είναι το ελεύθερο παιχνίδι του Factorio. Ο στόχος σου είναι να εκτοξεύσεις έναν πύραυλο στο διάστημα. Αυτό το κάνεις κατασκευάζοντας ένα σιλό πυραύλου και εκτοξεύοντας έναν πυραύλο με δορυφόρο. Θα πρέπει να ερευνήσεις προηγμένες τεχνολογίες για να ξεκλειδώσεις το σιλό πυραύλου. Ξεκινάς περιορισμένος, δούλεψε με αυτοματισμούς για την άνοδο σου και μην ξεχνάς να προστατέψεις τον εαυτό σου από τους ντόπιους.
|
||||
|
||||
msg-intro=Αυτό είναι το ελεύθερο παιχνίδι του Factorio. Ο στόχος σας είναι να εκτοξεύσετε έναν πύραυλο στο διάστημα. Αυτό το κάνετε κατασκευάζοντας ένα σιλό πυραύλου και εκτοξεύοντας έναν πύραυλο. Θα πρέπει να ερευνήσετε προηγμένες τεχνολογίες για να ξεκλειδώσετε το σιλό πυραύλου. Ξεκινήστε χαμηλά, φτάστε ψηλά χρησιμοποιώντας αυτοματισμούς και μην ξεχάσετε να προστατέψετε τον εαυτό σας από τους ντόπιους.
|
||||
scenario-name=Ελεύθερο παιχνίδι
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
msg-intro=This is the Factorio freeplay. Your task is to launch a rocket into space. Do this by constructing a Rocket Silo and launching a rocket with a satellite. You will need to research advanced technologies in order to unlock the Rocket Silo. Start small, work your way up with automation and don't forget to protect yourself from the natives.
|
||||
|
||||
msg-intro=This is the Factorio freeplay. Your task is to launch a rocket into space. Do this by constructing a rocket silo and launching a rocket. You will need to research advanced technologies in order to unlock the rocket silo. Start small, work your way up with automation and don't forget to protect yourself from the natives.
|
||||
scenario-name=Freeplay
|
||||
@@ -1,3 +1,2 @@
|
||||
msg-intro=Tio estas la Factorio Libera Ludado. Via tasko estas ĵeti raketon en spacon. Fari tiun per konstruanta Raketo Silo kaj ĵeti raketon kun satelito. Vi bezonos esplori altnivelaj teknologioj por malŝlosi la Raketo Silo. Start malgranda, labori vian vojon supren per aŭtomatigo kaj ne forgesu protekti vin mem de la indiĝenoj.
|
||||
|
||||
scenario-name=Libera Ludado
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Este es el modo de juego libre de Factorio. Tu tarea es la de lanzar un cohete al espacio. Logra esto construyendo un Silo de Cohetes y lanzando un cohete con un satélite. Necesitarás investigar tecnologías avanzadas para poder desbloquear el Silo de Cohetes. Comienza poco a poco, trabaja en tu sistema de automatización y no te olvides de protegerte de los nativos.
|
||||
|
||||
scenario-name=Modo libre
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
msg-intro=See on Factorio vabamäng. Sinu eesmärgiks on saata rakett avakosmosesse. Seda saad teha ehitades Raketi Silotorni ja lennutades Raketti koos satelliidiga. Selleks pead õppima selgeks keerukad tehnoloogiaharud, et Raketi Silotorni lahti lukustada. Alusta väikselt ning liigu edasi automatiseerimise abil ja ära unusta ennast kaitsta kohalike eluvormide eest.
|
||||
|
||||
scenario-name=Vabamäng
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Tämä on Factorion vapaapelimuoto. Tavoitteesi on lähettää satelliitti ulkoavaruuteen rakentamalla rakettisiilo ja laukaisemalla raketti. Rakettisiilon voit rakentaa vasta, kun olet kehittänyt siihen tarvittavat huipputeknologiat. Aloita pienestä ja automatisoi kaikki tarvittava, äläkä unohda suojella itseäsi alkuasukkailta.
|
||||
|
||||
scenario-name=Vapaapeli
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Il s'agit du mode de jeu libre de Factorio. Votre tâche consiste à lancer une fusée dans l'espace. Ceci en construisant un silo à fusée, et en lançant un satellite avec la fusée. Vous aurez besoin de rechercher des technologies de pointe afin de débloquer le silo. Commencez petit, évoluez en automatisant et n'oubliez pas de vous protéger contre les autochtones.
|
||||
|
||||
msg-intro=Il s'agit du mode de jeu libre de Factorio. Votre tâche consiste à lancer une fusée dans l'espace. Faites ceci en construisant une fusée et en la lançant dans l'espace. Vous aurez besoin de rechercher des technologies de pointe afin de débloquer le silo. Commencez petit, évoluez en automatisant et n'oubliez pas de vous protéger contre les autochtones.
|
||||
scenario-name=Jeu libre
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=.םיימוקמה םירוציה ינפמ םכמצע לע ןגהל וחכשת לאו ,יטמוטוא ןוכימ תועצמאב םכלש הישעתה תא וביחרה ,ןטקב וליחתה .ןקתמה תיינב תא רשפאל ידכ תומדקתמ תויגולונכט רוקחל םכילע היהי .ןייוול ליכמה ליט ותועצמאב רגשלו םיליט רוגיש ןקתמ תונבל םכילע .ללחל ליט רגשל איה םכתמישמ .ויִרֹוטְקַפ לש ישפוחה שיחרתה והז
|
||||
|
||||
msg-intro=.םיימוקמה םירוציה ינפמ םכמצע לע ןגהל וחכשת לאו ,יטמוטוא ןוכימ תועצמאב םכלש הישעתה תא וביחרה ,ןטקב וליחתה .ןקתמה תיינב תא רשפאל ידכ תומדקתמ תויגולונכט רוקחל םכילע היהי .ליט ותועצמאב רגשלו םיליט רוגיש ןקתמ תונבל םכילע .ללחל ליט רגשל איה םכתמישמ .ויִרֹוטְקַפ לש ישפוחה שיחרתה והז
|
||||
scenario-name=ישפוח קחשמ
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Ez a Factorio szabad játék módja. A feladatod az, hogy kilőj egy rakétát az űr felderítésére. Ehhez egy Rakétasilót kell építened, melyből aztán egy műholddal felszerelt rakétát kell kilőnöd. A Rakétasiló kifejlesztéséhez különféle technológiákat kell fejlesztened. Kezdj kicsiben és küzdd fel magad az automatizálásban. Ne felejts el védekezni a honos fajok ellen.
|
||||
|
||||
scenario-name=Szabad játék
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Questa è la modalità libera di Factorio. Il tuo obiettivo è quello di lanciare il razzo nello spazio. Completalo costruendo un Silo per Razzi e lanciando il razzo con il satellite. Avrai bisogno di ricercare tecnologie avanzate per riuscire a sbloccare il Silo per Razzi. Inizia in piccolo, espandi con l'automazione e non dimenticare di proteggerti dai nativi.
|
||||
|
||||
msg-intro=Questa è la modalità libera di Factorio. Il tuo obiettivo è quello di lanciare un razzo nello spazio e per farlo dovrai costruire un silo per razzi. Avrai bisogno di fare ricerche avanzate per sbloccare il silo. Inizia con poco, lavora sull'automazione e non dimenticarti di difenderti dai nativi.
|
||||
scenario-name=Gioco libero
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=これは、Factorioのフリープレイです。ロケットサイロを建造し、衛星を載せたロケットを宇宙へ打ち上げるのがあなたの任務です。ロケットサイロをアンロックするためには高度な技術研究をする必要があります。小規模に始め、自動化しつつ進み、原住生物から身を守ることも忘れないでください。
|
||||
|
||||
msg-intro=これは、Factorioのフリープレイです。ロケットサイロを建造し、ロケットを宇宙へ打ち上げるのがあなたの任務です。ロケットサイロの建造を行うためには高度な技術研究を行う必要があります。小規模に始め、自動化しつつ進み、原住生物から身を守ることも忘れないでください。
|
||||
scenario-name=フリープレイ
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Factorio 자유플레이입니다. 당신의 목표는 우주로 로켓을 발사하는 것입니다. 로켓 격납고를 건설하고 인공위성을 탑재한 로켓을 발사하여 이 목표를 달성하세요. 로켓 격납고의 잠금을 해제하기 위해 고급 기술들을 연구할 필요가 있습니다. 작은 것부터 시작해 자동화로 차근차근 발전하고, 토착 생물로부터 자신을 보호하는 것도 잊지 마세요.
|
||||
|
||||
msg-intro=Factorio 자유플레이입니다. 당신의 목표는 우주로 로켓을 발사하는 것입니다. 로켓 격납고를 건설하고 로켓을 발사하여 이 목표를 달성하세요. 로켓 격납고의 잠금을 해제하기 위해 고급 기술들을 연구할 필요가 있습니다. 작은 것부터 시작해 자동화로 차근차근 발전하고, 토착 생물로부터 자신을 보호하는 것도 잊지 마세요.
|
||||
scenario-name=자유플레이
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Tai Factorio laisvas žaidimas. Jūsų užduotis - paleisti raketą į kosmosą. Tam reikia pastatyti Raketos šachtą ir paleisti raketą su palydovu. Reikės atrasti pažangias technologijas kad atrakintumėte Raketos šachtą. Pradėkite palengva, tobulinkite automatiką ir neužmirškite apsisaugoti nuo vietinių.
|
||||
|
||||
scenario-name=Žaisti laisva forma
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
msg-intro=Šis ir Factorio brīvais režīms. Tavs uzdevums ir palaist raķeti kosmosā. Lai to izdarītu, tev vajadzēs uzbūvēt palaišanas platformu un palaist raķeti ar satelītu. Tev būs jāizpēta sarežģītas tehnoloģijas, lai atbloķētu palaišanas platformu. Sāc no mazumiņa, strādā un neaizmirsti pasargāt sevi no šīs planētas iemītniekiem...
|
||||
scenario-name=Brīvspēle
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Dit is Factorio vrij spel. Je taak is om een raket te lanceren in de ruimte. Bouw hiervoor een raket-silo en lanceer een raket met een satelliet. Je zult geavanceerde technologieën moeten onderzoeken om de raket-silo vrij te spelen. Begin op kleine schaal, breidt uit en automatiseer je fabriek en vergeet niet jezelf te beschermen tegen de inheemse bewoners.
|
||||
|
||||
msg-intro=Dit is Factorio vrij spel. Je taak is om een raket te lanceren in de ruimte. Bouw hiervoor een raket-silo en lanceer een raket. Je zult geavanceerde technologieën moeten onderzoeken om de raket-silo vrij te spelen. Begin op kleine schaal, breidt uit en automatiseer je fabriek en vergeet niet jezelf te beschermen tegen de inheemse bewoners.
|
||||
scenario-name=Freeplay
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Dette er Factorio frispilling. Din oppgave er å sende en rakett til verdensrommet. Bygg en rakettsilo og send opp en rakett med en satellitt. Du blir nødt til å utforske avanserte teknologier for å få tilgang til rakettsiloen. Begynn i det små, jobb deg oppover med hjelp av automatisering, men ikke glem å beskytte deg mot de innfødte.
|
||||
|
||||
msg-intro=Dette er Factorio freeplay. Din oppgave er å sende en rakett til verdensrommet. Bygg en rakettsilo og send opp en rakett med en satellitt. Du blir nødt til å utforske avanserte teknologier for å få tilgang til rakettsiloen. Begynn i det små, jobb deg oppover med hjelp av automatisering, men ikke glem å beskytte deg mot de innfødte.
|
||||
scenario-name=Frittspill
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=To jest swobodna rozgrywka Factorio. Twoim zadaniem jest wysłanie rakiety w kosmos. Skonstruuj silos rakietowy i wystrzel z niego rakietę z satelitą. Żeby odblokować silos rakietowy będziesz musiał odkryć zaawansowane technologie. Rozpocznij od podstaw, rozwijaj się wykorzystując automatyzację i nie zapomnij o obronie przed rodzimymi mieszkańcami planety.
|
||||
|
||||
msg-intro=Oto swobodna rozgrywka Factorio. Twoim zadaniem jest wysłanie rakiety w kosmos. Dokonaj tego przez skonstruowanie silosu rakietowego i wystrzelenie rakiety. Żeby odblokować silos rakietowy będziesz musiał odkryć zaawansowane technologie. Rozpocznij od podstaw, rozwijaj się wykorzystując automatyzację i nie zapomnij o obronie przed rodzimymi mieszkańcami planety.
|
||||
scenario-name=Gra swobodna
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Este é o modo livre do Factorio. Sua missão é lançar um foguete no espaço. Para fazer isso construa um centro de lançamento de foguetes e lance o foguete com um satélite. Você vai precisar pesquisar tecnologias avançadas para conseguir desbloquear um Centro de lançamento. Comece do começo, continue na automação e não se esqueça de se proteger dos nativos.
|
||||
|
||||
msg-intro=Este é o modo livre de Factorio. Sua tarefa é lançar um foguete no espaço. Faça isso construindo um silo de foguetes e lançando um foguete. Você precisará pesquisar tecnologias avançadas para desbloquear o silo de foguetes. Comece pequeno, trabalhe seu caminho com automação e não se esqueça de se proteger dos nativos.
|
||||
scenario-name=Jogo livre
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Este é o jogo livre Factorio. A tua tarefa é lançar um foguetão para explorar o espaço. Faça isto, construindo um Silo de Foguetão e lançar um foguetão com um satélite. Tu vais precisar de pesquisar tecnologias avançadas para poderes desbloquear o Silo de Foguetão. Começa simples, trabalha e evolui com a automação e não te esqueças de proteger-te dos nativos.
|
||||
|
||||
msg-intro=Este é o modo livre do Factorio. O teu objetivo é lançar um foguetão para o espaço. Faz isto, contruindo um silo de foguetão e lançando-o.\nVais precisar de pesquisar tecnologias avançadas para desploquear o silo de foguetão. Começa simples, e trabalha e evolui com automação mas não te esqueças de proteger do nativos.
|
||||
scenario-name=Modo Livre
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Acesta este modul Factorio freeplay. Sarcina ta este de a lansa o rachetă în spațiu. Fă acest lucru prin construirea unui siloz de rachete și lansarea unei rachete cu satelit. Va trebui să cercetezi tehnologiile avansate pentru a debloca silozul de rachete. Începe încet, avansează cu automatizarea și nu uita să te protejezi de inamici.
|
||||
|
||||
msg-intro=Acesta este modul Factorio freeplay. Sarcina ta este de a lansa o racheta în spațiu. Fă acest lucru prin construirea unui siloz de rachete și lansarea unei rachete. Va trebui să cercetezi tehnologiile avansate pentru a debloca silozul de rachete. Începe mic, avansează cu automatizarea și nu uita să te protejezi de nativi.
|
||||
scenario-name=Freeplay
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Это режим свободной игры Factorio. Ваша задача запустить спутник в космос. Для его запуска потребуется создать ракету и ракетную шахту. Начните с малого, работайте над автоматизацией производства, исследуйте новые технологии и не забывайте о защите от местных форм жизни.
|
||||
|
||||
msg-intro=Это режим свободной игры Factorio. Ваша задача - запустить ракету в космос. Для её запуска потребуется исследовать новые технологии и создать ракетную шахту. Начните с малого, работайте над автоматизацией производства и не забывайте о защите от местных форм жизни.
|
||||
scenario-name=Свободная игра
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Toto je voľná hra Factorio. Tvojou úlohou je vypustiť raketu do vesmíru. Postav raketové silo a vypusti raketu so satelitom. Pre odomknutie raketového sila budeš musieť vyskúmať pokročilé technológie. Začni v malom, postupuj s automatizáciou a nezabnudni sa chrániť pred domácimi.
|
||||
|
||||
msg-intro=Toto je voľná hra Factorio. Tvojou úlohou je vypustiť raketu do vesmíru. Postav raketové silo a vypusti raketu. Pre odomknutie raketového sila budeš musieť vyskúmať pokročilé technológie. Začni v malom, postupuj s automatizáciou a nezabnudni sa chrániť pred domácimi.
|
||||
scenario-name=Voľná hra
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=To je Factorio freeplay. Vaša naloga je poslati raketo v vesolje. To storite tako, da zgradite silos za raketo in jo skupaj s satelitom izstrelite v vesolje. Raziskati bo potrebno napredne tehnologije, da bi lahko odklenili Raketni silos. Začnete z majhnim, nato nadgradite vse s pomočjo avtomatizacije in ne pozabite, zaščitite se pred domorodci.
|
||||
|
||||
scenario-name=Prosta igra
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Det här är Factorio frispel. Ditt uppdrag är att avfyra en raket ut i rymden. Gör det genom att tillverka en raket silo och avfyra en raket med en satellit. Du kommer att behöva forska fram avancerade teknologier för att kunna låsa upp raket silon. Börja smått, jobba dig uppåt med hjälp av automatisering och glöm inte att försvara dig själv mot infödingarna.
|
||||
|
||||
scenario-name=Frispel
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
msg-intro=นี้คือโหมดอิสระของ Factorio ภารกิจของคุณคือส่งจรวดสู่อวกาศ ซึ่งทำได้โดยการสร้างไซโลจรวดและส่งจรวดขึ้นไปพร้อมกับดาวเทียม คุณต้องวิจัยค้นคว้าเทคโนโลยีขั้นสูงเพื่อปลดล็อคไซโลจรวด เริ่มจากเล็กๆไปเรื่อยๆด้วยระบบอัตโนมัติและอย่าลืมป้องกันตัวเองจากคนพื้นเมืองด้วย
|
||||
|
||||
scenario-name=โหมดอิสระ
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Bu Factorio'nun serbest oynanışı. Görevin roketleri uzaya yollamak. Bunu Roket Silosu yaparak ve uyduyla roket yollayarak yapacaksın. Roket Silo'sunu açmak için gelişmiş teknolojileri sırayla açmalısın. Yavaş başla, otomatikleş ve yerlilerden kendini korumayı untuma.
|
||||
|
||||
msg-intro=Bu Factorio'nun serbest oynanışıdır. Göreviniz uzaya bir roket göndermek. Bunu roket rampası inşa edip ve Roket fırlatarak yapınız. Roket rampasını açmak için gelişmiş teknolojileri araştırmanız gerekecek. Ufaktan başlayın ve otomasyonla büyüyün, ve yerlilerden kendiniz korumayı unutmayın.
|
||||
scenario-name=Serbest Oyun
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Це один з типів гри в Factorio - "Вільна гра". Ваше завдання - запустити ракету у космічний простір. Зробіть це, побудувавши ракетну шахту та запустивши ракету зі супутником. Вам потрібно дослідити передові технології, щоб мати змогу побудувати ракетну шахту. Починайте з малого, розвивайтеся за допомогою автоматизації, але не забувайте і про оборону!
|
||||
|
||||
msg-intro=Це один з типів гри в Factorio — "Вільна гра". Ваше завдання — запустити ракету у космічний простір. Зробіть це, побудувавши ракетну шахту та запустивши ракету. Вам потрібно дослідити передові технології, щоб мати змогу побудувати ракетну шахту. Починайте з малого, розвивайтеся за допомогою автоматизації, але не забувайте і про оборону.
|
||||
scenario-name=Вільна гра
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=Đầy là chế độ freeplay của Factorio. Nhiệm cụ của bạn là phóng một chiếc tên lửa lên vũ trụ. Hãy xây dựng một bệ phóng tên lửa để phóng một chiếc tên lửa kèm theo vệ tinh. Bạn sẽ cần phải nghiên cứu nhiều công nghệ tiên tiến để có thể xây dựng bệ phóng tên lửa. Bắt đầu từ quy mô nhỏ, phát triển dần dần bằng tự động hóa, và đừng quên bảo vệ bản thân mình khỏi những sinh vật sinh sống ở đây.
|
||||
|
||||
msg-intro=Đầy là chế độ chơi tự do của Factorio. Nhiệm vụ của bạn là phóng một chiếc tên lửa lên vũ trụ bằng cách xây dựng một bệ phóng. Bạn sẽ cần phải nghiên cứu nhiều công nghệ tiên tiến để có thể xây dựng bệ phóng đó. Hãy bắt đầu nhỏ, rồi phát triển từng bước bằng tự động hóa, và đừng quên bảo vệ chính mình khỏi những sinh vật ở đây.
|
||||
scenario-name=Chơi tự do
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=欢迎来到异星工厂标准模式,你的目标是要把火箭发射到太空。为此你需要研发一系列高等科技,解锁并修建火箭发射井,直到把携带有卫星的火箭发射出去。从小作坊生产起步,逐渐建立起属于自己的自动化大工厂吧!同时,别忘了防范异星虫子的骚扰 ...
|
||||
|
||||
msg-intro=欢迎来到异星工厂标准模式,你的目标是要把火箭发射到太空。为此你需要研发一系列高等科技,修建火箭发射井,最后发射火箭。从小作坊生产起步,逐渐建立起属于自己的自动化大工厂吧!同时,别忘了防范异星虫子们的骚扰 ...
|
||||
scenario-name=标准模式
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
msg-intro=這是異星工廠的自由模式。你的任務是發射火箭,探索外太空。請建造一個火箭發射井,並發射攜帶著衛星的火箭。你會需要研究大量的進階科技以解鎖火箭發射井。從頭開始,靠著自動化科技的協助向目標邁進時,也別忘記從外星住民手的中保護你自己啊!
|
||||
|
||||
msg-intro=這是異星工廠的自由模式。你的任務是發射火箭探索外太空。請建造火箭發射井並發射火箭。你會需要研究大量的進階科技以解鎖火箭發射井。從頭開始,靠著自動化的協助邁向目標的同時,也別忘了從外星住民手中保護你自己。
|
||||
scenario-name=自由模式
|
||||
|
||||
@@ -26,10 +26,6 @@ process.on('unhandledRejection', r => console.log(r));
|
||||
// configgy stuff
|
||||
debug = false;
|
||||
|
||||
// reduces loadtime from 1700 ms to 1200 ms on my i3 + SSD
|
||||
// tested by logging Date.now() here and when the webserver started listening
|
||||
require('cache-require-paths');
|
||||
|
||||
// argument parsing
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
|
||||
@@ -40,14 +36,13 @@ const deepmerge = require("deepmerge");
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const crypto = require('crypto');
|
||||
const base64url = require('base64url');
|
||||
const moment = require("moment");
|
||||
const request = require("request")
|
||||
const request = require("request");
|
||||
|
||||
// constants
|
||||
console.log(`Requiring config from ${args.config || './config'}`)
|
||||
console.log(`Requiring config from ${args.config || './config'}`);
|
||||
const config = require(args.config || './config');
|
||||
config.databaseDirectory = args.databaseDirectory || config.databaseDirectory || "./database";
|
||||
const masterModFolder = path.join(config.databaseDirectory, "/masterMods/");
|
||||
@@ -55,12 +50,10 @@ mkdirp.sync(config.databaseDirectory);
|
||||
mkdirp.sync(masterModFolder);
|
||||
|
||||
// homebrew modules
|
||||
const getFactorioLocale = require("./lib/getFactorioLocale");
|
||||
const stringUtils = require("./lib/stringUtils");
|
||||
const fileOps = require("_app/fileOps");
|
||||
const getFactorioLocale = require("lib/getFactorioLocale");
|
||||
|
||||
// homemade express middleware for token auth
|
||||
const authenticate = require("./lib/authenticate")(config);
|
||||
const authenticate = require("lib/authenticate")(config);
|
||||
|
||||
/** Sync */
|
||||
function randomStringAsBase64Url(size) {
|
||||
@@ -68,7 +61,7 @@ function randomStringAsBase64Url(size) {
|
||||
}
|
||||
if (!fs.existsSync("secret-api-token.txt")) {
|
||||
config.masterAuthSecret = randomStringAsBase64Url(256);
|
||||
fs.writeFileSync("config.json",JSON.stringify(config, null, 4));
|
||||
fs.writeFileSync("config.json",JSON.stringify(config, null, 4));
|
||||
fs.writeFileSync("secret-api-token.txt", jwt.sign({ id: "api" }, config.masterAuthSecret, {
|
||||
expiresIn: 86400*365 // expires in 1 year
|
||||
}));
|
||||
@@ -176,18 +169,13 @@ const prometheusUPSGauge = new Prometheus.Gauge({
|
||||
help: 'UPS of the current server',
|
||||
labelNames: ["instanceID", "instanceName"],
|
||||
});
|
||||
const prometheusTickGauge = new Prometheus.Gauge({
|
||||
name: prometheusPrefix+'tick_gauge',
|
||||
help: 'Servers current gametick',
|
||||
labelNames: ["instanceID", "instanceName"],
|
||||
});
|
||||
const prometheusMasterInventoryGauge = new Prometheus.Gauge({
|
||||
name: prometheusPrefix+'master_inventory_gauge',
|
||||
help: 'Amount of items stored on master',
|
||||
labelNames: ["itemName"],
|
||||
});
|
||||
setInterval(()=>{
|
||||
fs.writeFileSync("database/items.json", JSON.stringify(db.items));
|
||||
fs.writeFileSync("database/items.json", JSON.stringify(db.items));
|
||||
},config.autosaveInterval || 60000);
|
||||
/**
|
||||
GET Prometheus metrics endpoint. Returns performance and usage metrics in a prometheus readable format.
|
||||
@@ -198,7 +186,7 @@ GET Prometheus metrics endpoint. Returns performance and usage metrics in a prom
|
||||
app.get('/metrics', (req, res) => {
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
res.set('Content-Type', Prometheus.register.contentType);
|
||||
|
||||
|
||||
/// gather some static metrics
|
||||
registerMoreMetrics();
|
||||
res.end(Prometheus.register.metrics());
|
||||
@@ -248,7 +236,7 @@ const db = {};
|
||||
} catch (e){
|
||||
db.items = {};
|
||||
}
|
||||
})()
|
||||
})();
|
||||
|
||||
db.items.addItem = function(object) {
|
||||
if(object.name == "addItem" || object.name == "removeItem") {
|
||||
@@ -262,7 +250,7 @@ db.items.addItem = function(object) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
db.items.removeItem = function(object) {
|
||||
if(object.name == "addItem" || object.name == "removeItem") {
|
||||
console.error("Fuck you, that would screw everything up if you named your item that.");
|
||||
@@ -275,7 +263,7 @@ db.items.removeItem = function(object) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// store slaves and inventory in a .json full of JSON data
|
||||
process.on('SIGINT', shutdown); // ctrl + c
|
||||
@@ -308,8 +296,8 @@ async function shutdown() {
|
||||
console.log("Clusterio cleanly exited in "+(Date.now()-exitStartTime)+"ms");
|
||||
process.exit(0);
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
console.log("Clusterio failed to exit cleanly. Time elapsed: "+(Date.now()-exitStartTime)+"ms")
|
||||
console.log(e);
|
||||
console.log("Clusterio failed to exit cleanly. Time elapsed: "+(Date.now()-exitStartTime)+"ms");
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
@@ -351,7 +339,7 @@ app.post("/api/editSlaveMeta", authenticate.middleware, function(req,res) {
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
// request.body should be an object
|
||||
// {instanceID, pass, meta:{x,y,z}}
|
||||
|
||||
|
||||
if(req.body && req.body.instanceID && req.body.meta){
|
||||
if(slaves[req.body.instanceID]){
|
||||
if(!slaves[req.body.instanceID].meta) {
|
||||
@@ -360,8 +348,8 @@ app.post("/api/editSlaveMeta", authenticate.middleware, function(req,res) {
|
||||
slaves[req.body.instanceID].meta = deepmerge(slaves[req.body.instanceID].meta, req.body.meta, {clone:true});
|
||||
let metaPortion = JSON.stringify(req.body.meta);
|
||||
if(metaPortion.length > 50) {
|
||||
metaPortion = metaPortion.substring(0,20) + "...";
|
||||
}
|
||||
metaPortion = metaPortion.substring(0,20) + "...";
|
||||
}
|
||||
// console.log("Updating slave "+slaves[req.body.instanceID].instanceName+": " + slaves[req.body.instanceID].mac + " : " + slaves[req.body.instanceID].serverPort+" at " + slaves[req.body.instanceID].publicIP, metaPortion);
|
||||
res.sendStatus(200);
|
||||
} else {
|
||||
@@ -387,7 +375,7 @@ app.post("/api/getSlaveMeta", function (req, res) {
|
||||
}
|
||||
res.send(JSON.stringify(slaves[req.body.instanceID].meta))
|
||||
} else {
|
||||
res.status(404)
|
||||
res.status(404);
|
||||
res.send('{"status": 404, "info": "Slave not registered"}')
|
||||
}
|
||||
} else {
|
||||
@@ -440,8 +428,8 @@ app.post("/api/uploadMod", authenticate.middleware, function(req,res) {
|
||||
});
|
||||
} else {
|
||||
res.send('No files were uploaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
/**
|
||||
Prepare the slaveCache for both /api/slaves calls
|
||||
@@ -480,15 +468,15 @@ POST endpoint for getting the rconPasswords of all our slaves
|
||||
@alias /api/rconPasswords
|
||||
*/
|
||||
app.post("/api/rconPasswords", authenticate.middleware, function(req, res) {
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||
let slaveCache = getSlaves();
|
||||
let validKeys = ['rconPort', 'rconPassword', 'publicIP'];
|
||||
for(key in slaveCache.cache) {
|
||||
slaveCache.cache[key] = {'publicIP': slaveCache.cache[key].publicIP, 'rconPort': slaveCache.cache[key].rconPort, 'rconPassword': slaveCache.cache[key].rconPassword};
|
||||
}
|
||||
res.send(slaveCache.cache);
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||
let slaveCache = getSlaves();
|
||||
let validKeys = ['rconPort', 'rconPassword', 'publicIP'];
|
||||
for(key in slaveCache.cache) {
|
||||
slaveCache.cache[key] = {'publicIP': slaveCache.cache[key].publicIP, 'rconPort': slaveCache.cache[key].rconPort, 'rconPassword': slaveCache.cache[key].rconPassword};
|
||||
}
|
||||
res.send(slaveCache.cache);
|
||||
});
|
||||
/*
|
||||
var recievedItemStatistics = new averagedTimeSeries({
|
||||
@@ -559,20 +547,20 @@ app.post("/api/place", authenticate.middleware, function(req, res) {
|
||||
name:req.body.name,
|
||||
count:req.body.count
|
||||
});
|
||||
|
||||
|
||||
// Attempt confirming
|
||||
res.send("success");
|
||||
} else {
|
||||
res.send("failure");
|
||||
}
|
||||
});
|
||||
const routes_api_remove = require("./routes/api/remove.js")
|
||||
var neuralDole = null
|
||||
const routes_api_remove = require("./routes/api/remove.js");
|
||||
var neuralDole = null;
|
||||
|
||||
if(config.useNeuralNetDoleDivider)//Only initialize neural network when it's enabled, otherwise it might override gauge
|
||||
neuralDole=new routes_api_remove.neuralDole({
|
||||
items: db.items, gaugePrefix: prometheusPrefix
|
||||
})
|
||||
});
|
||||
/**
|
||||
POST endpoint to remove items from DB when client orders items.
|
||||
|
||||
@@ -597,7 +585,7 @@ app.post("/api/remove", authenticate.middleware, function(req, res) {
|
||||
object.instanceName = "unknown";
|
||||
}
|
||||
if(slaves[object.instanceID]) object.instanceName = slaves[object.instanceID].instanceName;
|
||||
let item = db.items[object.name]
|
||||
let item = db.items[object.name];
|
||||
// console.dir(doc);
|
||||
if((item === undefined)
|
||||
||((
|
||||
@@ -613,7 +601,7 @@ app.post("/api/remove", authenticate.middleware, function(req, res) {
|
||||
let numberToRemove = Math.min(Math.abs(Number(object.count)),Number(item));
|
||||
db.items.removeItem({count: numberToRemove, name: object.name});
|
||||
res.send({count: numberToRemove, name: object.name});
|
||||
|
||||
|
||||
// track statistics and do graphing things
|
||||
prometheusImportGauge.labels(object.instanceID, object.name).inc(Number(numberToRemove) || 0);
|
||||
let sentItemStatistics = sentItemStatisticsBySlaveID[object.instanceID];
|
||||
@@ -702,19 +690,19 @@ app.get("/api/inventoryAsObject", function(req, res) {
|
||||
*/
|
||||
|
||||
// wrap a request in an promise
|
||||
async function downloadPage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(url, (error, response, body) => {
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
}
|
||||
async function downloadPage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(url, (error, response, body) => {
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.get("/api/modmeta", async function(req, res) {
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
let modData = await downloadPage("https://mods.factorio.com/api/mods/" + req.query.modname);
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
let modData = await downloadPage("https://mods.factorio.com/api/mods/" + req.query.modname);
|
||||
res.send(modData);
|
||||
});
|
||||
|
||||
@@ -731,36 +719,36 @@ Requires x-access-token header to be set. Find you api token in secret-api-token
|
||||
*/
|
||||
app.post("/api/runCommand", authenticate.middleware, function(req,res) {
|
||||
endpointHitCounter.labels(req.route.path).inc();
|
||||
// validate request.body
|
||||
let body = req.body;
|
||||
// validate request.body
|
||||
let body = req.body;
|
||||
|
||||
if (
|
||||
body.broadcast
|
||||
&& body.command
|
||||
&& typeof body.command == "string"
|
||||
&& body.command[0] == "/")
|
||||
{
|
||||
let instanceResponses = [];
|
||||
for (let instanceID in wsSlaves) {
|
||||
// skip loop if the property is from prototype
|
||||
if (!wsSlaves.hasOwnProperty(instanceID)) continue;
|
||||
wsSlaves[instanceID].runCommand(body.command);
|
||||
}
|
||||
res.status(200).send({auth: true, message: "success", response: "Cluster wide messaging initiated"});
|
||||
} else if (
|
||||
body.instanceID
|
||||
&& wsSlaves[body.instanceID]
|
||||
&& body.command
|
||||
&& typeof body.command == "string"
|
||||
&& body.command[0] == "/")
|
||||
{
|
||||
// execute command
|
||||
wsSlaves[body.instanceID].runCommand(body.command, data => {
|
||||
res.status(200).send({auth: true, message: "success", data});
|
||||
});
|
||||
} else {
|
||||
res.status(400).send({auth: true, message: "Error: invalid request.body"});
|
||||
}
|
||||
if (
|
||||
body.broadcast
|
||||
&& body.command
|
||||
&& typeof body.command == "string"
|
||||
&& body.command[0] == "/")
|
||||
{
|
||||
let instanceResponses = [];
|
||||
for (let instanceID in wsSlaves) {
|
||||
// skip loop if the property is from prototype
|
||||
if (!wsSlaves.hasOwnProperty(instanceID)) continue;
|
||||
wsSlaves[instanceID].runCommand(body.command);
|
||||
}
|
||||
res.status(200).send({auth: true, message: "success", response: "Cluster wide messaging initiated"});
|
||||
} else if (
|
||||
body.instanceID
|
||||
&& wsSlaves[body.instanceID]
|
||||
&& body.command
|
||||
&& typeof body.command == "string"
|
||||
&& body.command[0] == "/")
|
||||
{
|
||||
// execute command
|
||||
wsSlaves[body.instanceID].runCommand(body.command, data => {
|
||||
res.status(200).send({auth: true, message: "success", data});
|
||||
});
|
||||
} else {
|
||||
res.status(400).send({auth: true, message: "Error: invalid request.body"});
|
||||
}
|
||||
});
|
||||
/**
|
||||
GET endpoint. Returns factorio's base locale as a JSON object.
|
||||
@@ -797,16 +785,17 @@ if(args.masterPort || config.masterPort){
|
||||
}
|
||||
|
||||
/* Websockets for remoteMap */
|
||||
var io = require("socket.io")(server);
|
||||
var io = require("socket.io")();
|
||||
const ioMetrics = require("socket.io-prometheus");
|
||||
ioMetrics(io);
|
||||
|
||||
var slaveMappers = {};
|
||||
class slaveMapper {
|
||||
constructor(instanceID, socket) {
|
||||
this.instanceID = instanceID;
|
||||
this.socket = socket;
|
||||
this.lastBeat = Date.now();
|
||||
|
||||
|
||||
this.socket.on("heartbeat", () => {
|
||||
prometheusWsUsageCounter.labels('heartbeat', this.instanceID).inc();
|
||||
// we aren't ready to die yet apparently
|
||||
@@ -821,7 +810,7 @@ class slaveMapper {
|
||||
prometheusWsUsageCounter.labels('sendEntity', this.instanceID).inc();
|
||||
Object.keys(mapRequesters).forEach(requesterName => {
|
||||
let requester = mapRequesters[requesterName];
|
||||
|
||||
|
||||
if(requester.instanceID == this.instanceID){
|
||||
// this mapRequester is listening to this slaveMapper, so we send it updates
|
||||
requester.socket.emit("displayEntity", entity);
|
||||
@@ -830,6 +819,8 @@ class slaveMapper {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var mapRequesters = {};
|
||||
class mapRequester {
|
||||
constructor(requesterID, socket, instanceID){
|
||||
@@ -837,7 +828,7 @@ class mapRequester {
|
||||
this.socket = socket;
|
||||
this.instanceID = instanceID;
|
||||
this.lastBeat = Date.now();
|
||||
|
||||
|
||||
this.socket.on("heartbeat", () => {
|
||||
prometheusWsUsageCounter.labels('heartbeat', "other").inc();
|
||||
// we aren't ready to die yet apparently
|
||||
@@ -869,6 +860,8 @@ class mapRequester {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Websockets for send and recieve combinators */
|
||||
var wsSlaves = {};
|
||||
class wsSlave {
|
||||
@@ -876,7 +869,7 @@ class wsSlave {
|
||||
this.instanceID = instanceID;
|
||||
this.socket = socket;
|
||||
this.lastBeat = Date.now();
|
||||
|
||||
|
||||
this.socket.on("heartbeat", () => {
|
||||
prometheusWsUsageCounter.labels('heartbeat', this.instanceID).inc();
|
||||
this.lastBeat = Date.now();
|
||||
@@ -898,7 +891,7 @@ class wsSlave {
|
||||
delete this.commandsWaitingForReturn[resp.commandID];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.socket.on("gameChat", data => {
|
||||
prometheusWsUsageCounter.labels('gameChat', this.instanceID).inc();
|
||||
if(typeof data === "object"){
|
||||
@@ -909,7 +902,7 @@ class wsSlave {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.socket.on("alert", alert => {
|
||||
prometheusWsUsageCounter.labels('alert', this.instanceID).inc();
|
||||
if(typeof alert === "object"){
|
||||
@@ -927,6 +920,7 @@ class wsSlave {
|
||||
if(commandID) this.commandsWaitingForReturn[commandID] = {callback, timestamp: Date.now()};
|
||||
}
|
||||
}
|
||||
|
||||
io.on('connection', function (socket) {
|
||||
// cleanup dead sockets from disconnected people
|
||||
let terminatedConnections = 0;
|
||||
@@ -941,10 +935,10 @@ io.on('connection', function (socket) {
|
||||
});
|
||||
});
|
||||
if(terminatedConnections > 0) console.log("SOCKET | There are currently "+currentConnections+" websocket connections, deleting "+terminatedConnections+" on timeout");
|
||||
|
||||
|
||||
// tell our friend that we are listening
|
||||
setTimeout(()=>socket.emit('hello', { hello: 'world' }), 5000);
|
||||
|
||||
|
||||
/* initial processing for remoteMap */
|
||||
socket.on('registerSlaveMapper', function (data) {
|
||||
prometheusWsUsageCounter.labels('registerSlaveMapper', "other").inc();
|
||||
@@ -959,7 +953,7 @@ io.on('connection', function (socket) {
|
||||
socket.emit("mapRequesterReady", true);
|
||||
console.log("remoteMap | SOCKET registered map requester for "+data.instanceID);
|
||||
});
|
||||
|
||||
|
||||
/* Websockets for send and recieve combinators */
|
||||
socket.on("registerSlave", function(data) {
|
||||
prometheusWsUsageCounter.labels('registerSlave', "other").inc();
|
||||
@@ -981,17 +975,9 @@ io.on('connection', function (socket) {
|
||||
});
|
||||
|
||||
// handle plugins on the master
|
||||
pluginManagement();
|
||||
masterPlugins = [];
|
||||
async function pluginManagement(){
|
||||
let startPluginLoad = Date.now();
|
||||
masterPlugins = await getPlugins();
|
||||
console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms");
|
||||
return;
|
||||
}
|
||||
|
||||
async function getPlugins(){
|
||||
const pluginManager = require("./lib/manager/pluginManager.js")(config);
|
||||
let startPluginLoad = Date.now();
|
||||
const pluginManager = require("lib/manager/pluginManager.js")(config);
|
||||
let plugins = [];
|
||||
let pluginsToLoad = await pluginManager.getPlugins();
|
||||
for(let i = 0; i < pluginsToLoad.length; i++){
|
||||
@@ -1012,7 +998,7 @@ async function getPlugins(){
|
||||
}),
|
||||
pluginConfig,
|
||||
});
|
||||
|
||||
|
||||
console.log("Loaded plugin "+pluginConfig.name+" in "+(Date.now() - pluginStartedLoading)+"ms");
|
||||
}
|
||||
}
|
||||
@@ -1021,8 +1007,15 @@ async function getPlugins(){
|
||||
let plugin = plugins[i];
|
||||
if(plugin.main.onLoadFinish && typeof plugin.main.onLoadFinish == "function") await plugin.main.onLoadFinish({plugins});
|
||||
}
|
||||
console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms");
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
||||
masterPlugins = await getPlugins();
|
||||
|
||||
|
||||
io.attach(server);
|
||||
|
||||
module.exports = app;
|
||||
})();
|
||||
+11
-22
@@ -5,15 +5,11 @@
|
||||
"repository": "https://github.com/Danielv123/factorioClusterio",
|
||||
"main": "master.js",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{{,node_modules/_app/**/},!(node_modules)/**/}*.spec.js\" -R spec || echo .",
|
||||
"test": "mocha -R spec -- \"./{,!(node_modules)/**/}*.spec.js\" || true",
|
||||
"cover": "nyc --reporter=lcovonly npm run-script test && codecov -f coverage/lcov.info -t f7b38085-36eb-4d72-84e6-64721910171e",
|
||||
"start": "echo Run node client.js start [instance] to start slaves && node master.js",
|
||||
"cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{{,node_modules/_app/**/},!(node_modules)/**/}*.spec.js\" -R spec || echo . && codecov -t f7b38085-36eb-4d72-84e6-64721910171e",
|
||||
"docs": "documentation build index.js --config documentation.yml -f html -o docs",
|
||||
"livedocs": "documentation serve --watch *.js lib/** --config documentation.yml",
|
||||
"install": "node lib/npmPostinstall.js",
|
||||
"bundle-dependencies": "bundle-dependencies",
|
||||
"snyk-protect": "snyk protect",
|
||||
"prepublish": "npm run snyk-protect"
|
||||
"bundle-dependencies": "bundle-dependencies"
|
||||
},
|
||||
"keywords": [
|
||||
"factorio"
|
||||
@@ -21,15 +17,12 @@
|
||||
"author": "Danielv123",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"app-root-path": "^2.0.1",
|
||||
"as-table": "^1.0.31",
|
||||
"averaged-timeseries": "^1.4.0",
|
||||
"base64url": "^3.0.0",
|
||||
"bcrypt": "^2.0.1",
|
||||
"bcrypt-promise": "^2.0.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.18.2",
|
||||
"cache-require-paths": "^0.3.0",
|
||||
"cli": "^1.0.1",
|
||||
"cli-interact": "^0.1.9",
|
||||
"codecov": "^3.0.1",
|
||||
@@ -43,10 +36,9 @@
|
||||
"follow-redirects": "^1.5.0",
|
||||
"fs-extra": "^6.0.1",
|
||||
"getmac": "^1.4.0",
|
||||
"hash-files": "^1.1.1",
|
||||
"html5-validator": "^1.0.7",
|
||||
"ini": "^1.3.5",
|
||||
"jsonwebtoken": "^8.2.1",
|
||||
"lib": "file:./lib",
|
||||
"luamin": "^1.0.4",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -54,33 +46,30 @@
|
||||
"moment": "^2.22.2",
|
||||
"ncp": "^2.0.0",
|
||||
"needle": "^2.2.4",
|
||||
"node-factorio-api": "^0.3.1-alpha",
|
||||
"node-factorio-api": "^0.3.5",
|
||||
"node-forge": "^0.7.1",
|
||||
"prom-client": "^10.2.3",
|
||||
"rcon-client": "git+https://gitlab.com/Hornwitser/rcon-client.git",
|
||||
"rcon-client-fork": "^3.2.0",
|
||||
"request": "^2.83.0",
|
||||
"right-pad": "^1.0.1",
|
||||
"rmdir-sync": "^1.0.1",
|
||||
"sanitizer": "^0.1.3",
|
||||
"simple-git": "^1.96.0",
|
||||
"single-line-log": "^1.1.2",
|
||||
"snyk": "^1.134.2",
|
||||
"socket.io": "^2.0.4",
|
||||
"socket.io-client": "^2.0.4",
|
||||
"socket.io-prometheus": "^0.2.1",
|
||||
"sync-request": "^4.0.1",
|
||||
"typescript-collections": "^1.3.2",
|
||||
"underscore": "^1.8.3"
|
||||
"tail": "^2.0.2",
|
||||
"typescript-collections": "^1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"documentation": "^5.4.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"html5-validator": "^1.0.7",
|
||||
"mocha": "^3.5.3",
|
||||
"mocha-sinon": "^2.0.0",
|
||||
"mocha.parallel": "^0.15.5",
|
||||
"nock": "^9.1.6",
|
||||
"nyc": "^14.1.1",
|
||||
"sinon": "^2.4.1",
|
||||
"supertest": "^3.0.0"
|
||||
},
|
||||
"snyk": true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const averagedTimeSeries = require("averaged-timeseries")
|
||||
|
||||
const doleNN = require("./../../lib/dole_nn_base.js")
|
||||
const doleNN = require("lib/dole_nn_base.js")
|
||||
|
||||
const Prometheus = require('prom-client');
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
--[[
|
||||
HUGE SHOUTOUT TO HORNWITSER FOR WRITING THIS SEXY AS MODLOADER <3
|
||||
and for not getting mad when i coppyed it :)
|
||||
]]
|
||||
|
||||
local modules = {
|
||||
require("pdnc"),
|
||||
require("doomsday"),
|
||||
require("gui"),
|
||||
}
|
||||
|
||||
script.on_init(function()
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.on_init then
|
||||
mod.on_init()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
script.on_load(function()
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.on_load then
|
||||
mod.on_load()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
script.on_configuration_changed(function(data)
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.on_configuration_changed then
|
||||
mod.on_configuration_changed(data)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function register_tick_handlers()
|
||||
local ticks = {}
|
||||
for i,mod in ipairs(modules) do
|
||||
print(mod.on_nth_ticks)
|
||||
if mod.on_nth_ticks then
|
||||
mod_ticks = mod.on_nth_ticks
|
||||
for t,f in pairs(mod_ticks) do
|
||||
if not ticks[t] then
|
||||
ticks[t] = {}
|
||||
end
|
||||
|
||||
table.insert(ticks[t], f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for t,fs in pairs(ticks) do
|
||||
script.on_nth_tick(t, function(event_data)
|
||||
for i,f in ipairs(fs) do
|
||||
f(event_data)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
register_tick_handlers()
|
||||
|
||||
local function register_events()
|
||||
local events = {}
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.get_events then
|
||||
mod_events = mod.get_events()
|
||||
for e,f in pairs(mod_events) do
|
||||
if not events[e] then
|
||||
events[e] = {}
|
||||
end
|
||||
|
||||
table.insert(events[e], f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for e,fs in pairs(events) do
|
||||
script.on_event(e, function(event_data)
|
||||
for i,f in ipairs(fs) do
|
||||
f(event_data)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
register_events()
|
||||
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.add_remote_interfaces then
|
||||
mod.add_remote_interfaces()
|
||||
end
|
||||
end
|
||||
|
||||
for i,mod in ipairs(modules) do
|
||||
if mod.add_commands then
|
||||
mod.add_commands()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
|
||||
-- This is a example of what to put at the end of your code, ie doomsday.lua
|
||||
-- replace all the EXAMPLE 's with any name you want, they have to be all the same
|
||||
-- replace tick with number if ticks and function with the function name to call
|
||||
-- everywhere there is a blank space is where you put stuff in
|
||||
|
||||
|
||||
|
||||
local EXAMPLE_init = {}
|
||||
|
||||
local script_events = {
|
||||
--place the here what you would normaly use Event.register for
|
||||
-- Event.register(defines.events.on_player_created, testfunction)
|
||||
-- is the same as
|
||||
-- [defines.events.on_player_created] = testfunction,
|
||||
-- where testfunction is | local function testfunction() { }
|
||||
--[Event] = function,
|
||||
--put stuff here
|
||||
|
||||
}
|
||||
|
||||
EXAMPLE_init.on_nth_ticks = {
|
||||
--place the here what you would normaly use
|
||||
--[tick] = function,
|
||||
--put stuff here
|
||||
|
||||
}
|
||||
|
||||
EXAMPLE_init.on_init = function() -- this runs when Event.core_events.init
|
||||
log("EXAMPLE init")
|
||||
--put stuff here
|
||||
|
||||
global.EXAMPLE_data = global.EXAMPLE_data or script_data -- NO TOUCHY
|
||||
|
||||
end
|
||||
|
||||
EXAMPLE_init.on_load = function() -- this runs when Event.core_events.load
|
||||
log("EXAMPLE load")
|
||||
--put stuff here
|
||||
|
||||
script_data = global.EXAMPLE_data or script_data -- NO TOUCHY
|
||||
end
|
||||
|
||||
EXAMPLE_init.get_events = function()
|
||||
return script_events
|
||||
end
|
||||
|
||||
return EXAMPLE_init
|
||||
]]
|
||||
@@ -1,251 +0,0 @@
|
||||
-- doomsday module. Requires the programmable day-night cycle (pdnc) module to work.
|
||||
-- made by Zr4g0n
|
||||
-- this module currently has issues with the event module and 'on nth tick'.
|
||||
require("pdnc") --is this the best way to do this?
|
||||
global.doomsday_enabled = true
|
||||
global.doomsday = global.doomsday or {} -- used to check if this exists.
|
||||
global.doomsday_start = 8.75 -- in ingame days. Use n.75 to make sure doomsday is at midnight.
|
||||
global.doomsday_pollution = 20 -- amount to be applied per tick
|
||||
global.doomsday_surface = 1
|
||||
global.doomsday_enable_players_online_compensator = false
|
||||
global.doomsday_current_fuzzy_playercount = 1.5 -- start assuming 1.5 players! :V
|
||||
global.doomsday_use_early_death = false
|
||||
global.doomsday_early_death_has_happened = false
|
||||
global.doomsday_use_different_spawn = false
|
||||
global.doomsday_different_spawn = {x = -2000, y = -500}
|
||||
global.doomsday_has_happened = false
|
||||
|
||||
function doomsday_status()
|
||||
stats = {
|
||||
"Time Left: " .. doomsday_time_left(),
|
||||
"global.doomsday_start: " .. global.doomsday_start,
|
||||
"global.doomsday_pollution: " .. global.doomsday_pollution,
|
||||
"global.doomsday_surface: " .. global.doomsday_surface,
|
||||
"global.doomsday_enabled: " .. pdnc_bool_to_string(global.doomsday_enabled),
|
||||
"global.doomsday_enable_players_online_compensator: " .. pdnc_bool_to_string(global.doomsday_enable_players_online_compensator),
|
||||
"global.doomsday_current_fuzzy_playercount: " .. global.doomsday_current_fuzzy_playercount,
|
||||
"global.doomsday_use_early_death: " .. pdnc_bool_to_string(global.doomsday_use_early_death),
|
||||
"global.doomsday_early_death_has_happened: " .. pdnc_bool_to_string(global.doomsday_early_death_has_happened),
|
||||
"global.doomsday_use_different_spawn: " .. pdnc_bool_to_string(global.doomsday_use_different_spawn),
|
||||
"global.doomsday_different_spawn: x = " .. global.doomsday_different_spawn.x .. ", y = " .. global.doomsday_different_spawn.y,
|
||||
"doomsday_has_happened: " .. pdnc_bool_to_string(global.doomsday_has_happened),
|
||||
}
|
||||
return stats
|
||||
end
|
||||
|
||||
function doomsday_console_status()
|
||||
--logs to console, used for debuging
|
||||
log("Time Left: " .. doomsday_time_left())
|
||||
log("global.doomsday_start: " .. global.doomsday_start)
|
||||
log("global.doomsday_pollution: " .. global.doomsday_pollution)
|
||||
log("global.doomsday_surface: " .. global.doomsday_surface)
|
||||
log("global.doomsday_enabled: " .. pdnc_bool_to_string(global.doomsday_enabled))
|
||||
log("global.doomsday_enable_players_online_compensator: " .. pdnc_bool_to_string(global.doomsday_enable_players_online_compensator))
|
||||
log("global.doomsday_current_fuzzy_playercount: " .. global.doomsday_current_fuzzy_playercount)
|
||||
log("global.doomsday_use_early_death: " .. pdnc_bool_to_string(global.doomsday_use_early_death))
|
||||
log("global.doomsday_early_death_has_happened: " .. pdnc_bool_to_string(global.doomsday_early_death_has_happened))
|
||||
log("global.doomsday_use_different_spawn: " .. pdnc_bool_to_string(global.doomsday_use_different_spawn))
|
||||
log("global.doomsday_different_spawn: x = " .. global.doomsday_different_spawn.x .. ", y = " .. global.doomsday_different_spawn.y)
|
||||
log("doomsday_has_happened: " .. pdnc_bool_to_string(global.doomsday_has_happened))
|
||||
return stats
|
||||
end
|
||||
|
||||
function doomsday_toggle()
|
||||
global.doomsday_enabled = not global.doomsday_enabled
|
||||
doomsday_status()
|
||||
end
|
||||
|
||||
function doomsday_setup()
|
||||
if global.doomsday_use_different_spawn then
|
||||
doomsday_activate_different_spawn()
|
||||
end
|
||||
end
|
||||
|
||||
function doomsday_early_death()
|
||||
local radius = 512
|
||||
local pollution = 2500
|
||||
doomsday_pollute(radius*0.66,pollution,12)
|
||||
doomsday_pollute(radius*1.00,pollution,24)
|
||||
doomsday_pollute(radius*1.50,pollution,36)
|
||||
global.doomsday_early_death_has_happened = true
|
||||
end
|
||||
|
||||
function doomsday_activate_different_spawn()
|
||||
game.forces["player"].set_spawn_position(global.doomsday_different_spawn, game.surfaces[global.doomsday_surface])
|
||||
end
|
||||
|
||||
function doomsday_core()
|
||||
local current_time = game.tick / game.surfaces[global.doomsday_surface].ticks_per_day
|
||||
local x = current_time * 6.2831853 --2pi
|
||||
local returnvalue = 0
|
||||
if(global.doomsday_enable_players_online_compensator)then
|
||||
doomsday_players_online_compensator()
|
||||
end
|
||||
|
||||
if global.doomsday_use_early_death
|
||||
and (current_time > 1)
|
||||
and not global.doomsday_early_death_has_happened then
|
||||
doomsday_early_death()
|
||||
end
|
||||
|
||||
local x = current_time * 6.2831853 --2pi
|
||||
local returnvalue = 0
|
||||
if (current_time < global.doomsday_start) then
|
||||
returnvalue = math.pow(pdnc_c_boxy(x), (1 + current_time / 4))
|
||||
-- days become darker over time towards n^6.125
|
||||
elseif (current_time < global.doomsday_start + 1) then
|
||||
returnvalue = doomsday_pollution_zero_hour(current_time)
|
||||
if not global.doomsday_has_happened then
|
||||
global.doomsday_has_happened = true
|
||||
log("Doomsday activated at tick: " .. game.tick)
|
||||
end
|
||||
else
|
||||
global.pdnc_enable_brightness_limit = true
|
||||
returnvalue = math.pow(pdnc_c_boxy(x), 6.125)--*0.5
|
||||
end
|
||||
return pdnc_scaler(returnvalue)
|
||||
end
|
||||
|
||||
function doomsday_pollution_zero_hour(current_time)
|
||||
local radius = 128 --make global
|
||||
local pollution = global.doomsday_pollution -- total pollution applied per tick
|
||||
local nodes = 5 -- the number of nodes to spread
|
||||
doomsday_pollute(radius*0.66,pollution,nodes*0.66)
|
||||
doomsday_pollute(radius*1.00,pollution,nodes*1.00)
|
||||
doomsday_pollute(radius*1.50,pollution,nodes*1.50)
|
||||
return math.pow(((global.doomsday_start + 1) - current_time), 7)
|
||||
end
|
||||
|
||||
|
||||
function doomsday_normal_curve(x)
|
||||
return (1+ ((math.sin(x) + (0.111 * math.sin(3 * x))) * 1.124859392575928))/2
|
||||
-- magic numbers to make it scale to (-1, 1)
|
||||
end
|
||||
|
||||
function doomsday_pollute(radius,pollution,nodes) -- spawn a ring of pollution based on radius, amount of pollution and number of points in the ring.
|
||||
local p = global.pdnc_stepsize * pollution -- needed to make it 'step size independant'
|
||||
p = p / nodes
|
||||
local position = {x = 0.0, y = 0.0}
|
||||
if global.doomsday_use_different_spawn then
|
||||
position = global.doomsday_different_spawn
|
||||
end
|
||||
--game.surfaces[global.doomsday_surface].pollute(position, p) --circle + center point
|
||||
local step = (math.pi * 2) / (nodes - 1)
|
||||
for i=0, (nodes - 1) do
|
||||
position = {x = math.sin(step*i)*radius, y = math.cos(step*i)*radius}
|
||||
game.surfaces[global.doomsday_surface].pollute(position, p)
|
||||
end
|
||||
end
|
||||
|
||||
function doomsday_time_left()
|
||||
if (global.doomsday_start > 0)then
|
||||
local ticks_until_doomsday = game.surfaces[global.doomsday_surface].ticks_per_day * global.doomsday_start
|
||||
local ticks = ticks_until_doomsday - game.tick
|
||||
if (ticks >= 0) then
|
||||
local seconds = math.floor(ticks/ 60)
|
||||
local minutes = math.floor(seconds / 60)
|
||||
local hours = math.floor(minutes / 60)
|
||||
local days = math.floor(hours / 24)
|
||||
return(string.format("-" .. "%d:%02d:%02d:%02d", hours, minutes % 60, seconds % 60, ticks % 60))
|
||||
else
|
||||
ticks = ticks * -1
|
||||
local seconds = math.floor(ticks / 60)
|
||||
local minutes = math.floor(seconds / 60)
|
||||
local hours = math.floor(minutes / 60)
|
||||
local days = math.floor(hours / 24)
|
||||
return(string.format("%d:%02d:%02d:%02d", hours, minutes % 60, seconds % 60, ticks % 60) .. " ago...")
|
||||
end
|
||||
else
|
||||
return("Nothing to see here, move along! No doomsday here, nope!")
|
||||
end
|
||||
end
|
||||
|
||||
function doomsday_time_left_with_ticks()
|
||||
local tick = (((game.surfaces[global.doomsday_surface].ticks_per_day * global.doomsday_start) - game.tick)%60)
|
||||
return doomsday_time_left() .. ":" .. string.format("%02d", tick)
|
||||
end
|
||||
|
||||
function doomsday_players_online_compensator()
|
||||
local target = #game.connected_players
|
||||
local current = global.doomsday_current_fuzzy_playercount
|
||||
local step_size = 0.02 -- (at 21 ticks per iteration, that's 17.5sec per player change.
|
||||
|
||||
if(current < 0)then
|
||||
game.print("Error; 'current' number of players' is less than 0!")
|
||||
end
|
||||
if(target < 0)then
|
||||
game.print("Error; 'number of players' is less than 0!")
|
||||
end
|
||||
|
||||
if(current == target) then
|
||||
-- do nothing, this should be most of the time
|
||||
elseif ( current < target) then
|
||||
current = current + step_size
|
||||
elseif ( current > target) then
|
||||
current = current - step_size
|
||||
else
|
||||
-- do nothing?
|
||||
end
|
||||
local modifier = 20/(0.1 * current * current + 6) -- Nice, smooth curve that should hopefully give boost equal to the playercount!
|
||||
game.forces.player.character_running_speed_modifier = modifier
|
||||
global.doomsday_current_fuzzy_playercount = current
|
||||
end
|
||||
|
||||
--[[
|
||||
function reduce_brightness(n)
|
||||
global.pdnc_max_brightness = 1 - ((global.pdnc_current_time / global.pdnc_doomsday_start)*n)
|
||||
if(global.pdnc_max_brightness < n) then
|
||||
global.pdnc_max_brightness = n
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
|
||||
|
||||
-- This is a example of what to put at the end of your code, ie doomsday.lua
|
||||
-- replaze EXAMPLE with any name you want
|
||||
-- replace tick with number if ticks and function with the function name to call
|
||||
-- everywhere there is a blank space is where you put stuff in
|
||||
|
||||
local doomsday_init = {}
|
||||
|
||||
local script_events = {
|
||||
--place the here what you would normaly use Event.register for
|
||||
-- Event.register(defines.events.on_player_created, testfunction)
|
||||
-- is the same as
|
||||
-- [defines.events.on_player_created] = testfunction,
|
||||
-- where testfunction is | local functuin testfunction() { }
|
||||
--[Event] = function,
|
||||
--put stuff here
|
||||
|
||||
}
|
||||
|
||||
doomsday_init.on_nth_ticks = {
|
||||
--place the here what you would normaly use
|
||||
--[tick] = function,
|
||||
--put stuff here
|
||||
--[60] = doomsday_console_status, -- prints status to console ever second
|
||||
|
||||
}
|
||||
|
||||
doomsday_init.on_init = function() -- this runs when Event.core_events.init
|
||||
log("doomsday init")
|
||||
--put stuff here
|
||||
doomsday_setup()
|
||||
global.doomsday_data = global.doomsday_data or script_data -- NO TOUCHY
|
||||
end
|
||||
|
||||
doomsday_init.on_load = function() -- this runs when Event.core_events.load
|
||||
log("doomsday load")
|
||||
--put stuff here
|
||||
script_data = global.doomsday_data or script_data -- NO TOUCHY
|
||||
end
|
||||
|
||||
doomsday_init.get_events = function()
|
||||
return script_events
|
||||
end
|
||||
|
||||
return doomsday_init
|
||||
|
||||
--Event.register(-global.pdnc_stepsize, doomsday_pdnc_program) --intentionally using the PDNC stepsize so the functions sync this can be skipped, since it's called from pdnc!
|
||||
--Event.register(Event.core_events.init, doomsday_setup)
|
||||
@@ -1,266 +0,0 @@
|
||||
local mod_gui = require 'mod-gui'
|
||||
|
||||
-- ADDS DOOMSDAY STATS BUTTON
|
||||
local function gui_update_DOOM(player)
|
||||
local gui = mod_gui.get_frame_flow(player)
|
||||
local frame = gui.doomsday_stats
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
|
||||
frame.clear()
|
||||
for i,stat in ipairs(doomsday_status()) do
|
||||
frame.add{
|
||||
type = "label",
|
||||
caption = stat
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function toggle_frame_DOOM(player)
|
||||
local gui = mod_gui.get_frame_flow(player)
|
||||
local frame = gui.doomsday_stats
|
||||
|
||||
if frame then
|
||||
frame.destroy()
|
||||
return
|
||||
end
|
||||
|
||||
frame = gui.add{
|
||||
type = "frame",
|
||||
name = "doomsday_stats",
|
||||
direction = "vertical",
|
||||
caption = "Doomsday stats",
|
||||
style = mod_gui.frame_style,
|
||||
}
|
||||
|
||||
frame.style.horizontally_stretchable = false
|
||||
frame.style.vertically_stretchable = false
|
||||
gui_update_DOOM(player)
|
||||
end
|
||||
|
||||
local function get_sprite_button_DOOM(player)
|
||||
local button_flow = mod_gui.get_button_flow(player)
|
||||
button_DOOM = button_flow.doomsday_stats_button
|
||||
|
||||
if button_DOOM then
|
||||
button_DOOM.destroy()
|
||||
end
|
||||
|
||||
if player.admin then
|
||||
button_DOOM = button_flow.add{
|
||||
type = "sprite-button",
|
||||
name = "doomsday_stats_button",
|
||||
sprite = "item/raw-fish",
|
||||
style = mod_gui.button_style,
|
||||
tooltip = "Show debug stats for doomsday",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
--ADDS PDNC STATS BUTTON
|
||||
local function gui_update_PDNC(player)
|
||||
local gui = mod_gui.get_frame_flow(player)
|
||||
local frame_PDNC = gui.PDNC_stats
|
||||
if not frame_PDNC then
|
||||
return
|
||||
end
|
||||
|
||||
frame_PDNC.clear()
|
||||
for i,stat in ipairs(pdnc_extended_status()) do
|
||||
frame_PDNC.add{
|
||||
type = "label",
|
||||
caption = stat
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function toggle_frame_PDNC(player)
|
||||
local gui = mod_gui.get_frame_flow(player)
|
||||
local frame_PDNC = gui.PDNC_stats
|
||||
|
||||
if frame_PDNC then
|
||||
frame_PDNC.destroy()
|
||||
return
|
||||
end
|
||||
|
||||
frame_PDNC = gui.add{
|
||||
type = "frame",
|
||||
name = "PDNC_stats",
|
||||
direction = "vertical",
|
||||
caption = "PDNC stats",
|
||||
style = mod_gui.frame_style,
|
||||
}
|
||||
|
||||
frame_PDNC.style.horizontally_stretchable = false
|
||||
frame_PDNC.style.vertically_stretchable = false
|
||||
gui_update_PDNC(player)
|
||||
end
|
||||
|
||||
local function get_sprite_button_PDNC(player)
|
||||
local button_flow = mod_gui.get_button_flow(player)
|
||||
local button_PDNC = button_flow.PDNC_stats_button
|
||||
|
||||
if button_PDNC then
|
||||
button_PDNC.destroy()
|
||||
end
|
||||
|
||||
if player.admin then
|
||||
button_PDNC = button_flow.add{
|
||||
type = "sprite-button",
|
||||
name = "PDNC_stats_button",
|
||||
sprite = "item/raw-fish",
|
||||
style = mod_gui.button_style,
|
||||
tooltip = "Show debug stats for PDNC",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- ADDS DOOMSDAY TIME LEFT COUNTER
|
||||
local function gui_update_counter(player)
|
||||
local gui = player.gui.screen --mod_gui.get_frame_flow(player)
|
||||
local frame_counter = gui.doomsday_counter
|
||||
if not frame_counter then
|
||||
return
|
||||
end
|
||||
|
||||
frame_counter.clear()
|
||||
frame_counter.add{
|
||||
type = "label",
|
||||
caption = doomsday_time_left()
|
||||
}
|
||||
frame_counter.location = {player.display_resolution.width/2 - 59, 10}
|
||||
frame_counter.visible = true
|
||||
end
|
||||
|
||||
local function toggle_frame_counter(player)
|
||||
--local player = player
|
||||
local gui = player.gui.screen --mod_gui.get_frame_flow(player)
|
||||
local frame_counter = gui.doomsday_counter
|
||||
-- if frame_counter then
|
||||
-- frame_counter.destroy()
|
||||
-- return
|
||||
-- end
|
||||
|
||||
frame_counter = gui.add{
|
||||
type = "frame",
|
||||
name = "doomsday_counter",
|
||||
direction = "horizontal",
|
||||
--caption = "Doomsday counter",
|
||||
style = mod_gui.frame_style,
|
||||
}
|
||||
|
||||
--frame_counter.style.horizontally_stretchable = false
|
||||
frame_counter.style.vertically_stretchable = false
|
||||
frame_counter.visible = false
|
||||
gui_update_counter(player)
|
||||
end
|
||||
|
||||
local function update_buttons(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not (player and player.valid) then
|
||||
return
|
||||
end
|
||||
|
||||
get_sprite_button_DOOM(player)
|
||||
get_sprite_button_PDNC(player)
|
||||
|
||||
-- Make sure the frames are not visible to players
|
||||
if not player.admin then
|
||||
local gui = mod_gui.get_frame_flow(player)
|
||||
local frame_PDNC = gui.PDNC_stats
|
||||
|
||||
if frame_PDNC then
|
||||
frame_PDNC.destroy()
|
||||
end
|
||||
|
||||
local frame_DOOM = gui.doomsday_stats
|
||||
|
||||
if frame_DOOM then
|
||||
frame_DOOM.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ON GUI CLICK AND OTHERS
|
||||
local function on_gui_click(event)
|
||||
local gui = event.element
|
||||
local player = game.players[event.player_index]
|
||||
if not (player and player.valid and gui and gui.valid) then
|
||||
return
|
||||
end
|
||||
|
||||
if gui.name == "doomsday_stats_button" then
|
||||
toggle_frame_DOOM(player)
|
||||
end
|
||||
if gui.name == "PDNC_stats_button" then
|
||||
toggle_frame_PDNC(player)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_player_created(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not (player and player.valid) then
|
||||
return
|
||||
end
|
||||
|
||||
update_buttons(event)
|
||||
toggle_frame_counter(player)
|
||||
end
|
||||
|
||||
local function gui_update_all()
|
||||
for _, player in pairs(game.players) do
|
||||
if player and player.valid then
|
||||
gui_update_DOOM(player)
|
||||
gui_update_PDNC(player)
|
||||
gui_update_counter(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_gui_tick(event)
|
||||
gui_update_all()
|
||||
end
|
||||
|
||||
local doomsdaygui_init = {}
|
||||
local script_events = {
|
||||
--place the here what you would normaly use Event.register for
|
||||
-- Event.register(defines.events.on_player_created, testfunction)
|
||||
-- is the same as
|
||||
-- [defines.events.on_player_created] = testfunction,
|
||||
-- where testfunction is | local functuin testfunction() { }
|
||||
--[Event] = function,
|
||||
--put stuff here
|
||||
[defines.events.on_gui_click] = on_gui_click,
|
||||
[defines.events.on_player_created] = on_player_created,
|
||||
[defines.events.on_player_promoted] = update_buttons,
|
||||
[defines.events.on_player_demoted] = update_buttons,
|
||||
}
|
||||
|
||||
doomsdaygui_init.on_nth_ticks = {
|
||||
--place the here what you would normaly use
|
||||
--[tick] = function,
|
||||
--put stuff here
|
||||
[1] = on_gui_tick,
|
||||
}
|
||||
|
||||
doomsdaygui_init.on_init = function() -- this runs when Event.core_events.init
|
||||
log("doomsdaygui init")
|
||||
--put stuff here
|
||||
|
||||
global.doomsdaygui_data = global.doomsdaygui_data or script_data -- NO TOUCHY
|
||||
|
||||
end
|
||||
|
||||
doomsdaygui_init.on_load = function() -- this runs when Event.core_events.load
|
||||
log("doomsdaygui load")
|
||||
--put stuff here
|
||||
|
||||
script_data = global.doomsdaygui_data or script_data -- NO TOUCHY
|
||||
end
|
||||
|
||||
doomsdaygui_init.get_events = function()
|
||||
return script_events
|
||||
end
|
||||
|
||||
return doomsdaygui_init
|
||||
@@ -1,309 +0,0 @@
|
||||
-- Third itteration of programmable day-night cycle for Factorio
|
||||
-- Developed by Zr4g0n with help
|
||||
-- Features needed:
|
||||
-- Set 'maximum brightness' from rocket launches cluster wide
|
||||
-- Set 'time until doomsday' to any time, in minutes.
|
||||
-- Set 'evolution target' to any number [0.0, 1.0]
|
||||
-- Changing day-night cycle based on days
|
||||
-- Handle 'next time' elegantly
|
||||
|
||||
-- lamps enable darkness: [0.595 - 0.425] scaled to [0.0 - 0.85] range from [0.0 - 1.0] range
|
||||
global.pdnc = global.pdnc or {}
|
||||
global.pdnc_enabled = true
|
||||
global.pdnc_stepsize = 53 -- also used for script.on_nth_tick. Cannot be changed at runtime!
|
||||
global.pdnc_surface = 1
|
||||
global.pdnc_current_time = 0
|
||||
global.pdnc_current_point = {x = 0, y = 1.0}
|
||||
global.pdnc_last_point = {x = -1, y = 0.0}
|
||||
global.pdnc_max_brightness = 0.5 -- for clusterio
|
||||
global.pdnc_debug = false
|
||||
global.pdnc_enable_brightness_limit = false
|
||||
global.pdnc_enable_rocket_darkness = false
|
||||
global.pdnc_rockets_launched = 0
|
||||
global.pdnc_rockets_launched_step_size = 0.025
|
||||
global.pdnc_rockets_launched_smooth = 0
|
||||
global.pdnc_min_per_day = 15.0
|
||||
|
||||
function pdnc_setup()
|
||||
--game.surfaces[global.pdnc_surface].ticks_per_day = pdnc_min_to_ticks(10.0)
|
||||
doomsday_setup()
|
||||
end
|
||||
|
||||
-- Returns true if the player is an admin
|
||||
function check_admin(ctx)
|
||||
local player = game.players[ctx.player_index]
|
||||
if not player.admin then
|
||||
player.print("Only admins can use /"..ctx.name)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function pdnc_toggle_debug(ctx)
|
||||
if not check_admin(ctx) then return end
|
||||
global.pdnc_debug = not global.pdnc_debug
|
||||
pdnc_print_status(ctx)
|
||||
end
|
||||
|
||||
function pdnc_toggle(ctx)
|
||||
if not check_admin(ctx) then return end
|
||||
global.pdnc_enabled = not global.pdnc_enabled
|
||||
pdnc_print_status(ctx)
|
||||
end
|
||||
|
||||
function pdnc_print_status(ctx)
|
||||
local player = game.players[ctx.player_index]
|
||||
if(global.pdnc_enabled)then
|
||||
player.print("PDNC is enabled")
|
||||
else
|
||||
player.print("PDNC is disabled")
|
||||
end
|
||||
|
||||
if(global.pdnc_debug)then
|
||||
player.print("PDNC debug is enabled")
|
||||
pdnc_extended_status()
|
||||
else
|
||||
player.print("PDNC debug is disabled")
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_extended_status()
|
||||
stats = {
|
||||
"global.pdnc_stepsize: " .. global.pdnc_stepsize,
|
||||
"global.pdnc_surface: " .. global.pdnc_surface,
|
||||
"global.pdnc_current_time: " .. global.pdnc_current_time,
|
||||
"global.pdnc_max_brightness: " .. global.pdnc_max_brightness,
|
||||
"global.pdnc_enable_brightness_limit: " .. pdnc_bool_to_string(global.pdnc_enable_brightness_limit),
|
||||
"global.pdnc_enable_rocket_darkness: " .. pdnc_bool_to_string(global.pdnc_enable_rocket_darkness),
|
||||
"global.pdnc_rockets_launched: " .. global.pdnc_rockets_launched,
|
||||
"global.pdnc_rockets_launched_step_size: " .. global.pdnc_rockets_launched_step_size,
|
||||
"global.pdnc_rockets_launched_smooth: " .. global.pdnc_rockets_launched_smooth,
|
||||
"ticks per day: " .. game.surfaces[global.pdnc_surface].ticks_per_day,
|
||||
"current tick: " .. game.tick,
|
||||
}
|
||||
return stats
|
||||
end
|
||||
|
||||
function pdnc_bool_to_string(b)
|
||||
if(b)then
|
||||
return "true"
|
||||
else
|
||||
return "false"
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_core()
|
||||
if(global.pdnc_enabled)then
|
||||
doomsday_core()
|
||||
local current_surface = game.surfaces[global.pdnc_surface]
|
||||
pdnc_freeze_check(current_surface)
|
||||
current_surface.ticks_per_day = pdnc_min_to_ticks(global.pdnc_min_per_day) -- move this somewhere else; doesn't need to run every nth tick!
|
||||
global.pdnc_current_time = game.tick / current_surface.ticks_per_day
|
||||
|
||||
-- get current Y coordinate. X = 0 always.
|
||||
local current_point = {x = global.pdnc_current_time, y = pdnc_program(global.pdnc_current_time)}
|
||||
-- get next Y coordinate, X = 'step size'
|
||||
local next_point = {x = (global.pdnc_current_time + (global.pdnc_stepsize/current_surface.ticks_per_day)), y = pdnc_program(global.pdnc_current_time + (global.pdnc_stepsize/current_surface.ticks_per_day))}
|
||||
|
||||
local top_point = pdnc_intersection_top(current_point, next_point)
|
||||
local bot_point = pdnc_intersection_bot(current_point, next_point)
|
||||
|
||||
-- the order is dusk - evening - morning - dawn. They *must* be in that order and they cannot be equal
|
||||
if(top_point < bot_point) then -- dusk -> evening
|
||||
pdnc_cleanup_last_tick(current_surface)
|
||||
current_surface.evening = bot_point - current_point.x
|
||||
current_surface.dusk = top_point - current_point.x
|
||||
elseif(top_point > bot_point) then -- morning -> dawn
|
||||
pdnc_cleanup_last_tick(current_surface)
|
||||
current_surface.morning = bot_point - current_point.x
|
||||
current_surface.dawn = top_point - current_point.x
|
||||
elseif(top_point == bot_point) then
|
||||
pdnc_debug_message("PDNC: Top and bot point equal")
|
||||
-- no cleanup is done here
|
||||
-- if the points are equal, use last value until not equal
|
||||
-- this should never be reached unless the pdnc_program() is broken.
|
||||
else
|
||||
pdnc_debug_message("Top and bot not different nor equal. probably a NaN error")
|
||||
pdnc_debug_message("bot_point: " .. bot_point)
|
||||
pdnc_debug_message("top_point: " .. top_point)
|
||||
-- this should never be reached.
|
||||
end
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_cleanup_last_tick(current_surface)
|
||||
current_surface.daytime = 0
|
||||
-- must be in this spesific order to
|
||||
-- preserve the order at all times
|
||||
-- dusk < evening < morning < dawn.
|
||||
-- DO NOT CHANGE THE ORDER!
|
||||
current_surface.dusk = -999999999999
|
||||
current_surface.dawn = 999999999999
|
||||
current_surface.evening = -999999999998
|
||||
current_surface.morning = 999999999998
|
||||
end
|
||||
|
||||
function pdnc_disable_and_reset(ctx)
|
||||
if not check_admin(ctx) then return end
|
||||
local current_surface = game.surfaces[global.pdnc_surface]
|
||||
pdnc_cleanup_last_tick(current_surface)
|
||||
-- DO NOT CHANGE THIS ORDER!
|
||||
current_surface.evening = 0.45
|
||||
current_surface.morning = 0.55
|
||||
current_surface.dusk = 0.25
|
||||
current_surface.dawn = 0.75
|
||||
current_surface.ticks_per_day = 25000
|
||||
global.pdnc_enabled = false
|
||||
pdnc_debug_message("PDNC Programable Day-Night Cycle disabled, normal day-night cycle enabled")
|
||||
end
|
||||
|
||||
function pdnc_freeze_check(current_surface)
|
||||
if(current_surface.freeze_daytime)then
|
||||
current_surface.freeze_daytime = false
|
||||
game.print("Can't use freeze_daytime while programmable day-night cycle is active; time has been unfrozen")
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_program(x)
|
||||
return pdnc_scaler(pdnc_c_boxy(x*6.28318530717958647692)) -- that's Tau, aka 2xPi
|
||||
end
|
||||
|
||||
function pdnc_c_boxy(x)
|
||||
return pdnc_normalize((math.sin(x) + (0.111 * math.sin(3 * x))) * 1.124859392575928)
|
||||
-- magic numbers to make it scale to (-1, 1)
|
||||
end
|
||||
|
||||
function pdnc_normalize(n)
|
||||
return (n + 1)/2
|
||||
end
|
||||
|
||||
function pdnc_scaler(r) -- a bit messy, but simplifies a lot elsewhere
|
||||
if(pdnc_check_valid(r, "pdnc_scaler"))then
|
||||
|
||||
local a = 1
|
||||
local b = 1
|
||||
|
||||
if(global.pdnc_enable_brightness_limit) then
|
||||
a = global.pdnc_max_brightness
|
||||
end
|
||||
|
||||
if(global.pdnc_enable_rocket_darkness) then
|
||||
b = 1 - pdnc_rocket_launch_darkness()
|
||||
end
|
||||
|
||||
return r * 0.85 * a * b -- 0 -> 0.85 is the 'brightness range' of factorio
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_intersection (s1, e1, s2, e2)
|
||||
local d = (s1.x - e1.x) * (s2.y - e2.y) - (s1.y - e1.y) * (s2.x - e2.x)
|
||||
local a = s1.x * e1.y - s1.y * e1.x
|
||||
local b = s2.x * e2.y - s2.y * e2.x
|
||||
local x = (a * (s2.x - e2.x) - (s1.x - e1.x) * b) / d
|
||||
--local y = (a * (s2.y - e2.y) - (s1.y - e1.y) * b) / d
|
||||
return x--, y
|
||||
end
|
||||
|
||||
function pdnc_intersection_top (s2, e2)
|
||||
local s1, e1 = {x = -999999999, y = 0.85}, {x = 999999999, y = 0.85}
|
||||
return pdnc_intersection (s1, e1, s2, e2)
|
||||
end
|
||||
|
||||
function pdnc_intersection_bot (s2, e2)
|
||||
local s1, e1 = {x = -999999999, y = 0.0}, {x = 999999999, y = 0.0}
|
||||
return pdnc_intersection (s1, e1, s2, e2)
|
||||
end
|
||||
|
||||
function pdnc_set_max_brightness(n)
|
||||
if(pdnc_check_valid(n, "pdnc_set_max_brightness")) then
|
||||
global.pdnc_max_brightness = n
|
||||
pdnc_debug_message("global.pdnc_max_brightness set to " .. global.pdnc_max_brightness)
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_min_to_ticks(m)
|
||||
return 60*60*m
|
||||
end
|
||||
|
||||
function pdnc_rocket_launch_counter()
|
||||
global.pdnc_rockets_launched = 1 + global.pdnc_rockets_launched
|
||||
end
|
||||
|
||||
function pdnc_rocket_launch_darkness()
|
||||
if (global.pdnc_rockets_launched_smooth < global.pdnc_rockets_launched)then
|
||||
global.pdnc_rockets_launched_smooth = global.pdnc_rockets_launched_step_size + global.pdnc_rockets_launched_smooth
|
||||
end
|
||||
return (1 - (50/(global.pdnc_rockets_launched_smooth+50)))
|
||||
end
|
||||
|
||||
function pdnc_check_valid(n, s)
|
||||
if (n == nil) then
|
||||
pdnc_debug_message(s .. " set to nil! Set to 1.0 instead")
|
||||
return false
|
||||
elseif (n < 0) then
|
||||
pdnc_debug_message(s .. " cannot be " .. n .. " limited to 0.0 instead")
|
||||
return false
|
||||
elseif (n > 1) then
|
||||
pdnc_debug_message(s .. " cannot be " .. n .. " limited to 1.0 instead")
|
||||
return false
|
||||
elseif (n ~= n) then
|
||||
pdnc_debug_message(s .. " cannot be " .. n .. " since it's not a valid number!")
|
||||
else return true
|
||||
end
|
||||
end
|
||||
|
||||
function pdnc_debug_message(s)
|
||||
if(global.pdnc_debug) then
|
||||
game.print(s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local PDNC_init = {}
|
||||
|
||||
local script_events = {
|
||||
--place the here what you would normaly use Event.register for
|
||||
-- Event.register(defines.events.on_player_created, testfunction)
|
||||
-- is the same as
|
||||
-- [defines.events.on_player_created] = testfunction,
|
||||
-- where testfunction is | local functuin testfunction() { }
|
||||
--[Event] = function,
|
||||
--put stuff here
|
||||
|
||||
}
|
||||
|
||||
PDNC_init.on_nth_ticks = {
|
||||
--place the here what you would normaly use
|
||||
--[tick] = function,
|
||||
--put stuff here
|
||||
[global.pdnc_stepsize] = pdnc_core,
|
||||
}
|
||||
|
||||
PDNC_init.add_commands = function()
|
||||
commands.add_command("pdnc", "gives PDNC's status", pdnc_print_status)
|
||||
commands.add_command("pdnc_toggle", "toggles pdnc", pdnc_toggle)
|
||||
commands.add_command("pdnc_toggle_debug", "toggles pdnc debug mode", pdnc_toggle_debug)
|
||||
commands.add_command("pdnc_disable_and_reset", "Disabled pdnc and resets the dnc to the vanilla default", pdnc_disable_and_reset)
|
||||
end
|
||||
|
||||
PDNC_init.on_init = function() -- this runs when Event.core_events.init
|
||||
log("PDNC init")
|
||||
--put stuff here
|
||||
global.PDNC_data = global.PDNC_data or script_data -- NO TOUCHY
|
||||
|
||||
end
|
||||
|
||||
PDNC_init.on_load = function() -- this runs when Event.core_events.load
|
||||
log("PDNC load")
|
||||
|
||||
--put stuff here
|
||||
|
||||
script_data = global.PDNC_data or script_data -- NO TOUCHY
|
||||
end
|
||||
|
||||
PDNC_init.get_events = function()
|
||||
return script_events
|
||||
end
|
||||
|
||||
return PDNC_init
|
||||
@@ -1,9 +1,9 @@
|
||||
const needle = require("needle");
|
||||
const fs = require("fs-extra");
|
||||
|
||||
const objectOps = require("./../../lib/objectOps.js");
|
||||
const fileOps = require("_app/fileOps");
|
||||
const stringUtils = require("./../../lib/stringUtils.js");
|
||||
const objectOps = require("lib/objectOps.js");
|
||||
const fileOps = require("lib/fileOps");
|
||||
const stringUtils = require("lib/stringUtils.js");
|
||||
|
||||
const pluginConfig = require("./config");
|
||||
const COMPRESS_LUA = false;
|
||||
@@ -307,7 +307,7 @@ module.exports = class remoteCommands {
|
||||
return new Promise((resolve, reject) => {
|
||||
let instance = this.instances[instanceID];
|
||||
if(!instance){
|
||||
needle.get(this.config.masterIP+":"+this.config.masterPort+ '/api/slaves', (err, response) => {
|
||||
needle.get(this.config.masterIP+":"+this.config.masterPort+ '/api/slaves', { compressed: true }, (err, response) => {
|
||||
if(err || response.statusCode != 200) {
|
||||
console.log("Unable to get JSON master/api/slaves, master might be unaccessible");
|
||||
} else if (response && response.body) {
|
||||
|
||||
@@ -55,7 +55,7 @@ module.exports = class remoteCommands {
|
||||
return new Promise((resolve, reject) => {
|
||||
let instance = this.instances[instanceID];
|
||||
if(!instance){
|
||||
needle.get(this.config.masterIP+":"+this.config.masterPort+ '/api/slaves', (err, response) => {
|
||||
needle.get(this.config.masterIP+":"+this.config.masterPort+ '/api/slaves', { compressed: true }, (err, response) => {
|
||||
if(err || response.statusCode != 200) {
|
||||
console.log("Unable to get JSON master/api/slaves, master might be unaccessible");
|
||||
} else if (response && response.body) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const objectOps = require("../../lib/objectOps.js");
|
||||
const objectOps = require("lib/objectOps.js");
|
||||
const needle = require("needle");
|
||||
const pluginConfig = require("./config")
|
||||
const isJson = require("./isJson")
|
||||
|
||||
@@ -2,7 +2,7 @@ const assert = require("assert");
|
||||
const child_process = require("child_process");
|
||||
const functions = require("./interfacingFunctions");
|
||||
const luamin = require("luamin");
|
||||
const isFactorioCommand = require("_app/isFactorioCommand");
|
||||
const isFactorioCommand = require("lib/isFactorioCommand");
|
||||
const nock = require("nock");
|
||||
|
||||
// sinon does weird prototype manipulation and so doen't need to be saved to anything.
|
||||
|
||||
Whitespace-only changes.
@@ -1,100 +0,0 @@
|
||||
var mkdirp = require("mkdirp-promise");
|
||||
var mkdirpSync = require("mkdirp").sync;
|
||||
var fs = require("fs");
|
||||
var events = require("events");
|
||||
var path = require("path");
|
||||
|
||||
var objectOps = require("./../../lib/objectOps");
|
||||
|
||||
var LinvoDB = require("linvodb3");
|
||||
// LinvoDB.dbPath = "./database/linvodb/";
|
||||
|
||||
|
||||
let allowLogging = true;
|
||||
let log = string => (allowLogging)? console.log(string):""
|
||||
|
||||
module.exports = class chunkStore {
|
||||
constructor(name, chunkSize = 32, path = "./chunkStore/"){
|
||||
if(!name || (typeof name != "string" && typeof name != "number")) throw new Error("chunkstore requires a name string as first param");
|
||||
name = name.toString();
|
||||
this.name = name;
|
||||
this.chunkSize = chunkSize;
|
||||
this.path = (path[path.length] == "/") ? path : path+"/";
|
||||
mkdirpSync(path+name);
|
||||
LinvoDB.dbPath = this.path;
|
||||
|
||||
this.db = new LinvoDB(name, {}, {});
|
||||
|
||||
this.eventEmitter = new events.EventEmitter();
|
||||
}
|
||||
setEntity(x/**{number}*/, y/**{number}*/, newEntity/**{object}*/){
|
||||
return new Promise((resolve, reject) => {
|
||||
x = Math.floor(x);
|
||||
y = Math.floor(y);
|
||||
this.db.find({x, y}, (err, docs) => {
|
||||
if(typeof newEntity == "object"){
|
||||
if(err) reject(err);
|
||||
let entityDoc = {x, y, entity: newEntity};
|
||||
if(docs && docs[0] && docs[0]._id){
|
||||
entityDoc._id = docs[0]._id;
|
||||
}
|
||||
this.db.save(entityDoc, (err,docs) => {
|
||||
if(err) reject(err);
|
||||
resolve(docs);
|
||||
this.eventEmitter.emit("change", docs)
|
||||
});
|
||||
} else {
|
||||
docs.forEach(doc => {
|
||||
doc.remove(()=>{
|
||||
// removed :)
|
||||
});
|
||||
});
|
||||
resolve();
|
||||
this.eventEmitter.emit("change", {x,y});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getEntity(x, y){
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.find({x:Math.floor(x), y:Math.floor(y)}, (err, docs) => {
|
||||
if(err) reject(err);
|
||||
resolve(docs);
|
||||
});
|
||||
});
|
||||
}
|
||||
onEntityChange(eventhandler/**function*/){
|
||||
if(typeof eventhandler == "function") {
|
||||
this.eventEmitter.on("change", eventhandler);
|
||||
}
|
||||
}
|
||||
getChunk(x, y){
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
/*
|
||||
// make new chunk in case we can't find an existing one
|
||||
let chunk = {
|
||||
dataObject: {},
|
||||
position:{x,y}, // fancy ES6 feature that generates to {x:x, y:y}
|
||||
chunkSize:this.chunkSize
|
||||
};
|
||||
fs.readFile(this.path+this.name+"/"+x+"/"+y, (err, chunkFile) => {
|
||||
|
||||
if(err){
|
||||
log(err)
|
||||
resolve(chunk);
|
||||
} else {
|
||||
if(objectOps.isJSON(chunkFile)) {
|
||||
log("returned from file")
|
||||
let chunk = JSON.parse(chunkFile);
|
||||
resolve(chunk);
|
||||
} else {
|
||||
log("file failed")
|
||||
resolve(chunk);
|
||||
}
|
||||
}
|
||||
}); // add x and y values as well and we are done?
|
||||
*/
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
var assert = require("assert");
|
||||
|
||||
var chunkStore = require("./chunkStore.js");
|
||||
|
||||
describe("remoteMap/chunkStore.js", () => {
|
||||
// define some values for our test
|
||||
const inserterX = 14.5; // rounds to 15
|
||||
const inserterY = -17.5; // rounds to -17, +64 would be 47
|
||||
const inserterName = "inserter";
|
||||
|
||||
let store // make our store elevated
|
||||
it("is a constructor", ()=>{
|
||||
assert.equal(typeof chunkStore, "function");
|
||||
// create a new store and elevate it
|
||||
store = new chunkStore("910239"/* instanceID, use defaults for the rest */, undefined, "./database/unitTests/chunkStore/");
|
||||
assert.equal(store.name, "910239");
|
||||
});
|
||||
it("allows you to store the locations of entities", done => {
|
||||
store.setEntity(inserterX, inserterY, {name:inserterName, otherData:"can be arbitrary"}).then(() => {
|
||||
done();
|
||||
// nothing to do here really
|
||||
});
|
||||
});
|
||||
it("returns entities at positions you query for", done => {
|
||||
setTimeout(()=>{
|
||||
store.getEntity(inserterX, inserterY).then(entities => {
|
||||
entity = entities[0]
|
||||
// console.log(entity)
|
||||
assert.equal(entity.x, 14);
|
||||
assert.equal(entity.y, -18);
|
||||
assert.equal(entity.entity.name, "inserter");
|
||||
assert.equal(entity.entity.otherData, "can be arbitrary");
|
||||
done();
|
||||
});
|
||||
},300);
|
||||
});
|
||||
it("deletes entities when not provided with an object as 3rd arg", done => {
|
||||
store.setEntity(inserterX, inserterY).then(entity => {
|
||||
assert(entity === undefined);
|
||||
setTimeout(()=>{
|
||||
store.getEntity(inserterX, inserterY).then(entities => {
|
||||
// console.log(entities)
|
||||
assert.equal(entities.length, 0, "Why are we getting results, it should be empty?");
|
||||
done();
|
||||
});
|
||||
},300);
|
||||
});
|
||||
});
|
||||
describe("validates input by throwing when it is invalid", ()=>{
|
||||
it("throws if called without a string as the first parameter", ()=>{
|
||||
assert.throws(()=>{
|
||||
let throwingChunkStore = new chunkStore(undefined, undefined, "asdasda");
|
||||
});
|
||||
assert.throws(()=>{
|
||||
let throwingChunkStore = new chunkStore({}, undefined, "asdasdas");
|
||||
});
|
||||
});
|
||||
it("converts numbers to strings automatically if they are provided as the first parameter", ()=>{
|
||||
assert.ok(()=>{
|
||||
let nonThrowingChunkStore = new chunkStore(42, undefined, "./database/unitTests/chunkStore/");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
Config for clusterio remote mapping plugin. Will handle writing out new map data, aggregating it and sending it to the master (or something) for
|
||||
display/interaction.
|
||||
*/
|
||||
var modInfo = require("./info.json");
|
||||
module.exports = {
|
||||
// Name of package. For display somewhere I guess.
|
||||
name: modInfo.name,
|
||||
version: modInfo.version,
|
||||
// Binary entrypoint for plugin. Don't let it crash. Stdout is sent to game as server chat (run /c commands from here for interface)
|
||||
// Make sure its cross platform somehow.
|
||||
binary: "nodePackage",
|
||||
description: modInfo.description,
|
||||
// We'll send everything in this file to your stdin. Beware.
|
||||
scriptOutputFileSubscription: "remoteMap.txt",
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
-- MIT License
|
||||
--
|
||||
-- Copyright (c) 2017 Florian Jung
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a
|
||||
-- copy of this factorio lua stub and associated
|
||||
-- documentation files (the "Software"), to deal in the Software without
|
||||
-- restriction, including without limitation the rights to use, copy, modify,
|
||||
-- merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
-- Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
-- DEALINGS IN THE SOFTWARE.
|
||||
|
||||
function write_file(data)
|
||||
stringsToWriteNextTick = stringsToWriteNextTick .. data .. '\n'
|
||||
-- game.write_file("remoteMap.txt", data, true)
|
||||
end
|
||||
|
||||
function dump_cached_writes()
|
||||
game.write_file("remoteMap.txt", stringsToWriteNextTick, true, 0)
|
||||
end
|
||||
|
||||
function complain(text)
|
||||
if false then -- only print during development
|
||||
print(text)
|
||||
game.forces["player"].print(text)
|
||||
end
|
||||
end
|
||||
|
||||
local todo_next_tick = {}
|
||||
must_write_initstuff = true
|
||||
function on_tick(event)
|
||||
dump_cached_writes()
|
||||
stringsToWriteNextTick = ''
|
||||
if must_write_initstuff then
|
||||
must_write_initstuff = false
|
||||
log("I must write init stuff!")
|
||||
writeout_initial_stuff()
|
||||
end
|
||||
if #todo_next_tick > 0 then
|
||||
complain("on_tick executing "..#todo_next_tick.." stored callbacks")
|
||||
for _,func in ipairs(todo_next_tick) do
|
||||
func()
|
||||
end
|
||||
todo_next_tick = {}
|
||||
end
|
||||
end
|
||||
|
||||
function writeout_initial_stuff()
|
||||
log("Writing out initial stuff...")
|
||||
writeout_objects(game.surfaces['nauvis'], {left_top={x=-64,y=-64},right_bottom={x=64,y=64}})
|
||||
end
|
||||
|
||||
function writeout_objects(surface, area)
|
||||
--if my_client_id ~= 1 then return end
|
||||
for idx, ent in pairs(surface.find_entities(area)) do
|
||||
if area.left_top.x <= ent.position.x and ent.position.x < area.right_bottom.x and area.left_top.y <= ent.position.y and ent.position.y < area.right_bottom.y then
|
||||
if ent.prototype.collision_mask ~= nil and ent.prototype.collision_mask['player-layer'] then
|
||||
-- line = line .. ent.name.." "..ent.position.x.." "..ent.position.y.."\n"
|
||||
-- local line = ent.name..","..ent.position.x..","..ent.position.y
|
||||
-- if ent.supports_direction then
|
||||
-- line = line .. ",rot="..ent.direction
|
||||
-- end
|
||||
-- write_file(line)
|
||||
on_some_entity_created(nil, ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
--write_file(header..table.concat(lines,"").."\n")
|
||||
--write_file(table.concat(lines,"").."\n")
|
||||
end
|
||||
function on_chunk_generated(event)
|
||||
local area = event.area
|
||||
local surface = event.surface
|
||||
--print("chunk generated at ("..area.left_top.x..","..area.left_top.y..") -- ("..area.right_bottom.x..","..area.right_bottom.y..")")
|
||||
|
||||
if surface ~= game.surfaces['nauvis'] then -- we only support one surface
|
||||
return
|
||||
end
|
||||
|
||||
-- writeout_resources(surface, area)
|
||||
writeout_objects(surface, area)
|
||||
-- writeout_tiles(surface, area)
|
||||
end
|
||||
function on_some_entity_created(event, entp)
|
||||
local ent
|
||||
if entp then
|
||||
ent = entp
|
||||
else
|
||||
ent = event.entity or event.created_entity or nil
|
||||
end
|
||||
|
||||
if ent == nil then
|
||||
complain("wtf, on_some_entity_created has nil entity")
|
||||
return
|
||||
end
|
||||
local entityData = ent.name..","..ent.position.x..","..ent.position.y
|
||||
if ent.supports_direction then
|
||||
entityData = entityData .. ",rot="..ent.direction
|
||||
end
|
||||
-- log(entityData)
|
||||
write_file(entityData)
|
||||
--writeout_objects(ent.surface, {left_top={x=math.floor(ent.position.x), y=math.floor(ent.position.y)}, right_bottom={x=math.floor(ent.position.x)+1, y=math.floor(ent.position.y)+1}})
|
||||
--complain("on_some_entity_created: "..ent.name.." at "..ent.position.x..","..ent.position.y)
|
||||
end
|
||||
function on_some_entity_deleted(event)
|
||||
local ent = event.entity
|
||||
if ent == nil then
|
||||
complain("wtf, on_some_entity_deleted has nil entity")
|
||||
return
|
||||
end
|
||||
|
||||
-- we can't do this now, because the entity still exists at this point. instead, we schedule the writeout for the next tick
|
||||
|
||||
--local surface = ent.surface
|
||||
--local area = {left_top={x=math.floor(ent.position.x), y=math.floor(ent.position.y)}, right_bottom={x=math.floor(ent.position.x)+1, y=math.floor(ent.position.y)+1}}
|
||||
|
||||
--table.insert(todo_next_tick, function () writeout_objects(surface, area ) end)
|
||||
write_file("deleted,"..ent.position.x..","..ent.position.y)
|
||||
--complain("on_some_entity_deleted: "..ent.name.." at "..ent.position.x..","..ent.position.y)
|
||||
end
|
||||
|
||||
script.on_event(defines.events.on_tick, on_tick)
|
||||
script.on_event(defines.events.on_chunk_generated, on_chunk_generated)
|
||||
|
||||
script.on_event(defines.events.on_biter_base_built, on_some_entity_created) --entity
|
||||
script.on_event(defines.events.on_built_entity, on_some_entity_created) --created_entity
|
||||
script.on_event(defines.events.on_robot_built_entity, on_some_entity_created) --created_entity
|
||||
script.on_event(defines.events.on_player_rotated_entity, on_some_entity_created) --created_entity
|
||||
|
||||
script.on_event(defines.events.on_entity_died, on_some_entity_deleted) --entity
|
||||
script.on_event(defines.events.on_player_mined_entity, on_some_entity_deleted) --entity
|
||||
script.on_event(defines.events.on_robot_mined_entity, on_some_entity_deleted) --entity
|
||||
script.on_event(defines.events.on_resource_depleted, on_some_entity_deleted) --entity
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
/c
|
||||
local toDelete = game.surfaces[1].find_entities({{3,7},{4,8}})
|
||||
for i, entity in pairs(toDelete) do
|
||||
entity.die()
|
||||
end
|
||||
@@ -1,130 +0,0 @@
|
||||
const needle = require("needle");
|
||||
const ioClient = require("socket.io-client");
|
||||
|
||||
const chunkStore = require("./chunkStore.js");
|
||||
|
||||
const moduleConfig = require("./config"); // not to be confused with clusterio config. This config is private to this plugin.
|
||||
|
||||
class remoteMap {
|
||||
constructor(slaveConfig, messageInterface){
|
||||
this.config = slaveConfig;
|
||||
this.messageInterface = messageInterface;
|
||||
|
||||
// initialize chunk database
|
||||
this.chunkMap = new chunkStore(this.config.unique, 64, "./database/chunkStore/");
|
||||
this.chunkMap.onEntityChange(entity => {
|
||||
// this.messageInterface("Entity changed: ")
|
||||
// this.messageInterface(entity);
|
||||
// check valid coordinates (dunno what could go wrong, but can never be too sure)
|
||||
if(entity && entity.x !== undefined && !isNaN(Number(entity.x)) && entity.y !== undefined && !isNaN(Number(entity.y))){
|
||||
this.socket.emit("sendEntity", entity);
|
||||
}
|
||||
});
|
||||
|
||||
// set up websocket communication and handle requests from web interface users (which are going through master)
|
||||
// socket should be a global
|
||||
this.socket = ioClient("http://"+this.config.masterIP+":"+this.config.masterPort);
|
||||
this.socket.on("hello", data => {
|
||||
this.socket.emit("registerSlaveMapper", {instanceID: this.config.unique});
|
||||
setInterval(()=>{
|
||||
this.socket.emit("heartbeat"); // send our heartbeat to prevent being assumed dead
|
||||
},10000);
|
||||
});
|
||||
this.socket.on("getChunk", req => {
|
||||
this.messageInterface("getChunk is not supported!");
|
||||
/*this.chunkMap.getChunk(req.x, req.y).then(chunk => {
|
||||
chunk.requesterID = req.requesterID;
|
||||
this.socket.emit("sendChunk", chunk);
|
||||
});*/
|
||||
});
|
||||
this.socket.on("getEntity", req => {
|
||||
// check for valid coordinates
|
||||
if(req.x !== undefined && !isNaN(Number(req.x)) && req.y !== undefined && !isNaN(Number(req.y))){
|
||||
this.chunkMap.getEntity(req.x, req.y).then(entities => {
|
||||
if(entities && entities.length > 0){
|
||||
entities.forEach(entity => {
|
||||
this.socket.emit("sendEntity", entity);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.messageInterface("socket.getEntity triggered called with invalid parameters");
|
||||
}
|
||||
});
|
||||
// both placing and deleting entities via the web interface
|
||||
this.socket.on("placeEntity", req => {
|
||||
// game.player.surface.create_entity{name="small-ship-wreck", position={game.player.position.x-2, game.player.position.y+1}, direction=..., force=...}
|
||||
this.messageInterface("placing "+JSON.stringify(req));
|
||||
if(req.name && typeof req.name == "string" && req.position && !isNaN(Number(req.position.x)) && !isNaN(Number(req.position.y))){
|
||||
if(req.name == "deleted"){ // {{'+req.position.x+','+req.position.y+'},{'+req.position.x+','+req.position.y+'}}
|
||||
// delete entity from game world using LUA command
|
||||
this.messageInterface('/silent-command local toDelete = game.surfaces[1].find_entities({{'+req.position.x+','+req.position.y+'},{'+(req.position.x+1)+','+(req.position.y+1)+'}}) for i, entity in pairs(toDelete) do entity.die() end');
|
||||
// this position is now empty, delete whatever was there from DB
|
||||
this.deleteEntity(req.position.x, req.position.y);
|
||||
} else {
|
||||
let command = '/silent-command game.surfaces[1].create_entity{name="'+req.name+'", position={'+Number(req.position.x)+', '+Number(req.position.y)+'}, force="neutral"'
|
||||
if(req.direction) command += ', direction='+req.direction;
|
||||
command += '}';
|
||||
this.messageInterface(command); // execute command
|
||||
|
||||
// add entity to the chunkStore database because create_entity does not trigger any events
|
||||
let name = req.name;
|
||||
let entity = {name};
|
||||
if(typeof req.direction == "string"){
|
||||
entity.rot = req.direction;
|
||||
}
|
||||
this.addEntity(req.position.x, req.position.y, entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.messageInterface("/silent-command game.print('"+moduleConfig.name+" version "+moduleConfig.version+" enabled')");
|
||||
}
|
||||
addEntity(x,y,entity){ // add entity to database
|
||||
this.chunkMap.setEntity(Number(x), Number(y), entity).then(data => {
|
||||
this.messageInterface("Added "+entity.name+" to data "+Number(x)+", "+Number(y));
|
||||
}).catch((err)=>this.messageInterface(err));
|
||||
}
|
||||
deleteEntity(x,y){ // remove entity from database
|
||||
// this position is now empty, delete whatever was there from DB
|
||||
this.chunkMap.setEntity(x, y, "delete this entity").then(() => {
|
||||
this.messageInterface("Deleted entity at "+x+", "+y);
|
||||
}).catch((err)=>this.messageInterface(err));
|
||||
}
|
||||
scriptOutput(data){
|
||||
if(data){
|
||||
this.messageInterface(data);
|
||||
// express-transport-belt,-35.5,-14.5,[...]
|
||||
data = data.split(",");
|
||||
if(data && data[0] == undefined){
|
||||
this.messageInterface("empty: ");
|
||||
this.messageInterface(data);
|
||||
return
|
||||
}
|
||||
let name = data[0];
|
||||
let xPos = data[1];
|
||||
let yPos = data[2];
|
||||
if(xPos && yPos && name){
|
||||
if(name == "deleted"){
|
||||
// this position is now empty, delete whatever was there from DB
|
||||
this.deleteEntity(xPos, yPos);
|
||||
} else {
|
||||
let entity = {name};
|
||||
for(let i = 3; i < data.length; i++){
|
||||
if(data[i].includes("=")){
|
||||
let kv = data[i].split("=");
|
||||
entity[kv[0]] = kv[1];
|
||||
}
|
||||
}
|
||||
// save this entity in the database
|
||||
this.addEntity(xPos, yPos, entity);
|
||||
/* this.chunkMap.setEntity(xPos, yPos, entity).then(data => {
|
||||
this.messageInterface("Added "+name+" to data "+xPos+", "+yPos);
|
||||
}).catch((err)=>this.messageInterface(err)); */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = remoteMap;
|
||||
@@ -1,9 +0,0 @@
|
||||
var assert = require("assert");
|
||||
|
||||
var remoteMap = require("./index.js");
|
||||
|
||||
describe("remoteMap/index.js", () => {
|
||||
it("exports a single class (or at least a function)", ()=>{
|
||||
assert.equal(typeof remoteMap, "function");
|
||||
});
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
{
|
||||
"name": "remoteMap",
|
||||
"title": "Clusterio remote map",
|
||||
"version": "2.2.0",
|
||||
"date": "18-11-2017",
|
||||
"author": "Danielv123",
|
||||
"dependencies": ["base >= 0.15"],
|
||||
"factorio_version": "0.16",
|
||||
"description": "Clusterio plugin for mapping and building through the browser. No more need to start game, download and catching up for every game, also mobile factorio confirmed?"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const assert = require("assert");
|
||||
|
||||
const isFactorioCommand = require("_app/isFactorioCommand");
|
||||
const isFactorioCommand = require("lib/isFactorioCommand");
|
||||
|
||||
const researchSync = require("./index.js");
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const clusterTools = require("_app/clusterTools")();
|
||||
const clusterTools = require("lib/clusterTools")();
|
||||
|
||||
module.exports = class remoteCommands {
|
||||
constructor(mergedConfig, messageInterface, extras){
|
||||
|
||||
@@ -9,9 +9,9 @@ module.exports = class remoteCommands {
|
||||
this.messageInterface = messageInterface;
|
||||
this.config = mergedConfig;
|
||||
this.socket = extras.socket;
|
||||
const pluginManager = require("./../../lib/manager/pluginManager.js")(this.config);
|
||||
const configManager = require("./../../lib/manager/configManager.js");
|
||||
// const modManager = require("./../../lib/manager/modManager.js")(this.config);
|
||||
const pluginManager = require("lib/manager/pluginManager.js")(this.config);
|
||||
const configManager = require("lib/manager/configManager.js");
|
||||
// const modManager = require("lib/manager/modManager.js")(this.config);
|
||||
this.socket.on("hello", () => {
|
||||
this.socket.emit("registerServerManager");
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ const needle = require("needle");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
|
||||
const clusterTools = require("_app/clusterTools")();
|
||||
const clusterTools = require("lib/clusterTools")();
|
||||
const pluginConfig = require("./config");
|
||||
|
||||
module.exports = class remoteCommands {
|
||||
|
||||
@@ -3,7 +3,7 @@ const path = require("path");
|
||||
|
||||
class masterPlugin {
|
||||
constructor({config, socketio, express, Prometheus}){
|
||||
const authenticate = require("./../../lib/authenticate")(config);
|
||||
const authenticate = require("lib/authenticate")(config);
|
||||
|
||||
this.config = config;
|
||||
this.io = socketio;
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@
|
||||
console.log(":)")
|
||||
/// ES6 imports
|
||||
// rules for how entities are drawn (sizes, offset etc)
|
||||
import {entityDrawRules} from "./lib/entityDrawRules.js";
|
||||
import {getParameterByName, drawImageWithRotation, isImageOk} from "./lib/utility.js";
|
||||
import {entityDrawRules} from "lib/entityDrawRules.js";
|
||||
import {getParameterByName, drawImageWithRotation, isImageOk} from "lib/utility.js";
|
||||
import spritesheetJson from "./pictures/spritesheet.js";
|
||||
import {itemSelector} from "./remoteMap/itemSelection.js";
|
||||
var global = {};
|
||||
|
||||
Reference in new issue
Block a user