mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-16 07:24:53 +09:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0c42225a4 | ||
|
|
0b0e2fd377 |
No files matched your search
-125
@@ -31,131 +31,6 @@ Many thanks to the following for contributing to this release:
|
|||||||
[@username](https://github.com/username)
|
[@username](https://github.com/username)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
- Fixed cramped plugin rows and wrapping header branding on mobile screens.
|
|
||||||
|
|
||||||
|
|
||||||
## Version 2.1.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- Added `Instance.autoPaused` and the `autoPauseChanged` instance hook so plugins can skip periodic commands while the game is auto paused. [#258](<https://github.com/clusterio/clusterio/issues/258>)
|
|
||||||
- Hosts ask to join the cluster when first started and are approved in the web interface or with clusterioctl host approve. [#1161](<https://github.com/clusterio/clusterio/pull/1161>)
|
|
||||||
- Exporting data from an instance running the headless build of Factorio now reads the base game's icons from a full install of the same version in `host.factorio_directory`, if there is one. [#1068](<https://github.com/clusterio/clusterio/issues/1068>)
|
|
||||||
- Added instance templates that other instances inherit config values from, with linked inherited values in the web interface, see `instance.is_template` and `instance.template_id`. [#200](<https://github.com/clusterio/clusterio/issues/200>)
|
|
||||||
- Added the mod pack an instance uses to the instance list and instance page, with a link to it. [#852](<https://github.com/clusterio/clusterio/issues/852>)
|
|
||||||
- Added controller metrics for WebSocket ready, resume, drop, session timeout and failed resume events. [#1192](<https://github.com/clusterio/clusterio/issues/1192>)
|
|
||||||
- Added online player count column to the instance list in the web interface. [#1193](<https://github.com/clusterio/clusterio/issues/1193>)
|
|
||||||
- Added controller.log_retention_days and host.log_retention_days to delete log files older than the given number of days. [#1200](<https://github.com/clusterio/clusterio/pull/1200>)
|
|
||||||
- Added a plugin browser to the plugins page that searches npm for packages with the clusterio-plugin keyword and installs them on a chosen set of machines, defaulting to every connected machine that allows remote installs. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added a field to the plugin browser for installing a package by name. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added a Versions column to the plugins page showing how many machines run each plugin version now and after a restart, with restart pending, disabled, not installed and offline counts. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added a table of machines to the plugin page with the version and status of the plugin on each, and an install or update button for machines that aren't on the latest version. Plugins only installed on hosts have a page too. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added "Update to X" and "Update all" actions to the plugins page that bring every machine to the latest version of a plugin on npm, installing it where it's missing, with a checklist of the changes to make. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added a "Restart pending" action to the plugins page that restarts the chosen machines with a pending restart, hosts first and the controller last. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added support for `name@version` in plugin install and update requests and the matching clusterioctl commands, for installing a specific version or updating past the range saved in package.json. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added `PluginLatestVersionsRequest` for looking up the latest version of plugins published on npm. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added `controller plugin search` command to clusterioctl. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added `core.plugin.search` permission. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Added a `handleSignals` constructor argument to `LockFile` for locks that should only be released on exit. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
- Fixed the plugin template asking for an older TypeBox version than lib, which made new plugins fail to compile. [#1127](<https://github.com/clusterio/clusterio/issues/1127>)
|
|
||||||
- Fixed subspace_storage and statistics_exporter keeping auto paused servers running with their periodic RCON commands. [#258](<https://github.com/clusterio/clusterio/issues/258>)
|
|
||||||
- Fixed research_sync losing research progress and finished research while the controller was unreachable. [#1123](<https://github.com/clusterio/clusterio/issues/1123>)
|
|
||||||
- Fixed stopping an instance while it is starting failing with "Expected state running but state is stopping" or leaving it running. The stop now takes effect once the instance has started, and the control button shows as loading while the instance is starting or stopping. [#879](<https://github.com/clusterio/clusterio/issues/879>)
|
|
||||||
- Raised the required Node.js version to 22.18 or 24.2, since earlier 22.x and 24.x releases silently skipped CLI startup. [#1109](<https://github.com/clusterio/clusterio/issues/1109>)
|
|
||||||
- Fixed user view page not updating the displayed roles when they change. [#775](<https://github.com/clusterio/clusterio/issues/775>)
|
|
||||||
- Fixed role view page discarding unsaved edits when the role is updated, and not showing changes made elsewhere alongside local edits. [#776](<https://github.com/clusterio/clusterio/issues/776>)
|
|
||||||
- Fixed data export failing completely when one icon could not be read or decoded, the icon is now skipped with a warning. [#841](<https://github.com/clusterio/clusterio/issues/841>)
|
|
||||||
- Fixed mod settings added by a data export showing up empty on an open mod pack page until it was reloaded. [#840](<https://github.com/clusterio/clusterio/issues/840>)
|
|
||||||
- Fixed Player Sync dropping recipe notifications on download when the player had no cleared recipes, which also replaced the stored list on the next upload. [#964](<https://github.com/clusterio/clusterio/issues/964>)
|
|
||||||
- Fixed export-data on headless Factorio counting blank layered icons as exported and not reporting that the base game icons were missing. [#1068](<https://github.com/clusterio/clusterio/issues/1068>)
|
|
||||||
- Controller drops export data whose files were deleted, at startup or when a missing file is requested, and the web interface falls back cleanly instead of showing errors. [#878](<https://github.com/clusterio/clusterio/issues/878>)
|
|
||||||
- Fixed player_sync leaving a player as a spectator with no body, and uploading that empty state, after a download stalled while the controller was down. [#1124](<https://github.com/clusterio/clusterio/issues/1124>)
|
|
||||||
- Fixed player_sync teleporting a player back after another mod, such as universal edges, teleported them while their player data downloaded. [#908](<https://github.com/clusterio/clusterio/issues/908>)
|
|
||||||
- Fixed plugins that need save patching, script commands or Lua UDP defaulting to loaded on instances with that feature disabled, which made saving the instance config fail. [#651](<https://github.com/clusterio/clusterio/issues/651>)
|
|
||||||
- Fixed Player Sync keeping cleared recipe notifications and losing ones for recipes missing here when a download changed the player's force. [#964](<https://github.com/clusterio/clusterio/issues/964>)
|
|
||||||
- Fixed the instance filter on the users page and the icon reference search being lost when navigating back to the page. [#1185](<https://github.com/clusterio/clusterio/pull/1185>)
|
|
||||||
- Fixed the web UI ignoring `localised_name` and `localised_description` of mod settings and showing rich text tags in setting names. [#768](<https://github.com/clusterio/clusterio/issues/768>)
|
|
||||||
- Fixed saves patched by a newer version of Clusterio not being rejected. [#1184](<https://github.com/clusterio/clusterio/pull/1184>)
|
|
||||||
- Fixed the research_sync queue chat line showing the same level for every queued level of an infinite technology. [#1186](<https://github.com/clusterio/clusterio/issues/1186>)
|
|
||||||
- Fixed the web interface logging MaxListenersExceededWarning when plugins create more than 7 subscribers. [#1145](<https://github.com/clusterio/clusterio/issues/1145>)
|
|
||||||
- Fixed Player Sync failing with "Failed to create character" when the player was in a chunk that was not generated yet or had no land nearby. [#1187](<https://github.com/clusterio/clusterio/issues/1187>)
|
|
||||||
- Fixed player_sync not syncing which page each quick bar row shows, so the quick bar could show other slots after switching servers. [#1201](<https://github.com/clusterio/clusterio/issues/1201>)
|
|
||||||
- Fixed the controller reading the whole body of save, mod and export uploads it had rejected, when running behind a reverse proxy. [#595](<https://github.com/clusterio/clusterio/issues/595>)
|
|
||||||
- Fixed uploads to the controller taking more than 5 minutes failing. [#1173](<https://github.com/clusterio/clusterio/pull/1173>)
|
|
||||||
- Fixed a plugin list entry for a path that no longer exists taking precedence over an npm package providing the same plugin. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Fixed npm plugins already in the plugin list logging "already in the plugin list, but with a different path" on every startup. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
|
|
||||||
### Changes
|
|
||||||
|
|
||||||
- Errors thrown by request handlers are received by the requester as the same class instead of a generic `RequestError`. Plugins can register their own error classes with `errors` in the plugin declaration. [#900](<https://github.com/clusterio/clusterio/issues/900>)
|
|
||||||
- Replaced webpack with Vite for building the web interface and plugin web modules. `--dev` now uses the Vite development server with hot module replacement. [#1127](<https://github.com/clusterio/clusterio/issues/1127>)
|
|
||||||
- Removed the separate web interface load state from the plugins pages, web load errors now show with the plugin's versions. [#1140](<https://github.com/clusterio/clusterio/pull/1140>)
|
|
||||||
- Build the published packages with TypeScript 7, and use it in the plugin template. Plugins can keep using TypeScript 5.9 or 6. [#1147](<https://github.com/clusterio/clusterio/pull/1147>)
|
|
||||||
- `ctl host plugin list` shows the plugin state in place of the loaded and enabled columns. [#1134](<https://github.com/clusterio/clusterio/issues/1134>)
|
|
||||||
- Reduced the size of a host install by replacing jimp with only the jimp modules the export uses. [#1162](<https://github.com/clusterio/clusterio/pull/1162>)
|
|
||||||
- `@clusterio/web_ui` no longer installs typescript. Plugins that compile with `tsc` need typescript in their own devDependencies. [#1163](<https://github.com/clusterio/clusterio/pull/1163>)
|
|
||||||
- Commands of clusteriocontroller and clusteriohost that don't start the server, like config and bootstrap, start about 45% faster. [#1159](<https://github.com/clusterio/clusterio/pull/1159>)
|
|
||||||
- Made web interface view pages show resources that are loading, missing, deleted or failed to load the same way, instead of spinning forever without the subscribe permission. [#1165](<https://github.com/clusterio/clusterio/issues/1165>)
|
|
||||||
- Added `ResourceState` and `useResourceState` to the web_ui package, and `useHost`, `useInstance`, `useUser`, `useRole` and `useModPack` now also return the subscription error. [#1165](<https://github.com/clusterio/clusterio/issues/1165>)
|
|
||||||
- Reduced the download size of the create tool by replacing inquirer 8 with the standalone @inquirer prompt packages. [#1164](<https://github.com/clusterio/clusterio/pull/1164>)
|
|
||||||
- `clusterioctl instance config edit` only sends the fields changed in the editor. [#200](<https://github.com/clusterio/clusterio/issues/200>)
|
|
||||||
- Mod settings that no mod in a mod pack's export data defines are now hidden in a collapsed section on the mod pack page and can be removed from there. [#773](<https://github.com/clusterio/clusterio/issues/773>)
|
|
||||||
- Added `UserManager.verifyUserToken` for checking user tokens. [#1180](<https://github.com/clusterio/clusterio/pull/1180>)
|
|
||||||
- subspace_storage now requires save patching on the instance, it did not work without it. [#651](<https://github.com/clusterio/clusterio/issues/651>)
|
|
||||||
- Saves started with save patching disabled now have the Clusterio modules removed and their original control.lua restored. [#651](<https://github.com/clusterio/clusterio/issues/651>)
|
|
||||||
- The research_sync chat line for a research queue change names only the technologies added, removed or moved, instead of printing the whole queue. [#1186](<https://github.com/clusterio/clusterio/issues/1186>)
|
|
||||||
- Focused the search input when opening mod search on the mods page and the mod pack view. [#1194](<https://github.com/clusterio/clusterio/issues/1194>)
|
|
||||||
- The online player list on the instance page is always shown, collapsed when no players are online. [#1191](<https://github.com/clusterio/clusterio/issues/1191>)
|
|
||||||
- The installer detects Factorio installed through Steam on macOS. [#1190](<https://github.com/clusterio/clusterio/issues/1190>)
|
|
||||||
- Log files older than 30 days are now deleted by default. Existing installs will lose logs older than 30 days on the first start after upgrading. To keep them, set controller.log_retention_days and host.log_retention_days to 0 with the config command after upgrading but before starting the controller and hosts. [#1200](<https://github.com/clusterio/clusterio/pull/1200>)
|
|
||||||
- Requests to the controller moving less than controller.transfer_min_progress KiB in 30 seconds are now closed. [#842](<https://github.com/clusterio/clusterio/issues/842>)
|
|
||||||
- Plugin lists now include the version installed on disk and npm plugins installed since startup. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- System metrics now include the plugins on each machine, kept for hosts while they are offline. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- The "Updates" button on the host and controller pages is now "Update Clusterio" and no longer installs or updates plugins. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Plugin installs and updates are rejected while another npm operation is running on the same machine, including between a standalone controller and host sharing an installation directory. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Plugin installs and pinned updates are rejected when the package by that name is installed from a path, git, url, tarball or alias, either by the root package.json or by another plugin, as installing by name would replace it. Such plugins no longer report an npm package. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Hosts and the controller refuse to restart while a plugin install or update is running on the machine. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Lock files now include the process start time, so a lock left by an earlier process with the same pid is treated as stale. A lock file is only removed by the process that wrote it. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
- Plugins in `optionalDependencies` are now found on startup, and their entries override `dependencies` of the same name like they do in npm. [#1001](<https://github.com/clusterio/clusterio/issues/1001>)
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
|
|
||||||
- Removed `lib.findUnusedName`, its result could be taken by another process before use. [#1128](<https://github.com/clusterio/clusterio/pull/1128>)
|
|
||||||
- Replaced `DatastoreProvider.bootstrap()` with `KeyValueDatastore.fromProvider()` and `SubscribableDatastore.fromProvider()`, and `Controller.bootstrap()` / `Host.bootstrap()` with `Controller.fromConfig()` / `Host.fromConfig()`. [#1126](<https://github.com/clusterio/clusterio/issues/1126>)
|
|
||||||
- Replaced `FactorioServer.init()` and `setTargetVersion()` with `FactorioServer.fromOptions()`, and `FactorioServer.checkForUpdates()` with an exported `checkForUpdates()` function. [#1126](<https://github.com/clusterio/clusterio/issues/1126>)
|
|
||||||
- Replaced `Instance.init()` with `Instance.fromConfig()` followed by `instance.loadPlugins()`. The `Instance` constructor now takes the `FactorioServer` instead of the factorio directory. [#1126](<https://github.com/clusterio/clusterio/issues/1126>)
|
|
||||||
- Replaced `RequestError` with error classes following HTTP status codes: `ClientError` (`ValidationError`, `ForbiddenError`, `NotFoundError`, `TimeoutError`, `ConflictError`) and `ServerError` (`InternalServerError`, `UnavailableError`). [#900](<https://github.com/clusterio/clusterio/issues/900>)
|
|
||||||
- Renamed `InvalidValue`, `InvalidField` and `InvalidAccess` to `InvalidValueError`, `InvalidFieldError` and `InvalidAccessError`. [#900](<https://github.com/clusterio/clusterio/issues/900>)
|
|
||||||
- Plugin web modules must be built with Vite using `clusterioPlugin` from `@clusterio/web_ui/vite.mjs`. Plugins built with webpack no longer load. See docs/devs/plugin-migration-to-2.1.md. [#1127](<https://github.com/clusterio/clusterio/issues/1127>)
|
|
||||||
- Renamed `lib.PluginWebpackEnvInfo` to `lib.PluginWebEnvInfo`. [#1127](<https://github.com/clusterio/clusterio/issues/1127>)
|
|
||||||
- `PluginDetails` takes a `state` in place of `loaded` and `enabled`, use the `loaded` and `loadsOnRestart` getters to read them. [#1134](<https://github.com/clusterio/clusterio/issues/1134>)
|
|
||||||
- Node.js 22.0 to 22.17 and 24.0 to 24.1 are no longer supported. [#1109](<https://github.com/clusterio/clusterio/issues/1109>)
|
|
||||||
- Removed host token management: clusterioctl host generate-token, revoke-token and create-config, bootstrap generate-host-token and their permissions. Hosts are revoked from the host page or with clusterioctl host revoke. [#1161](<https://github.com/clusterio/clusterio/pull/1161>)
|
|
||||||
- Renamed `lib.safeOutputFile` to `lib.atomicOverwriteFile`. [#876](<https://github.com/clusterio/clusterio/issues/876>)
|
|
||||||
- Added `HostManager` as `controller.hosts`; the hosts datastore moved to `controller.hosts.records`, `Controller.generateHostToken` and `verifyHostToken` became `controller.hosts.signHostToken` and `verifyHostToken`, and `HostPairingManager.createHost` became `controller.hosts.createHost`, which returns the host record. [#1180](<https://github.com/clusterio/clusterio/pull/1180>)
|
|
||||||
|
|
||||||
### Meta
|
|
||||||
|
|
||||||
- Replaced ESLint with Oxlint. [#1127](<https://github.com/clusterio/clusterio/issues/1127>)
|
|
||||||
- Removed TypeDoc and the `pnpm docs` script. [#1147](<https://github.com/clusterio/clusterio/pull/1147>)
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
|
|
||||||
Many thanks to the following for contributing to this release:
|
|
||||||
[@psihius](<https://github.com/psihius>), [@bbassie](<https://github.com/bbassie>), [@Danielv123](<https://github.com/Danielv123>), [@ILLISIS](<https://github.com/ILLISIS>)
|
|
||||||
|
|
||||||
### Statistics
|
|
||||||
|
|
||||||
Commits: 148 Pull Requests: 55 Files Changed: 296 Additions: +17,343 Deletions: -4,190
|
|
||||||
|
|
||||||
|
|
||||||
## Version 2.0.3
|
## Version 2.0.3
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/controller",
|
"name": "@clusterio/controller",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Implementation of Clusterio controller",
|
"description": "Implementation of Clusterio controller",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/create",
|
"name": "@clusterio/create",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Installer for clusterio",
|
"description": "Installer for clusterio",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/ctl",
|
"name": "@clusterio/ctl",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio command line management tool",
|
"description": "Clusterio command line management tool",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/host",
|
"name": "@clusterio/host",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Implementation of Clusterio host server",
|
"description": "Implementation of Clusterio host server",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/lib",
|
"name": "@clusterio/lib",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Shared library for Clusterio",
|
"description": "Shared library for Clusterio",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/web_ui",
|
"name": "@clusterio/web_ui",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio web interface implementation",
|
"description": "Clusterio web interface implementation",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser",
|
"author": "Hornwitser",
|
||||||
|
|||||||
@@ -172,12 +172,10 @@ export function PluginVersions(props: { status: PluginStatus, latest?: string })
|
|||||||
{versions.map(version => {
|
{versions.map(version => {
|
||||||
const now = count(machine => machine.running === version);
|
const now = count(machine => machine.running === version);
|
||||||
const after = count(machine => machine.afterRestart === version);
|
const after = count(machine => machine.afterRestart === version);
|
||||||
return <div key={version} className="plugin-version">
|
return <div key={version}>
|
||||||
<span>
|
<Text>{version}</Text>
|
||||||
<Text>{version}</Text>
|
{props.latest === version && <Tag color="success" style={{ marginLeft: 6 }}>latest</Tag>}
|
||||||
{props.latest === version && <Tag color="success" style={{ marginLeft: 6 }}>latest</Tag>}
|
<Text type="secondary" style={{ marginLeft: 12 }}>
|
||||||
</span>
|
|
||||||
<Text type="secondary">
|
|
||||||
{plural(now, "machine")}{now !== after && ` → ${after} after restart`}
|
{plural(now, "machine")}{now !== after && ` → ${after} after restart`}
|
||||||
</Text>
|
</Text>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { Grid, Space, Table, Tooltip, Typography } from "antd";
|
import { Space, Table, Tooltip, Typography } from "antd";
|
||||||
import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled";
|
import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled";
|
||||||
|
|
||||||
import type { PluginWebApi } from "@clusterio/lib";
|
import type { PluginWebApi } from "@clusterio/lib";
|
||||||
@@ -48,8 +48,6 @@ function WebError(props: { error: string }) {
|
|||||||
export default function PluginsPage() {
|
export default function PluginsPage() {
|
||||||
const control = useContext(ControlContext);
|
const control = useContext(ControlContext);
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const screens = Grid.useBreakpoint();
|
|
||||||
const isMobile = screens.md === false;
|
|
||||||
let [pluginList, setPluginList] = useState<PluginWebApi[]>([]);
|
let [pluginList, setPluginList] = useState<PluginWebApi[]>([]);
|
||||||
const tableState = useTableQueryState<PluginRow>({
|
const tableState = useTableQueryState<PluginRow>({
|
||||||
namespace: "plugin", defaultSortKey: "name",
|
namespace: "plugin", defaultSortKey: "name",
|
||||||
@@ -98,24 +96,6 @@ export default function PluginsPage() {
|
|||||||
const latestOf = (plugin: PluginRow) => (
|
const latestOf = (plugin: PluginRow) => (
|
||||||
plugin.status?.npmPackage ? latestVersions[plugin.status.npmPackage] : undefined
|
plugin.status?.npmPackage ? latestVersions[plugin.status.npmPackage] : undefined
|
||||||
);
|
);
|
||||||
const versions = (plugin: PluginRow) => <>
|
|
||||||
{plugin.status
|
|
||||||
? <PluginVersions status={plugin.status} latest={latestOf(plugin)} />
|
|
||||||
: plugin.meta?.version}
|
|
||||||
{plugin.webError && <WebError error={plugin.webError} />}
|
|
||||||
</>;
|
|
||||||
const updateAction = (plugin: PluginRow) => (plugin.status
|
|
||||||
// Keep clicks on the update action from opening the plugin.
|
|
||||||
? <span onClick={event => { event.stopPropagation(); }}>
|
|
||||||
<PluginUpdateButton
|
|
||||||
status={plugin.status}
|
|
||||||
title={plugin.title}
|
|
||||||
latest={latestOf(plugin)}
|
|
||||||
installer={installer}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
return <PageLayout nav={[{ name: "Plugins" }]}>
|
return <PageLayout nav={[{ name: "Plugins" }]}>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
@@ -135,22 +115,14 @@ export default function PluginsPage() {
|
|||||||
</Space>}
|
</Space>}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
className="plugins-table"
|
|
||||||
tableLayout={isMobile ? "fixed" : "auto"}
|
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
key: "name",
|
key: "name",
|
||||||
className: "table-link-cell",
|
className: "table-link-cell",
|
||||||
render: (_, plugin) => <>
|
render: (_, plugin) => <Link to={pluginPath(plugin)} style={{ color: "inherit" }}>
|
||||||
<Link to={pluginPath(plugin)} style={{ color: "inherit" }}>
|
{plugin.title}
|
||||||
{plugin.title}
|
</Link>,
|
||||||
</Link>
|
|
||||||
{isMobile && <div className="plugin-mobile-details">
|
|
||||||
<div>{versions(plugin)}</div>
|
|
||||||
{updateAction(plugin)}
|
|
||||||
</div>}
|
|
||||||
</>,
|
|
||||||
sorter: (a, b) => strcmp(a.title, b.title),
|
sorter: (a, b) => strcmp(a.title, b.title),
|
||||||
sortOrder: tableState.sortOrder("name"),
|
sortOrder: tableState.sortOrder("name"),
|
||||||
filteredValue: tableState.filteredValue("name"),
|
filteredValue: tableState.filteredValue("name"),
|
||||||
@@ -159,14 +131,28 @@ export default function PluginsPage() {
|
|||||||
{
|
{
|
||||||
title: "Versions",
|
title: "Versions",
|
||||||
key: "versions",
|
key: "versions",
|
||||||
responsive: ["md"],
|
render: (_, plugin) => <>
|
||||||
render: (_, plugin) => versions(plugin),
|
{plugin.status
|
||||||
|
? <PluginVersions status={plugin.status} latest={latestOf(plugin)} />
|
||||||
|
: plugin.meta?.version}
|
||||||
|
{plugin.webError && <WebError error={plugin.webError} />}
|
||||||
|
</>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "actions",
|
key: "actions",
|
||||||
align: "right",
|
align: "right",
|
||||||
responsive: ["md"],
|
render: (_, plugin) => (plugin.status
|
||||||
render: (_, plugin) => updateAction(plugin),
|
// Keep clicks on the update action from opening the plugin
|
||||||
|
? <span onClick={event => { event.stopPropagation(); }}>
|
||||||
|
<PluginUpdateButton
|
||||||
|
status={plugin.status}
|
||||||
|
title={plugin.title}
|
||||||
|
latest={latestOf(plugin)}
|
||||||
|
installer={installer}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
: null
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
dataSource={tableContents}
|
dataSource={tableContents}
|
||||||
|
|||||||
@@ -169,10 +169,8 @@ export default function SiteLayout() {
|
|||||||
/>;
|
/>;
|
||||||
|
|
||||||
let accountMenu = <Dropdown menu={accountMenuProps} trigger={["click"]} placement="bottomRight">
|
let accountMenu = <Dropdown menu={accountMenuProps} trigger={["click"]} placement="bottomRight">
|
||||||
<Button type="text" icon={<UserOutlined />} style={{ marginInlineStart: "auto" }}
|
<Button type="text" icon={<UserOutlined />} style={{ marginInlineStart: "auto" }}>
|
||||||
aria-label={`Account menu for ${account.name}`}
|
{account.name}
|
||||||
>
|
|
||||||
<span className="site-layout-account-name">{account.name}</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>;
|
</Dropdown>;
|
||||||
|
|
||||||
@@ -230,7 +228,7 @@ export default function SiteLayout() {
|
|||||||
<Link to="/" style={{ display: "flex", alignItems: "center" }}>
|
<Link to="/" style={{ display: "flex", alignItems: "center" }}>
|
||||||
<img src={logo} width={56} height={56} alt="Clusterio logo" />
|
<img src={logo} width={56} height={56} alt="Clusterio logo" />
|
||||||
</Link>
|
</Link>
|
||||||
<Flex vertical justify="center" className="site-layout-brand">
|
<Flex vertical justify="center">
|
||||||
<Typography.Title level={4} style={{ margin: 0, lineHeight: 1.2 }}>Clusterio</Typography.Title>
|
<Typography.Title level={4} style={{ margin: 0, lineHeight: 1.2 }}>Clusterio</Typography.Title>
|
||||||
<Tooltip title="View changelog" placement="right">
|
<Tooltip title="View changelog" placement="right">
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
|
|||||||
@@ -72,38 +72,6 @@ body {
|
|||||||
background: #001529;
|
background: #001529;
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-layout-brand {
|
|
||||||
flex-shrink: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
.site-layout-account-name {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plugins-table .table-link-cell > a {
|
|
||||||
font-weight: 600;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.plugin-mobile-details {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 12px;
|
|
||||||
margin-top: 8px;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plugin-version {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: baseline;
|
|
||||||
column-gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep the sidebar fixed in place (below the header) while content scrolls. */
|
/* Keep the sidebar fixed in place (below the header) while content scrolls. */
|
||||||
.site-layout-sider {
|
.site-layout-sider {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-global_chat",
|
"name": "@clusterio/plugin-global_chat",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin forwarding between Factorio servers",
|
"description": "Clusterio plugin forwarding between Factorio servers",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-player_auth",
|
"name": "@clusterio/plugin-player_auth",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin authenticating logged in players to the web interface",
|
"description": "Clusterio plugin authenticating logged in players to the web interface",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -634,6 +634,7 @@ player_sync.on_nth_tick[33] = function(event)
|
|||||||
player_sync.check_players_in_cutscene()
|
player_sync.check_players_in_cutscene()
|
||||||
player_sync.check_active_downloads()
|
player_sync.check_active_downloads()
|
||||||
player_sync.check_active_uploads()
|
player_sync.check_active_uploads()
|
||||||
|
player_sync.check_missing_quick_bar_records()
|
||||||
end
|
end
|
||||||
|
|
||||||
player_sync.events[defines.events.on_cutscene_waypoint_reached] = function(event)
|
player_sync.events[defines.events.on_cutscene_waypoint_reached] = function(event)
|
||||||
@@ -781,6 +782,25 @@ function player_sync.check_active_downloads()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Quick bar slots pointing into the blueprint library are set once the save has the player's library
|
||||||
|
function player_sync.check_missing_quick_bar_records()
|
||||||
|
local script_data = get_script_data()
|
||||||
|
-- Not once the quick bar stopped being synced here. The entries stay for the upload, which goes by the
|
||||||
|
-- components of the player's session.
|
||||||
|
if not serialize.syncs(script_data.components, "quick_bar") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for player_name, failed in pairs(script_data.failed_deserialization) do
|
||||||
|
local player = failed.quick_bar and game.get_player(player_name)
|
||||||
|
if player and player.connected then
|
||||||
|
failed.quick_bar = serialize.restore_missing_quick_bar_records(player, failed.quick_bar)
|
||||||
|
if not next(failed) then
|
||||||
|
script_data.failed_deserialization[player_name] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function player_sync.check_active_uploads()
|
function player_sync.check_active_uploads()
|
||||||
local script_data = get_script_data()
|
local script_data = get_script_data()
|
||||||
for player_name, record in pairs(script_data.active_uploads) do
|
for player_name, record in pairs(script_data.active_uploads) do
|
||||||
|
|||||||
@@ -494,6 +494,27 @@ local function find_item_path(inventory, item_number)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- The record a quick bar slot entry points at, if the player's library has it at that path
|
||||||
|
--- @param player LuaPlayer
|
||||||
|
--- @param entry QuickBarSlotEncoded.record
|
||||||
|
--- @return LuaRecord?
|
||||||
|
local function find_record(player, entry)
|
||||||
|
-- The record must still be what was stored, a shifted library must not set the wrong one
|
||||||
|
local records = player.blueprints
|
||||||
|
local record --[[@as LuaRecord?]]
|
||||||
|
for _, index in ipairs(entry.p) do
|
||||||
|
record = records[index]
|
||||||
|
if not (record and record.valid) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
records = record.type == "blueprint-book" and record.contents or {}
|
||||||
|
end
|
||||||
|
if record and record.type == entry.y and (record.label or "") == (entry.l or "") then
|
||||||
|
return record
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- @param filter ItemFilter
|
--- @param filter ItemFilter
|
||||||
--- @return QuickBarSlotEncoded
|
--- @return QuickBarSlotEncoded
|
||||||
local function serialize_filter(filter)
|
local function serialize_filter(filter)
|
||||||
@@ -611,18 +632,8 @@ function serialize.deserialize_quick_bar_slot(entry, player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if entry.t == "r" then
|
if entry.t == "r" then
|
||||||
-- The record must still be what was stored, a shifted library must not set the wrong one
|
local record = find_record(player, entry)
|
||||||
local records = player.blueprints
|
if record then
|
||||||
local record --[[@as LuaRecord?]]
|
|
||||||
for _, index in ipairs(entry.p) do
|
|
||||||
record = records[index]
|
|
||||||
if not (record and record.valid) then
|
|
||||||
record = nil
|
|
||||||
break
|
|
||||||
end
|
|
||||||
records = record.type == "blueprint-book" and record.contents or {}
|
|
||||||
end
|
|
||||||
if record and record.valid and record.type == entry.y and (record.label or "") == (entry.l or "") then
|
|
||||||
return { type = "record", record = record } --[[@as QuickBarSlot]]
|
return { type = "record", record = record } --[[@as QuickBarSlot]]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -670,8 +681,9 @@ local function quick_bar_slot_count(player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @param player LuaPlayer
|
--- @param player LuaPlayer
|
||||||
|
--- @param missing table<string, QuickBarSlotEncoded.record>? Entries deserialize_quick_bar could not find the record of
|
||||||
--- @return table<string, QuickBarSlotEncoded>?
|
--- @return table<string, QuickBarSlotEncoded>?
|
||||||
function serialize.serialize_quick_bar(player)
|
function serialize.serialize_quick_bar(player, missing)
|
||||||
local serialized
|
local serialized
|
||||||
local width = v2_1_quick_bar_api and player.quick_bar_width
|
local width = v2_1_quick_bar_api and player.quick_bar_width
|
||||||
|
|
||||||
@@ -696,19 +708,63 @@ function serialize.serialize_quick_bar(player)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- A record still missing here is stored again as it came, unless the player put something else in its slot
|
||||||
|
for index, entry in pairs(missing or {}) do
|
||||||
|
serialized = serialized or {}
|
||||||
|
serialized[index] = serialized[index] or entry
|
||||||
|
end
|
||||||
|
|
||||||
return serialized
|
return serialized
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Slot index to page and slot on it
|
||||||
|
--- @param player LuaPlayer
|
||||||
|
--- @param i integer
|
||||||
|
local function quick_bar_location(player, i)
|
||||||
|
local width = player.quick_bar_width
|
||||||
|
return math.floor((i - 1) / width) + 1, ((i - 1) % width) + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the slots whose records were not in the player's library when the data was applied. A save gets the library
|
||||||
|
--- of a player from the client some seconds after the join, later than the data of a player new to it is applied.
|
||||||
|
--- @param player LuaPlayer
|
||||||
|
--- @param missing table<string, QuickBarSlotEncoded.record>
|
||||||
|
--- @return table<string, QuickBarSlotEncoded.record>? missing Entries whose record is still not there
|
||||||
|
function serialize.restore_missing_quick_bar_records(player, missing)
|
||||||
|
for index, entry in pairs(missing) do
|
||||||
|
local page, page_slot = quick_bar_location(player, tonumber(index) --[[@as integer]])
|
||||||
|
if player.get_quick_bar_slot(page, page_slot) ~= nil then
|
||||||
|
-- The player put something else there
|
||||||
|
missing[index] = nil
|
||||||
|
else
|
||||||
|
local record = find_record(player, entry)
|
||||||
|
if record then
|
||||||
|
player.set_quick_bar_slot(page, page_slot, { type = "record", record = record })
|
||||||
|
missing[index] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return next(missing) and missing or nil
|
||||||
|
end
|
||||||
|
|
||||||
--- @param player LuaPlayer
|
--- @param player LuaPlayer
|
||||||
--- @param serialized table<string, string | QuickBarSlotEncoded>?
|
--- @param serialized table<string, string | QuickBarSlotEncoded>?
|
||||||
|
--- @return table<string, QuickBarSlotEncoded.record>? missing Entries whose record is not in the player's library
|
||||||
function serialize.deserialize_quick_bar(player, serialized)
|
function serialize.deserialize_quick_bar(player, serialized)
|
||||||
-- Every slot is set, and the ones not in the data are cleared, so a slot moved or cleared on the
|
-- Every slot is set, and the ones not in the data are cleared, so a slot moved or cleared on the
|
||||||
-- previous server does not come back from this server's old copy
|
-- previous server does not come back from this server's old copy
|
||||||
serialized = serialized or {}
|
serialized = serialized or {}
|
||||||
local width = v2_1_quick_bar_api and player.quick_bar_width
|
local width = v2_1_quick_bar_api and player.quick_bar_width
|
||||||
|
local missing
|
||||||
for i = 1, quick_bar_slot_count(player) do
|
for i = 1, quick_bar_slot_count(player) do
|
||||||
local entry = serialized[tostring(i)]
|
local entry = serialized[tostring(i)]
|
||||||
local slot = entry ~= nil and serialize.deserialize_quick_bar_slot(entry, player) or nil
|
local slot = entry ~= nil and serialize.deserialize_quick_bar_slot(entry, player) or nil
|
||||||
|
if slot == nil and v2_1_quick_bar_api and type(entry) == "table" and entry.t == "r" then
|
||||||
|
-- ponytail: kept until the record shows up or the slot is used for something else, so the entry of a
|
||||||
|
-- record deleted from the library is carried along. Drop them by age if that ever matters.
|
||||||
|
missing = missing or {}
|
||||||
|
missing[tostring(i)] = entry
|
||||||
|
end
|
||||||
if v2_1_quick_bar_api then
|
if v2_1_quick_bar_api then
|
||||||
local page = math.floor((i - 1) / width) + 1
|
local page = math.floor((i - 1) / width) + 1
|
||||||
local page_slot = ((i - 1) % width) + 1
|
local page_slot = ((i - 1) % width) + 1
|
||||||
@@ -718,6 +774,7 @@ function serialize.deserialize_quick_bar(player, serialized)
|
|||||||
player.set_quick_bar_slot(i, slot)
|
player.set_quick_bar_slot(i, slot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return missing
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Quick bar page shown on each quick bar row of the screen, kept by every save on its own
|
--- Quick bar page shown on each quick bar row of the screen, kept by every save on its own
|
||||||
@@ -1212,7 +1269,7 @@ function serialize.serialize_player(player, failed_deserialization, components)
|
|||||||
|
|
||||||
-- Serialize quick bar
|
-- Serialize quick bar
|
||||||
if syncs(components, "quick_bar") then
|
if syncs(components, "quick_bar") then
|
||||||
serialized.quick_bar = serialize.serialize_quick_bar(player)
|
serialized.quick_bar = serialize.serialize_quick_bar(player, failed_deserialization.quick_bar)
|
||||||
serialized.quick_bar_pages = serialize.serialize_quick_bar_pages(player)
|
serialized.quick_bar_pages = serialize.serialize_quick_bar_pages(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1333,6 +1390,7 @@ end
|
|||||||
|
|
||||||
--- @class FailedDeserializationPlayerData
|
--- @class FailedDeserializationPlayerData
|
||||||
--- @field recipe_notifications string[]?
|
--- @field recipe_notifications string[]?
|
||||||
|
--- @field quick_bar table<string, QuickBarSlotEncoded.record>? Slots whose record is not in the library (yet)
|
||||||
|
|
||||||
--- @param player LuaPlayer
|
--- @param player LuaPlayer
|
||||||
--- @param serialized SerializedPlayerData
|
--- @param serialized SerializedPlayerData
|
||||||
@@ -1495,7 +1553,9 @@ function serialize.deserialize_player(player, serialized, components, local_cont
|
|||||||
|
|
||||||
-- Deserialize quick bar (named hotbar in old data)
|
-- Deserialize quick bar (named hotbar in old data)
|
||||||
if syncs(components, "quick_bar") then
|
if syncs(components, "quick_bar") then
|
||||||
serialize.deserialize_quick_bar(player, serialized.quick_bar or serialized.hotbar)
|
failed_deserialization.quick_bar = serialize.deserialize_quick_bar(
|
||||||
|
player, serialized.quick_bar or serialized.hotbar
|
||||||
|
)
|
||||||
serialize.deserialize_quick_bar_pages(player, serialized.quick_bar_pages)
|
serialize.deserialize_quick_bar_pages(player, serialized.quick_bar_pages)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-player_sync",
|
"name": "@clusterio/plugin-player_sync",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin forwarding between Factorio servers",
|
"description": "Clusterio plugin forwarding between Factorio servers",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Danielv <danielv@danielv.no>",
|
"author": "Danielv <danielv@danielv.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-research_sync",
|
"name": "@clusterio/plugin-research_sync",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin for synchronizing research between Factorio servers",
|
"description": "Clusterio plugin for synchronizing research between Factorio servers",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-statistics_exporter",
|
"name": "@clusterio/plugin-statistics_exporter",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin for gathering statistics from Factorio servers",
|
"description": "Clusterio plugin for gathering statistics from Factorio servers",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@clusterio/plugin-subspace_storage",
|
"name": "@clusterio/plugin-subspace_storage",
|
||||||
"version": "2.1.0",
|
"version": "2.0.3",
|
||||||
"description": "Clusterio plugin for sharing storage between Factorio servers",
|
"description": "Clusterio plugin for sharing storage between Factorio servers",
|
||||||
"homepage": "https://github.com/clusterio/clusterio#readme",
|
"homepage": "https://github.com/clusterio/clusterio#readme",
|
||||||
"author": "Hornwitser <github@hornwitser.no>",
|
"author": "Hornwitser <github@hornwitser.no>",
|
||||||
|
|||||||
@@ -2,21 +2,6 @@
|
|||||||
|
|
||||||
This folder contains files used for performing manual tests that are non-trivial to automate.
|
This folder contains files used for performing manual tests that are non-trivial to automate.
|
||||||
|
|
||||||
|
|
||||||
## Plugins page on small screens
|
|
||||||
|
|
||||||
Use a cluster with several plugins, including one with different running and installed versions and one with a web load error.
|
|
||||||
Compare the Plugins page at 390px and 1440px viewport widths, and check the 768px breakpoint.
|
|
||||||
|
|
||||||
1. On phones, check that each row shows its name, versions, machine counts, status and update action vertically, without clipping or breaking ordinary words.
|
|
||||||
2. Check that a long plugin name wraps and the header branding stays on one line.
|
|
||||||
3. Search and sort by name, reload, and check that the selected filter and sort order remain applied.
|
|
||||||
4. Open an update or restart checklist, then cancel it; update actions must not navigate to the plugin page.
|
|
||||||
5. Open a plugin by its name and by clicking elsewhere in its row.
|
|
||||||
6. Open the account menu using the user icon on mobile; the full account name should appear in the menu.
|
|
||||||
7. On desktop, check that the name, versions and update action remain separate columns.
|
|
||||||
|
|
||||||
|
|
||||||
## Setup test mod
|
## Setup test mod
|
||||||
|
|
||||||
Run `node packages/lib/dist/node/build_mod.js --source-dir test/manual/test_mod/ --output-dir mods/` to create the test mod.
|
Run `node packages/lib/dist/node/build_mod.js --source-dir test/manual/test_mod/ --output-dir mods/` to create the test mod.
|
||||||
|
|||||||
Reference in new issue
Block a user