mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f85c0575e | ||
|
|
cf2b3db20d | ||
|
|
a9c6430531 | ||
|
|
8a580a66be | ||
|
|
8ae5e11021 | ||
|
|
fac04adc6f | ||
|
|
d8f1d8a3e2 | ||
|
|
abcbf3fddc | ||
|
|
e5a4d51c86 | ||
|
|
97e615fc21 | ||
|
|
e990134b70 | ||
|
|
c2af8a2377 | ||
|
|
c9609259db | ||
|
|
3cddd2b9f8 | ||
|
|
8879f8dd80 | ||
|
|
4aca5050b9 | ||
|
|
89919f7a89 | ||
|
|
e2da9ac03d | ||
|
|
e8d9eb6916 | ||
|
|
8865509011 | ||
|
|
bcda88710e | ||
|
|
d6b0e88dda | ||
|
|
1021e29c98 | ||
|
|
96082e32b7 | ||
|
|
f70b4f74b1 | ||
|
|
ebb93ada94 | ||
|
|
597d080d71 | ||
|
|
e4f90b3480 | ||
|
|
8c4d2db192 | ||
|
|
152eb7d454 | ||
|
|
8e5d581b9b | ||
|
|
99990c7a0d | ||
|
|
ffd61b961d | ||
|
|
dcf216ca09 | ||
|
|
6bf2bf51f2 | ||
|
|
44b0a8ff83 | ||
|
|
d3f8c993b1 | ||
|
|
535e43b527 | ||
|
|
2df67e32da | ||
|
|
6b85567bc6 | ||
|
|
2e25831b4d | ||
|
|
021af793fc | ||
|
|
7ba14f7433 | ||
|
|
40f5a6ab7c | ||
|
|
48c31b4825 | ||
|
|
520007c8d1 |
No files matched your search
+2
-2
@@ -320,7 +320,7 @@ module.exports = {
|
||||
"semi-style": "off",
|
||||
"sort-imports": "off",
|
||||
"sort-keys": "off",
|
||||
"sort-vars": "error",
|
||||
"sort-vars": "off",
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": "off",
|
||||
"space-in-parens": ["error", "never"],
|
||||
@@ -391,7 +391,7 @@ module.exports = {
|
||||
"node/process-exit-as-throw": "error",
|
||||
"node/shebang": ["error", {
|
||||
"convertPath": {
|
||||
"*.ts": ["^(.+)\\.ts$", "dist/$1.js"],
|
||||
"*.ts": ["^(.+)\\.ts$", "dist/node/$1.js"],
|
||||
},
|
||||
}],
|
||||
},
|
||||
|
||||
+32
-1
@@ -1,8 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## Version 2.0.0-alpha.17
|
||||
|
||||
### Features
|
||||
|
||||
- Made the host an instance is assigned to link to the host page for that host in the Web UI [#587](https://github.com/clusterio/clusterio/pull/587).
|
||||
- Added pagination to the saves list.
|
||||
|
||||
### Changes
|
||||
|
||||
- Fixed an unhandled error in Subspace Storage crashing the host.
|
||||
- Fixed an unhandled rejection in disconnect logic crashing the host.
|
||||
- Fixed instance screen in Web UI not updating when an instance was reassigned.
|
||||
- Fixed an error thrown in the rendering of one page in the Web UI causing all pages to become inaccessible.
|
||||
- Fixed Web UI not showing instance saves when the permission to list and subscribe the saves is granted but the user is not a cluster admin.
|
||||
- Similarly fixed Web UI not showing the mod pack create, delete, list and host revoke token actions if the user is not a cluster admin.
|
||||
- Fixed Research Sync applying progress of a previous techology level to the level on the controller when it's the active research during instance startup.
|
||||
- Fixed data export with a mod using shorthand notation for the default value of a color-setting causing the ModPack data to be set to invalid data.
|
||||
- Fixed per user per instance player stats not being deleted when the instance it's for is deleted.
|
||||
- Fixed mods setting the item prototype icons property to an object mapping instead of an array breaking the data export.
|
||||
- Fixed a bad mod in the host's mods folder causing instance start to fail instead of the host re-downloading the mod.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Renamed the permission `core.instance.save.list.subscribe` to `core.instance.save.subscribe`.
|
||||
|
||||
Many thanks to the following for contributing to this release:
|
||||
[@Cooldude2606](https://github.com/Cooldude2606)
|
||||
[@Danielv123](https://github.com/Danielv123)
|
||||
[@Hornwitser](https://github.com/Hornwitser)
|
||||
|
||||
|
||||
## Version 2.0.0-alpha.16
|
||||
|
||||
## Changes
|
||||
### Changes
|
||||
|
||||
- Fixed message sequences not being renumbered when forwarded between links causing reconnects to send duplicated messages.
|
||||
|
||||
|
||||
@@ -270,6 +270,10 @@ If you want to use a different user for the command line interface, you can gene
|
||||
|
||||
- When the `server_select` plugin is installed you may see `[error] Unhandled event server_select:UpdateInstancesEvent` logged when starting an instance.
|
||||
This should be ignored as it does not indicate an error occured.
|
||||
- After updating Factorio you may get the error `Error: Expected empty response but got "Cannot execute command. Error: [string "clusterio_private.update_instance(..."]:1: attempt to index global 'clusterio_private' (a nil value)?` on instance start.
|
||||
This is due to Factorio applying a migration replacing the patched content of the save with the original scenario, attempting to start the instance again should resolve the issue.
|
||||
- In the mod pack view of the web interface the "base" mod is displayed with an error complaining about the mod being missing from the controller storage.
|
||||
This is due to the "base" mod currently not being connectly handled as a built-in mod to Factorio in the web interface, this error message should be ignored.
|
||||
- After migrating from Alpha 13 to Alpha 14 running an instance data export fails with the error `Upload failed: 401 Unauthorized` due to the host token being issued to a slave.
|
||||
To workaround this find the id of the host in the host config and generate a new host token with the same id.
|
||||
Stop the host and replace the value of the `host.controller_token` field in `config-host.json` with the new host token before starting it up again.
|
||||
|
||||
@@ -81,6 +81,9 @@ Note that to use `clusterioctl` you will have to create a control config first:
|
||||
|
||||
Once you've set up the cluster you can use `node packages/controller run` to run the controller and `node packages/host run` to run the host which connects to the controller.
|
||||
|
||||
When editing TypeScript files ran by Node.js you need to run `pnpm build` to compile the files to JavaScript before restarting the controller/host/ctl for changes take effect.
|
||||
To have TypeScript watch for changes and rebuild immediadly you may run `pnpm watch` instead.
|
||||
|
||||
For web development on the controller there are also the following flags:
|
||||
|
||||
`--dev` - Recompile the `web_ui` package on the fly providing live reloading of it.
|
||||
@@ -316,6 +319,6 @@ You will need commit access to the repository as well publish access to the @clu
|
||||
4. Publish the packages.
|
||||
|
||||
```sh
|
||||
# Warning: Double check your current working directory is the repository before running this
|
||||
pnpm -r --filter='!./external_plugins/**' publish
|
||||
rm -r ./{plugins,packages}/*/dist
|
||||
pnpm -r --filter='./{plugins,packages}/*' publish
|
||||
```
|
||||
@@ -78,7 +78,7 @@ import { BaseInstancePlugin } from "@clusterio/host";
|
||||
|
||||
Previously the info of a plugin would be loaded by requiring `./plugin_dir/info`, this has been changed to require `./plugin_dir` which in turn means that `info.js` is no longer the default entrypoint.
|
||||
To solve this you can either add `"main": "info.js",` to package.json or rename `info.js` to `index.js` (the rest of this guide assumes the entrypoint was renamed to `index.js`).
|
||||
The motivation behind this change was to allow TypeScript to output its build to a sub-folder of the plugin, if you're using TypeScript you'll likely have the entrypoint be located in `dist/index.js` instead.
|
||||
The motivation behind this change was to allow TypeScript to output its build to a sub-folder of the plugin, if you're using TypeScript you'll likely have the entrypoint be located in `dist/node/index.js` instead.
|
||||
|
||||
The expected place where the declaration of a plugin is exported has also changed from `module.exports` to `module.exports.plugin`:
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"watch": "tsc --build --watch",
|
||||
"test": "pnpm run build && mocha --check-leaks -R spec --recursive test \"plugins/*/test/**\"",
|
||||
"fast-test": "FAST_TEST=y pnpm test",
|
||||
"cover": "nyc pnpm test",
|
||||
@@ -27,6 +28,7 @@
|
||||
"@clusterio/ctl": "workspace:*",
|
||||
"@clusterio/host": "workspace:*",
|
||||
"@clusterio/lib": "workspace:*",
|
||||
"@swc/core": "^1.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/parser": "^6.4.1",
|
||||
"eslint": "^8.45.0",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -3,14 +3,14 @@
|
||||
"description": "Implementation of Clusterio controller",
|
||||
"author": "Hornwitser",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"main": "dist/index.js",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"bin": {
|
||||
"clusteriocontroller": "./dist/controller.js"
|
||||
"clusteriocontroller": "./dist/node/controller.js"
|
||||
},
|
||||
"keywords": [
|
||||
"factorio"
|
||||
@@ -19,7 +19,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@sinclair/typebox": "^0.30.4",
|
||||
"busboy": "^1.6.0",
|
||||
"compression": "^1.7.4",
|
||||
@@ -35,7 +35,8 @@
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@swc/core": "^1.4.0",
|
||||
"@types/busboy": "^1.5.0",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/express": "^4.17.17",
|
||||
|
||||
@@ -274,7 +274,7 @@ export default class Controller {
|
||||
Controller.addAppRoutes(this.app, this.pluginInfos);
|
||||
|
||||
if (!args.dev) {
|
||||
let manifestPath = path.join(__dirname, "..", "web", "manifest.json");
|
||||
let manifestPath = path.join(__dirname, "..", "..", "web", "manifest.json");
|
||||
|
||||
let manifest = await Controller.loadJsonObject(manifestPath);
|
||||
if (!manifest["main.js"]) {
|
||||
@@ -333,7 +333,7 @@ export default class Controller {
|
||||
|
||||
if (args.dev) {
|
||||
// eslint-disable-next-line node/no-missing-require
|
||||
webpackConfigs.push(require("../../webpack.config")({})); // Path outside of build
|
||||
webpackConfigs.push(require("../../../webpack.config")({})); // Path outside of build
|
||||
}
|
||||
if (args.devPlugin) {
|
||||
let devPlugins = new Map();
|
||||
@@ -737,7 +737,7 @@ export default class Controller {
|
||||
// Set folder to serve static content from (the website)
|
||||
const staticOptions = { immutable: true, maxAge: 1000 * 86400 * 365 };
|
||||
app.use("/static",
|
||||
express.static(path.join(__dirname, "..", "..", "dist", "web", "static"), staticOptions)
|
||||
express.static(path.join(__dirname, "..", "..", "web", "static"), staticOptions)
|
||||
);
|
||||
app.use("/static", express.static("static", staticOptions)); // Used for data export files
|
||||
|
||||
@@ -876,9 +876,11 @@ export default class Controller {
|
||||
*/
|
||||
clearSavesOfInstance(instanceId: number) {
|
||||
const updates = [];
|
||||
const now = Date.now();
|
||||
for (const [id, save] of this.saves) {
|
||||
if (save.instanceId === instanceId) {
|
||||
save.isDeleted = true;
|
||||
save.updatedAtMs = now;
|
||||
updates.push(save);
|
||||
this.saves.delete(id);
|
||||
}
|
||||
@@ -939,9 +941,9 @@ export default class Controller {
|
||||
);
|
||||
} else {
|
||||
instance.status = "unassigned";
|
||||
instance.updatedAtMs = Date.now();
|
||||
this.instanceDetailsUpdated([instance]);
|
||||
}
|
||||
instance.updatedAtMs = Date.now();
|
||||
this.instanceDetailsUpdated([instance]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -962,6 +964,7 @@ export default class Controller {
|
||||
this.instancesDirty = true;
|
||||
|
||||
this.clearSavesOfInstance(instanceId);
|
||||
this.userManager.clearStatsOfInstance(instanceId);
|
||||
|
||||
let prev = instance.status;
|
||||
instance.status = "deleted";
|
||||
@@ -1224,7 +1227,7 @@ export default class Controller {
|
||||
mainBundle = stats.toJson().assetsByChunkName["main"];
|
||||
}
|
||||
|
||||
fs.readFile(path.join(__dirname, "..", "..", "web", "index.html"), "utf8").then((content) => {
|
||||
fs.readFile(path.join(__dirname, "..", "..", "..", "web", "index.html"), "utf8").then((content) => {
|
||||
res.type("text/html");
|
||||
res.send(content
|
||||
.replace(/__CLUSTER_NAME__/g, res.app.locals.controller.config.get("controller.name"))
|
||||
|
||||
@@ -178,7 +178,10 @@ export default class HostConnection extends BaseConnection {
|
||||
// changed. Hosts also send status updates on assignInstance which
|
||||
// for hacky reason is also used to push config changes and
|
||||
// restablish status after a connection loss.
|
||||
if (instance.status === request.status) {
|
||||
if (
|
||||
instance.status === request.status
|
||||
&& instance.gamePort === request.gamePort
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,4 +113,13 @@ export default class UserManager {
|
||||
this.onlineUsers.delete(user);
|
||||
}
|
||||
}
|
||||
|
||||
clearStatsOfInstance(instanceId: number) {
|
||||
for (const user of this.users.values()) {
|
||||
this.notifyLeave(user, instanceId);
|
||||
user.instanceStats.delete(instanceId);
|
||||
user.recalculatePlayerStats();
|
||||
}
|
||||
this.dirty = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.browser.json" },
|
||||
{ "path": "../web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"rootDir": "..",
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["index.ts", "controller.ts", "package.json", "src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["index.ts", "controller.ts", "package.json", "src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.web.json" },
|
||||
{ "path": "../web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"rootDir": "..",
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Installer for clusterio",
|
||||
"author": "Hornwitser",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"main": "create.js",
|
||||
"bin": {
|
||||
"clusteriocreate": "./create.js"
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"description": "Clusterio command line management tool",
|
||||
"author": "Hornwitser",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"main": "dist/index.js",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"bin": {
|
||||
"clusterioctl": "./dist/ctl.js"
|
||||
"clusterioctl": "./dist/node/ctl.js"
|
||||
},
|
||||
"keywords": [
|
||||
"factorio"
|
||||
@@ -19,7 +19,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"as-table": "^1.0.55",
|
||||
"fs-extra": "^11.1.1",
|
||||
"phin": "^3.7.0",
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
],
|
||||
"include": ["index.ts", "package.json", "ctl.ts", "src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["index.ts", "package.json", "ctl.ts", "src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -3,14 +3,14 @@
|
||||
"description": "Implementation of Clusterio host server",
|
||||
"author": "Hornwitser",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"main": "dist/index.js",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"bin": {
|
||||
"clusteriohost": "./dist/host.js"
|
||||
"clusteriohost": "./dist/node/host.js"
|
||||
},
|
||||
"keywords": [
|
||||
"factorio"
|
||||
@@ -19,7 +19,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@sinclair/typebox": "^0.30.4",
|
||||
"fs-extra": "^11.1.1",
|
||||
"jimp": "^0.22.8",
|
||||
|
||||
@@ -600,8 +600,9 @@ export default class Host extends lib.Link {
|
||||
try {
|
||||
modInfo = await this.modStore.loadMod(mod.name, mod.version);
|
||||
} catch (err: any) {
|
||||
// A broken zip file may have been left in the mods folder
|
||||
if (err.code !== "ENOENT") {
|
||||
throw err;
|
||||
logger.error(`Error loading mod ${mod.name} ${mod.version}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
if (modInfo && (!mod.sha1 || mod.sha1 === modInfo.sha1)) {
|
||||
|
||||
@@ -800,7 +800,7 @@ rcon.print(game.table_to_json(players))`.replace(/\r?\n/g, " ");
|
||||
|
||||
// Find stand alone modules to load
|
||||
// XXX for now only the included clusterio module is loaded
|
||||
let modulesDirectory = path.join(__dirname, "..", "..", "modules");
|
||||
let modulesDirectory = path.join(__dirname, "..", "..", "..", "modules");
|
||||
for (let entry of await fs.readdir(modulesDirectory, { withFileTypes: true })) {
|
||||
if (entry.isDirectory()) {
|
||||
if (modules.has(entry.name)) {
|
||||
|
||||
+19
-13
@@ -5,7 +5,7 @@ import Jimp from "jimp";
|
||||
import JSZip from "jszip";
|
||||
|
||||
import * as lib from "@clusterio/lib";
|
||||
import * as libBuildMod from "@clusterio/lib/dist/build_mod";
|
||||
import * as libBuildMod from "@clusterio/lib/dist/node/build_mod";
|
||||
import type { FactorioServer } from "./server";
|
||||
|
||||
interface Prototype {
|
||||
@@ -18,11 +18,10 @@ interface SimpleIconSpecification {
|
||||
icon_size: number;
|
||||
icon_mipmaps?: number;
|
||||
}
|
||||
type Color = { r: number, g: number, b: number, a: number };
|
||||
interface IconLayer {
|
||||
icon: string;
|
||||
icon_size: number;
|
||||
tint?: Color,
|
||||
tint?: lib.FactorioColor,
|
||||
shift?: [number, number],
|
||||
scale?: number,
|
||||
icon_mipmaps?: number,
|
||||
@@ -62,7 +61,7 @@ async function generateExportMod(server: FactorioServer) {
|
||||
clean: false,
|
||||
build: true,
|
||||
pack: true,
|
||||
sourceDir: path.join(__dirname, "..", "..", "lua", "export"),
|
||||
sourceDir: path.join(__dirname, "..", "..", "..", "lua", "export"),
|
||||
outputDir: server.writePath("mods"),
|
||||
bumpPatch: false,
|
||||
factorioVersion: server.version.replace(/\.\d+$/, ""),
|
||||
@@ -202,15 +201,9 @@ async function loadLayeredIcon(
|
||||
|
||||
iconLayer = iconLayer.clone();
|
||||
|
||||
let tint: Color;
|
||||
let tint: lib.Color;
|
||||
if (layer.tint) {
|
||||
let divisor = (layer.tint.r > 1 || layer.tint.g > 1 || layer.tint.b > 1) ? 255 : 1;
|
||||
tint = {
|
||||
r: (layer.tint.r || 0) / divisor,
|
||||
g: (layer.tint.g || 0) / divisor,
|
||||
b: (layer.tint.b || 0) / divisor,
|
||||
a: layer.tint.a || 1,
|
||||
};
|
||||
tint = lib.normalizeColor(layer.tint);
|
||||
} else {
|
||||
tint = { r: 1, b: 1, g: 1, a: 1 };
|
||||
}
|
||||
@@ -252,6 +245,18 @@ async function loadLayeredIcon(
|
||||
return icon;
|
||||
}
|
||||
|
||||
function fixIcons(item: ItemPrototype) {
|
||||
const icons = item.icons;
|
||||
if (typeof icons === "object" && !(icons instanceof Array) && icons !== null) {
|
||||
// It's possible to specify icons as an object with arbitrary keys
|
||||
// and the game will still accept it, cast the value to array if
|
||||
// this is the case.
|
||||
item = { ...item };
|
||||
item.icons = Object.values(icons);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
const itemTypes = new Set([
|
||||
"item",
|
||||
"ammo",
|
||||
@@ -284,7 +289,8 @@ const itemTypes = new Set([
|
||||
function filterItems(prototypes: Prototypes): ItemPrototype[] {
|
||||
return Object.entries(prototypes)
|
||||
.filter(([type, _]) => itemTypes.has(type))
|
||||
.flatMap(([_, typePrototypes]) => Object.values(typePrototypes)) as ItemPrototype[]
|
||||
.flatMap(([_, typePrototypes]) => Object.values(typePrototypes) as ItemPrototype[])
|
||||
.map(fixIcons)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
],
|
||||
"include": ["index.ts", "host.ts", "package.json", "src/**/*.ts", "src/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["index.ts", "host.ts", "package.json", "src/**/*.ts", "src/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -6,6 +6,7 @@ export * from "./src/factorio/exchange_string";
|
||||
export * from "./src/helpers";
|
||||
export * from "./src/ini";
|
||||
export * from "./src/link";
|
||||
export * from "./src/lua_tools";
|
||||
export * from "./src/logging";
|
||||
export * from "./src/permissions";
|
||||
export * from "./src/plugin";
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "@clusterio/lib",
|
||||
"description": "Shared library for Clusterio",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"repository": "https://github.com/clusterio/clusterio",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && pnpm run compile_validator",
|
||||
"compile_validator": "node scripts/compile_validator.js"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"browser": "browser.ts",
|
||||
"keywords": [
|
||||
"factorio"
|
||||
|
||||
@@ -7,9 +7,9 @@ const standaloneCode = require("ajv/dist/standalone").default;
|
||||
/* eslint-disable node/no-sync */
|
||||
|
||||
// Provide an empty file for message_core to load
|
||||
fs.outputFileSync(path.join(__dirname, "../dist/src/data/message_validate.js"), "");
|
||||
fs.outputFileSync(path.join(__dirname, "../dist/node/src/data/message_validate.js"), "");
|
||||
|
||||
const { Message } = require("../dist/src/data/messages_core");
|
||||
const { Message } = require("../dist/node/src/data/messages_core");
|
||||
|
||||
const ajv = new Ajv({
|
||||
allowUnionTypes: true,
|
||||
@@ -28,5 +28,5 @@ function write(output) {
|
||||
`// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}`
|
||||
);
|
||||
}
|
||||
write("../dist/src/data/message_validate.js");
|
||||
write("../dist/node/src/data/message_validate.js");
|
||||
write("../src/data/message_validate.js");
|
||||
@@ -2,6 +2,7 @@ import zlib from "zlib";
|
||||
import { Type, Static } from "@sinclair/typebox";
|
||||
|
||||
import * as libSchema from "../schema";
|
||||
import * as libLuaTools from "../lua_tools";
|
||||
|
||||
import ExportManifest from "./ExportManifest";
|
||||
import { integerFactorioVersion } from "./version";
|
||||
@@ -363,7 +364,11 @@ export default class ModPack {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.settings[settingType].set(prototype.name, { value: prototype.default_value });
|
||||
let value = prototype.default_value;
|
||||
if (prototype.type === "color-setting") {
|
||||
value = libLuaTools.normalizeColor(value);
|
||||
}
|
||||
this.settings[settingType].set(prototype.name, { value });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,20 @@ export default class Role {
|
||||
permissions: Type.Array(Type.String()),
|
||||
});
|
||||
|
||||
static remapPermission(permission: string) {
|
||||
// migrate: core.instance.save.list.subscribe was renamed in alpha 17
|
||||
if (permission === "core.instance.save.list.subscribe") {
|
||||
return "core.instance.save.subscribe";
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
|
||||
static fromJSON(json: Static<typeof this.jsonSchema>) {
|
||||
return new this(
|
||||
json.id,
|
||||
json.name,
|
||||
json.description,
|
||||
new Set(json.permissions),
|
||||
new Set(json.permissions.map(this.remapPermission)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ export class InstanceSaveDetailsUpdatesEvent {
|
||||
static type = "event" as const;
|
||||
static src = ["instance", "host", "controller"] as const;
|
||||
static dst = ["controller", "control"] as const;
|
||||
static permission = "core.instance.save.list.subscribe" as const;
|
||||
static permission = "core.instance.save.subscribe" as const;
|
||||
|
||||
constructor(
|
||||
public updates: SaveDetails[],
|
||||
@@ -385,7 +385,7 @@ export class InstanceSaveDetailsUpdatesEvent {
|
||||
export class InstanceCreateSaveRequest {
|
||||
declare ["constructor"]: typeof InstanceCreateSaveRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.save.create" as const;
|
||||
|
||||
@@ -596,7 +596,7 @@ export class InstancePushSaveRequest {
|
||||
export class InstanceLoadScenarioRequest {
|
||||
declare ["constructor"]: typeof InstanceLoadScenarioRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.load_scenario" as const;
|
||||
|
||||
@@ -622,7 +622,7 @@ export class InstanceLoadScenarioRequest {
|
||||
export class InstanceExportDataRequest {
|
||||
declare ["constructor"]: typeof InstanceExportDataRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.export_data" as const;
|
||||
}
|
||||
@@ -630,7 +630,7 @@ export class InstanceExportDataRequest {
|
||||
export class InstanceExtractPlayersRequest {
|
||||
declare ["constructor"]: typeof InstanceExtractPlayersRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.extract_players" as const;
|
||||
}
|
||||
@@ -638,7 +638,7 @@ export class InstanceExtractPlayersRequest {
|
||||
export class InstanceStopRequest {
|
||||
declare ["constructor"]: typeof InstanceStopRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.stop" as const;
|
||||
}
|
||||
@@ -646,7 +646,7 @@ export class InstanceStopRequest {
|
||||
export class InstanceKillRequest {
|
||||
declare ["constructor"]: typeof InstanceKillRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.kill" as const;
|
||||
}
|
||||
@@ -693,7 +693,7 @@ export class InstanceDeleteInternalRequest {
|
||||
export class InstanceSendRconRequest {
|
||||
declare ["constructor"]: typeof InstanceSendRconRequest;
|
||||
static type = "request" as const;
|
||||
static src = "control" as const;
|
||||
static src = ["control", "controller"] as const;
|
||||
static dst = "instance" as const;
|
||||
static permission = "core.instance.send_rcon" as const;
|
||||
|
||||
|
||||
@@ -126,16 +126,15 @@ export class Link {
|
||||
});
|
||||
|
||||
connector.on("disconnectPrepare", () => {
|
||||
this.prepareDisconnect().catch(
|
||||
err => {
|
||||
logger.error(`Unexpected error preparing disconnect:\n${err.stack}`);
|
||||
this.prepareDisconnect().finally(() => {
|
||||
// Only WebSocket connectors issue disconnection events.
|
||||
const webSocketConnector = this.connector as WebSocketBaseConnector;
|
||||
if (webSocketConnector.hasSession) {
|
||||
webSocketConnector.sendDisconnectReady();
|
||||
}
|
||||
).finally(
|
||||
() => {
|
||||
// Only WebSocket connectors issue disconnection events.
|
||||
(this.connector as WebSocketBaseConnector).sendDisconnectReady();
|
||||
},
|
||||
);
|
||||
}).catch(err => {
|
||||
logger.error(`Unexpected error preparing disconnect:\n${err.stack}`);
|
||||
});
|
||||
});
|
||||
|
||||
connector.on("invalidate", () => {
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
* @module lib/lua_tools
|
||||
*/
|
||||
|
||||
/**
|
||||
* A colour as it may originate from Factorio Lua code, see
|
||||
* https://lua-api.factorio.com/latest/types/Color.html
|
||||
*/
|
||||
export type FactorioColor = { r?: number, g?: number, b?: number, a?: number } | [number, number, number, number?];
|
||||
|
||||
export type Color = { r: number, g: number, b: number, a: number };
|
||||
|
||||
/**
|
||||
* Normalise a color as it may appear in Factorio Lua code to be an object
|
||||
* with r, g, b, a properties in the range 0 to 1.
|
||||
*
|
||||
* @param color -
|
||||
* A colour that may be specified with left out components or as an
|
||||
* array and have the range 1 to 0 or 255 to 0.
|
||||
* @returns
|
||||
* Normalised colour where all 4 components are present as properties in
|
||||
* the range 0 to 1.
|
||||
*/
|
||||
export function normalizeColor(color: FactorioColor) {
|
||||
let r, g, b, a;
|
||||
if (color instanceof Array) {
|
||||
([r, g, b, a] = color);
|
||||
} else {
|
||||
({ r, g, b, a } = color);
|
||||
}
|
||||
|
||||
r ??= NaN;
|
||||
g ??= NaN;
|
||||
b ??= NaN;
|
||||
a ??= NaN;
|
||||
|
||||
// Note NaN > 1 is false and NaN / 255 is NaN
|
||||
if (r > 1 || g > 1 || b > 1 || a > 1) {
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
a /= 255;
|
||||
}
|
||||
|
||||
return {
|
||||
r: Number.isNaN(r) ? 0 : r,
|
||||
g: Number.isNaN(g) ? 0 : g,
|
||||
b: Number.isNaN(b) ? 0 : b,
|
||||
a: Number.isNaN(a) ? 1 : a,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string for inclusion into a lua string
|
||||
*
|
||||
|
||||
@@ -239,11 +239,11 @@ definePermission({
|
||||
definePermission({
|
||||
name: "core.instance.save.list",
|
||||
title: "List saves",
|
||||
description: "List the saves currently on the instance.",
|
||||
description: "List the saves currently on instances.",
|
||||
});
|
||||
definePermission({
|
||||
name: "core.instance.save.list.subscribe",
|
||||
title: "Subscribe to save list updatess",
|
||||
name: "core.instance.save.subscribe",
|
||||
title: "Subscribe to save updates",
|
||||
description: "Subscribe to be notifed on updates to the list of saves of instances.",
|
||||
});
|
||||
definePermission({
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Type, Static } from "@sinclair/typebox";
|
||||
import { Link, Event, EventClass, RequestHandler, WebSocketClientConnector, WebSocketBaseConnector } from "./link";
|
||||
import { logger } from "./logging";
|
||||
import { Address, MessageRequest, IControllerUser } from "./data";
|
||||
import isDeepStrictEqual from "./is_deep_strict_equal";
|
||||
|
||||
export type SubscriptionRequestHandler<T> = RequestHandler<SubscriptionRequest, Event<T> | null>;
|
||||
export type EventSubscriberCallback<T> = (updates: T[], synced: boolean) => void
|
||||
@@ -204,6 +205,14 @@ export class EventSubscriber<
|
||||
async _handle(event: EventSubscribable<T, V>) {
|
||||
for (const value of event.updates) {
|
||||
this.lastResponseTimeMs = Math.max(this.lastResponseTimeMs, value.updatedAtMs);
|
||||
// Warn about updates which changes content but don't update updatedAtMs
|
||||
const existing = this.values.get(value.id as K);
|
||||
if (existing && existing.updatedAtMs === value.updatedAtMs && !isDeepStrictEqual(existing, value)) {
|
||||
logger.warn(
|
||||
`${this.Event.name} contains update for value with id ${value.id} that has an identical ` +
|
||||
"updatedAtMs timestamp but differing content"
|
||||
);
|
||||
}
|
||||
if (value.isDeleted) {
|
||||
this.values.delete(value.id as K);
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"include": [
|
||||
"browser.ts",
|
||||
"package.json",
|
||||
@@ -12,6 +12,7 @@
|
||||
"./src/hash.ts",
|
||||
"./src/ini.ts",
|
||||
"./src/link/*.ts",
|
||||
"./src/lua_tools.ts",
|
||||
"./src/logging.ts",
|
||||
"./src/permissions.ts",
|
||||
"./src/prometheus.ts",
|
||||
@@ -25,6 +26,6 @@
|
||||
],
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "dist/web/build",
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"include": ["index.ts", "browser.ts", "build_mod.js", "src/**/*.ts", "src/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"allowJs": true,
|
||||
},
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"include": ["index.ts", "browser.ts", "build_mod.js", "src/**/*.ts", "src/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
"allowJs": true,
|
||||
},
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
"description": "Clusterio web interface implementation",
|
||||
"author": "Hornwitser",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"browser": "src/index.ts",
|
||||
"main": "dist/src/index.js",
|
||||
"main": "dist/browser/src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
@@ -17,12 +17,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.1.4",
|
||||
"@babel/core": "^7.22.9",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@swc/core": "^1.4.0",
|
||||
"antd": "^5.13.0",
|
||||
"assert": "^2.0.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"buffer": "^6.0.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
@@ -39,8 +37,8 @@
|
||||
"react-router-dom": "^6.14.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.3",
|
||||
"swc-loader": "^0.2.6",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"ts-loader": "^9.4.4",
|
||||
"typescript": "^5.1.6",
|
||||
"util": "^0.12.5",
|
||||
"webpack": "^5.88.2",
|
||||
|
||||
@@ -104,16 +104,16 @@ export default function App(props: AppProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary Component={ErrorCard}>
|
||||
<ConfigProvider
|
||||
theme={{ algorithm: theme.darkAlgorithm }}
|
||||
>
|
||||
<ConfigProvider
|
||||
theme={{ algorithm: theme.darkAlgorithm }}
|
||||
>
|
||||
<ErrorBoundary Component={ErrorCard}>
|
||||
<ControlContext.Provider value={props.control}>
|
||||
<BrowserRouter basename={webRoot}>
|
||||
{page}
|
||||
</BrowserRouter>
|
||||
</ControlContext.Provider>
|
||||
</ConfigProvider>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export default function HostViewPage() {
|
||||
|
||||
let hostButtons = <Space>
|
||||
{
|
||||
account.hasPermission("core.host.revoke_access")
|
||||
account.hasPermission("core.host.revoke_token")
|
||||
&& <Popconfirm
|
||||
title={`Revoke tokens of ${host.name}?`}
|
||||
placement="bottomRight"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { message, Button, Space, Table } from "antd";
|
||||
import { message, Button, Space, Table, Typography } from "antd";
|
||||
import CopyOutlined from "@ant-design/icons/CopyOutlined";
|
||||
import type { SizeType } from "antd/es/config-provider/SizeContext";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
@@ -10,6 +10,7 @@ import { useHosts } from "../model/host";
|
||||
import InstanceStatusTag from "./InstanceStatusTag";
|
||||
import StartStopInstanceButton from "./StartStopInstanceButton";
|
||||
import { InstanceDetails } from "@clusterio/lib";
|
||||
import Link from "./Link";
|
||||
|
||||
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
|
||||
|
||||
@@ -55,7 +56,12 @@ export default function InstanceList(props: InstanceListProps) {
|
||||
{
|
||||
title: "Assigned Host",
|
||||
key: "assignedHost",
|
||||
render: (_, instance) => hostName(instance.assignedHost),
|
||||
render: (_, instance) => <Link
|
||||
to={`/hosts/${instance.assignedHost}/view`}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{hostName(instance.assignedHost)}
|
||||
</Link>,
|
||||
sorter: (a, b) => strcmp(hostName(a.assignedHost), hostName(b.assignedHost)),
|
||||
responsive: ["sm"],
|
||||
},
|
||||
@@ -68,10 +74,10 @@ export default function InstanceList(props: InstanceListProps) {
|
||||
{publicAddress}
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CopyOutlined/>}
|
||||
icon={<CopyOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(publicAddress??"");
|
||||
navigator.clipboard.writeText(publicAddress ?? "");
|
||||
message.success("Copied public address!");
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { notifyErrorHandler } from "../util/notify";
|
||||
import { useInstance } from "../model/instance";
|
||||
import { useHost } from "../model/host";
|
||||
import InstanceStatusTag from "./InstanceStatusTag";
|
||||
import Link from "./Link";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
@@ -42,7 +43,9 @@ function InstanceDescription(props: InstanceDescriptionProps) {
|
||||
<Descriptions.Item label="Host">
|
||||
{!assigned
|
||||
? <em>Unassigned</em>
|
||||
: host?.name ?? instance.assignedHost
|
||||
: <Link to={`/hosts/${host?.id ?? instance.assignedHost}/view`}>
|
||||
{host?.name ?? instance.assignedHost}
|
||||
</Link>
|
||||
}
|
||||
{account.hasPermission("core.instance.assign") && <AssignInstanceModal
|
||||
id={instance.id}
|
||||
@@ -104,7 +107,7 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
|
||||
}
|
||||
let instanceButtonsMenuProps: MenuProps = {
|
||||
items: instanceButtonMenuItems,
|
||||
onClick: ({ key }: { key:string }) => {
|
||||
onClick: ({ key }: { key: string }) => {
|
||||
if (key === "export") {
|
||||
setExportingData(true);
|
||||
control.sendTo(
|
||||
@@ -156,9 +159,10 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
|
||||
"core.instance.extract_players",
|
||||
"core.instance.kill",
|
||||
"core.instance.delete",
|
||||
) && <Dropdown placement="bottomRight" trigger={["click"]} menu={instanceButtonsMenuProps}>
|
||||
<Button>More <DownOutlined /></Button>
|
||||
</Dropdown>}
|
||||
)
|
||||
&& <Dropdown placement="bottomRight" trigger={["click"]} menu={instanceButtonsMenuProps}>
|
||||
<Button>More <DownOutlined /></Button>
|
||||
</Dropdown>}
|
||||
</Space>;
|
||||
}
|
||||
|
||||
@@ -180,7 +184,7 @@ export default function InstanceViewPage() {
|
||||
|
||||
return <PageLayout nav={nav}>
|
||||
<Alert
|
||||
message={"Instance not found" }
|
||||
message={"Instance not found"}
|
||||
showIcon
|
||||
description={<>Instance with id {instanceId} was not found on the controller.</>}
|
||||
type="warning"
|
||||
@@ -198,13 +202,13 @@ export default function InstanceViewPage() {
|
||||
|
||||
return <PageLayout nav={nav}>
|
||||
<PageHeader
|
||||
title={instance.name??""}
|
||||
extra={<InstanceButtons instance={instance}/>}
|
||||
title={instance.name ?? ""}
|
||||
extra={<InstanceButtons instance={instance} />}
|
||||
/>
|
||||
<InstanceDescription host={host} instance={instance} />
|
||||
|
||||
{
|
||||
account.hasAllPermission("core.instance.save.list", "core.instance.save.list_subscribe")
|
||||
account.hasAllPermission("core.instance.save.list", "core.instance.save.subscribe")
|
||||
&& <SavesList instance={instance} />
|
||||
}
|
||||
{
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import { Typography } from "antd";
|
||||
import {
|
||||
LinkProps,
|
||||
useHref,
|
||||
useLinkClickHandler,
|
||||
} from "react-router-dom";
|
||||
import { BaseType } from "antd/es/typography/Base";
|
||||
|
||||
const Link = React.forwardRef(
|
||||
(
|
||||
{
|
||||
onClick,
|
||||
replace = false,
|
||||
state,
|
||||
target,
|
||||
to,
|
||||
type,
|
||||
...rest
|
||||
}: LinkProps,
|
||||
ref
|
||||
) => {
|
||||
let href = useHref(to);
|
||||
let handleClick = useLinkClickHandler(to, {
|
||||
replace,
|
||||
state,
|
||||
target,
|
||||
}) as (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
|
||||
return (
|
||||
<Typography.Link
|
||||
{...rest}
|
||||
href={href}
|
||||
onClick={(event) => {
|
||||
onClick?.(event as React.MouseEvent<HTMLAnchorElement, MouseEvent>);
|
||||
if (!event.defaultPrevented) {
|
||||
handleClick(event);
|
||||
}
|
||||
}}
|
||||
ref={ref as React.RefObject<HTMLElement> | null}
|
||||
target={target}
|
||||
type={type as BaseType}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
export default Link;
|
||||
@@ -202,7 +202,7 @@ function SearchModsTable(props: SearchModsTableProps) {
|
||||
align: "right",
|
||||
render: (_, result) => <Select
|
||||
size="small"
|
||||
bordered={false}
|
||||
variant="borderless"
|
||||
value={modResultSelectedVersion.get(result.name) || 0}
|
||||
onChange={index => {
|
||||
const newVersions = new Map(modResultSelectedVersion);
|
||||
@@ -801,7 +801,7 @@ export default function ModPackViewPage() {
|
||||
title={modPack.name}
|
||||
extra={<Space>
|
||||
<ExportButton modPack={modifiedModPack}/>
|
||||
{account.hasPermission("core.mod-pack.delete") && <Popconfirm
|
||||
{account.hasPermission("core.mod_pack.delete") && <Popconfirm
|
||||
title="Delete mod pack and all of its settings?"
|
||||
placement="bottomRight"
|
||||
okText="Delete"
|
||||
|
||||
@@ -186,8 +186,8 @@ export default function ModsPage() {
|
||||
<SectionHeader
|
||||
title="Mod Packs"
|
||||
extra={<Space>
|
||||
{account.hasPermission("core.mod-pack.create") && <ImportModPackButton />}
|
||||
{account.hasPermission("core.mod-pack.create") && <CreateModPackButton />}
|
||||
{account.hasPermission("core.mod_pack.create") && <ImportModPackButton />}
|
||||
{account.hasPermission("core.mod_pack.create") && <CreateModPackButton />}
|
||||
</Space>}
|
||||
/>
|
||||
<Table
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Layout, Breadcrumb } from "antd";
|
||||
import Link from "./Link";
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
||||
@@ -207,6 +207,7 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) {
|
||||
|
||||
let hostOffline = ["unassigned", "unknown"].includes(props.instance.status!);
|
||||
const saveTable = <Table
|
||||
className="save-list-table"
|
||||
size="small"
|
||||
columns={[
|
||||
{
|
||||
@@ -236,7 +237,6 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) {
|
||||
]}
|
||||
dataSource={[...saves.values()]}
|
||||
rowKey={save => save.name}
|
||||
pagination={false}
|
||||
expandable={{
|
||||
columnWidth: 33,
|
||||
expandRowByClick: true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import React, { Fragment, useContext, useEffect, useState } from "react";
|
||||
import { Route, Routes, useNavigate } from "react-router-dom";
|
||||
import { Dropdown, Layout, Menu, MenuProps } from "antd";
|
||||
import UserOutlined from "@ant-design/icons/UserOutlined";
|
||||
@@ -115,12 +115,12 @@ export default function SiteLayout() {
|
||||
{combinedPages.map(({path, sidebarPath, content}) => <Route
|
||||
path={path}
|
||||
key={path}
|
||||
element={<>
|
||||
element={<Fragment key={path}>
|
||||
<SetSidebar path={sidebarPath ? sidebarPath : path} />
|
||||
<ErrorBoundary Component={ErrorPage}>
|
||||
{content}
|
||||
</ErrorBoundary>
|
||||
</>}
|
||||
</Fragment>}
|
||||
/>)}
|
||||
<Route element={<SetSidebar path={null} />} />
|
||||
</Routes>
|
||||
|
||||
@@ -88,7 +88,6 @@ export default function UserViewPage() {
|
||||
onChange={() => setRolesDirty(true)}
|
||||
loading={!roles}
|
||||
mode="multiple"
|
||||
showArrow={true}
|
||||
filterOption={(inputValue:string, option: any): boolean => {
|
||||
let role = roles.get(option.value);
|
||||
return role?.name.toLowerCase().includes(inputValue.toLowerCase()) ?? false;
|
||||
|
||||
@@ -12,6 +12,7 @@ import PageHeader from "./PageHeader";
|
||||
import PageLayout from "./PageLayout";
|
||||
import PluginExtra from "./PluginExtra";
|
||||
import { formatFirstSeen, formatLastSeen, sortFirstSeen, sortLastSeen, useUsers } from "../model/user";
|
||||
import Link from "./Link";
|
||||
|
||||
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
|
||||
|
||||
@@ -49,7 +50,7 @@ function CreateUserButton() {
|
||||
>
|
||||
<Form form={form}>
|
||||
<Form.Item name="userName" label="Name">
|
||||
<Input/>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
@@ -97,7 +98,9 @@ export default function UsersPage() {
|
||||
title: "Roles",
|
||||
key: "roles",
|
||||
render: (_, user) => (
|
||||
[...user.roleIds].map(id => <Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>)
|
||||
[...user.roleIds].map(id => <Link to={`/roles/${id}/view`} onClick={e => e.stopPropagation()}>
|
||||
<Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>
|
||||
</Link>)
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,3 +28,5 @@ export { default as PageHeader } from "./components/PageHeader";
|
||||
export { default as PageLayout } from "./components/PageLayout";
|
||||
export { default as SavesList } from "./components/SavesList";
|
||||
export { default as SectionHeader } from "./components/SectionHeader";
|
||||
|
||||
export { default as Link } from "./components/Link";
|
||||
@@ -22,6 +22,12 @@ export function useAccount(): UserAccount {
|
||||
};
|
||||
}, [control]);
|
||||
|
||||
function checkPermissionExists(permission: string) {
|
||||
if (!lib.permissions.has(permission)) {
|
||||
throw new Error(`permission ${permission} does not exist`);
|
||||
}
|
||||
}
|
||||
|
||||
function hasPermission(permission: string): boolean {
|
||||
for (let role of roles) {
|
||||
if (role.permissions.includes("core.admin") || role.permissions.includes(permission)) {
|
||||
@@ -38,12 +44,16 @@ export function useAccount(): UserAccount {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
checkPermissionExists(permission);
|
||||
return hasPermission(permission);
|
||||
},
|
||||
hasAnyPermission(...permissions: string[]): boolean | null {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
for (let permission of permissions) {
|
||||
checkPermissionExists(permission);
|
||||
}
|
||||
for (let permission of permissions) {
|
||||
if (hasPermission(permission)) {
|
||||
return true;
|
||||
@@ -55,6 +65,9 @@ export function useAccount(): UserAccount {
|
||||
if (!roles) {
|
||||
return null;
|
||||
}
|
||||
for (let permission of permissions) {
|
||||
checkPermissionExists(permission);
|
||||
}
|
||||
for (let permission of permissions) {
|
||||
if (!hasPermission(permission)) {
|
||||
return false;
|
||||
|
||||
@@ -47,7 +47,7 @@ export const pages: PluginPage[] = [
|
||||
{
|
||||
path: "/mods",
|
||||
sidebarName: "Mods",
|
||||
permission: (account: UserAccount) => account.hasAnyPermission("core.mod.list", "core.mod-pack.list"),
|
||||
permission: (account: UserAccount) => account.hasAnyPermission("core.mod.list", "core.mod_pack.list"),
|
||||
content: <ModsPage />,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -182,10 +182,19 @@ body {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.save-list-dragger .ant-upload.ant-upload-drag {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.save-list-dragger .ant-upload.ant-upload-btn {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.save-list-table .ant-table-pagination.ant-pagination {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.rcon-result code {
|
||||
display: block;
|
||||
background: #000;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../lib/tsconfig.web.json" },
|
||||
{ "path": "../lib/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
}
|
||||
@@ -65,32 +65,47 @@ module.exports = (env = {}) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
resolve: {
|
||||
// XXX Required due to bug in babel-runtime
|
||||
// see: https://github.com/babel/babel/issues/12058
|
||||
fullySpecified: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: require.resolve("ts-loader"),
|
||||
loader: require.resolve("swc-loader"),
|
||||
options: {
|
||||
configFile: "tsconfig.web.json",
|
||||
projectReferences: true,
|
||||
sourceMaps: true,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
tsx: true,
|
||||
},
|
||||
target: "es2022",
|
||||
transform: {
|
||||
react: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.jsx$/,
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: require.resolve("babel-loader"),
|
||||
loader: require.resolve("swc-loader"),
|
||||
options: {
|
||||
presets: [require.resolve("@babel/preset-react")],
|
||||
sourceMaps: true,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "ecmascript",
|
||||
jsx: true,
|
||||
},
|
||||
target: "es2022",
|
||||
transform: {
|
||||
react: {
|
||||
runtime: "automatic",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -123,10 +138,10 @@ module.exports = (env = {}) => ({
|
||||
moduleIds: "deterministic",
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
minify: TerserPlugin.swcMinify,
|
||||
terserOptions: {
|
||||
compress: {
|
||||
keep_classnames: true,
|
||||
passes: 2,
|
||||
},
|
||||
mangle: {
|
||||
keep_classnames: true,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -6,8 +6,8 @@ export const plugin: lib.PluginDeclaration = {
|
||||
name: "global_chat",
|
||||
title: "Global Chat",
|
||||
description: "Forwards chat between instances.",
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
ctlEntrypoint: "dist/plugin/control",
|
||||
instanceEntrypoint: "dist/node/instance",
|
||||
ctlEntrypoint: "dist/node/control",
|
||||
|
||||
messages: [
|
||||
ChatEvent,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@clusterio/plugin-global_chat",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"description": "Clusterio plugin forwarding between Factorio servers",
|
||||
"author": "Hornwitser <github@hornwitser.no>",
|
||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||
"license": "MIT",
|
||||
"repository": "clusterio/clusterio",
|
||||
"main": "dist/plugin/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
@@ -21,8 +21,8 @@
|
||||
"@clusterio/lib": "^2.0.0-alpha.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"typescript": "^5.1.6",
|
||||
|
||||
@@ -4,9 +4,9 @@ const lib = require("@clusterio/lib");
|
||||
|
||||
const mock = require("../../../test/mock");
|
||||
const lines = require("../../../test/lib/factorio/lines");
|
||||
const instance = require("../dist/plugin/instance");
|
||||
const info = require("../dist/plugin/index").plugin;
|
||||
const { ChatEvent } = require("../dist/plugin/messages");
|
||||
const instance = require("../dist/node/instance");
|
||||
const info = require("../dist/node/index").plugin;
|
||||
const { ChatEvent } = require("../dist/node/messages");
|
||||
|
||||
|
||||
describe("global_chat plugin", function() {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/plugin",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.web.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -22,7 +22,7 @@ export const plugin: lib.PluginDeclaration = {
|
||||
title: "Inventory sync",
|
||||
description: "Synchronizes players inventories between instances",
|
||||
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
instanceEntrypoint: "dist/node/instance",
|
||||
instanceConfigFields: {
|
||||
"inventory_sync.rcon_chunk_size": {
|
||||
title: "Rcon inventory chunk size",
|
||||
@@ -33,7 +33,7 @@ export const plugin: lib.PluginDeclaration = {
|
||||
},
|
||||
},
|
||||
|
||||
controllerEntrypoint: "dist/plugin/controller",
|
||||
controllerEntrypoint: "dist/node/controller",
|
||||
controllerConfigFields: {
|
||||
"inventory_sync.player_lock_timeout": {
|
||||
title: "Player Lock Timeout",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@clusterio/plugin-inventory_sync",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"description": "Clusterio plugin forwarding between Factorio servers",
|
||||
"author": "Danielv <danielv@danielv.no>",
|
||||
"homepage": "https://github.com/clusterio/factorioClusterio#readme",
|
||||
"license": "MIT",
|
||||
"repository": "clusterio/factorioClusterio",
|
||||
"main": "dist/plugin/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
@@ -25,8 +25,8 @@
|
||||
"fs-extra": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/plugin",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.web.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -15,8 +15,8 @@ export const plugin: lib.PluginDeclaration = {
|
||||
name: "player_auth",
|
||||
title: "Player Auth",
|
||||
description: "Provides authentication to the cluster via logging into a Factorio server.",
|
||||
controllerEntrypoint: "dist/plugin/controller",
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
controllerEntrypoint: "dist/node/controller",
|
||||
instanceEntrypoint: "dist/node/instance",
|
||||
webEntrypoint: "./web",
|
||||
controllerConfigFields: {
|
||||
"player_auth.code_length": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@clusterio/plugin-player_auth",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"description": "Clusterio plugin authenticating logged in players to the web interface",
|
||||
"author": "Hornwitser <github@hornwitser.no>",
|
||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||
"license": "MIT",
|
||||
"repository": "clusterio/clusterio",
|
||||
"main": "dist/plugin/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
@@ -26,8 +26,8 @@
|
||||
"jsonwebtoken": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/node": "^20.4.5",
|
||||
|
||||
@@ -5,10 +5,10 @@ const jwt = require("jsonwebtoken");
|
||||
|
||||
const mock = require("../../../test/mock");
|
||||
|
||||
const controller = require("../dist/plugin/controller");
|
||||
const instance = require("../dist/plugin/instance");
|
||||
const info = require("../dist/plugin/index").plugin;
|
||||
const { FetchPlayerCodeRequest, SetVerifyCodeRequest } = require("../dist/plugin/messages");
|
||||
const controller = require("../dist/node/controller");
|
||||
const instance = require("../dist/node/instance");
|
||||
const info = require("../dist/node/index").plugin;
|
||||
const { FetchPlayerCodeRequest, SetVerifyCodeRequest } = require("../dist/node/messages");
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/plugin",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.web.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -6,8 +6,8 @@ export const plugin: lib.PluginDeclaration = {
|
||||
name: "research_sync",
|
||||
title: "Research Sync",
|
||||
description: "Synchronises technology research progress between instances.",
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
controllerEntrypoint: "dist/plugin/controller",
|
||||
instanceEntrypoint: "dist/node/instance",
|
||||
controllerEntrypoint: "dist/node/controller",
|
||||
|
||||
messages: [
|
||||
messages.ContributionEvent,
|
||||
|
||||
@@ -137,10 +137,21 @@ function research_sync.sync_technologies(data)
|
||||
for _, tech_data in pairs(game.json_to_table(data)) do
|
||||
local tech = force.technologies[tech_data[nameIndex]]
|
||||
if tech and tech.level <= tech_data[levelIndex] then
|
||||
tech.level = math.min(tech_data[levelIndex], tech.prototype.max_level)
|
||||
local new_level = math.min(tech_data[levelIndex], tech.prototype.max_level)
|
||||
if new_level ~= tech.level then
|
||||
-- when the level of the current research changes the
|
||||
-- progress is not automatically reset.
|
||||
if force.current_research == tech then
|
||||
force.research_progress = 0
|
||||
end
|
||||
tech.level = new_level
|
||||
end
|
||||
|
||||
local progress
|
||||
if tech_data[researchedIndex] then
|
||||
if force.current_research == tech then
|
||||
force.research_progress = 0
|
||||
end
|
||||
tech.researched = true
|
||||
progress = nil
|
||||
elseif tech_data[progressIndex] then
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@clusterio/plugin-research_sync",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"description": "Clusterio plugin for synchronizing research between Factorio servers",
|
||||
"author": "Hornwitser <github@hornwitser.no>",
|
||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||
"license": "MIT",
|
||||
"repository": "clusterio/clusterio",
|
||||
"main": "dist/plugin/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
@@ -25,8 +25,8 @@
|
||||
"fs-extra": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
// required here in order to track coverage for them.
|
||||
|
||||
"use strict";
|
||||
require("../dist/plugin/instance");
|
||||
require("../dist/plugin/controller");
|
||||
require("../dist/node/instance");
|
||||
require("../dist/node/controller");
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/plugin",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.web.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
webpack.config.js
|
||||
dist/web/build
|
||||
dist/browser
|
||||
@@ -12,7 +12,7 @@ export const plugin: lib.PluginDeclaration = {
|
||||
description:
|
||||
"Provides in-game item/fluid production, builds, kills, and pollution "+
|
||||
"statistics to the cluster's Prometheus endpoint.",
|
||||
instanceEntrypoint: "dist/plugin/instance",
|
||||
instanceEntrypoint: "dist/node/instance",
|
||||
instanceConfigFields: {
|
||||
"statistics_exporter.command_timeout": {
|
||||
title: "Command Timeout",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@clusterio/plugin-statistics_exporter",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"version": "2.0.0-alpha.17",
|
||||
"description": "Clusterio plugin for gathering statistics from Factorio servers",
|
||||
"author": "Hornwitser <github@hornwitser.no>",
|
||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||
"license": "MIT",
|
||||
"repository": "clusterio/clusterio",
|
||||
"main": "dist/plugin/index.js",
|
||||
"main": "dist/node/index.js",
|
||||
"scripts": {
|
||||
"prepare": "tsc --build && webpack-cli --env production",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
@@ -21,8 +21,8 @@
|
||||
"@clusterio/lib": "^2.0.0-alpha.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clusterio/lib": "^2.0.0-alpha.16",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.16",
|
||||
"@clusterio/lib": "^2.0.0-alpha.17",
|
||||
"@clusterio/web_ui": "^2.0.0-alpha.17",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/react": "^18.2.21",
|
||||
"typescript": "^5.1.6",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
const assert = require("assert").strict;
|
||||
|
||||
const mock = require("../../../test/mock");
|
||||
const instance = require("../dist/plugin/instance");
|
||||
const info = require("../dist/plugin/index").plugin;
|
||||
const instance = require("../dist/node/instance");
|
||||
const info = require("../dist/node/index").plugin;
|
||||
|
||||
|
||||
describe("statistics_exporter plugin", function() {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.browser.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/browser",
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "../../packages/lib" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.browser.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/plugin",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.node.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.node.json" },
|
||||
],
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": ["test/*", "./dist/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/node",
|
||||
},
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"references": [
|
||||
{ "path": "../../packages/lib/tsconfig.web.json" },
|
||||
{ "path": "../../packages/web_ui/tsconfig.web.json" },
|
||||
],
|
||||
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/web/build",
|
||||
}
|
||||
}
|
||||
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user