@quenty/studio-bridge 0.21.1 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +109 -17
- package/build-scripts/transform-add-session-attribute.lua +1 -0
- package/deploy.nevermore.json +10 -0
- package/dist/src/bridge/bridge-connection.d.ts +26 -2
- package/dist/src/bridge/bridge-connection.d.ts.map +1 -1
- package/dist/src/bridge/bridge-connection.js +159 -14
- package/dist/src/bridge/bridge-connection.js.map +1 -1
- package/dist/src/bridge/bridge-connection.test.js +81 -0
- package/dist/src/bridge/bridge-connection.test.js.map +1 -1
- package/dist/src/bridge/bridge-session.d.ts +35 -2
- package/dist/src/bridge/bridge-session.d.ts.map +1 -1
- package/dist/src/bridge/bridge-session.js +185 -28
- package/dist/src/bridge/bridge-session.js.map +1 -1
- package/dist/src/bridge/bridge-session.test.js +6 -4
- package/dist/src/bridge/bridge-session.test.js.map +1 -1
- package/dist/src/bridge/index.d.ts +1 -1
- package/dist/src/bridge/index.d.ts.map +1 -1
- package/dist/src/bridge/index.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.d.ts +4 -0
- package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-client.js +20 -7
- package/dist/src/bridge/internal/bridge-client.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.test.js +18 -0
- package/dist/src/bridge/internal/bridge-client.test.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.d.ts +13 -1
- package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-host.js +35 -0
- package/dist/src/bridge/internal/bridge-host.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.test.js +31 -0
- package/dist/src/bridge/internal/bridge-host.test.js.map +1 -1
- package/dist/src/bridge/internal/hand-off.d.ts.map +1 -1
- package/dist/src/bridge/internal/hand-off.js +4 -1
- package/dist/src/bridge/internal/hand-off.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.d.ts +8 -0
- package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -1
- package/dist/src/bridge/internal/host-protocol.js +120 -13
- package/dist/src/bridge/internal/host-protocol.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.test.js +13 -0
- package/dist/src/bridge/internal/host-protocol.test.js.map +1 -1
- package/dist/src/bridge/internal/instance-state.d.ts +9 -0
- package/dist/src/bridge/internal/instance-state.d.ts.map +1 -0
- package/dist/src/bridge/internal/instance-state.js +28 -0
- package/dist/src/bridge/internal/instance-state.js.map +1 -0
- package/dist/src/bridge/internal/transport-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-client.js +2 -1
- package/dist/src/bridge/internal/transport-client.js.map +1 -1
- package/dist/src/bridge/internal/transport-server.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-server.js +5 -1
- package/dist/src/bridge/internal/transport-server.js.map +1 -1
- package/dist/src/bridge/types.d.ts +43 -4
- package/dist/src/bridge/types.d.ts.map +1 -1
- package/dist/src/bridge/types.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +24 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.d.ts +6 -0
- package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.js +107 -11
- package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.d.ts +3 -0
- package/dist/src/cli/adapters/group-builder.d.ts.map +1 -1
- package/dist/src/cli/adapters/group-builder.js +34 -15
- package/dist/src/cli/adapters/group-builder.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.test.js +26 -2
- package/dist/src/cli/adapters/group-builder.test.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -1
- package/dist/src/cli/adapters/help-formatter.js +4 -3
- package/dist/src/cli/adapters/help-formatter.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js.map +1 -1
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts +2 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js +28 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js.map +1 -0
- package/dist/src/cli/args/global-args.d.ts +1 -1
- package/dist/src/cli/args/global-args.d.ts.map +1 -1
- package/dist/src/cli/cli-output-context.d.ts +14 -0
- package/dist/src/cli/cli-output-context.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.js +39 -0
- package/dist/src/cli/cli-output-context.js.map +1 -0
- package/dist/src/cli/cli-output-context.test.d.ts +2 -0
- package/dist/src/cli/cli-output-context.test.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.test.js +38 -0
- package/dist/src/cli/cli-output-context.test.js.map +1 -0
- package/dist/src/cli/cli.js +40 -17
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/cli/managed-studio-run.d.ts +21 -0
- package/dist/src/cli/managed-studio-run.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.js +75 -0
- package/dist/src/cli/managed-studio-run.js.map +1 -0
- package/dist/src/cli/managed-studio-run.test.d.ts +2 -0
- package/dist/src/cli/managed-studio-run.test.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.test.js +52 -0
- package/dist/src/cli/managed-studio-run.test.js.map +1 -0
- package/dist/src/cli/resolve-session.js +1 -1
- package/dist/src/cli/resolve-session.js.map +1 -1
- package/dist/src/cli/resolve-session.test.js +1 -1
- package/dist/src/cli/resolve-session.test.js.map +1 -1
- package/dist/src/cli/script-executor.d.ts +13 -1
- package/dist/src/cli/script-executor.d.ts.map +1 -1
- package/dist/src/cli/script-executor.js +77 -47
- package/dist/src/cli/script-executor.js.map +1 -1
- package/dist/src/commands/console/exec/exec.d.ts +17 -0
- package/dist/src/commands/console/exec/exec.d.ts.map +1 -1
- package/dist/src/commands/console/exec/exec.js +39 -7
- package/dist/src/commands/console/exec/exec.js.map +1 -1
- package/dist/src/commands/console/exec/exec.test.js +62 -3
- package/dist/src/commands/console/exec/exec.test.js.map +1 -1
- package/dist/src/commands/console/logs/logs.d.ts +30 -2
- package/dist/src/commands/console/logs/logs.d.ts.map +1 -1
- package/dist/src/commands/console/logs/logs.js +82 -7
- package/dist/src/commands/console/logs/logs.js.map +1 -1
- package/dist/src/commands/console/logs/logs.test.js +63 -4
- package/dist/src/commands/console/logs/logs.test.js.map +1 -1
- package/dist/src/commands/explorer/export/export.d.ts +19 -0
- package/dist/src/commands/explorer/export/export.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.js +37 -0
- package/dist/src/commands/explorer/export/export.js.map +1 -0
- package/dist/src/commands/explorer/export/export.test.d.ts +2 -0
- package/dist/src/commands/explorer/export/export.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.test.js +25 -0
- package/dist/src/commands/explorer/export/export.test.js.map +1 -0
- package/dist/src/commands/explorer/find/find.d.ts +28 -0
- package/dist/src/commands/explorer/find/find.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.js +75 -0
- package/dist/src/commands/explorer/find/find.js.map +1 -0
- package/dist/src/commands/explorer/find/find.test.d.ts +2 -0
- package/dist/src/commands/explorer/find/find.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.test.js +44 -0
- package/dist/src/commands/explorer/find/find.test.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.d.ts +24 -0
- package/dist/src/commands/explorer/grep/grep.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.js +63 -0
- package/dist/src/commands/explorer/grep/grep.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts +2 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.js +42 -0
- package/dist/src/commands/explorer/grep/grep.test.js.map +1 -0
- package/dist/src/commands/explorer/import/import.d.ts +21 -0
- package/dist/src/commands/explorer/import/import.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.js +37 -0
- package/dist/src/commands/explorer/import/import.js.map +1 -0
- package/dist/src/commands/explorer/import/import.test.d.ts +2 -0
- package/dist/src/commands/explorer/import/import.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.test.js +45 -0
- package/dist/src/commands/explorer/import/import.test.js.map +1 -0
- package/dist/src/commands/formatters.test.js +7 -1
- package/dist/src/commands/formatters.test.js.map +1 -1
- package/dist/src/commands/framework/action-loader.d.ts +5 -1
- package/dist/src/commands/framework/action-loader.d.ts.map +1 -1
- package/dist/src/commands/framework/action-loader.js +9 -2
- package/dist/src/commands/framework/action-loader.js.map +1 -1
- package/dist/src/commands/framework/action-loader.test.js +23 -0
- package/dist/src/commands/framework/action-loader.test.js.map +1 -1
- package/dist/src/commands/framework/arg-builder.d.ts +15 -2
- package/dist/src/commands/framework/arg-builder.d.ts.map +1 -1
- package/dist/src/commands/framework/arg-builder.js +22 -2
- package/dist/src/commands/framework/arg-builder.js.map +1 -1
- package/dist/src/commands/framework/define-command.d.ts +19 -3
- package/dist/src/commands/framework/define-command.d.ts.map +1 -1
- package/dist/src/commands/framework/define-command.js.map +1 -1
- package/dist/src/commands/framework/index.d.ts +1 -1
- package/dist/src/commands/framework/index.d.ts.map +1 -1
- package/dist/src/commands/framework/index.js.map +1 -1
- package/dist/src/commands/index.d.ts +11 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +11 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js +1 -3
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -1
- package/dist/src/commands/linux/setup/setup.d.ts.map +1 -1
- package/dist/src/commands/linux/setup/setup.js +1 -3
- package/dist/src/commands/linux/setup/setup.js.map +1 -1
- package/dist/src/commands/linux/status/status.d.ts +9 -1
- package/dist/src/commands/linux/status/status.d.ts.map +1 -1
- package/dist/src/commands/linux/status/status.js +66 -86
- package/dist/src/commands/linux/status/status.js.map +1 -1
- package/dist/src/commands/playtest/player/add/add.d.ts +17 -0
- package/dist/src/commands/playtest/player/add/add.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/add/add.js +45 -0
- package/dist/src/commands/playtest/player/add/add.js.map +1 -0
- package/dist/src/commands/playtest/player/list/list.d.ts +13 -0
- package/dist/src/commands/playtest/player/list/list.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/list/list.js +33 -0
- package/dist/src/commands/playtest/player/list/list.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts +36 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.js +187 -0
- package/dist/src/commands/playtest/player/playtest-players.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts +2 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js +89 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts +17 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.js +42 -0
- package/dist/src/commands/playtest/player/remove/remove.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts +49 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.js +283 -0
- package/dist/src/commands/playtest/playtest-sessions.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts +2 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js +87 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js.map +1 -0
- package/dist/src/commands/playtest/restart/restart.d.ts +23 -0
- package/dist/src/commands/playtest/restart/restart.d.ts.map +1 -0
- package/dist/src/commands/playtest/restart/restart.js +105 -0
- package/dist/src/commands/playtest/restart/restart.js.map +1 -0
- package/dist/src/commands/playtest/run/run.d.ts +47 -0
- package/dist/src/commands/playtest/run/run.d.ts.map +1 -0
- package/dist/src/commands/playtest/run/run.js +137 -0
- package/dist/src/commands/playtest/run/run.js.map +1 -0
- package/dist/src/commands/playtest/start/start.d.ts +42 -0
- package/dist/src/commands/playtest/start/start.d.ts.map +1 -0
- package/dist/src/commands/playtest/start/start.js +70 -0
- package/dist/src/commands/playtest/start/start.js.map +1 -0
- package/dist/src/commands/playtest/stop/stop.d.ts +15 -0
- package/dist/src/commands/playtest/stop/stop.d.ts.map +1 -0
- package/dist/src/commands/playtest/stop/stop.js +24 -0
- package/dist/src/commands/playtest/stop/stop.js.map +1 -0
- package/dist/src/commands/process/close/close.d.ts +19 -10
- package/dist/src/commands/process/close/close.d.ts.map +1 -1
- package/dist/src/commands/process/close/close.js +118 -15
- package/dist/src/commands/process/close/close.js.map +1 -1
- package/dist/src/commands/process/close/close.test.d.ts +2 -0
- package/dist/src/commands/process/close/close.test.d.ts.map +1 -0
- package/dist/src/commands/process/close/close.test.js +190 -0
- package/dist/src/commands/process/close/close.test.js.map +1 -0
- package/dist/src/commands/process/info/info.d.ts +45 -5
- package/dist/src/commands/process/info/info.d.ts.map +1 -1
- package/dist/src/commands/process/info/info.js +117 -13
- package/dist/src/commands/process/info/info.js.map +1 -1
- package/dist/src/commands/process/info/info.test.js +140 -38
- package/dist/src/commands/process/info/info.test.js.map +1 -1
- package/dist/src/commands/process/launch/launch.d.ts +11 -1
- package/dist/src/commands/process/launch/launch.d.ts.map +1 -1
- package/dist/src/commands/process/launch/launch.js +37 -7
- package/dist/src/commands/process/launch/launch.js.map +1 -1
- package/dist/src/commands/process/launch/launch.test.js +99 -20
- package/dist/src/commands/process/launch/launch.test.js.map +1 -1
- package/dist/src/commands/process/list/list.d.ts +2 -1
- package/dist/src/commands/process/list/list.d.ts.map +1 -1
- package/dist/src/commands/process/list/list.js +9 -6
- package/dist/src/commands/process/list/list.js.map +1 -1
- package/dist/src/commands/process/list/list.test.js +1 -0
- package/dist/src/commands/process/list/list.test.js.map +1 -1
- package/dist/src/commands/process/run/run.d.ts +9 -0
- package/dist/src/commands/process/run/run.d.ts.map +1 -1
- package/dist/src/commands/process/run/run.js +15 -5
- package/dist/src/commands/process/run/run.js.map +1 -1
- package/dist/src/commands/serve/serve.d.ts +2 -0
- package/dist/src/commands/serve/serve.d.ts.map +1 -1
- package/dist/src/commands/serve/serve.js +17 -4
- package/dist/src/commands/serve/serve.js.map +1 -1
- package/dist/src/commands/serve/serve.test.js +9 -2
- package/dist/src/commands/serve/serve.test.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js +4 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.test.js +5 -9
- package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -1
- package/dist/src/deploy/deploy-places.d.ts +39 -0
- package/dist/src/deploy/deploy-places.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.js +162 -0
- package/dist/src/deploy/deploy-places.js.map +1 -0
- package/dist/src/deploy/deploy-places.test.d.ts +2 -0
- package/dist/src/deploy/deploy-places.test.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.test.js +96 -0
- package/dist/src/deploy/deploy-places.test.js.map +1 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.js +2 -1
- package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
- package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
- package/dist/src/plugin/plugin-build-version.d.ts +7 -0
- package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.js +42 -0
- package/dist/src/plugin/plugin-build-version.js.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.js +41 -0
- package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
- package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
- package/dist/src/plugin/plugin-injector.js +7 -1
- package/dist/src/plugin/plugin-injector.js.map +1 -1
- package/dist/src/process/studio-process-closer.d.ts +35 -0
- package/dist/src/process/studio-process-closer.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.js +129 -0
- package/dist/src/process/studio-process-closer.js.map +1 -0
- package/dist/src/process/studio-process-closer.test.d.ts +2 -0
- package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.test.js +80 -0
- package/dist/src/process/studio-process-closer.test.js.map +1 -0
- package/dist/src/process/studio-settings.d.ts +14 -0
- package/dist/src/process/studio-settings.d.ts.map +1 -0
- package/dist/src/process/studio-settings.js +50 -0
- package/dist/src/process/studio-settings.js.map +1 -0
- package/dist/src/process/studio-settings.test.d.ts +2 -0
- package/dist/src/process/studio-settings.test.d.ts.map +1 -0
- package/dist/src/process/studio-settings.test.js +16 -0
- package/dist/src/process/studio-settings.test.js.map +1 -0
- package/dist/src/process/studio-window-state.d.ts +13 -0
- package/dist/src/process/studio-window-state.d.ts.map +1 -0
- package/dist/src/process/studio-window-state.js +48 -0
- package/dist/src/process/studio-window-state.js.map +1 -0
- package/dist/src/server/studio-bridge-server.d.ts +3 -1
- package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
- package/dist/src/server/studio-bridge-server.js +12 -4
- package/dist/src/server/studio-bridge-server.js.map +1 -1
- package/dist/src/server/web-socket-protocol.d.ts +173 -6
- package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
- package/dist/src/server/web-socket-protocol.js +244 -1
- package/dist/src/server/web-socket-protocol.js.map +1 -1
- package/dist/src/server/web-socket-protocol.test.js +48 -0
- package/dist/src/server/web-socket-protocol.test.js.map +1 -1
- package/dist/src/test/e2e/persistent-session.test.js +121 -0
- package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
- package/dist/src/test/e2e/split-server.test.d.ts +3 -5
- package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
- package/dist/src/test/e2e/split-server.test.js +3 -5
- package/dist/src/test/e2e/split-server.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docker/Dockerfile +3 -0
- package/package.json +8 -6
- package/src/bridge/bridge-connection.test.ts +126 -0
- package/src/bridge/bridge-connection.ts +217 -22
- package/src/bridge/bridge-session.test.ts +6 -4
- package/src/bridge/bridge-session.ts +273 -28
- package/src/bridge/index.ts +3 -0
- package/src/bridge/internal/bridge-client.test.ts +23 -0
- package/src/bridge/internal/bridge-client.ts +25 -7
- package/src/bridge/internal/bridge-host.test.ts +38 -0
- package/src/bridge/internal/bridge-host.ts +47 -0
- package/src/bridge/internal/hand-off.ts +4 -1
- package/src/bridge/internal/host-protocol.test.ts +15 -0
- package/src/bridge/internal/host-protocol.ts +148 -37
- package/src/bridge/internal/instance-state.ts +34 -0
- package/src/bridge/internal/transport-client.ts +2 -1
- package/src/bridge/internal/transport-server.ts +5 -1
- package/src/bridge/types.ts +47 -2
- package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
- package/src/cli/adapters/cli-command-adapter.ts +150 -18
- package/src/cli/adapters/group-builder.test.ts +34 -2
- package/src/cli/adapters/group-builder.ts +52 -20
- package/src/cli/adapters/help-formatter.test.ts +1 -1
- package/src/cli/adapters/help-formatter.ts +4 -3
- package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
- package/src/cli/args/global-args.ts +1 -1
- package/src/cli/cli-output-context.test.ts +55 -0
- package/src/cli/cli-output-context.ts +50 -0
- package/src/cli/cli.ts +49 -17
- package/src/cli/managed-studio-run.test.ts +71 -0
- package/src/cli/managed-studio-run.ts +135 -0
- package/src/cli/resolve-session.test.ts +1 -1
- package/src/cli/resolve-session.ts +1 -1
- package/src/cli/script-executor.ts +99 -51
- package/src/commands/console/exec/exec.test.ts +96 -3
- package/src/commands/console/exec/exec.ts +59 -6
- package/src/commands/console/exec/execute.lua +356 -240
- package/src/commands/console/exec/execute.spec.lua +286 -0
- package/src/commands/console/logs/logs.test.ts +76 -4
- package/src/commands/console/logs/logs.ts +132 -17
- package/src/commands/console/logs/query-logs.lua +85 -52
- package/src/commands/console/logs/query-logs.spec.lua +132 -0
- package/src/commands/explorer/export/export-instances.lua +108 -0
- package/src/commands/explorer/export/export-instances.spec.lua +87 -0
- package/src/commands/explorer/export/export.test.ts +27 -0
- package/src/commands/explorer/export/export.ts +60 -0
- package/src/commands/explorer/find/find-instances.lua +174 -0
- package/src/commands/explorer/find/find-instances.spec.lua +122 -0
- package/src/commands/explorer/find/find.test.ts +53 -0
- package/src/commands/explorer/find/find.ts +113 -0
- package/src/commands/explorer/grep/grep-scripts.lua +214 -0
- package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
- package/src/commands/explorer/grep/grep.test.ts +45 -0
- package/src/commands/explorer/grep/grep.ts +93 -0
- package/src/commands/explorer/import/import-instances.lua +106 -0
- package/src/commands/explorer/import/import-instances.spec.lua +84 -0
- package/src/commands/explorer/import/import.test.ts +60 -0
- package/src/commands/explorer/import/import.ts +66 -0
- package/src/commands/explorer/query/query-data-model.lua +124 -152
- package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
- package/src/commands/formatters.test.ts +7 -1
- package/src/commands/framework/action-loader.test.ts +28 -0
- package/src/commands/framework/action-loader.ts +10 -2
- package/src/commands/framework/arg-builder.ts +26 -3
- package/src/commands/framework/define-command.ts +32 -3
- package/src/commands/framework/index.ts +1 -0
- package/src/commands/index.ts +50 -0
- package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
- package/src/commands/linux/setup/setup.ts +1 -3
- package/src/commands/linux/status/status.ts +105 -98
- package/src/commands/playtest/player/add/add.ts +85 -0
- package/src/commands/playtest/player/list/list.ts +57 -0
- package/src/commands/playtest/player/playtest-players.test.ts +146 -0
- package/src/commands/playtest/player/playtest-players.ts +270 -0
- package/src/commands/playtest/player/remove/remove.ts +78 -0
- package/src/commands/playtest/playtest-sessions.test.ts +143 -0
- package/src/commands/playtest/playtest-sessions.ts +417 -0
- package/src/commands/playtest/restart/restart.ts +199 -0
- package/src/commands/playtest/run/playtest-runner.luau +264 -0
- package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
- package/src/commands/playtest/run/run-playtest-script.lua +139 -0
- package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
- package/src/commands/playtest/run/run.ts +239 -0
- package/src/commands/playtest/start/start-playtest.lua +108 -0
- package/src/commands/playtest/start/start.ts +138 -0
- package/src/commands/playtest/stop/end-playtest.lua +47 -0
- package/src/commands/playtest/stop/stop.ts +51 -0
- package/src/commands/process/close/close.test.ts +250 -0
- package/src/commands/process/close/close.ts +183 -17
- package/src/commands/process/info/info.test.ts +198 -44
- package/src/commands/process/info/info.ts +204 -15
- package/src/commands/process/info/query-state.lua +42 -29
- package/src/commands/process/info/query-state.spec.lua +39 -0
- package/src/commands/process/launch/launch.test.ts +138 -21
- package/src/commands/process/launch/launch.ts +69 -9
- package/src/commands/process/list/list.test.ts +1 -0
- package/src/commands/process/list/list.ts +14 -6
- package/src/commands/process/run/run.ts +26 -5
- package/src/commands/serve/serve.test.ts +17 -2
- package/src/commands/serve/serve.ts +25 -6
- package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
- package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
- package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
- package/src/commands/viewport/screenshot/screenshot.ts +6 -1
- package/src/deploy/deploy-places.test.ts +127 -0
- package/src/deploy/deploy-places.ts +229 -0
- package/src/index.ts +11 -0
- package/src/plugin/persistent-plugin-installer.test.ts +4 -0
- package/src/plugin/persistent-plugin-installer.ts +2 -1
- package/src/plugin/plugin-build-version.test.ts +55 -0
- package/src/plugin/plugin-build-version.ts +53 -0
- package/src/plugin/plugin-injector.ts +7 -1
- package/src/process/studio-process-closer.test.ts +107 -0
- package/src/process/studio-process-closer.ts +187 -0
- package/src/process/studio-settings.test.ts +18 -0
- package/src/process/studio-settings.ts +61 -0
- package/src/process/studio-window-state.ts +65 -0
- package/src/server/studio-bridge-server.ts +15 -4
- package/src/server/web-socket-protocol.test.ts +55 -0
- package/src/server/web-socket-protocol.ts +468 -8
- package/src/test/e2e/persistent-session.test.ts +137 -0
- package/src/test/e2e/split-server.test.ts +3 -5
- package/templates/studio-bridge-plugin/default.project.json +3 -0
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
- package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
- package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
- package/test/default.project.json +41 -0
- package/test/jest.config.lua +3 -0
- package/test/scripts/Server/ServerMain.server.lua +10 -0
- package/tsconfig.json +1 -0
- package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
- package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
- package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
- package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
- package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
- package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
- package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
- package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
- package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
- package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
- package/templates/studio-bridge-plugin-test/test/test-runner.lua +0 -112
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
addPlaytestPlayersAsync,
|
|
4
|
+
listPlaytestPlayersAsync,
|
|
5
|
+
removePlaytestPlayersAsync,
|
|
6
|
+
} from './playtest-players.js';
|
|
7
|
+
|
|
8
|
+
function createSession(
|
|
9
|
+
sessionId: string,
|
|
10
|
+
context: string,
|
|
11
|
+
returnValues: unknown[][],
|
|
12
|
+
playerName?: string
|
|
13
|
+
) {
|
|
14
|
+
const scripts: string[] = [];
|
|
15
|
+
return {
|
|
16
|
+
scripts,
|
|
17
|
+
info: { sessionId, instanceId: 'place-1a2b', context, playerName },
|
|
18
|
+
context,
|
|
19
|
+
execAsync: vi.fn(async (script: string) => {
|
|
20
|
+
scripts.push(script);
|
|
21
|
+
return { success: true, output: [], returnValues: returnValues.shift() };
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createConnection(sessions: Array<ReturnType<typeof createSession>>) {
|
|
27
|
+
return {
|
|
28
|
+
listSessions: () => sessions.map((s) => s.info),
|
|
29
|
+
getSession: (id: string) => sessions.find((s) => s.info.sessionId === id),
|
|
30
|
+
} as any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const player = (name: string, userId: number) => ({
|
|
34
|
+
name,
|
|
35
|
+
userId,
|
|
36
|
+
displayName: name,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('listPlaytestPlayersAsync', () => {
|
|
40
|
+
it('pairs each player with its client session', async () => {
|
|
41
|
+
const server = createSession('place-1a2b-server', 'server', [
|
|
42
|
+
[[player('Player1', -1), player('Player2', -2)]],
|
|
43
|
+
]);
|
|
44
|
+
const client = createSession(
|
|
45
|
+
'place-1a2b-client-player1',
|
|
46
|
+
'client',
|
|
47
|
+
[],
|
|
48
|
+
'Player1'
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const players = await listPlaytestPlayersAsync(
|
|
52
|
+
createConnection([server, client]),
|
|
53
|
+
server as any
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(players).toEqual([
|
|
57
|
+
{ ...player('Player1', -1), sessionId: 'place-1a2b-client-player1' },
|
|
58
|
+
player('Player2', -2),
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('addPlaytestPlayersAsync', () => {
|
|
64
|
+
it('adds players from the server and reports the ones that joined', async () => {
|
|
65
|
+
const server = createSession('place-1a2b-server', 'server', [
|
|
66
|
+
[[player('Player3', -3)]],
|
|
67
|
+
]);
|
|
68
|
+
const client = createSession(
|
|
69
|
+
'place-1a2b-client-player3',
|
|
70
|
+
'client',
|
|
71
|
+
[],
|
|
72
|
+
'Player3'
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const players = await addPlaytestPlayersAsync(
|
|
76
|
+
createConnection([server, client]),
|
|
77
|
+
server as any,
|
|
78
|
+
1
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(server.scripts[0]).toContain('AddPlayers(1)');
|
|
82
|
+
expect(players).toEqual([
|
|
83
|
+
{ ...player('Player3', -3), sessionId: 'place-1a2b-client-player3' },
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('removePlaytestPlayersAsync', () => {
|
|
89
|
+
it('has a connected client leave and kicks the rest', async () => {
|
|
90
|
+
const server = createSession('place-1a2b-server', 'server', [
|
|
91
|
+
[[player('Player1', -1), player('Player2', -2)]],
|
|
92
|
+
[],
|
|
93
|
+
[[]],
|
|
94
|
+
]);
|
|
95
|
+
const client = createSession(
|
|
96
|
+
'place-1a2b-client-player1',
|
|
97
|
+
'client',
|
|
98
|
+
[[]],
|
|
99
|
+
'Player1'
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const removed = await removePlaytestPlayersAsync(
|
|
103
|
+
createConnection([server, client]),
|
|
104
|
+
server as any,
|
|
105
|
+
['player1', 'Player2']
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(removed).toEqual([
|
|
109
|
+
{ name: 'Player1', how: 'left' },
|
|
110
|
+
{ name: 'Player2', how: 'kicked' },
|
|
111
|
+
]);
|
|
112
|
+
expect(client.scripts[0]).toContain('LeaveTest');
|
|
113
|
+
expect(server.scripts[1]).toContain('"Player2"');
|
|
114
|
+
expect(server.scripts[1]).not.toContain('"Player1"');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('removes a player named twice only once', async () => {
|
|
118
|
+
const server = createSession('place-1a2b-server', 'server', [
|
|
119
|
+
[[player('Player2', -2)]],
|
|
120
|
+
[],
|
|
121
|
+
[[]],
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const removed = await removePlaytestPlayersAsync(
|
|
125
|
+
createConnection([server]),
|
|
126
|
+
server as any,
|
|
127
|
+
['Player2', 'player2']
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(removed).toEqual([{ name: 'Player2', how: 'kicked' }]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('refuses an unknown name before removing anyone', async () => {
|
|
134
|
+
const server = createSession('place-1a2b-server', 'server', [
|
|
135
|
+
[[player('Player1', -1)]],
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
await expect(
|
|
139
|
+
removePlaytestPlayersAsync(createConnection([server]), server as any, [
|
|
140
|
+
'Player1',
|
|
141
|
+
'Nobody',
|
|
142
|
+
])
|
|
143
|
+
).rejects.toThrow("No player named 'Nobody' (players: Player1)");
|
|
144
|
+
expect(server.scripts).toHaveLength(1);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The players of a running playtest, read and changed through its server
|
|
3
|
+
* session.
|
|
4
|
+
*
|
|
5
|
+
* StudioTestService only adds players from the server DataModel of a
|
|
6
|
+
* multiplayer test, and each player is a client in its own Studio process.
|
|
7
|
+
* Kicking a player leaves that process open, so a player whose client
|
|
8
|
+
* session is connected leaves through `LeaveTest` instead, which closes it.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { BridgeConnection, BridgeSession } from '../../../bridge/index.js';
|
|
12
|
+
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
13
|
+
import {
|
|
14
|
+
formatTable,
|
|
15
|
+
type TableColumn,
|
|
16
|
+
} from '@quenty/cli-output-helpers/reporting';
|
|
17
|
+
|
|
18
|
+
const PLAYER_CHANGE_TIMEOUT_SEC = 60;
|
|
19
|
+
const CLIENT_SESSION_TIMEOUT_MS = 30_000;
|
|
20
|
+
const SCRIPT_GRACE_MS = 10_000;
|
|
21
|
+
|
|
22
|
+
export interface PlaytestPlayer {
|
|
23
|
+
name: string;
|
|
24
|
+
userId: number;
|
|
25
|
+
displayName: string;
|
|
26
|
+
/** The player's client session, when it is connected. */
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const PLAYER_INFO_FUNCTION = [
|
|
31
|
+
'local function playerInfo(player)',
|
|
32
|
+
'\treturn { name = player.Name, userId = player.UserId, displayName = player.DisplayName }',
|
|
33
|
+
'end',
|
|
34
|
+
].join('\n');
|
|
35
|
+
|
|
36
|
+
const LIST_PLAYERS_SCRIPT = [
|
|
37
|
+
PLAYER_INFO_FUNCTION,
|
|
38
|
+
'local players = {}',
|
|
39
|
+
'for _, player in game:GetService("Players"):GetPlayers() do',
|
|
40
|
+
'\ttable.insert(players, playerInfo(player))',
|
|
41
|
+
'end',
|
|
42
|
+
'return players',
|
|
43
|
+
].join('\n');
|
|
44
|
+
|
|
45
|
+
function addPlayersScript(count: number): string {
|
|
46
|
+
return [
|
|
47
|
+
PLAYER_INFO_FUNCTION,
|
|
48
|
+
'local Players = game:GetService("Players")',
|
|
49
|
+
'local before = {}',
|
|
50
|
+
'for _, player in Players:GetPlayers() do',
|
|
51
|
+
'\tbefore[player] = true',
|
|
52
|
+
'end',
|
|
53
|
+
`game:GetService("StudioTestService"):AddPlayers(${count})`,
|
|
54
|
+
`local deadline = os.clock() + ${PLAYER_CHANGE_TIMEOUT_SEC}`,
|
|
55
|
+
'local added = {}',
|
|
56
|
+
'repeat',
|
|
57
|
+
'\ttask.wait(0.25)',
|
|
58
|
+
'\tadded = {}',
|
|
59
|
+
'\tfor _, player in Players:GetPlayers() do',
|
|
60
|
+
'\t\tif not before[player] then',
|
|
61
|
+
'\t\t\ttable.insert(added, playerInfo(player))',
|
|
62
|
+
'\t\tend',
|
|
63
|
+
'\tend',
|
|
64
|
+
`until #added >= ${count} or os.clock() > deadline`,
|
|
65
|
+
'return added',
|
|
66
|
+
].join('\n');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function kickPlayersScript(names: string[]): string {
|
|
70
|
+
return [
|
|
71
|
+
`local names = ${luaStringList(names)}`,
|
|
72
|
+
'local Players = game:GetService("Players")',
|
|
73
|
+
'for _, name in names do',
|
|
74
|
+
'\tlocal player = Players:FindFirstChild(name)',
|
|
75
|
+
'\tif player then',
|
|
76
|
+
'\t\tplayer:Kick("Removed by studio-bridge")',
|
|
77
|
+
'\tend',
|
|
78
|
+
'end',
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function waitForPlayersGoneScript(names: string[]): string {
|
|
83
|
+
return [
|
|
84
|
+
`local names = ${luaStringList(names)}`,
|
|
85
|
+
'local Players = game:GetService("Players")',
|
|
86
|
+
`local deadline = os.clock() + ${PLAYER_CHANGE_TIMEOUT_SEC}`,
|
|
87
|
+
'local remaining',
|
|
88
|
+
'repeat',
|
|
89
|
+
'\tremaining = {}',
|
|
90
|
+
'\tfor _, name in names do',
|
|
91
|
+
'\t\tif Players:FindFirstChild(name) then',
|
|
92
|
+
'\t\t\ttable.insert(remaining, name)',
|
|
93
|
+
'\t\tend',
|
|
94
|
+
'\tend',
|
|
95
|
+
'\tif #remaining > 0 then',
|
|
96
|
+
'\t\ttask.wait(0.25)',
|
|
97
|
+
'\tend',
|
|
98
|
+
'until #remaining == 0 or os.clock() > deadline',
|
|
99
|
+
'return remaining',
|
|
100
|
+
].join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const LEAVE_TEST_SCRIPT =
|
|
104
|
+
'task.defer(function() game:GetService("StudioTestService"):LeaveTest() end)';
|
|
105
|
+
|
|
106
|
+
function luaStringList(values: string[]): string {
|
|
107
|
+
return `{ ${values.map((value) => JSON.stringify(value)).join(', ')} }`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function runScriptAsync(
|
|
111
|
+
session: BridgeSession,
|
|
112
|
+
script: string
|
|
113
|
+
): Promise<unknown> {
|
|
114
|
+
const result = await session.execAsync(
|
|
115
|
+
script,
|
|
116
|
+
PLAYER_CHANGE_TIMEOUT_SEC * 1000 + SCRIPT_GRACE_MS
|
|
117
|
+
);
|
|
118
|
+
if (!result.success) {
|
|
119
|
+
throw new Error(result.error ?? 'Player script failed');
|
|
120
|
+
}
|
|
121
|
+
return result.returnValues?.[0];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toPlayers(value: unknown): PlaytestPlayer[] {
|
|
125
|
+
if (!Array.isArray(value)) return [];
|
|
126
|
+
return value.map((entry) => ({
|
|
127
|
+
name: String(entry.name),
|
|
128
|
+
userId: Number(entry.userId),
|
|
129
|
+
displayName: String(entry.displayName),
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function findClientSession(
|
|
134
|
+
connection: BridgeConnection,
|
|
135
|
+
instanceId: string,
|
|
136
|
+
playerName: string
|
|
137
|
+
): BridgeSession | undefined {
|
|
138
|
+
const info = connection
|
|
139
|
+
.listSessions()
|
|
140
|
+
.find(
|
|
141
|
+
(s) =>
|
|
142
|
+
s.instanceId === instanceId &&
|
|
143
|
+
s.context === 'client' &&
|
|
144
|
+
s.playerName === playerName
|
|
145
|
+
);
|
|
146
|
+
return info ? connection.getSession(info.sessionId) : undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function withSessions(
|
|
150
|
+
connection: BridgeConnection,
|
|
151
|
+
instanceId: string,
|
|
152
|
+
players: PlaytestPlayer[]
|
|
153
|
+
): PlaytestPlayer[] {
|
|
154
|
+
return players.map((player) => {
|
|
155
|
+
const session = findClientSession(connection, instanceId, player.name);
|
|
156
|
+
return session ? { ...player, sessionId: session.info.sessionId } : player;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The players in the playtest `serverSession` belongs to. */
|
|
161
|
+
export async function listPlaytestPlayersAsync(
|
|
162
|
+
connection: BridgeConnection,
|
|
163
|
+
serverSession: BridgeSession
|
|
164
|
+
): Promise<PlaytestPlayer[]> {
|
|
165
|
+
const players = toPlayers(
|
|
166
|
+
await runScriptAsync(serverSession, LIST_PLAYERS_SCRIPT)
|
|
167
|
+
);
|
|
168
|
+
return withSessions(connection, serverSession.info.instanceId, players);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add `count` players to a multiplayer test, resolving with them once they
|
|
173
|
+
* have joined and their client sessions have connected, or after a timeout.
|
|
174
|
+
*/
|
|
175
|
+
export async function addPlaytestPlayersAsync(
|
|
176
|
+
connection: BridgeConnection,
|
|
177
|
+
serverSession: BridgeSession,
|
|
178
|
+
count: number
|
|
179
|
+
): Promise<PlaytestPlayer[]> {
|
|
180
|
+
const added = toPlayers(
|
|
181
|
+
await runScriptAsync(serverSession, addPlayersScript(count))
|
|
182
|
+
);
|
|
183
|
+
const { instanceId } = serverSession.info;
|
|
184
|
+
|
|
185
|
+
const deadline = Date.now() + CLIENT_SESSION_TIMEOUT_MS;
|
|
186
|
+
while (
|
|
187
|
+
Date.now() < deadline &&
|
|
188
|
+
added.some(
|
|
189
|
+
(player) => !findClientSession(connection, instanceId, player.name)
|
|
190
|
+
)
|
|
191
|
+
) {
|
|
192
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return withSessions(connection, instanceId, added);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface RemovedPlayer {
|
|
199
|
+
name: string;
|
|
200
|
+
/** `left` closed the player's client; `kicked` leaves its window open. */
|
|
201
|
+
how: 'left' | 'kicked';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Remove players by name, case-insensitively, resolving once they are gone.
|
|
206
|
+
* Throws for a name that is not in the playtest, before removing anyone.
|
|
207
|
+
*/
|
|
208
|
+
export async function removePlaytestPlayersAsync(
|
|
209
|
+
connection: BridgeConnection,
|
|
210
|
+
serverSession: BridgeSession,
|
|
211
|
+
names: string[]
|
|
212
|
+
): Promise<RemovedPlayer[]> {
|
|
213
|
+
const current = await listPlaytestPlayersAsync(connection, serverSession);
|
|
214
|
+
const found = names.map((name) => {
|
|
215
|
+
const player = current.find(
|
|
216
|
+
(p) => p.name.toLowerCase() === name.toLowerCase()
|
|
217
|
+
);
|
|
218
|
+
if (!player) {
|
|
219
|
+
const known = current.map((p) => p.name).join(', ') || 'none';
|
|
220
|
+
throw new Error(`No player named '${name}' (players: ${known})`);
|
|
221
|
+
}
|
|
222
|
+
return player;
|
|
223
|
+
});
|
|
224
|
+
const targets = [...new Set(found)];
|
|
225
|
+
|
|
226
|
+
const removed: RemovedPlayer[] = [];
|
|
227
|
+
const toKick: string[] = [];
|
|
228
|
+
for (const player of targets) {
|
|
229
|
+
const client = player.sessionId
|
|
230
|
+
? connection.getSession(player.sessionId)
|
|
231
|
+
: undefined;
|
|
232
|
+
if (client) {
|
|
233
|
+
await runScriptAsync(client, LEAVE_TEST_SCRIPT);
|
|
234
|
+
removed.push({ name: player.name, how: 'left' });
|
|
235
|
+
} else {
|
|
236
|
+
toKick.push(player.name);
|
|
237
|
+
removed.push({ name: player.name, how: 'kicked' });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (toKick.length > 0) {
|
|
241
|
+
await runScriptAsync(serverSession, kickPlayersScript(toKick));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const remaining = await runScriptAsync(
|
|
245
|
+
serverSession,
|
|
246
|
+
waitForPlayersGoneScript(targets.map((p) => p.name))
|
|
247
|
+
);
|
|
248
|
+
if (Array.isArray(remaining) && remaining.length > 0) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`Players still in the playtest after ${PLAYER_CHANGE_TIMEOUT_SEC}s: ${remaining.join(
|
|
251
|
+
', '
|
|
252
|
+
)}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return removed;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const playerColumns: TableColumn<PlaytestPlayer>[] = [
|
|
259
|
+
{ header: 'Player', value: (p) => p.name },
|
|
260
|
+
{ header: 'UserId', value: (p) => String(p.userId) },
|
|
261
|
+
{
|
|
262
|
+
header: 'Session',
|
|
263
|
+
value: (p) => p.sessionId ?? '',
|
|
264
|
+
format: (v) => OutputHelper.formatHint(v),
|
|
265
|
+
},
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
export function formatPlayersTable(players: PlaytestPlayer[]): string {
|
|
269
|
+
return formatTable(players, playerColumns);
|
|
270
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `playtest player remove` — remove players from the running playtest.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { defineCommand } from '../../../framework/define-command.js';
|
|
6
|
+
import { arg } from '../../../framework/arg-builder.js';
|
|
7
|
+
import type { BridgeConnection } from '../../../../bridge/index.js';
|
|
8
|
+
import { resolvePlaytestServerSessionAsync } from '../../playtest-sessions.js';
|
|
9
|
+
import {
|
|
10
|
+
removePlaytestPlayersAsync,
|
|
11
|
+
type RemovedPlayer,
|
|
12
|
+
} from '../playtest-players.js';
|
|
13
|
+
|
|
14
|
+
export interface PlaytestPlayerRemoveResult {
|
|
15
|
+
players: RemovedPlayer[];
|
|
16
|
+
summary: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface PlaytestPlayerRemoveArgs {
|
|
20
|
+
names: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function playtestPlayerRemoveHandlerAsync(
|
|
24
|
+
connection: BridgeConnection,
|
|
25
|
+
names: string[],
|
|
26
|
+
target?: string
|
|
27
|
+
): Promise<PlaytestPlayerRemoveResult> {
|
|
28
|
+
const serverSession = await resolvePlaytestServerSessionAsync(
|
|
29
|
+
connection,
|
|
30
|
+
target
|
|
31
|
+
);
|
|
32
|
+
const players = await removePlaytestPlayersAsync(
|
|
33
|
+
connection,
|
|
34
|
+
serverSession,
|
|
35
|
+
names
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
players,
|
|
40
|
+
summary: `Removed ${players.map((p) => p.name).join(', ')}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function formatPlayerRemoveText(
|
|
45
|
+
result: PlaytestPlayerRemoveResult
|
|
46
|
+
): string {
|
|
47
|
+
const kicked = result.players.filter((p) => p.how === 'kicked');
|
|
48
|
+
if (kicked.length === 0) return result.summary;
|
|
49
|
+
|
|
50
|
+
return `${result.summary}\nKicked ${kicked
|
|
51
|
+
.map((p) => p.name)
|
|
52
|
+
.join(
|
|
53
|
+
', '
|
|
54
|
+
)}, whose client session was not connected, so its window stays open`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const playtestPlayerRemoveCommand = defineCommand<
|
|
58
|
+
PlaytestPlayerRemoveArgs,
|
|
59
|
+
PlaytestPlayerRemoveResult
|
|
60
|
+
>({
|
|
61
|
+
group: 'playtest player',
|
|
62
|
+
name: 'remove',
|
|
63
|
+
description: 'Remove players from the running playtest by name',
|
|
64
|
+
category: 'execution',
|
|
65
|
+
safety: 'mutate',
|
|
66
|
+
scope: 'connection',
|
|
67
|
+
args: {
|
|
68
|
+
names: arg.positional({
|
|
69
|
+
description: 'Player names (e.g. Player2)',
|
|
70
|
+
array: true,
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
cli: {
|
|
74
|
+
format: formatPlayerRemoveText,
|
|
75
|
+
},
|
|
76
|
+
handler: async (connection, args, targeting) =>
|
|
77
|
+
playtestPlayerRemoveHandlerAsync(connection, args.names, targeting.target),
|
|
78
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('../../plugin/plugin-build-version.js', () => ({
|
|
4
|
+
getPluginBuildVersionAsync: async () => 'current',
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
const settings = vi.hoisted(() => ({
|
|
8
|
+
loadUserPluginsOnPlaytest: undefined as boolean | undefined,
|
|
9
|
+
}));
|
|
10
|
+
vi.mock('../../process/studio-settings.js', () => ({
|
|
11
|
+
tryReadLoadUserPluginsOnPlaytestAsync: async () =>
|
|
12
|
+
settings.loadUserPluginsOnPlaytest,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
import { ContextNotFoundError } from '../../bridge/index.js';
|
|
16
|
+
import {
|
|
17
|
+
explainMissingPlaytestContextAsync,
|
|
18
|
+
resolvePlaytestServerSessionAsync,
|
|
19
|
+
startPlaytestAsync,
|
|
20
|
+
} from './playtest-sessions.js';
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
settings.loadUserPluginsOnPlaytest = undefined;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function createEditSession(pluginVersion: string) {
|
|
27
|
+
return {
|
|
28
|
+
context: 'edit',
|
|
29
|
+
info: {
|
|
30
|
+
sessionId: 'place-1a2b-edit',
|
|
31
|
+
placeName: 'place.rbxl',
|
|
32
|
+
pluginVersion,
|
|
33
|
+
},
|
|
34
|
+
runPlaytestAsync: vi.fn(),
|
|
35
|
+
} as any;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('startPlaytestAsync multiplayer checks', () => {
|
|
39
|
+
it('refuses a player count that is not a whole number of at least 1', async () => {
|
|
40
|
+
for (const players of [0, 2.5, -1]) {
|
|
41
|
+
const editSession = createEditSession('current');
|
|
42
|
+
await expect(
|
|
43
|
+
startPlaytestAsync({} as any, editSession, 'play', players)
|
|
44
|
+
).rejects.toThrow('--players must be a whole number of at least 1');
|
|
45
|
+
expect(editSession.runPlaytestAsync).not.toHaveBeenCalled();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('refuses run mode', async () => {
|
|
50
|
+
await expect(
|
|
51
|
+
startPlaytestAsync({} as any, createEditSession('current'), 'run', 2)
|
|
52
|
+
).rejects.toThrow('A multiplayer test runs in play mode');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('refuses a Studio with an older plugin before starting anything', async () => {
|
|
56
|
+
const editSession = createEditSession('old');
|
|
57
|
+
|
|
58
|
+
await expect(
|
|
59
|
+
startPlaytestAsync({} as any, editSession, 'play', 2)
|
|
60
|
+
).rejects.toThrow("Run 'studio-bridge plugin install' first");
|
|
61
|
+
expect(editSession.runPlaytestAsync).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('with "Load user plugins on playtest" off', () => {
|
|
66
|
+
it('refuses to start a playtest whose sessions could never connect', async () => {
|
|
67
|
+
settings.loadUserPluginsOnPlaytest = false;
|
|
68
|
+
const editSession = createEditSession('current');
|
|
69
|
+
|
|
70
|
+
await expect(
|
|
71
|
+
startPlaytestAsync({} as any, editSession, 'play')
|
|
72
|
+
).rejects.toThrow(
|
|
73
|
+
`Studio's "Load user plugins on playtest" setting is off`
|
|
74
|
+
);
|
|
75
|
+
expect(editSession.runPlaytestAsync).not.toHaveBeenCalled();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('names the setting when no server session is connected', async () => {
|
|
79
|
+
settings.loadUserPluginsOnPlaytest = false;
|
|
80
|
+
const connection = { listSessions: () => [] } as any;
|
|
81
|
+
|
|
82
|
+
await expect(resolvePlaytestServerSessionAsync(connection)).rejects.toThrow(
|
|
83
|
+
`No playtest is connected. Studio's "Load user plugins on playtest" setting is off`
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("ignores this machine's setting when Studio is remote", async () => {
|
|
88
|
+
settings.loadUserPluginsOnPlaytest = false;
|
|
89
|
+
const connection = { listSessions: () => [], isRemote: true } as any;
|
|
90
|
+
|
|
91
|
+
await expect(resolvePlaytestServerSessionAsync(connection)).rejects.toThrow(
|
|
92
|
+
"No playtest is running. Start one with 'studio-bridge playtest start'."
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('explainMissingPlaytestContextAsync', () => {
|
|
98
|
+
const connection = {
|
|
99
|
+
listSessions: () => [
|
|
100
|
+
{ instanceId: 'inst-1', placeName: 'place.rbxl', context: 'edit' },
|
|
101
|
+
],
|
|
102
|
+
} as any;
|
|
103
|
+
|
|
104
|
+
it('says no playtest is running when the setting is on', async () => {
|
|
105
|
+
settings.loadUserPluginsOnPlaytest = true;
|
|
106
|
+
const err = await explainMissingPlaytestContextAsync(
|
|
107
|
+
connection,
|
|
108
|
+
new ContextNotFoundError('server', 'inst-1', ['edit'])
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect((err as Error).message).toBe(
|
|
112
|
+
"No playtest of 'place.rbxl' is running, so there is no server session. Start one with 'studio-bridge playtest start'."
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('names the setting when it is off', async () => {
|
|
117
|
+
settings.loadUserPluginsOnPlaytest = false;
|
|
118
|
+
const err = await explainMissingPlaytestContextAsync(
|
|
119
|
+
connection,
|
|
120
|
+
new ContextNotFoundError('client', 'inst-1', ['edit'])
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect((err as Error).message).toContain(
|
|
124
|
+
`No playtest of 'place.rbxl' is connected. Studio's "Load user plugins on playtest" setting is off`
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('points a client request at play mode when a run-mode playtest is running', async () => {
|
|
129
|
+
const err = await explainMissingPlaytestContextAsync(
|
|
130
|
+
connection,
|
|
131
|
+
new ContextNotFoundError('client', 'inst-1', ['edit', 'server'])
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect((err as Error).message).toContain('runs in run mode');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('passes other errors through', async () => {
|
|
138
|
+
const original = new ContextNotFoundError('edit', 'inst-1', ['server']);
|
|
139
|
+
expect(await explainMissingPlaytestContextAsync(connection, original)).toBe(
|
|
140
|
+
original
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
});
|