From 695d3be25cd30efa4de4427123104f6341d5388b Mon Sep 17 00:00:00 2001 From: Joywayer Date: Sun, 6 Sep 2026 09:06:00 +0800 Subject: [PATCH] feat: migrate platform login scenes and native login flows --- .../adapters.meta} | 2 +- .../assets/framework/adapters/local.meta | 9 + .../local/local-storage.ts.meta} | 2 +- .../local/login-sources.ts.meta} | 2 +- .../adapters/local/visitor-account.ts | 2 +- .../adapters/local/visitor-account.ts.meta} | 2 +- .../assets/framework/adapters/native.meta | 9 + .../adapters/native/login-accounts.ts | 169 ++ .../adapters/native/login-accounts.ts.meta} | 2 +- .../adapters/native/native-contracts.ts.meta | 9 + .../native/native-settings-adapter.ts.meta | 9 + .../framework/adapters/native/phone-code.ts | 9 + .../adapters/native/phone-code.ts.meta | 9 + .../native/webview-javascript-bridge.ts.meta | 9 + .../assets/framework/config/local-startup.ts | 5 +- .../framework/config/local-startup.ts.meta | 9 + .../assets/framework/config/profiles.ts | 16 +- .../assets/framework/config/remote-config.ts | 57 +- .../assets/framework/config/runtime-config.ts | 17 +- .../framework/config/runtime-config.ts.meta | 9 + .../framework/config/sources/defaults.ts | 5 +- .../assets/framework/net/wire-client.ts.meta | 9 + .../framework/platform/commands.ts.meta | 9 + .../platform/connection-intent.ts.meta | 9 + .../platform/game-host-adapter.ts.meta | 9 + .../framework/platform/ready-gate.ts.meta | 9 + .../platform/runtime-session.ts.meta | 9 + .../assets/framework/platform/runtime.ts.meta | 9 + .../framework/platform/scene-port.ts.meta | 9 + .../platform/stores/platform-store.ts.meta | 9 + .../platform/stores/platform-types.ts.meta | 9 + .../platform/stores/selectors.ts.meta | 9 + .../assets/framework/presentation.meta | 9 + .../presentation/frame-renderer.ts.meta | 9 + .../framework/presentation/login-policy.ts | 42 + .../presentation/login-policy.ts.meta | 9 + .../framework/presentation/page-model.ts.meta | 9 + .../platform-ui-controller.ts.meta | 9 + .../presentation/ui-contracts.ts.meta | 9 + .../assets/framework/protocol/contracts.meta | 9 + .../protocol/contracts/index.ts.meta | 9 + .../protocol/contracts/login-contract.ts | 8 +- .../protocol/contracts/login-contract.ts.meta | 9 + .../protocol/contracts/room-contracts.ts.meta | 9 + .../protocol/contracts/validation.ts.meta | 9 + .../protocol/first-slice-routes.ts.meta | 9 + .../protocol/platform-handlers.ts.meta | 9 + .../assets/framework/sdk/contracts.meta | 9 + .../sdk/contracts/game-entry.ts.meta | 9 + .../framework/sdk/contracts/game-host.ts.meta | 9 + .../sdk/contracts/game-module.ts.meta | 9 + .../sdk/contracts/game-seat-mapper.ts.meta | 9 + .../framework/sdk/contracts/index.ts.meta | 9 + .../sdk/contracts/platform-events.ts.meta | 9 + .../framework/sdk/contracts/snapshots.ts.meta | 9 + .../assets/framework/sdk/runtime.meta | 9 + .../sdk/runtime/game-session-host.ts.meta | 9 + .../assets/framework/sdk/testing.meta | 9 + .../sdk/testing/game-contract-harness.ts.meta | 9 + .../assets/framework/ui/animations.meta | 9 + .../framework/ui/animations/LoginLoading.anim | 86 + .../ui/animations/LoginLoading.anim.meta | 13 + .../framework/ui/prefabs/Login_Layer.prefab | 731 +++++--- .../ui/prefabs/widgets/Layer10_Protol.prefab | 381 +++-- .../ui/prefabs/widgets/Layer1_Logo.prefab | 313 ++-- .../prefabs/widgets/Layer29_PhoneInfo.prefab | 416 ++--- .../YouleNexus/assets/resources.meta | 14 + .../assets/resources/dev-config.meta | 9 + .../resources/dev-config/update_jsonv2.txt | 1500 +++++++++++++++++ .../dev-config/update_jsonv2.txt.meta | 11 + .../assets/scenes/LocalPlatformLogin.scene | 1393 --------------- .../assets/scenes/PlatformLoading.scene | 416 +++++ ....scene.meta => PlatformLoading.scene.meta} | 2 +- .../assets/scenes/PlatformLogin.scene | 416 +++++ .../assets/scenes/PlatformLogin.scene.meta | 11 + .../assets/scenes/PlatformStartup.scene | 316 ++++ .../assets/scenes/PlatformStartup.scene.meta | 11 + .../YouleNexus/assets/scenes/scene-2d.scene | 449 +++++ .../assets/scenes/scene-2d.scene.meta | 11 + .../LocalPlatformLogin.ts | 237 --- .../local-platform-login/local-login-host.ts | 225 --- .../local-login-host.ts.meta | 9 - .../local-login-transport.ts | 138 -- .../local-login-transport.ts.meta | 9 - .../local-platform-login/local-login-view.ts | 317 ---- .../local-login-view.ts.meta | 9 - .../local-platform-login/local-login-wire.ts | 120 -- .../local-login-wire.ts.meta | 9 - .../assets/scripts/platform-login.meta | 9 + .../platform-login/PhoneLoginAssets.ts | 9 + .../platform-login/PhoneLoginAssets.ts.meta | 9 + .../scripts/platform-login/PlatformStartup.ts | 217 +++ .../platform-login/PlatformStartup.ts.meta | 9 + .../platform-login/local-config-fetcher.ts | 29 + .../local-config-fetcher.ts.meta | 9 + .../scripts/platform-login/login-panel.ts | 166 ++ .../platform-login/login-panel.ts.meta | 9 + .../login-stage-game-entry.ts} | 10 +- .../login-stage-game-entry.ts.meta | 9 + .../scripts/platform-login/logo-countdown.ts | 116 ++ .../platform-login/logo-countdown.ts.meta | 9 + .../scripts/platform-login/node-bindings.ts | 8 + .../platform-login/node-bindings.ts.meta | 9 + .../scripts/platform-login/scene-flow.ts | 18 + .../scripts/platform-login/scene-flow.ts.meta | 9 + .../legacy-migration/BackHall_Layer.prefab | 20 +- .../out/legacy-migration/Protol_Layer.prefab | 44 +- .../YouleNexus/tsconfig.json | 3 +- .../adapters/local/login-sources.test.ts | 4 +- .../adapters/local/visitor-account.test.ts | 6 +- .../framework-tests/config/bootstrap.test.ts | 6 +- .../config/local-config-fetcher.test.ts | 47 + .../config/local-startup.test.ts | 15 +- .../framework-tests/config/profiles.test.ts | 14 +- .../config/remote-config-fetcher.test.ts | 6 +- .../config/remote-config.test.ts | 13 + .../config/runtime-config.test.ts | 45 +- .../integration/local-login-sources.test.ts | 3 +- .../integration/login-flow.test.ts | 4 +- .../platform-vertical-slice.test.ts | 4 +- .../local-platform-login/host.test.ts | 307 ---- .../local-platform-login/transport.test.ts | 323 ---- .../local-platform-login/view.test.ts | 391 ----- .../local-platform-login/wire.test.ts | 144 -- .../game-entry.test.ts | 4 +- .../platform-login/loading-animation.test.ts | 32 + .../platform-login/login-accounts.test.ts | 76 + .../platform-login/login-policy.test.ts | 40 + .../platform-login/logo-countdown.test.ts | 144 ++ .../platform-login/phone-code.test.ts | 15 + .../platform-login/scene-flow.test.ts | 12 + .../framework-tests/platform/commands.test.ts | 4 +- .../platform/game-host-adapter.test.ts | 4 +- .../framework-tests/platform/runtime.test.ts | 4 +- .../framework-tests/platform/startup.test.ts | 6 +- .../sdk/game-contract-harness.test.ts | 4 +- docs/development-local-config.md | 33 + docs/protocol/07-RPC全量清单.md | 833 +++++++++ .../2026-09-06-login-scenes-and-contract.md | 67 + ...6-09-05-platform-ui-function-comparison.md | 348 ++++ 140 files changed, 7599 insertions(+), 4397 deletions(-) rename cocoscreator_projects/YouleNexus/assets/{scripts/local-platform-login.meta => framework/adapters.meta} (70%) create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/local.meta rename cocoscreator_projects/YouleNexus/assets/framework/{platform/native-bridge.ts.meta => adapters/local/local-storage.ts.meta} (70%) rename cocoscreator_projects/YouleNexus/assets/framework/{protocol/active-game.ts.meta => adapters/local/login-sources.ts.meta} (70%) rename cocoscreator_projects/YouleNexus/assets/{scripts/local-platform-login/local-login-game-entry.ts.meta => framework/adapters/local/visitor-account.ts.meta} (70%) create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts rename cocoscreator_projects/YouleNexus/assets/{scripts/local-platform-login/LocalPlatformLogin.ts.meta => framework/adapters/native/login-accounts.ts.meta} (70%) create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-contracts.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-settings-adapter.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/adapters/native/webview-javascript-bridge.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/net/wire-client.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/commands.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/connection-intent.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/game-host-adapter.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/ready-gate.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/runtime-session.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/runtime.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/scene-port.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-store.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-types.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/platform/stores/selectors.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/frame-renderer.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/page-model.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/platform-ui-controller.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/presentation/ui-contracts.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/index.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/room-contracts.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/validation.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/first-slice-routes.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/protocol/platform-handlers.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-entry.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-host.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-module.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-seat-mapper.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/index.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/platform-events.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/snapshots.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime/game-session-host.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/testing.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/animations.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/resources.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/resources/dev-config.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt create mode 100644 cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt.meta delete mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene rename cocoscreator_projects/YouleNexus/assets/scenes/{LocalPlatformLogin.scene.meta => PlatformLoading.scene.meta} (72%) create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene create mode 100644 cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene.meta delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts.meta delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts.meta delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts.meta delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts delete mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts.meta rename cocoscreator_projects/YouleNexus/assets/scripts/{local-platform-login/local-login-game-entry.ts => platform-login/login-stage-game-entry.ts} (71%) create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts create mode 100644 cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts.meta create mode 100644 cocoscreator_projects/framework-tests/config/local-config-fetcher.test.ts delete mode 100644 cocoscreator_projects/framework-tests/local-platform-login/host.test.ts delete mode 100644 cocoscreator_projects/framework-tests/local-platform-login/transport.test.ts delete mode 100644 cocoscreator_projects/framework-tests/local-platform-login/view.test.ts delete mode 100644 cocoscreator_projects/framework-tests/local-platform-login/wire.test.ts rename cocoscreator_projects/framework-tests/{local-platform-login => platform-login}/game-entry.test.ts (93%) create mode 100644 cocoscreator_projects/framework-tests/platform-login/loading-animation.test.ts create mode 100644 cocoscreator_projects/framework-tests/platform-login/login-accounts.test.ts create mode 100644 cocoscreator_projects/framework-tests/platform-login/login-policy.test.ts create mode 100644 cocoscreator_projects/framework-tests/platform-login/logo-countdown.test.ts create mode 100644 cocoscreator_projects/framework-tests/platform-login/phone-code.test.ts create mode 100644 cocoscreator_projects/framework-tests/platform-login/scene-flow.test.ts create mode 100644 docs/development-local-config.md create mode 100644 docs/protocol/07-RPC全量清单.md create mode 100644 docs/superpowers/plans/2026-09-06-login-scenes-and-contract.md create mode 100644 docs/superpowers/reports/2026-09-05-platform-ui-function-comparison.md diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters.meta similarity index 70% rename from cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login.meta rename to cocoscreator_projects/YouleNexus/assets/framework/adapters.meta index 42f296d..6ac2cb6 100644 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login.meta +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters.meta @@ -2,7 +2,7 @@ "ver": "1.2.0", "importer": "directory", "imported": true, - "uuid": "6072a7f3-9095-4e71-9dbd-c952d89a64a7", + "uuid": "aea92450-9c09-40ed-8a4d-5cced7a56b3b", "files": [], "subMetas": {}, "userData": {} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/local.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local.meta new file mode 100644 index 0000000..b587a55 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "c1b8189d-1134-4ff3-9426-d2668fd9ab6f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/native-bridge.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/local-storage.ts.meta similarity index 70% rename from cocoscreator_projects/YouleNexus/assets/framework/platform/native-bridge.ts.meta rename to cocoscreator_projects/YouleNexus/assets/framework/adapters/local/local-storage.ts.meta index 5992bba..c6f04a6 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/platform/native-bridge.ts.meta +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/local-storage.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "2eb9409f-115d-4a7d-bdc4-30a333b57a28", + "uuid": "0693ece5-bed1-4b21-8ae7-7393c2eee8a7", "files": [], "subMetas": {}, "userData": {} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/active-game.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/login-sources.ts.meta similarity index 70% rename from cocoscreator_projects/YouleNexus/assets/framework/protocol/active-game.ts.meta rename to cocoscreator_projects/YouleNexus/assets/framework/adapters/local/login-sources.ts.meta index 5c2c120..72215d8 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/protocol/active-game.ts.meta +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/login-sources.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "d8981b12-16b5-49a7-95d6-a51eb735462a", + "uuid": "4deb8dec-916d-4dda-b709-ac5f38fa0253", "files": [], "subMetas": {}, "userData": {} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts index 8e33121..262dd60 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts @@ -70,7 +70,7 @@ function sample(random: () => number, key: string): number { return sampleLocalRandom(random, (reason, cause) => wrap(key, 'random', reason, cause)); } -function generate(random: () => number, key: string): LoginAccountIdentity { +export function generate(random: () => number, key: string): LoginAccountIdentity { const id = Math.floor(sample(random, key) * 100000); const avatar = AVATARS[Math.floor(sample(random, key) * AVATARS.length)]!; const sex = Math.floor(sample(random, key) * 2) + 1; diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts.meta similarity index 70% rename from cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts.meta rename to cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts.meta index 0f093bc..db7f163 100644 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts.meta +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/local/visitor-account.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "aea065db-0dfa-4da8-a2cb-f1fe0165d910", + "uuid": "bb7c9b58-c08f-4dc9-b8d1-57c2b20f9c28", "files": [], "subMetas": {}, "userData": {} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native.meta new file mode 100644 index 0000000..4cea785 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "8499ef07-6051-4d8f-aca6-11996e448b7f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts new file mode 100644 index 0000000..22deb2b --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts @@ -0,0 +1,169 @@ +import type { LoginAccountIdentity, DeviceLoginMode } from '../../protocol/contracts/login-contract.ts'; +import type { LoginPolicy } from '../../presentation/login-policy.ts'; +import { generate } from '../local/visitor-account.ts'; +import { readPhoneCode } from './phone-code.ts'; + +export interface LoginNativeSettings { + accreditlogin(provider: number): void; + getcompareCode(): number | string; +} +interface Options { + storage: {getItem(key:string):string|null;setItem(key:string,value:string):void}; + settings?:LoginNativeSettings; random():number; now():number; + gameid:string|number;agentid:string|number;policy:LoginPolicy; + submit(account:LoginAccountIdentity,device:DeviceLoginMode):void;tip(message:string):void; +} + +/** MD5 is the legacy nickname derivation, not a security primitive. */ +export function nicknameHash(input:string):string { + const bytes=Array.from(new TextEncoder().encode(input));const bitLength=bytes.length*8; + bytes.push(128);while(bytes.length%64!==56)bytes.push(0); + for(let i=0;i<8;i++)bytes.push(i<4?(bitLength>>>(8*i))&255:0); + const shifts=[7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21]; + let state=[0x67452301,0xefcdab89,0x98badcfe,0x10325476]; + for(let offset=0;offsetbytes[offset+j*4]|bytes[offset+j*4+1]<<8|bytes[offset+j*4+2]<<16|bytes[offset+j*4+3]<<24); + let [a,b,c,d]=state; + for(let i=0;i<64;i++){ + const round=Math.floor(i/16);let f:number,g:number; + if(round===0){f=(b&c)|(~b&d);g=i;}else if(round===1){f=(d&b)|(~d&c);g=(5*i+1)%16;} + else if(round===2){f=b^c^d;g=(3*i+5)%16;}else{f=c^(b|~d);g=(7*i)%16;} + const sum=(a+f+Math.floor(Math.abs(Math.sin(i+1))*4294967296)+words[g])|0; + const shift=shifts[round*4+i%4];const next=(b+((sum<>>(32-shift))))|0; + a=d;d=c;c=b;b=next; + } + state=state.map((v,i)=>(v+[a,b,c,d][i])|0); + } + return state.map(v=>[0,8,16,24].map(s=>((v>>>s)&255).toString(16).padStart(2,'0')).join('')).join(''); +} +function record(value:unknown):Record{ + if(typeof value==='string')value=JSON.parse(value); + if(!value||typeof value!=='object'||Array.isArray(value))throw Error('Invalid sharelogin account'); + return value as Record; +} +function accountFrom(value:unknown):LoginAccountIdentity { + const data=record(value); + for(const field of ['openid','headimgurl','nickname','city','Province'])if(typeof data[field]!=='string')throw Error(`Invalid sharelogin.${field}`); + if(typeof data.unionid!=='string'&&typeof data.unionid!=='number')throw Error('Invalid sharelogin.unionid'); + const sex=data.sex?data.sex:1; // Func.sharelogin explicitly owns this default. + if(typeof sex!=='number'||!Number.isFinite(sex))throw Error('Invalid sharelogin.sex'); + if(!data.nickname&&!data.headimgurl)throw Error('授权失败,请重新授权!'); + return {openid:data.openid as string,avatar:data.headimgurl as string,nickname:data.nickname as string, + city:data.city as string,province:data.Province as string,unionid:data.unionid as string|number,sex}; +} + +/** Exact native/cached account boundary; UI never invents account or server fields. */ +/** 账户统一入口:协议确认、缓存选择、原生授权及提交模式均由此处管理。 */ +export class LoginAccounts { + readonly versionState:0|1; + private hiddenClicks=0; + /** 每次启动默认同意;按钮、缓存登录及原生回调读取同一份状态。 */ + private agreementAccepted=true; + get agreed():boolean{return this.agreementAccepted;} + setAgreement(accepted:boolean):void{ + if(accepted&&!this.agreementAccepted)this.options.tip(''); + this.agreementAccepted=accepted; + } + /** 未同意时只提示,不调用授权、不写缓存、不提交请求。 */ + requireAgreement():boolean{ + if(this.agreementAccepted)return true; + this.options.tip('未同意协议');return false; + } + constructor(private readonly options:Options){ + // Browser source explicitly uses the legacy getVersionState default of zero. + const state=options.settings?Number(options.settings.getcompareCode()):0; + if(state!==0&&state!==1)throw Error('Invalid getcompareCode result');this.versionState=state; + } + private read(key:string,cookie:boolean):Record|null { + if(cookie&&this.versionState===0){ + const expiry=this.options.storage.getItem('tsgame_validtime'); + if(expiry===null)return null; + if(!Number.isFinite(Number(expiry)))throw Error('Invalid tsgame_validtime'); + if(this.options.now()>=Number(expiry))return null; + } + const raw=this.options.storage.getItem(key);return raw===null?null:record(raw); + } + private save(account:LoginAccountIdentity,key:string,telphone?:string){ + const data={openid:account.openid,headimgurl:account.avatar,nickname:account.nickname,sex:account.sex, + city:account.city,Province:account.province,unionid:account.unionid,...(telphone===undefined?{}:{telphone})}; + this.options.storage.setItem('tsgame_validtime',String(this.options.now()+48*60*60*1000)); + this.options.storage.setItem(key,JSON.stringify(data)); + } + /** 保留原工程缓存优先级,游客开关先于手机模式判断。 */ + cached():LoginAccountIdentity|null { + if(this.versionState!==0)return null; + const policy=this.options.policy; + const data=this.read(policy.showVisitor?'tsgame_visitorinfo':policy.deviceLogin?'tsgame_devicePlayer':'tsgame_wxinfo',true); + return data===null?null:accountFrom(data); + } + autoLogin():void { + if(!this.requireAgreement())return; + const account=this.cached();if(account===null)throw Error('No cached login'); + // showVisitor selects the visitor cache above, even when the phone button is enabled. + if(!this.options.policy.showVisitor&&this.options.policy.deviceLogin){ + const data=this.read('tsgame_devicePlayer',true)!; + if(typeof data.telphone!=='string')throw Error('Cached device account missing telphone'); + this.options.submit(account,{enabled:true,telphone:data.telphone}); + }else this.options.submit(account,{enabled:false}); + } + authorize(provider:'wechat'|'qq'):void { + if(!this.requireAgreement())return; + if(!this.options.settings){this.options.tip('当前宿主未提供原生登录授权接口');return;} + if(provider==='wechat')this.options.storage.setItem(`tsgame_playerid_${this.options.gameid}${this.options.agentid}`,''); + this.options.settings.accreditlogin(provider==='wechat'?2:1); + } + acceptNative(data:unknown,channel:'global'|'bridge'):void { + if(!this.requireAgreement())return; + const account=accountFrom(data); + this.save(account,channel==='global'&&this.options.policy.showVisitor?'tsgame_visitorinfo':'tsgame_wxinfo'); + this.options.submit(account,{enabled:false}); + } + /** 仅本次手机登录要求 tel,不能依据按钮显示配置要求游客响应携带手机号。 */ + loginSucceeded(value:unknown,mode:DeviceLoginMode):void { + const data=record(value); + if(data.state!==0)throw Error('Cannot cache an unsuccessful login'); + if(typeof data.playerid!=='string'&&(typeof data.playerid!=='number'||!Number.isFinite(data.playerid)))throw Error('Invalid login playerid'); + // Button availability is not the authentication mode of this request. + if(mode.enabled){ + if(typeof data.tel!=='string')throw Error('Device login response missing tel'); + const account=accountFrom({...data,headimgurl:data.avatar,Province:data.province}); + this.save(account,'tsgame_devicePlayer',data.tel); + this.options.storage.setItem('tsgame_dLogin','1'); + } + this.options.storage.setItem(`tsgame_playerid_${this.options.gameid}${this.options.agentid}`,String(data.playerid)); + } + visitor():void { + if(!this.requireAgreement())return; + const cached=this.read('tsgame_visitorinfo',false); + const account=cached===null?generate(this.options.random,'tsgame_visitorinfo'):accountFrom(cached); + this.save(account,this.options.policy.showVisitor?'tsgame_visitorinfo':'tsgame_wxinfo'); + this.options.submit(account,{enabled:false}); + } + hiddenFirst():void{this.hiddenClicks++;} + phonePanelState(){return readPhoneCode(this.options.storage.getItem('sgame_phoneCode'),this.options.now());} + hiddenSecond():void{if(this.hiddenClicks>=5&&this.options.policy.hiddenVisitor){this.hiddenClicks=0;this.visitor();}} + private deviceAccount(phone?:string):LoginAccountIdentity { + const cached=this.read('tsgame_devicePlayer',true);if(cached)return accountFrom(cached); + let unionid:string; + if(phone!==undefined)unionid=`phone_${phone}`; + else { + const info=this.read('tsgame_phoneInfo1',false); + if(info&&typeof info.PhoneIMEI==='string'&&info.PhoneIMEI)unionid=info.PhoneIMEI; + else { + const sets=['0123456789','qwertyuioplkjhgfdsazxcvbnm','QWERTYUIOPLKJHGFDSAZXCVBNM']; + unionid=Array.from({length:25},()=>{const chars=sets[Math.round(this.options.random()*2)];return chars[Math.ceil(this.options.random()*100000000)%chars.length];}).join(''); + this.options.storage.setItem('tsgame_phoneInfo1',JSON.stringify({...info,PhoneIMEI:unionid})); + } + } + const {avatarUrl,avatarRange}=this.options.policy; + return {openid:unionid,unionid,nickname:`User_${nicknameHash(unionid).slice(0,6)}`, + avatar:avatarUrl?`${avatarUrl}${avatarRange[0]+Math.round(this.options.random()*(avatarRange[1]-avatarRange[0]))}.jpg`:'', + sex:Math.floor(this.options.random()*2)+1,city:'China',province:phone===undefined?'HongKong':'ShangHai'}; + } + device():void{if(!this.requireAgreement())return;this.options.submit(this.deviceAccount(),{enabled:false});} + phone(phone:string,code:string):void { + if(!this.requireAgreement())return; + if(!phone||!code){this.options.tip('手机号与验证码均不能为空!');return;} + this.options.submit(this.deviceAccount(phone),{enabled:true,telphone:phone,smmcode:code}); + } +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts.meta similarity index 70% rename from cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts.meta rename to cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts.meta index 6737380..1e263cb 100644 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts.meta +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/login-accounts.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "2597060c-ad16-46f1-89ed-42bfaec74ad9", + "uuid": "57d7618f-3e88-4036-83de-96a6e63be657", "files": [], "subMetas": {}, "userData": {} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-contracts.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-contracts.ts.meta new file mode 100644 index 0000000..695aad1 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-contracts.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "27e9345c-5f75-4829-be9f-174141dec6e6", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-settings-adapter.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-settings-adapter.ts.meta new file mode 100644 index 0000000..18d341a --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/native-settings-adapter.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "df3fb4b3-bb92-40f7-b661-05a158dec617", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts new file mode 100644 index 0000000..eceeb80 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts @@ -0,0 +1,9 @@ +/** Logic.readPhoneCode: sgame_phoneCode owns the phone number and 30-second expiry. */ +export function readPhoneCode(raw:string|null,now:number):{phoneNumber:string;restTime:number}|null { + if(raw===null)return null; + const data=JSON.parse(raw); + if(!data||typeof data!=='object'||typeof data.phoneNumber!=='string')throw Error('Invalid phoneCode.phoneNumber'); + if(!Object.prototype.hasOwnProperty.call(data,'codeTime')||!['string','number'].includes(typeof data.codeTime)||!Number.isFinite(Number(data.codeTime)))throw Error('Invalid phoneCode.codeTime'); + const elapsed=now-Number(data.codeTime); + return {phoneNumber:data.phoneNumber,restTime:elapsed>30000?0:Math.trunc(30-elapsed/1000)}; +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts.meta new file mode 100644 index 0000000..0a49fb4 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/phone-code.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "bb46066c-73af-4603-9f7a-5579a2ad030b", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/webview-javascript-bridge.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/webview-javascript-bridge.ts.meta new file mode 100644 index 0000000..fab20bb --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/adapters/native/webview-javascript-bridge.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "588b5341-c0f6-4eef-bf31-b47c815db2b0", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts b/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts index 3bb72b6..382e283 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts @@ -63,8 +63,9 @@ export function assertLocalStartupContext(context: LocalStartupContext): void { if (!startupConfig.useLocalServer) fail('useLocalServer', 'source must be true'); if (config.mode !== 'debug') fail('mode', 'must be debug'); if (config.isDebugger !== true) fail('isDebugger', 'must be true'); - if (config.source !== 'remote') fail('source', 'must be remote'); - if (config.gameserver !== startupConfig.gameserver) fail('gameserver', 'must equal startupConfig remote entry'); + if (config.source !== startupConfig.configSource) fail('source', 'must equal startupConfig.configSource'); + const configEntry = startupConfig.configSource === 'local' ? startupConfig.localConfigResource : startupConfig.gameserver; + if (config.gameserver !== configEntry) fail('gameserver', 'must equal startupConfig selected entry'); if (!config.remoteConfig || typeof config.remoteConfig.getValue !== 'function') fail('remoteConfig', 'must provide memory view'); if (!config.rawConfig || config.rawConfig !== config.remoteConfig.raw) fail('rawConfig', 'must share remoteConfig.raw'); diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts.meta new file mode 100644 index 0000000..5fccd1e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/local-startup.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "1d920343-a60b-4b40-8eba-f29b4a7d53fa", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/profiles.ts b/cocoscreator_projects/YouleNexus/assets/framework/config/profiles.ts index b19c9a1..8b72a41 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/config/profiles.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/profiles.ts @@ -13,6 +13,10 @@ export interface DebugAccount { export interface StartupConfig { readonly mode: RuntimeMode; + /** 配置内容来源;local 仅允许 debug,独立于 WebSocket 服务器选择。 */ + readonly configSource: 'remote' | 'local'; + /** resources 下的 txt 路径,不含扩展名。 */ + readonly localConfigResource: string; readonly gameserver: string; readonly useLocalServer: boolean; readonly localServers: readonly string[]; @@ -21,7 +25,10 @@ export interface StartupConfig { /** 唯一发布配置入口;mode 与服务器选择互相独立。 */ export const STARTUP_CONFIG: Readonly = Object.freeze({ mode: 'debug', - gameserver: 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt', + // 开发时改为 local,编辑 assets/resources/dev-config/update_jsonv2.txt 后重新预览。 + configSource: 'local', + localConfigResource: 'dev-config/update_jsonv2', + gameserver: 'https://tsgames.daoqi88.cn/config_test/update_jsonv2.txt', useLocalServer: true, localServers: Object.freeze(['ws://127.0.0.1:3088']), }); @@ -46,6 +53,11 @@ export function validateLocalServer(value: string, field = 'localServers'): void export function validateStartupConfig(source: Readonly): Readonly { if (!source || typeof source !== 'object') invalid('source', 'must be an object'); const mode = resolveRuntimeMode(source.mode); + if (source.configSource !== 'remote' && source.configSource !== 'local') invalid('configSource', 'must be remote or local'); + if (source.configSource === 'local' && mode !== 'debug') invalid('configSource', 'local is only allowed in debug mode'); + if (typeof source.localConfigResource !== 'string' || !/^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$/.test(source.localConfigResource)) { + invalid('localConfigResource', 'must be a resources path without extension or traversal'); + } if (typeof source.useLocalServer !== 'boolean') invalid('useLocalServer', 'must be boolean'); if (typeof source.gameserver !== 'string' || source.gameserver.trim() !== source.gameserver) invalid('gameserver', 'must be http(s) URL'); let url: URL; @@ -53,7 +65,7 @@ export function validateStartupConfig(source: Readonly): Readonly if (url.protocol !== 'http:' && url.protocol !== 'https:') invalid('gameserver', 'must be http(s) URL'); if (!Array.isArray(source.localServers) || source.localServers.length === 0) invalid('localServers', 'must be a nonempty array'); for (let index = 0; index < source.localServers.length; index++) validateLocalServer(source.localServers[index], 'localServers[' + index + ']'); - return Object.freeze({mode, gameserver:source.gameserver, useLocalServer:source.useLocalServer, localServers:Object.freeze([...source.localServers])}); + return Object.freeze({mode, configSource:source.configSource, localConfigResource:source.localConfigResource, gameserver:source.gameserver, useLocalServer:source.useLocalServer, localServers:Object.freeze([...source.localServers])}); } /** 忠实旧 min_replaceAll 链:- → /,# → :,再包成 http://...txt。 */ diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/remote-config.ts b/cocoscreator_projects/YouleNexus/assets/framework/config/remote-config.ts index 3e44ffa..d581246 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/config/remote-config.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/remote-config.ts @@ -51,29 +51,52 @@ function validateHierarchy(scope: ConfigObject, depth: number): void { for (const entry of list) validateHierarchy(requireObject(entry, listName), depth + 1); } -/** Preserve first loose identity match, truthy overrides and whole-object replacement. */ +/** + * Port of Game_Surface_3/12_Logic.js get_paravalue. Only GameData globals are + * replaced with the resolved identity argument; lookup order and truthiness stay exact. + * createRemoteConfig validates the downloaded JSON before exposing this reader. + */ +export function get_paravalue( + o_config: { readonly data?: Readonly | null }, + paraname: string, + identity: Readonly, +): unknown { + const {agentid, gameid, channelid, marketid} = identity; + let paravalue: unknown = null; + const do_get_para = (arylist: unknown, keyname: string, keyvalue: string | number): ConfigObject | null => { + if (arylist && keyname && keyvalue) { + const list = arylist as ConfigObject[]; + for (let i = 0; i < list.length; i++) { + if (list[i][keyname] == keyvalue) { + if (list[i][paraname]) paravalue = list[i][paraname]; + return list[i]; + } + } + } + return null; + }; + if (!o_config.data) return paravalue; + if (o_config.data[paraname]) paravalue = o_config.data[paraname]; + const _o_agent = do_get_para(o_config.data.agentlist, 'agentid', agentid); + if (!_o_agent) return paravalue; + const _o_game = do_get_para(_o_agent.gamelist, 'gameid', gameid); + if (!_o_game) return paravalue; + const _o_channel = do_get_para(_o_game.channellist, 'channelid', channelid); + if (!_o_channel) return paravalue; + const _o_market = do_get_para(_o_channel.marketlist, 'marketid', marketid); + if (!_o_market) return paravalue; + return paravalue; +} + +/** All consumers share get_paravalue; no separate hierarchy or override rules. */ export function createRemoteConfig(input: unknown, identity: Readonly): RemoteConfig { const raw = requireObject(input, 'root'); validateHierarchy(raw, 0); - const scopes = [raw]; - let scope = raw; - for (const [listName, key] of SCOPES) { - const list = scope[listName] as ConfigObject[] | undefined; - // Legacy do_get_para skips any falsy identity, including numeric marketid=0. - if (!list || !identity[key]) break; - const match = list.find(entry => entry[key] == identity[key]); - if (!match) break; - scopes.push(match); - scope = match; - } + const wrapper = {data: raw}; return Object.freeze({ raw, getValue(name: string): unknown { - let value: unknown = null; - for (const current of scopes) { - if (Object.prototype.hasOwnProperty.call(current, name) && current[name]) value = current[name]; - } - return value; + return get_paravalue(wrapper, name, identity); }, }); } diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts b/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts index 6a8c41b..7acb047 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts @@ -32,7 +32,8 @@ export interface RuntimeConfig { readonly isDebugger: boolean; readonly identity: Readonly; readonly servers: readonly string[]; - readonly source: 'remote'; + readonly source: 'remote' | 'local'; + /** 实际读取入口:remote 为最终 URL,local 为 resources 资源路径。 */ readonly gameserver: string; readonly rawConfig: RemoteConfig['raw']; readonly remoteConfig: RemoteConfig; @@ -44,6 +45,8 @@ export interface ResolveRuntimeConfigOptions { readonly win: NativeHost; readonly search: string; readonly fetcher: ConfigFetcher; + /** 由引擎层注入项目 txt 读取器;仅 local 模式需要,缺失直接报错。 */ + readonly localFetcher?: ConfigFetcher; readonly cacheBust?: () => string; } @@ -78,17 +81,21 @@ export async function resolveRuntimeConfig( const startupConfig = validateStartupConfig(options.startupConfig === undefined ? STARTUP_CONFIG : options.startupConfig); const identity = resolveHostIdentity(options); - const gameserver = resolveGameServer({ + // 先选择唯一来源;本地模式不读取或应用宿主的远程 gameconfig 覆盖。 + const local = startupConfig.configSource === 'local'; + const gameserver = local ? startupConfig.localConfigResource : resolveGameServer({ configured: startupConfig.gameserver, injectedGameConfig: resolveInjectedGameConfig(options), }); + const fetcher = local ? options.localFetcher : options.fetcher; + if (!fetcher) throw new ConfigFetchError('本地配置缺少 localFetcher'); let raw: unknown; try { - raw = await options.fetcher.fetch(gameserver, options.cacheBust); + raw = await fetcher.fetch(gameserver, options.cacheBust); } catch (error) { if (error instanceof ConfigFetchError || error instanceof ConfigParseError) throw error; - throw new ConfigFetchError(`远程配置抓取失败: ${gameserver}`); + throw new ConfigFetchError(`${local ? '本地配置读取失败' : '远程配置抓取失败'}: ${gameserver}`); } const remoteConfig = createRemoteConfig(raw, identity); const servers = startupConfig.useLocalServer ? startupConfig.localServers : parseUrlServers(remoteConfig); @@ -99,7 +106,7 @@ export async function resolveRuntimeConfig( isDebugger: startupConfig.mode === 'debug', identity, servers, - source: 'remote', + source: startupConfig.configSource, gameserver, rawConfig: remoteConfig.raw, remoteConfig, diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts.meta new file mode 100644 index 0000000..f664b2a --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/runtime-config.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "4e90f0c8-b271-40a4-9909-68aa8f7d5974", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/config/sources/defaults.ts b/cocoscreator_projects/YouleNexus/assets/framework/config/sources/defaults.ts index 2dcf69e..56e2e53 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/config/sources/defaults.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/config/sources/defaults.ts @@ -8,8 +8,9 @@ export const DEFAULT_LAUNCH_WAIT_TIME_MS = 10; * gameid 每子游戏固定写死;agentid/channelid/marketid/version 为默认,运行期由 query/native/profile 覆盖。 */ export const BUILD_IDENTITY: ChannelIdentity = { - agentid: 'veRa0qrBf0df2K1G4de2tgfmVxB2jxpv', - channelid: 'FtJf073aa0d6rI1xD8J1Y42fINTm0ziK', + // This game's entry in config_test/update_jsonv2.txt belongs to this agent/channel. + agentid: '1B2h0ccl205c390Y28m1Ajdplkuu4wgy', + channelid: 'aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG', gameid: 'J9r4022t80pxlb01zOK76DN1WD4QewFl', marketid: 4, version: 1, // 数字 versionCode(联调时改成本地服要求的版本号) diff --git a/cocoscreator_projects/YouleNexus/assets/framework/net/wire-client.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/net/wire-client.ts.meta new file mode 100644 index 0000000..1307294 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/net/wire-client.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "613b0457-be57-44e3-a1fb-e5d086b69468", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/commands.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/commands.ts.meta new file mode 100644 index 0000000..2416586 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/commands.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "4098ba4c-c444-459f-9427-0412f1a15bb6", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/connection-intent.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/connection-intent.ts.meta new file mode 100644 index 0000000..8b8ad20 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/connection-intent.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "1a6fd8e5-5d0c-4fbb-a035-ef3a4f90badd", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/game-host-adapter.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/game-host-adapter.ts.meta new file mode 100644 index 0000000..83e4702 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/game-host-adapter.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "92bf4247-2b4b-460d-8992-8922baf78df2", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/ready-gate.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/ready-gate.ts.meta new file mode 100644 index 0000000..1c6a98d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/ready-gate.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "ac23132b-03ac-47c4-b6f8-93602c94f080", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime-session.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime-session.ts.meta new file mode 100644 index 0000000..021ff3e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime-session.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "3e0b81db-7986-47f4-9e23-fbc9b47d8a36", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime.ts.meta new file mode 100644 index 0000000..756bd7e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/runtime.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "9f495e9b-65ae-4b3e-b44c-9c9a91f6679f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/scene-port.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/scene-port.ts.meta new file mode 100644 index 0000000..d5ae7e6 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/scene-port.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "4f5169b6-e140-4a3a-9eed-e155fecf24df", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-store.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-store.ts.meta new file mode 100644 index 0000000..7c8be57 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-store.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "3b11f970-2e0d-4784-8d65-c0fcc5e4ac6a", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-types.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-types.ts.meta new file mode 100644 index 0000000..3644b3e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/platform-types.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "68da9cca-3c8e-400b-ba7e-539b49994ed4", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/selectors.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/selectors.ts.meta new file mode 100644 index 0000000..a270936 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/selectors.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "8eb5d9a1-cfd3-4df4-b311-c67cb7db4423", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation.meta new file mode 100644 index 0000000..ce03bbb --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "717f5c0e-b085-47f8-879c-1b605e4fc773", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/frame-renderer.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation/frame-renderer.ts.meta new file mode 100644 index 0000000..6db2fe9 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/frame-renderer.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "8650f2a7-9471-4a3e-bec9-0aecf6405cdb", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts b/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts new file mode 100644 index 0000000..0d9a01c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts @@ -0,0 +1,42 @@ +import type { RemoteConfig } from '../config/remote-config.ts'; + +/** Defaults owned here reproduce 04_Data.js and Game_Surface_3 Debugger. */ +const LOGIN_DEFAULTS = Object.freeze({ deviceLogin:0, showVisitor:0, qqLogin:0, + avatarUrl:'https://projectimage.tscce.cn/image_132/', avatarRange:[100,10000] as readonly number[], + hiddenVisitor:true }); +export interface LoginPolicy { + deviceLogin:boolean; showVisitor:boolean; qqLogin:boolean; hiddenVisitor:boolean; + avatarUrl:string; avatarRange:readonly number[]; +} +function flag(value:unknown, name:string):boolean { + if (![0,1,false,true].includes(value as number)) throw Error(`Invalid login config ${name}`); + return Boolean(value); +} +export function resolveLoginPolicy(remote:RemoteConfig):LoginPolicy { + const hallValue=remote.getValue('hall_config'); + if (hallValue != null && (typeof hallValue!=='object' || Array.isArray(hallValue))) throw Error('Invalid hall_config'); + const hall=hallValue as Record | null; + // Legacy hall_config merge precedes hallConfigName top-level overrides. + const hallField=(name:'showVisitor'|'qqLogin')=> { + const override=remote.getValue(name); + if(override!=null) return override; + return hall && Object.prototype.hasOwnProperty.call(hall,name)?hall[name]:LOGIN_DEFAULTS[name]; + }; + const sourceField=(name:'deviceLogin'|'avatarUrl'|'avatarRange')=> { + const value=remote.getValue(name);return value==null?LOGIN_DEFAULTS[name]:value; + }; + const avatarUrl=sourceField('avatarUrl'),range=sourceField('avatarRange'); + if(typeof avatarUrl!=='string')throw Error('Invalid login config avatarUrl'); + if(!Array.isArray(range)||range.length!==2||range.some(v=>!Number.isInteger(v))||range[1]<=range[0])throw Error('Invalid login config avatarRange'); + return Object.freeze({deviceLogin:flag(sourceField('deviceLogin'),'deviceLogin'), + showVisitor:flag(hallField('showVisitor'),'showVisitor'),qqLogin:flag(hallField('qqLogin'),'qqLogin'), + hiddenVisitor:LOGIN_DEFAULTS.hiddenVisitor,avatarUrl,avatarRange:Object.freeze([...range])}); +} +/** Assignment order is significant: JumpWxAuth applies showVisitor/QQ after cache hiding. */ +export function loginButtons(policy:LoginPolicy, versionState:0|1, cached:boolean) { + const account=versionState===0&&cached; + let wechat=!policy.deviceLogin&&!account; + let visitor=!account; + if(versionState===0){visitor=false;if(policy.showVisitor){wechat=false;visitor=true;}} + return {wechat,visitor,phone:policy.deviceLogin&&!account,quick:false,qq:policy.qqLogin,account}; +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts.meta new file mode 100644 index 0000000..4e6ce14 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/login-policy.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "fe78702a-5e02-4006-844c-15123d950d80", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/page-model.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation/page-model.ts.meta new file mode 100644 index 0000000..6196110 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/page-model.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "093506bf-570f-47f8-9f4d-0a87d78cba2e", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/platform-ui-controller.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation/platform-ui-controller.ts.meta new file mode 100644 index 0000000..1410f4d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/platform-ui-controller.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "024324ab-1239-4568-9b72-5f957e32ba15", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/presentation/ui-contracts.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/presentation/ui-contracts.ts.meta new file mode 100644 index 0000000..b17b110 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/presentation/ui-contracts.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e6f7adfb-42be-43ed-a658-73aac741e2f2", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts.meta new file mode 100644 index 0000000..84d067d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "65fb20da-5c43-4171-a53a-66ae24890751", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/index.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/index.ts.meta new file mode 100644 index 0000000..264cf2e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/index.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "0b2dae51-cba0-4d30-95f6-a6ca50bc2f83", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts index 3711fb0..119c6fa 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts @@ -42,7 +42,7 @@ export interface LoginAccountIdentity { export type DeviceLoginMode = | { readonly enabled: false } - | { readonly enabled: true; readonly telphone: string }; + | { readonly enabled: true; readonly telphone: string; readonly smmcode?: string }; export type LoginPlayerIdMode = | { readonly enabled: false } @@ -112,7 +112,11 @@ export function buildLoginRequest( const deviceLogin = requireRecord(deviceRecord.deviceLogin, '$.device.deviceLogin'); if (requireBoolean(deviceLogin.enabled, '$.device.deviceLogin.enabled')) { data.telphone = requireString(deviceLogin.telphone, '$.device.deviceLogin.telphone'); - data.telphoneAuto = true; + // Explicit phone verification (sharelogin) differs from cached device login. + if (hasOwn(deviceLogin, 'smmcode')) { + data.smmcode = requireString(deviceLogin.smmcode, '$.device.deviceLogin.smmcode'); + data.telphoneAuto = false; + } else data.telphoneAuto = true; } const loginPlayerId = requireRecord(deviceRecord.loginPlayerId, '$.device.loginPlayerId'); diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts.meta new file mode 100644 index 0000000..f38dbb0 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/login-contract.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e473d40d-15cb-4ba0-a9dd-2aee7db34288", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/room-contracts.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/room-contracts.ts.meta new file mode 100644 index 0000000..3bbe24d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/room-contracts.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "c39d248f-79e2-4e7e-935c-8640d7ba2c1d", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/validation.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/validation.ts.meta new file mode 100644 index 0000000..0245aec --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/contracts/validation.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "464618de-1031-4d9d-b47a-5abfc3884b2a", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/first-slice-routes.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/first-slice-routes.ts.meta new file mode 100644 index 0000000..a4edb99 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/first-slice-routes.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "396594e0-d025-4f59-8f09-55f5563149fc", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/protocol/platform-handlers.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/protocol/platform-handlers.ts.meta new file mode 100644 index 0000000..f152a7b --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/protocol/platform-handlers.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "bc28a4de-e56a-4ff3-9793-4a358b7f40e1", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts.meta new file mode 100644 index 0000000..2b7eb2d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "d8033485-9f99-48f1-8c69-80173036f33c", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-entry.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-entry.ts.meta new file mode 100644 index 0000000..1f6a409 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-entry.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "7e0c41b9-1036-46cd-9401-98c4ae86794e", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-host.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-host.ts.meta new file mode 100644 index 0000000..2dde16c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-host.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "f657f863-7e72-4697-827c-11240096d8be", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-module.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-module.ts.meta new file mode 100644 index 0000000..13ef97e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-module.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "3ad6eade-cc0c-4971-bdfb-453f0016df2c", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-seat-mapper.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-seat-mapper.ts.meta new file mode 100644 index 0000000..1589867 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/game-seat-mapper.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "bd3872ba-100e-4b2a-838f-2098366df9fb", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/index.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/index.ts.meta new file mode 100644 index 0000000..d902197 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/index.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "03a2c16b-b059-4ef9-8c26-3fe3a9761720", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/platform-events.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/platform-events.ts.meta new file mode 100644 index 0000000..238e5ae --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/platform-events.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "a61c62b7-e973-4fb6-97be-69db3ed6fd90", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/snapshots.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/snapshots.ts.meta new file mode 100644 index 0000000..8989dac --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/contracts/snapshots.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "5e7d1166-152b-4697-a882-032da5c60f10", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime.meta new file mode 100644 index 0000000..1a67c14 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "f6eae679-99d7-45f3-b301-319b961625f1", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime/game-session-host.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime/game-session-host.ts.meta new file mode 100644 index 0000000..a5cc0f9 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/runtime/game-session-host.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "650981bd-d942-4bb9-8e68-3efb07dda4dd", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing.meta new file mode 100644 index 0000000..8c7ed76 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "f1d3ef88-5577-4be2-853b-27900231ff29", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts.meta b/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts.meta new file mode 100644 index 0000000..9d0e601 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/sdk/testing/game-contract-harness.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "754c5d58-de20-4a2d-9a62-406126c1cf93", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/animations.meta b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations.meta new file mode 100644 index 0000000..65c8ad8 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "2ade8276-e1d3-46ca-9c77-46b3c5f71413", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim new file mode 100644 index 0000000..2871465 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim @@ -0,0 +1,86 @@ +[ + { + "__type__": "cc.AnimationClip", + "_name": "LoginLoading", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "sample": 3, + "speed": 1, + "wrapMode": 2, + "enableTrsBlending": false, + "_duration": 1, + "_hash": 0, + "_tracks": [ + { + "__id__": 1 + } + ], + "_exoticAnimation": null, + "_events": [], + "_embeddedPlayers": [], + "_additiveSettings": { + "__id__": 6 + }, + "_auxiliaryCurveEntries": [] + }, + { + "__type__": "cc.animation.ObjectTrack", + "_binding": { + "__type__": "cc.animation.TrackBinding", + "path": { + "__id__": 2 + }, + "proxy": null + }, + "_channel": { + "__id__": 4 + } + }, + { + "__type__": "cc.animation.TrackPath", + "_paths": [ + { + "__id__": 3 + }, + "spriteFrame" + ] + }, + { + "__type__": "cc.animation.ComponentPath", + "component": "cc.Sprite" + }, + { + "__type__": "cc.animation.Channel", + "_curve": { + "__id__": 5 + } + }, + { + "__type__": "cc.ObjectCurve", + "_times": [ + 0, + 0.3333333333333333, + 0.6666666666666666 + ], + "_values": [ + { + "__uuid__": "41c0f42d-631a-4a3f-abf1-a36ed787e563@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + { + "__uuid__": "bb80821b-0b3f-460e-a74a-1b1a7eaad560@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + { + "__uuid__": "b39af9d6-4d70-43ca-a312-f993de9f961c@f9941", + "__expectedType__": "cc.SpriteFrame" + } + ] + }, + { + "__type__": "cc.AnimationClipAdditiveSettings", + "enabled": false, + "refClip": null + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim.meta b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim.meta new file mode 100644 index 0000000..6ffc96e --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/animations/LoginLoading.anim.meta @@ -0,0 +1,13 @@ +{ + "ver": "2.0.4", + "importer": "animation-clip", + "imported": true, + "uuid": "d7f4b280-dc40-47c7-bfe5-9421350459fe", + "files": [ + ".bin" + ], + "subMetas": {}, + "userData": { + "name": "LoginLoading" + } +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab index 7d76acc..4def164 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab @@ -25,14 +25,14 @@ "_active": true, "_components": [ { - "__id__": 152 + "__id__": 166 }, { - "__id__": 154 + "__id__": 168 } ], "_prefab": { - "__id__": 156 + "__id__": 170 }, "_lpos": { "__type__": "cc.Vec3", @@ -85,59 +85,44 @@ "__id__": 27 }, { - "__id__": 35 + "__id__": 41 }, { - "__id__": 43 + "__id__": 49 }, { - "__id__": 51 + "__id__": 57 }, { - "__id__": 59 + "__id__": 65 }, { - "__id__": 67 + "__id__": 73 }, { - "__id__": 75 + "__id__": 81 }, { - "__id__": 83 + "__id__": 89 }, { - "__id__": 91 + "__id__": 97 }, { - "__id__": 99 - }, - { - "__id__": 107 - }, - { - "__id__": 115 - }, - { - "__id__": 123 - }, - { - "__id__": 131 - }, - { - "__id__": 139 + "__id__": 105 } ], "_active": true, "_components": [ { - "__id__": 147 + "__id__": 161 }, { - "__id__": 149 + "__id__": 163 } ], "_prefab": { - "__id__": 151 + "__id__": 165 }, "_lpos": { "__type__": "cc.Vec3", @@ -585,8 +570,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 84.51171875, - "height": 50.4 + "width": 63.37372911777421, + "height": 50.39999999999999 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -624,8 +609,8 @@ "_string": "label", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 40, - "_fontSize": 40, + "_actualFontSize": 31.625, + "_fontSize": 30, "_fontFamily": "Arial", "_lineHeight": 40, "_overflow": 0, @@ -681,9 +666,9 @@ }, "_alignFlags": 9, "_target": null, - "_left": 16, + "_left": 26.568994816112895, "_right": 0, - "_top": 5, + "_top": 5.0000000000000036, "_bottom": 0, "_horizontalCenter": 0, "_verticalCenter": 0, @@ -724,26 +709,80 @@ "_parent": { "__id__": 2 }, + "_children": [ + { + "__id__": 28 + } + ], + "_active": true, + "_components": [ + { + "__id__": 34 + }, + { + "__id__": 36 + }, + { + "__id__": 38 + } + ], + "_prefab": { + "__id__": 40 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": -76.807, + "y": -207.5, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "check", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 27 + }, "_children": [], "_active": true, "_components": [ { - "__id__": 28 + "__id__": 29 }, { - "__id__": 30 - }, - { - "__id__": 32 + "__id__": 31 } ], "_prefab": { - "__id__": 34 + "__id__": 33 }, "_lpos": { "__type__": "cc.Vec3", - "x": -135, - "y": -207.5, + "x": 0, + "y": 0, "z": 0 }, "_lrot": { @@ -769,6 +808,92 @@ }, "_id": "" }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 28 + }, + "_enabled": true, + "__prefab": { + "__id__": 30 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 40, + "height": 40 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "b6bRc0cFBLhrtni64efWOT" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 28 + }, + "_enabled": true, + "__prefab": { + "__id__": 32 + }, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_spriteFrame": { + "__uuid__": "83e400a9-4b21-47e0-ab49-9ba2fe18ad54@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_type": 0, + "_fillType": 0, + "_sizeMode": 1, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_useGrayscale": false, + "_atlas": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "a0BP4H5odKxZFOrqZfmFMw" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "844Kue3d9DIq0010JH7pFx", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, { "__type__": "cc.UITransform", "_name": "", @@ -779,7 +904,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 29 + "__id__": 35 }, "_contentSize": { "__type__": "cc.Size", @@ -807,7 +932,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 31 + "__id__": 37 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -852,7 +977,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 33 + "__id__": 39 }, "_alignFlags": 16, "_target": null, @@ -860,7 +985,7 @@ "_right": 0, "_top": 0, "_bottom": 0, - "_horizontalCenter": -135, + "_horizontalCenter": -76.807, "_verticalCenter": 0, "_isAbsLeft": true, "_isAbsRight": true, @@ -887,8 +1012,6 @@ "__id__": 0 }, "fileId": "7bOUfugfZGRJARCABMOobI", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -903,21 +1026,21 @@ "_active": true, "_components": [ { - "__id__": 36 + "__id__": 42 }, { - "__id__": 38 + "__id__": 44 }, { - "__id__": 40 + "__id__": 46 } ], "_prefab": { - "__id__": 42 + "__id__": 48 }, "_lpos": { "__type__": "cc.Vec3", - "x": 35, + "x": 82.11, "y": -210.5, "z": 0 }, @@ -950,11 +1073,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 35 + "__id__": 41 }, "_enabled": true, "__prefab": { - "__id__": 37 + "__id__": 43 }, "_contentSize": { "__type__": "cc.Size", @@ -978,11 +1101,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 35 + "__id__": 41 }, "_enabled": true, "__prefab": { - "__id__": 39 + "__id__": 45 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1023,11 +1146,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 35 + "__id__": 41 }, "_enabled": true, "__prefab": { - "__id__": 41 + "__id__": 47 }, "_alignFlags": 16, "_target": null, @@ -1035,7 +1158,7 @@ "_right": 0, "_top": 0, "_bottom": 0, - "_horizontalCenter": 35, + "_horizontalCenter": 82.11, "_verticalCenter": 0, "_isAbsLeft": true, "_isAbsRight": true, @@ -1062,8 +1185,6 @@ "__id__": 0 }, "fileId": "aa6JBxpmNMqbBB+2K20Gpm", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -1078,17 +1199,17 @@ "_active": true, "_components": [ { - "__id__": 44 + "__id__": 50 }, { - "__id__": 46 + "__id__": 52 }, { - "__id__": 48 + "__id__": 54 } ], "_prefab": { - "__id__": 50 + "__id__": 56 }, "_lpos": { "__type__": "cc.Vec3", @@ -1125,11 +1246,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 43 + "__id__": 49 }, "_enabled": true, "__prefab": { - "__id__": 45 + "__id__": 51 }, "_contentSize": { "__type__": "cc.Size", @@ -1153,11 +1274,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 43 + "__id__": 49 }, "_enabled": true, "__prefab": { - "__id__": 47 + "__id__": 53 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1198,11 +1319,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 43 + "__id__": 49 }, "_enabled": true, "__prefab": { - "__id__": 49 + "__id__": 55 }, "_alignFlags": 32, "_target": null, @@ -1253,17 +1374,17 @@ "_active": true, "_components": [ { - "__id__": 52 + "__id__": 58 }, { - "__id__": 54 + "__id__": 60 }, { - "__id__": 56 + "__id__": 62 } ], "_prefab": { - "__id__": 58 + "__id__": 64 }, "_lpos": { "__type__": "cc.Vec3", @@ -1300,11 +1421,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 51 + "__id__": 57 }, "_enabled": true, "__prefab": { - "__id__": 53 + "__id__": 59 }, "_contentSize": { "__type__": "cc.Size", @@ -1328,11 +1449,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 51 + "__id__": 57 }, "_enabled": true, "__prefab": { - "__id__": 55 + "__id__": 61 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1373,11 +1494,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 51 + "__id__": 57 }, "_enabled": true, "__prefab": { - "__id__": 57 + "__id__": 63 }, "_alignFlags": 8, "_target": null, @@ -1428,17 +1549,17 @@ "_active": true, "_components": [ { - "__id__": 60 + "__id__": 66 }, { - "__id__": 62 + "__id__": 68 }, { - "__id__": 64 + "__id__": 70 } ], "_prefab": { - "__id__": 66 + "__id__": 72 }, "_lpos": { "__type__": "cc.Vec3", @@ -1475,11 +1596,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 59 + "__id__": 65 }, "_enabled": true, "__prefab": { - "__id__": 61 + "__id__": 67 }, "_contentSize": { "__type__": "cc.Size", @@ -1503,11 +1624,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 59 + "__id__": 65 }, "_enabled": true, "__prefab": { - "__id__": 63 + "__id__": 69 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1548,11 +1669,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 59 + "__id__": 65 }, "_enabled": true, "__prefab": { - "__id__": 65 + "__id__": 71 }, "_alignFlags": 16, "_target": null, @@ -1603,17 +1724,17 @@ "_active": true, "_components": [ { - "__id__": 68 + "__id__": 74 }, { - "__id__": 70 + "__id__": 76 }, { - "__id__": 72 + "__id__": 78 } ], "_prefab": { - "__id__": 74 + "__id__": 80 }, "_lpos": { "__type__": "cc.Vec3", @@ -1650,11 +1771,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 67 + "__id__": 73 }, "_enabled": true, "__prefab": { - "__id__": 69 + "__id__": 75 }, "_contentSize": { "__type__": "cc.Size", @@ -1678,11 +1799,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 67 + "__id__": 73 }, "_enabled": true, "__prefab": { - "__id__": 71 + "__id__": 77 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1723,11 +1844,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 67 + "__id__": 73 }, "_enabled": true, "__prefab": { - "__id__": 73 + "__id__": 79 }, "_alignFlags": 16, "_target": null, @@ -1778,17 +1899,17 @@ "_active": true, "_components": [ { - "__id__": 76 + "__id__": 82 }, { - "__id__": 78 + "__id__": 84 }, { - "__id__": 80 + "__id__": 86 } ], "_prefab": { - "__id__": 82 + "__id__": 88 }, "_lpos": { "__type__": "cc.Vec3", @@ -1825,11 +1946,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 75 + "__id__": 81 }, "_enabled": true, "__prefab": { - "__id__": 77 + "__id__": 83 }, "_contentSize": { "__type__": "cc.Size", @@ -1853,11 +1974,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 75 + "__id__": 81 }, "_enabled": true, "__prefab": { - "__id__": 79 + "__id__": 85 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -1898,11 +2019,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 75 + "__id__": 81 }, "_enabled": true, "__prefab": { - "__id__": 81 + "__id__": 87 }, "_alignFlags": 16, "_target": null, @@ -1953,17 +2074,17 @@ "_active": true, "_components": [ { - "__id__": 84 + "__id__": 90 }, { - "__id__": 86 + "__id__": 92 }, { - "__id__": 88 + "__id__": 94 } ], "_prefab": { - "__id__": 90 + "__id__": 96 }, "_lpos": { "__type__": "cc.Vec3", @@ -2000,11 +2121,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 83 + "__id__": 89 }, "_enabled": true, "__prefab": { - "__id__": 85 + "__id__": 91 }, "_contentSize": { "__type__": "cc.Size", @@ -2028,11 +2149,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 83 + "__id__": 89 }, "_enabled": true, "__prefab": { - "__id__": 87 + "__id__": 93 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -2073,11 +2194,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 83 + "__id__": 89 }, "_enabled": true, "__prefab": { - "__id__": 89 + "__id__": 95 }, "_alignFlags": 16, "_target": null, @@ -2128,17 +2249,17 @@ "_active": true, "_components": [ { - "__id__": 92 + "__id__": 98 }, { - "__id__": 94 + "__id__": 100 }, { - "__id__": 96 + "__id__": 102 } ], "_prefab": { - "__id__": 98 + "__id__": 104 }, "_lpos": { "__type__": "cc.Vec3", @@ -2175,11 +2296,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 91 + "__id__": 97 }, "_enabled": true, "__prefab": { - "__id__": 93 + "__id__": 99 }, "_contentSize": { "__type__": "cc.Size", @@ -2203,11 +2324,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 91 + "__id__": 97 }, "_enabled": true, "__prefab": { - "__id__": 95 + "__id__": 101 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -2248,11 +2369,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 91 + "__id__": 97 }, "_enabled": true, "__prefab": { - "__id__": 97 + "__id__": 103 }, "_alignFlags": 16, "_target": null, @@ -2293,27 +2414,96 @@ }, { "__type__": "cc.Node", - "_name": "登录中遮罩", + "_name": "Logining", "_objFlags": 0, "__editorExtras__": {}, "_parent": { "__id__": 2 }, + "_children": [ + { + "__id__": 106 + }, + { + "__id__": 114 + }, + { + "__id__": 122 + }, + { + "__id__": 130 + }, + { + "__id__": 138 + }, + { + "__id__": 148 + } + ], + "_active": true, + "_components": [ + { + "__id__": 156 + }, + { + "__id__": 158 + } + ], + "_prefab": { + "__id__": 160 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "登录中遮罩", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 105 + }, "_children": [], "_active": true, "_components": [ { - "__id__": 100 + "__id__": 107 }, { - "__id__": 102 + "__id__": 109 }, { - "__id__": 104 + "__id__": 111 } ], "_prefab": { - "__id__": 106 + "__id__": 113 }, "_lpos": { "__type__": "cc.Vec3", @@ -2350,11 +2540,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 99 + "__id__": 106 }, "_enabled": true, "__prefab": { - "__id__": 101 + "__id__": 108 }, "_contentSize": { "__type__": "cc.Size", @@ -2378,11 +2568,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 99 + "__id__": 106 }, "_enabled": true, "__prefab": { - "__id__": 103 + "__id__": 110 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -2423,11 +2613,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 99 + "__id__": 106 }, "_enabled": true, "__prefab": { - "__id__": 105 + "__id__": 112 }, "_alignFlags": 45, "_target": null, @@ -2472,23 +2662,23 @@ "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 105 }, "_children": [], "_active": true, "_components": [ { - "__id__": 108 + "__id__": 115 }, { - "__id__": 110 + "__id__": 117 }, { - "__id__": 112 + "__id__": 119 } ], "_prefab": { - "__id__": 114 + "__id__": 121 }, "_lpos": { "__type__": "cc.Vec3", @@ -2525,11 +2715,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 107 + "__id__": 114 }, "_enabled": true, "__prefab": { - "__id__": 109 + "__id__": 116 }, "_contentSize": { "__type__": "cc.Size", @@ -2553,11 +2743,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 107 + "__id__": 114 }, "_enabled": true, "__prefab": { - "__id__": 111 + "__id__": 118 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -2598,11 +2788,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 107 + "__id__": 114 }, "_enabled": true, "__prefab": { - "__id__": 113 + "__id__": 120 }, "_alignFlags": 16, "_target": null, @@ -2647,23 +2837,23 @@ "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 105 }, "_children": [], "_active": true, "_components": [ { - "__id__": 116 + "__id__": 123 }, { - "__id__": 118 + "__id__": 125 }, { - "__id__": 120 + "__id__": 127 } ], "_prefab": { - "__id__": 122 + "__id__": 129 }, "_lpos": { "__type__": "cc.Vec3", @@ -2700,11 +2890,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 115 + "__id__": 122 }, "_enabled": true, "__prefab": { - "__id__": 117 + "__id__": 124 }, "_contentSize": { "__type__": "cc.Size", @@ -2728,11 +2918,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 115 + "__id__": 122 }, "_enabled": true, "__prefab": { - "__id__": 119 + "__id__": 126 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -2773,11 +2963,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 115 + "__id__": 122 }, "_enabled": true, "__prefab": { - "__id__": 121 + "__id__": 128 }, "_alignFlags": 16, "_target": null, @@ -2822,27 +3012,27 @@ "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 105 }, "_children": [], "_active": true, "_components": [ { - "__id__": 124 + "__id__": 131 }, { - "__id__": 126 + "__id__": 133 }, { - "__id__": 128 + "__id__": 135 } ], "_prefab": { - "__id__": 130 + "__id__": 137 }, "_lpos": { "__type__": "cc.Vec3", - "x": 67.5, + "x": 53.645, "y": -46, "z": 0 }, @@ -2875,16 +3065,16 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 123 + "__id__": 130 }, "_enabled": true, "__prefab": { - "__id__": 125 + "__id__": 132 }, "_contentSize": { "__type__": "cc.Size", - "width": 84.51171875, - "height": 50.4 + "width": 63.37372911777421, + "height": 50.39999999999999 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -2903,27 +3093,27 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 123 + "__id__": 130 }, "_enabled": true, "__prefab": { - "__id__": 127 + "__id__": 134 }, "_customMaterial": null, "_srcBlendFactor": 2, "_dstBlendFactor": 4, "_color": { "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, + "r": 49, + "g": 10, + "b": 10, "a": 255 }, "_string": "label", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 40, - "_fontSize": 40, + "_actualFontSize": 31.625, + "_fontSize": 30, "_fontFamily": "Arial", "_lineHeight": 40, "_overflow": 0, @@ -2971,11 +3161,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 123 + "__id__": 130 }, "_enabled": true, "__prefab": { - "__id__": 129 + "__id__": 136 }, "_alignFlags": 16, "_target": null, @@ -2983,7 +3173,7 @@ "_right": 0, "_top": 0, "_bottom": 0, - "_horizontalCenter": 67.5, + "_horizontalCenter": 53.645, "_verticalCenter": 0, "_isAbsLeft": true, "_isAbsRight": true, @@ -3020,27 +3210,30 @@ "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 105 }, "_children": [], "_active": true, "_components": [ { - "__id__": 132 + "__id__": 139 }, { - "__id__": 134 + "__id__": 141 }, { - "__id__": 136 + "__id__": 143 + }, + { + "__id__": 145 } ], "_prefab": { - "__id__": 138 + "__id__": 147 }, "_lpos": { "__type__": "cc.Vec3", - "x": 73, + "x": 59.144, "y": -106, "z": 0 }, @@ -3073,11 +3266,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 131 + "__id__": 138 }, "_enabled": true, "__prefab": { - "__id__": 133 + "__id__": 140 }, "_contentSize": { "__type__": "cc.Size", @@ -3101,11 +3294,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 131 + "__id__": 138 }, "_enabled": true, "__prefab": { - "__id__": 135 + "__id__": 142 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3146,11 +3339,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 131 + "__id__": 138 }, "_enabled": true, "__prefab": { - "__id__": 137 + "__id__": 144 }, "_alignFlags": 16, "_target": null, @@ -3158,7 +3351,7 @@ "_right": 0, "_top": 0, "_bottom": 0, - "_horizontalCenter": 73, + "_horizontalCenter": 59.144, "_verticalCenter": 0, "_isAbsLeft": true, "_isAbsRight": true, @@ -3176,6 +3369,35 @@ "__type__": "cc.CompPrefabInfo", "fileId": "uEwYXwzNJK+j9XvosozgjD" }, + { + "__type__": "cc.Animation", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 138 + }, + "_enabled": true, + "__prefab": { + "__id__": 146 + }, + "playOnLoad": false, + "_clips": [ + { + "__uuid__": "d7f4b280-dc40-47c7-bfe5-9421350459fe", + "__expectedType__": "cc.AnimationClip" + } + ], + "_defaultClip": { + "__uuid__": "d7f4b280-dc40-47c7-bfe5-9421350459fe", + "__expectedType__": "cc.AnimationClip" + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "b7dBKCl1ZCMZL20vV4j20y" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -3195,23 +3417,23 @@ "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 105 }, "_children": [], "_active": true, "_components": [ { - "__id__": 140 + "__id__": 149 }, { - "__id__": 142 + "__id__": 151 }, { - "__id__": 144 + "__id__": 153 } ], "_prefab": { - "__id__": 146 + "__id__": 155 }, "_lpos": { "__type__": "cc.Vec3", @@ -3248,11 +3470,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 139 + "__id__": 148 }, "_enabled": true, "__prefab": { - "__id__": 141 + "__id__": 150 }, "_contentSize": { "__type__": "cc.Size", @@ -3276,11 +3498,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 139 + "__id__": 148 }, "_enabled": true, "__prefab": { - "__id__": 143 + "__id__": 152 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3321,11 +3543,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 139 + "__id__": 148 }, "_enabled": true, "__prefab": { - "__id__": 145 + "__id__": 154 }, "_alignFlags": 16, "_target": null, @@ -3364,6 +3586,83 @@ "targetOverrides": null, "nestedPrefabInstanceRoots": null }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 105 + }, + "_enabled": true, + "__prefab": { + "__id__": 157 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 1600, + "height": 720 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "basldfhxdJQrqpNsve4d7+" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 105 + }, + "_enabled": true, + "__prefab": { + "__id__": 159 + }, + "_alignFlags": 45, + "_target": null, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, + "_horizontalCenter": 0, + "_verticalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 687.479, + "_originalHeight": 180, + "_alignMode": 2, + "_lockFlags": 0, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "06PjqbFMhCfYvaP6IN87ad" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "00RsNEn29JXZu+dl3t3qBQ", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, { "__type__": "cc.UITransform", "_name": "", @@ -3374,7 +3673,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 148 + "__id__": 162 }, "_contentSize": { "__type__": "cc.Size", @@ -3402,7 +3701,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 150 + "__id__": 164 }, "_alignFlags": 45, "_target": null, @@ -3437,8 +3736,6 @@ "__id__": 0 }, "fileId": "d4P87gqa5AIJltQ5D4Vthy", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -3451,7 +3748,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 153 + "__id__": 167 }, "_contentSize": { "__type__": "cc.Size", @@ -3479,7 +3776,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 155 + "__id__": 169 }, "_alignFlags": 45, "_target": null, diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab index 42f7a1e..d72bee5 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab @@ -25,14 +25,14 @@ "_active": true, "_components": [ { - "__id__": 40 + "__id__": 50 }, { - "__id__": 42 + "__id__": 52 } ], "_prefab": { - "__id__": 44 + "__id__": 54 }, "_lpos": { "__type__": "cc.Vec3", @@ -61,7 +61,7 @@ "y": 0, "z": 0 }, - "_id": "acrZGZbf1NerIwhXMce5C9" + "_id": "" }, { "__type__": "cc.Node", @@ -79,23 +79,23 @@ "__id__": 11 }, { - "__id__": 19 - }, - { - "__id__": 27 - } - ], - "_active": true, - "_components": [ - { - "__id__": 35 + "__id__": 21 }, { "__id__": 37 } ], + "_active": true, + "_components": [ + { + "__id__": 45 + }, + { + "__id__": 47 + } + ], "_prefab": { - "__id__": 39 + "__id__": 49 }, "_lpos": { "__type__": "cc.Vec3", @@ -124,7 +124,7 @@ "y": 0, "z": 0 }, - "_id": "e6VeYSc7NPirZdAaN4p44e" + "_id": "" }, { "__type__": "cc.Node", @@ -177,7 +177,7 @@ "y": 0, "z": 0 }, - "_id": "f1pHSoD4dILLRVFTIObRtT" + "_id": "" }, { "__type__": "cc.UITransform", @@ -201,7 +201,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "a9XLkwiTVFH68tpCqSosju" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -246,7 +246,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "15y0dNzphPKaTgoMZ3YkkU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -282,7 +282,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "8b8p/boZJEqocxUC1G22sv" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -320,10 +320,13 @@ }, { "__id__": 16 + }, + { + "__id__": 18 } ], "_prefab": { - "__id__": 18 + "__id__": 20 }, "_lpos": { "__type__": "cc.Vec3", @@ -352,7 +355,7 @@ "y": 0, "z": 0 }, - "_id": "03KAkUqStGDaHp3GFOQbYR" + "_id": "" }, { "__type__": "cc.UITransform", @@ -376,7 +379,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "feVdaeICBA07uZAmLtAU07" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -421,7 +424,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "b6GW+6L8VHILl5D4tnXajn" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -457,12 +460,30 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "5dO4+lWM1I74eFWhnD+bgo" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", "fileId": "MW2xRLaeE9itOcgHj0p+dQ" }, + { + "__type__": "cc.BlockInputEvents", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 11 + }, + "_enabled": true, + "__prefab": { + "__id__": 19 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "4fv3LfgR5DPIoVUfROUOzr" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -478,32 +499,39 @@ }, { "__type__": "cc.Node", - "_name": "协议内容", + "_name": "协议滚动", "_objFlags": 0, "__editorExtras__": {}, "_parent": { "__id__": 2 }, - "_children": [], + "_children": [ + { + "__id__": 22 + } + ], "_active": true, "_components": [ { - "__id__": 20 + "__id__": 28 }, { - "__id__": 22 + "__id__": 30 }, { - "__id__": 24 + "__id__": 32 + }, + { + "__id__": 34 } ], "_prefab": { - "__id__": 26 + "__id__": 36 }, "_lpos": { "__type__": "cc.Vec3", "x": -5.5, - "y": -208.5, + "y": -5, "z": 0 }, "_lrot": { @@ -527,7 +555,57 @@ "y": 0, "z": 0 }, - "_id": "fcOwSwvSpMlaZQPDjbA1f7" + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "协议内容", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 21 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 23 + }, + { + "__id__": 25 + } + ], + "_prefab": { + "__id__": 27 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": -203.5, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" }, { "__type__": "cc.UITransform", @@ -535,11 +613,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 19 + "__id__": 22 }, "_enabled": true, "__prefab": { - "__id__": 21 + "__id__": 24 }, "_contentSize": { "__type__": "cc.Size", @@ -551,7 +629,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "e4LJQjJ6FE6LjQy5yIP382" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -563,11 +641,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 19 + "__id__": 22 }, "_enabled": true, "__prefab": { - "__id__": 23 + "__id__": 26 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -596,48 +674,12 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "bfkfehGNlIxp/fTJn+oOTB" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", "fileId": "1jOdeOrfg2vu2QJl+s+VeA" }, - { - "__type__": "cc.Widget", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 19 - }, - "_enabled": true, - "__prefab": { - "__id__": 25 - }, - "_alignFlags": 16, - "_target": null, - "_left": 0, - "_right": 0, - "_top": 0, - "_bottom": 0, - "_horizontalCenter": -5.5, - "_verticalCenter": 0, - "_isAbsLeft": true, - "_isAbsRight": true, - "_isAbsTop": true, - "_isAbsBottom": true, - "_isAbsHorizontalCenter": true, - "_isAbsVerticalCenter": true, - "_originalWidth": 0, - "_originalHeight": 0, - "_alignMode": 2, - "_lockFlags": 0, - "_id": "32AaRKNa5IUaPeF5jIfKTl" - }, - { - "__type__": "cc.CompPrefabInfo", - "fileId": "D1Qfc6QTWy0vKDGsx+qLQg" - }, { "__type__": "cc.PrefabInfo", "root": { @@ -651,6 +693,146 @@ "targetOverrides": null, "nestedPrefabInstanceRoots": null }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 21 + }, + "_enabled": true, + "__prefab": { + "__id__": 29 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 729, + "height": 590 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "0bFbdo0btAnLpeg5XLVeih" + }, + { + "__type__": "cc.Mask", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 21 + }, + "_enabled": true, + "__prefab": { + "__id__": 31 + }, + "_type": 0, + "_inverted": false, + "_segments": 64, + "_alphaThreshold": 0.1, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "8bWDB4GxZEjZGyBHlWT8Ry" + }, + { + "__type__": "cc.Graphics", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 21 + }, + "_enabled": true, + "__prefab": { + "__id__": 33 + }, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_lineWidth": 1, + "_strokeColor": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 255 + }, + "_lineJoin": 2, + "_lineCap": 0, + "_fillColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 0 + }, + "_miterLimit": 10, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "d5mkJTZEZFOru7Ifoe3YX/" + }, + { + "__type__": "cc.ScrollView", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 21 + }, + "_enabled": true, + "__prefab": { + "__id__": 35 + }, + "bounceDuration": 1, + "brake": 0.5, + "elastic": true, + "inertia": true, + "horizontal": false, + "vertical": true, + "cancelInnerEvents": true, + "scrollEvents": [], + "_content": { + "__id__": 22 + }, + "_horizontalScrollBar": null, + "_verticalScrollBar": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "cfxheTMxVE45LJ6nYtvr4t" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "edhlQTi55MNo1ZWcRw9+qA", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, { "__type__": "cc.Node", "_name": "Spirit253", @@ -663,17 +845,17 @@ "_active": true, "_components": [ { - "__id__": 28 + "__id__": 38 }, { - "__id__": 30 + "__id__": 40 }, { - "__id__": 32 + "__id__": 42 } ], "_prefab": { - "__id__": 34 + "__id__": 44 }, "_lpos": { "__type__": "cc.Vec3", @@ -702,7 +884,7 @@ "y": 0, "z": 0 }, - "_id": "770rm+dOJPVJdLQnhaoDaZ" + "_id": "" }, { "__type__": "cc.UITransform", @@ -710,11 +892,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 27 + "__id__": 37 }, "_enabled": true, "__prefab": { - "__id__": 29 + "__id__": 39 }, "_contentSize": { "__type__": "cc.Size", @@ -726,7 +908,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "02BeJasixOrpU3LpuhrkT+" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -738,11 +920,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 27 + "__id__": 37 }, "_enabled": true, "__prefab": { - "__id__": 31 + "__id__": 41 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -771,7 +953,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "f372YRc0pCR5LhJkRDp5mj" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -783,11 +965,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 27 + "__id__": 37 }, "_enabled": true, "__prefab": { - "__id__": 33 + "__id__": 43 }, "_alignFlags": 16, "_target": null, @@ -807,7 +989,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "46JhwhkSdK0KV8Lt3p+HOy" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -836,7 +1018,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 36 + "__id__": 46 }, "_contentSize": { "__type__": "cc.Size", @@ -848,7 +1030,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "07nq/yMyxNHq4xU2y77PJH" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -864,7 +1046,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 38 + "__id__": 48 }, "_alignFlags": 45, "_target": null, @@ -884,7 +1066,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "fdaBoOjw5KN4+Xi8tr/T8R" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -913,7 +1095,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 41 + "__id__": 51 }, "_contentSize": { "__type__": "cc.Size", @@ -925,7 +1107,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "52OXNuOopHTL574Hc5gszK" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -941,7 +1123,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 43 + "__id__": 53 }, "_alignFlags": 45, "_target": null, @@ -961,7 +1143,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "73lGoAY61JlYZxdhx3MnbQ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -977,7 +1159,6 @@ }, "fileId": "xSY2ymJa1y54ctanvQH+WQ", "instance": null, - "targetOverrides": null, - "nestedPrefabInstanceRoots": null + "targetOverrides": null } -] +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab index 6650630..0403efb 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab @@ -76,9 +76,6 @@ }, { "__id__": 29 - }, - { - "__id__": 37 } ], "_active": true, @@ -687,21 +684,25 @@ "_parent": { "__id__": 2 }, - "_children": [], + "_children": [ + { + "__id__": 30 + } + ], "_active": true, "_components": [ { - "__id__": 30 + "__id__": 38 }, { - "__id__": 32 + "__id__": 40 }, { - "__id__": 34 + "__id__": 42 } ], "_prefab": { - "__id__": 36 + "__id__": 44 }, "_lpos": { "__type__": "cc.Vec3", @@ -732,156 +733,34 @@ }, "_id": "" }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 29 - }, - "_enabled": true, - "__prefab": { - "__id__": 31 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 208, - "height": 59 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "" - }, - { - "__type__": "cc.CompPrefabInfo", - "fileId": "t4s+2kLm2F//czXxT9Xo8A" - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 29 - }, - "_enabled": true, - "__prefab": { - "__id__": 33 - }, - "_customMaterial": null, - "_srcBlendFactor": 2, - "_dstBlendFactor": 4, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_spriteFrame": { - "__uuid__": "d313aa40-9397-4b44-8100-65ea389233f3@f9941", - "__expectedType__": "cc.SpriteFrame" - }, - "_type": 0, - "_fillType": 0, - "_sizeMode": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_useGrayscale": false, - "_atlas": null, - "_id": "" - }, - { - "__type__": "cc.CompPrefabInfo", - "fileId": "/rrApudp/UA/nOAEHDamNA" - }, - { - "__type__": "cc.Widget", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 29 - }, - "_enabled": true, - "__prefab": { - "__id__": 35 - }, - "_alignFlags": 16, - "_target": null, - "_left": 0, - "_right": 0, - "_top": 0, - "_bottom": 0, - "_horizontalCenter": 671.37, - "_verticalCenter": 0, - "_isAbsLeft": true, - "_isAbsRight": true, - "_isAbsTop": true, - "_isAbsBottom": true, - "_isAbsHorizontalCenter": true, - "_isAbsVerticalCenter": true, - "_originalWidth": 0, - "_originalHeight": 0, - "_alignMode": 2, - "_lockFlags": 0, - "_id": "" - }, - { - "__type__": "cc.CompPrefabInfo", - "fileId": "v+C5/D9J96sTVhcAhSrUng" - }, - { - "__type__": "cc.PrefabInfo", - "root": { - "__id__": 1 - }, - "asset": { - "__id__": 0 - }, - "fileId": "QQkO56LzjjkpTd+xNv6zcA", - "instance": null, - "targetOverrides": null, - "nestedPrefabInstanceRoots": null - }, { "__type__": "cc.Node", "_name": "Spirit3104", "_objFlags": 0, "__editorExtras__": {}, "_parent": { - "__id__": 2 + "__id__": 29 }, "_children": [], "_active": true, "_components": [ { - "__id__": 38 + "__id__": 31 }, { - "__id__": 40 + "__id__": 33 }, { - "__id__": 42 + "__id__": 35 } ], "_prefab": { - "__id__": 44 + "__id__": 37 }, "_lpos": { "__type__": "cc.Vec3", - "x": 671.87, - "y": 311.5, + "x": 0, + "y": 0, "z": 0 }, "_lrot": { @@ -913,16 +792,16 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 37 + "__id__": 30 }, "_enabled": true, "__prefab": { - "__id__": 39 + "__id__": 32 }, "_contentSize": { "__type__": "cc.Size", - "width": 63.005419342885375, - "height": 17.64 + "width": 81.03130596899703, + "height": 22.680000000000003 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -941,11 +820,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 37 + "__id__": 30 }, "_enabled": true, "__prefab": { - "__id__": 41 + "__id__": 34 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -960,10 +839,10 @@ "_string": "Spirit3104", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14.758333333333333, - "_fontSize": 14, + "_actualFontSize": 18.975, + "_fontSize": 18, "_fontFamily": "Arial", - "_lineHeight": 14, + "_lineHeight": 18, "_overflow": 0, "_enableWrapText": true, "_font": null, @@ -1009,19 +888,19 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 37 + "__id__": 30 }, "_enabled": true, "__prefab": { - "__id__": 43 + "__id__": 36 }, - "_alignFlags": 16, + "_alignFlags": 18, "_target": null, - "_left": 0, - "_right": 0, - "_top": 0, - "_bottom": 0, - "_horizontalCenter": 671.87, + "_left": 63.484347015501484, + "_right": 63.484347015501484, + "_top": 18.159999999999997, + "_bottom": 18.159999999999997, + "_horizontalCenter": 0, "_verticalCenter": 0, "_isAbsLeft": true, "_isAbsRight": true, @@ -1029,8 +908,8 @@ "_isAbsBottom": true, "_isAbsHorizontalCenter": true, "_isAbsVerticalCenter": true, - "_originalWidth": 0, - "_originalHeight": 0, + "_originalWidth": 81.03130596899703, + "_originalHeight": 22.680000000000003, "_alignMode": 2, "_lockFlags": 0, "_id": "" @@ -1052,6 +931,128 @@ "targetOverrides": null, "nestedPrefabInstanceRoots": null }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 29 + }, + "_enabled": true, + "__prefab": { + "__id__": 39 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 208, + "height": 59 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "t4s+2kLm2F//czXxT9Xo8A" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 29 + }, + "_enabled": true, + "__prefab": { + "__id__": 41 + }, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_spriteFrame": { + "__uuid__": "d313aa40-9397-4b44-8100-65ea389233f3@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_type": 0, + "_fillType": 0, + "_sizeMode": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_useGrayscale": false, + "_atlas": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "/rrApudp/UA/nOAEHDamNA" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 29 + }, + "_enabled": true, + "__prefab": { + "__id__": 43 + }, + "_alignFlags": 16, + "_target": null, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, + "_horizontalCenter": 671.37, + "_verticalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 0, + "_originalHeight": 0, + "_alignMode": 2, + "_lockFlags": 0, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "v+C5/D9J96sTVhcAhSrUng" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "QQkO56LzjjkpTd+xNv6zcA", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, { "__type__": "cc.UITransform", "_name": "", diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab index 02305f3..80f1cb3 100644 --- a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab @@ -32,10 +32,13 @@ }, { "__id__": 320 + }, + { + "__id__": 322 } ], "_prefab": { - "__id__": 322 + "__id__": 324 }, "_lpos": { "__type__": "cc.Vec3", @@ -64,7 +67,7 @@ "y": 0, "z": 0 }, - "_id": "0dPWQwe71CaaWxFukC0dmh" + "_id": "" }, { "__type__": "cc.Node", @@ -160,7 +163,7 @@ "y": 0, "z": 0 }, - "_id": "86DrwwFWFLnpMhnKei8SJB" + "_id": "" }, { "__type__": "cc.Node", @@ -171,7 +174,7 @@ "__id__": 2 }, "_children": [], - "_active": false, + "_active": true, "_components": [ { "__id__": 4 @@ -213,7 +216,7 @@ "y": 0, "z": 0 }, - "_id": "7e/bY1uCVF5ZFp4QVhiNPw" + "_id": "" }, { "__type__": "cc.UITransform", @@ -237,7 +240,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "5989hTVRxCRo43NyQedMpF" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -282,7 +285,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "683oZQuadHQpmE0P1ziIiW" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -318,7 +321,7 @@ "_originalHeight": 720, "_alignMode": 2, "_lockFlags": 0, - "_id": "8bBAPPl1dK6olXY7EYnOrM" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -388,7 +391,7 @@ "y": 0, "z": 0 }, - "_id": "9aoFkdrG9ErKTNmXj812dZ" + "_id": "" }, { "__type__": "cc.UITransform", @@ -412,7 +415,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "7ecVAjjZ5HZIyWUsSpKzv/" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -457,7 +460,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "d9oWjMnlBGXqqfkzm+P3UV" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -493,7 +496,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "73N6D7U7pMuZLW7yZuS+ef" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -563,7 +566,7 @@ "y": 0, "z": 0 }, - "_id": "53jWyGF5NOtajgp/LveP18" + "_id": "" }, { "__type__": "cc.UITransform", @@ -587,7 +590,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "43Q1lbFTZIoLlTTmYFZTsX" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -632,7 +635,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "e72EamOwRAZqckjgnGHyR2" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -668,7 +671,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "6eCEM1ytpPzqou/LABRGYO" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -738,7 +741,7 @@ "y": 0, "z": 0 }, - "_id": "1a2gKhsSZCP5hW12/voZEl" + "_id": "" }, { "__type__": "cc.UITransform", @@ -762,7 +765,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "f5WtI5yDFB3Ip1kj1fJsAc" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -807,7 +810,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "d4HpMkwZ1My7tFucG/dm5i" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -843,7 +846,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "394zAowV5HabKbqzz0HaM6" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -913,7 +916,7 @@ "y": 0, "z": 0 }, - "_id": "4ewPrUGYtAwYwMCK8/4jsb" + "_id": "" }, { "__type__": "cc.UITransform", @@ -937,7 +940,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "08AjAOPNFDPYihcKySCYB2" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -982,7 +985,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "76ujKeDbBPTIB5RxMawiCG" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1018,7 +1021,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "7dJe6eMqlPo5F07daSEMVR" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1088,7 +1091,7 @@ "y": 0, "z": 0 }, - "_id": "9fU1XNIgJDmLNB2oCfl75V" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1112,7 +1115,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "b7r6oHttdOYKCMCwOE45+R" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1157,7 +1160,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "9fQkvrVBBNdJiGqRcRXJO+" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1193,7 +1196,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "f8FfupKAVMOq5bvsEuUUql" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1263,7 +1266,7 @@ "y": 0, "z": 0 }, - "_id": "52lXeZ+oxFJZW/2aSn4pGs" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1287,7 +1290,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "4fE+ftaAdLXJoLhcezPm3w" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1332,7 +1335,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "051/PLFkZP+IGCYKxADK98" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1368,7 +1371,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "36mPfX5CZOn5dnLRg+XCgz" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1438,7 +1441,7 @@ "y": 0, "z": 0 }, - "_id": "5c+tsK7mVPT5sUHS/xPBaX" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1462,7 +1465,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "09sT0ltspO9KHQwcfi8ziC" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1507,7 +1510,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "2f2n1+Qn9B5ImRCrpNgjdm" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1543,7 +1546,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "e4tYm2UQtAIZmzCc+Xu3pw" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1613,7 +1616,7 @@ "y": 0, "z": 0 }, - "_id": "7eHwHzVzBLzpletxq+jP4c" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1637,7 +1640,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "0aLrSq37xMcIno0s1pJkT4" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1682,7 +1685,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "b01/qWg+FEfpQs9dFCwH92" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1718,7 +1721,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "9cdZn2n19B3YmHQzMZXgjm" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1788,7 +1791,7 @@ "y": 0, "z": 0 }, - "_id": "aa1/EBoUlDf7Fhqw0ZRRtk" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1812,7 +1815,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "f294EtQppC97JWm0co96ej" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1857,7 +1860,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "21wSO5r0JD7qEeA0/f1KlA" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1893,7 +1896,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "b9UOxfC0JPcZG5gMpOewdy" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -1963,7 +1966,7 @@ "y": 0, "z": 0 }, - "_id": "728OYDDFdEfL6aSJ8BUJA0" + "_id": "" }, { "__type__": "cc.UITransform", @@ -1987,7 +1990,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "659TR3AuhHcqES+wU7ZpDg" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2032,7 +2035,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "0cgHK/G+dFF7nrFqgA5q8U" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2068,7 +2071,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "e7e+1BmG9EuqCjCsK0B4fZ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2138,7 +2141,7 @@ "y": 0, "z": 0 }, - "_id": "62wF9JzidKZaQB+QgAZh3f" + "_id": "" }, { "__type__": "cc.UITransform", @@ -2162,7 +2165,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "3fPUohOw5L8J35Iqd6TSW+" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2207,7 +2210,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "72la0KbCVDzZa9wEOrHuJN" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2243,7 +2246,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "19L36fWjVI7ZcWHi+j3S+0" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2313,7 +2316,7 @@ "y": 0, "z": 0 }, - "_id": "31NUUmgnNDXqXHJEy9e4Mp" + "_id": "" }, { "__type__": "cc.UITransform", @@ -2337,7 +2340,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "4d2lX7kiVBeJ+CgpXKwztk" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2382,7 +2385,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "dasR3WOMJN6pwuUh4RGcuI" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2418,7 +2421,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "baO9WLid1APLlFVd/k5dG8" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2488,7 +2491,7 @@ "y": 0, "z": 0 }, - "_id": "70vHlSs4NFeqdqH15EJ0so" + "_id": "" }, { "__type__": "cc.UITransform", @@ -2512,7 +2515,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "cbwdGyC+hPG75NBnKs/Jep" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2557,7 +2560,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "38VhFZLt9OZr5d5ZbFa6PG" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2593,7 +2596,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "a7FXh0c2dNQrhUDmXf6WvH" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2663,7 +2666,7 @@ "y": 0, "z": 0 }, - "_id": "95VStiTktL/a3m8mN0eY+J" + "_id": "" }, { "__type__": "cc.UITransform", @@ -2687,7 +2690,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "e0tCsXeYBIlqnTh3cq3TeY" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2732,7 +2735,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "90PJXf4WJAcYU6bQmK/Emj" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2768,7 +2771,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "7cG9TDPVVDmqi7cyx4pEht" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2809,7 +2812,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "e5pU3pIMlPFIpj7ZsZeeii" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2827,7 +2830,7 @@ "__prefab": { "__id__": 126 }, - "_alignFlags": 45, + "_alignFlags": 18, "_target": null, "_left": 0, "_right": 0, @@ -2843,9 +2846,9 @@ "_isAbsVerticalCenter": true, "_originalWidth": 1600, "_originalHeight": 720, - "_alignMode": 2, + "_alignMode": 1, "_lockFlags": 0, - "_id": "9bVnexdDdJeYOXtk4J1K+Q" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -2982,7 +2985,7 @@ "y": 0, "z": 0 }, - "_id": "8dgbqBz3dD3Lha2yc4c9qD" + "_id": "" }, { "__type__": "cc.Node", @@ -3035,7 +3038,7 @@ "y": 0, "z": 0 }, - "_id": "39RXBuBihLQKlCruhksgFN" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3059,7 +3062,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "4aV0pJ4XhOtKdicKV5uNN1" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3104,7 +3107,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "23dd90UtdJVKD+5SYjGCda" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3140,7 +3143,7 @@ "_originalHeight": 720, "_alignMode": 2, "_lockFlags": 0, - "_id": "d77ZF1XEZCA6h9JpbBWZGn" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3210,7 +3213,7 @@ "y": 0, "z": 0 }, - "_id": "8fuVQGqBBMxqrCMcK2ZoRM" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3234,7 +3237,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "89nPGxAI5AlKpyNJUnFe3f" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3279,7 +3282,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "99OFhaoRtBzYuHM8iERlKM" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3315,7 +3318,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "2dhUBG3yVMbp689pTvfeNq" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3385,7 +3388,7 @@ "y": 0, "z": 0 }, - "_id": "16nothxgdO2IrdT2HABlbh" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3409,7 +3412,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "21SRUm5UhHx7lZgk1R4JX+" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3454,7 +3457,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "3f4j083+lOgqs5W8LnaNc7" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3490,7 +3493,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "d9B422Ly1KNJpeMROXwDBv" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3560,7 +3563,7 @@ "y": 0, "z": 0 }, - "_id": "8bW8+AD1ZDAb5C4QnHpGw0" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3584,7 +3587,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "11NbKrGv9MaILj5vMbhHHU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3629,7 +3632,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "67MkGpQ0hF574JLahXOgBU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3665,7 +3668,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "05XE9oJg5CeIWIr+MDJG8s" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3735,7 +3738,7 @@ "y": 0, "z": 0 }, - "_id": "84f/m9axpNALfUMiZ3IGWS" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3759,7 +3762,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "37LlmtIrlHXoxsmNr2iaSX" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3804,7 +3807,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "40G+/3joBC86mEnn2z5yxf" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3840,7 +3843,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "093oOLNHRIcpWhGSOz0vWe" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3910,7 +3913,7 @@ "y": 0, "z": 0 }, - "_id": "e9rkaNax1HD7cNqTXQDZ2i" + "_id": "" }, { "__type__": "cc.UITransform", @@ -3934,7 +3937,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "2dI383bndIYpmcieOf3S1U" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -3979,7 +3982,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "5eoE1afCJA5JPI/ISgbZBF" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4015,7 +4018,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "442t4nVztHhprHagKBPaK2" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4085,7 +4088,7 @@ "y": 0, "z": 0 }, - "_id": "97Wg7UCppAnraeyh7YB5W5" + "_id": "" }, { "__type__": "cc.UITransform", @@ -4109,7 +4112,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "34oKLLk0FFK5qpk3M8s9xD" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4154,7 +4157,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "99r4pIyJZHQZpaenz0RVLQ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4190,7 +4193,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "d9j7DamKpJHrPPRLsAQRMn" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4260,7 +4263,7 @@ "y": 0, "z": 0 }, - "_id": "29Z/fwQuhJ9Z6voO/ExPK0" + "_id": "" }, { "__type__": "cc.UITransform", @@ -4284,7 +4287,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "78kEqA0IlGCqDFMrKrOenF" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4329,7 +4332,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "621ebtLyFG95Brdbg4Q0yx" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4365,7 +4368,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "afW0gxqslL9qSFWlYZYamZ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4435,7 +4438,7 @@ "y": 0, "z": 0 }, - "_id": "c5i4asRitEfYkh77k7J6Dp" + "_id": "" }, { "__type__": "cc.UITransform", @@ -4459,7 +4462,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "77BvNPgD9OI68JL0IcyrQz" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4504,7 +4507,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "10pK+msrFIRZwcGMg2r80E" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4540,7 +4543,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "95R1f5CNZI7Y1Z1bXO1A24" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4610,7 +4613,7 @@ "y": 0, "z": 0 }, - "_id": "afAceb/oZNRbc7BDg/DeVk" + "_id": "" }, { "__type__": "cc.UITransform", @@ -4626,7 +4629,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 63.041015625, + "width": 63.005419342885375, "height": 17.64 }, "_anchorPoint": { @@ -4634,7 +4637,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "84qBz2a21C76+LwY2n2VhL" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4665,7 +4668,7 @@ "_string": "Spirit3209", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -4702,7 +4705,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "acmPhlelVMTqP/8hCNh3cf" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4738,7 +4741,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "1cOcNVOs5Kd5olEnJpshPw" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4808,7 +4811,7 @@ "y": 0, "z": 0 }, - "_id": "94vSoOVH5EJIu6jxU6o2BH" + "_id": "" }, { "__type__": "cc.UITransform", @@ -4824,7 +4827,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 7, + "width": 6.996047430830039, "height": 17.64 }, "_anchorPoint": { @@ -4832,7 +4835,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "51UEIa/tpBzJaYGdGHGujH" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4863,7 +4866,7 @@ "_string": "s", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -4900,7 +4903,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "08NgyiXqhIJKBCZ0KqErxl" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -4936,7 +4939,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "3b745UjWJPxY6OlRHIxdtU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5006,7 +5009,7 @@ "y": 0, "z": 0 }, - "_id": "26CgRNnK1C2rGI6SNZoz+4" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5030,7 +5033,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "14PIh8hSpPE4Q4fejcigiL" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5075,7 +5078,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "99SRtZ48NJn4mx4B+Icki8" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5111,7 +5114,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "b1+ejEWN1MJ4ub10FjIsXt" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5181,7 +5184,7 @@ "y": 0, "z": 0 }, - "_id": "dcNTDteZNGB7PvbzGt3T1t" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5205,7 +5208,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "e278RSXK1Jf5Lv63tdyNOH" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5250,7 +5253,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "19C5kbiq9HLLyOzabILujW" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5286,7 +5289,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "d9ttEdRO5F3LdCNsiZJFeK" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5356,7 +5359,7 @@ "y": 0, "z": 0 }, - "_id": "892QkGYKBMsYXwMvT78MO+" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5372,7 +5375,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 7, + "width": 6.996047430830039, "height": 17.64 }, "_anchorPoint": { @@ -5380,7 +5383,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "b7cEkjYgpLkLv2aLCHfsUK" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5411,7 +5414,7 @@ "_string": "s", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -5448,7 +5451,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "194sR/MxhD6LZqSMoFFB47" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5484,7 +5487,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "57WOIdUsRICpIXSeh8e8ZJ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5554,7 +5557,7 @@ "y": 0, "z": 0 }, - "_id": "8fnWiuky5CA4BZgFepro2L" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5570,15 +5573,15 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 100, - "height": 25.2 + "width": 99.98412603446145, + "height": 25.200000000000003 }, "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 }, - "_id": "2dEGd+IX9CFYfweu/yHGnu" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5609,7 +5612,7 @@ "_string": "白名单管理", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 20, + "_actualFontSize": 21.083333333333336, "_fontSize": 20, "_fontFamily": "Arial", "_lineHeight": 20, @@ -5646,7 +5649,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "05A3g4cFhLzLWjAbxWIlrO" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5682,7 +5685,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "08g653mOpAAoGkGS3mTlIB" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5752,7 +5755,7 @@ "y": 0, "z": 0 }, - "_id": "ccD/pDBLVKFqA/nBVMRMa6" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5768,7 +5771,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 42, + "width": 41.976284584980235, "height": 17.64 }, "_anchorPoint": { @@ -5776,7 +5779,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "6dxhQYkXlL+6xnBkBpV4Cb" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5807,7 +5810,7 @@ "_string": "玩家ID", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -5844,7 +5847,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "274PHLgUZKE4ZrBBgWak6I" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5880,7 +5883,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "d3QuwIs1lJQ4ut/HiGIv+I" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -5950,7 +5953,7 @@ "y": 0, "z": 0 }, - "_id": "a27a2oVvtI17jSXe9eGulB" + "_id": "" }, { "__type__": "cc.UITransform", @@ -5966,7 +5969,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 42, + "width": 41.976284584980235, "height": 17.64 }, "_anchorPoint": { @@ -5974,7 +5977,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "424VxEYSJHz5Kv2+kbpWeU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6005,7 +6008,7 @@ "_string": "魅力值", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -6042,7 +6045,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "04nx3Zd21KKLIz/rrl58N3" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6078,7 +6081,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "3fS2iYXtlNGra4VtnjMdDB" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6148,7 +6151,7 @@ "y": 0, "z": 0 }, - "_id": "b0UpjXiNtAzbz4EvLZ9NX8" + "_id": "" }, { "__type__": "cc.UITransform", @@ -6172,7 +6175,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "95Fu/mZ2FOzL8ObtiGwrw6" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6217,7 +6220,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "b7pEF4K+hBs4bQZuoQzTx8" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6253,7 +6256,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "2ecQOFjp9NN4PFR5nTgze3" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6323,7 +6326,7 @@ "y": 0, "z": 0 }, - "_id": "02e8BvZj5PHZYoEQL1+d0O" + "_id": "" }, { "__type__": "cc.UITransform", @@ -6339,7 +6342,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 7, + "width": 6.996047430830039, "height": 17.64 }, "_anchorPoint": { @@ -6347,7 +6350,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "21ImE9y7xPUoWnJ6BRJAjK" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6378,7 +6381,7 @@ "_string": "s", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -6415,7 +6418,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "79BlKSnqFCgqfLFUzx0iCi" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6451,7 +6454,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "27UDfiRrVEq6x3U0Xgnejd" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6521,7 +6524,7 @@ "y": 0, "z": 0 }, - "_id": "e9eUCPLzlH55cgnH9pVFu7" + "_id": "" }, { "__type__": "cc.UITransform", @@ -6545,7 +6548,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "77My4sdJ1LLL+VpzpW5ZPt" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6590,7 +6593,7 @@ "_isTrimmedMode": true, "_useGrayscale": false, "_atlas": null, - "_id": "2cKvhycypD9Ze4vE0e7MeI" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6626,7 +6629,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "61T8CQiGtDDpyBOA/XlBdP" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6696,7 +6699,7 @@ "y": 0, "z": 0 }, - "_id": "59N94sZO1FTaqAY8PfPFzl" + "_id": "" }, { "__type__": "cc.UITransform", @@ -6712,7 +6715,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 56, + "width": 55.96837944664031, "height": 17.64 }, "_anchorPoint": { @@ -6720,7 +6723,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "23D+A+CzNIM7TDjONY3XsZ" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6751,7 +6754,7 @@ "_string": "搜索玩家", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -6788,7 +6791,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "f3dIFarwZACad3UFoZll2n" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6824,7 +6827,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "c01kZCw5lJsJ6iHA0opZON" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6894,7 +6897,7 @@ "y": 0, "z": 0 }, - "_id": "57cb+FJA5HebHvk1S2jOnA" + "_id": "" }, { "__type__": "cc.UITransform", @@ -6910,15 +6913,15 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 51.333984375, - "height": 15.120000000000001 + "width": 51.3339583204669, + "height": 15.12 }, "_anchorPoint": { "__type__": "cc.Vec2", "x": 0.5, "y": 0.5 }, - "_id": "6aEpOIMXlCZYJ8VxAsRyw2" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -6949,7 +6952,7 @@ "_string": "添加/修改", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 12, + "_actualFontSize": 12.65, "_fontSize": 12, "_fontFamily": "Arial", "_lineHeight": 12, @@ -6986,7 +6989,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "1b3qYF4B1Nz5oQxofgqsXo" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7022,7 +7025,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "feO6lB3GtCypo5xj1Z8yUG" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7092,7 +7095,7 @@ "y": 0, "z": 0 }, - "_id": "54PFQON6VKKp4DmBXUUG44" + "_id": "" }, { "__type__": "cc.UITransform", @@ -7108,7 +7111,7 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 63.041015625, + "width": 63.005419342885375, "height": 17.64 }, "_anchorPoint": { @@ -7116,7 +7119,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "47Vnj6mrxCGoG1Tj0WqePA" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7147,7 +7150,7 @@ "_string": "Spirit3209", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 14, + "_actualFontSize": 14.758333333333333, "_fontSize": 14, "_fontFamily": "Arial", "_lineHeight": 14, @@ -7184,7 +7187,7 @@ "y": 2 }, "_shadowBlur": 2, - "_id": "09qyMH43NANo0GL+WNXlrU" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7220,7 +7223,7 @@ "_originalHeight": 0, "_alignMode": 2, "_lockFlags": 0, - "_id": "ffm/CJXWpNLrcS5tBt4vzl" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7261,7 +7264,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "c0qS436yxH4J5vT4vqSmYa" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7297,7 +7300,7 @@ "_originalHeight": 720, "_alignMode": 2, "_lockFlags": 0, - "_id": "5c7V/F3wtMa61Sm7CVUwHb" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7338,7 +7341,7 @@ "x": 0.5, "y": 0.5 }, - "_id": "31h+R9KaVECIUc5kPB++8q" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", @@ -7358,10 +7361,10 @@ }, "_alignFlags": 45, "_target": null, - "_left": -800, - "_right": 800, - "_top": 360, - "_bottom": -360, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, "_horizontalCenter": 0, "_verticalCenter": 0, "_isAbsLeft": true, @@ -7374,12 +7377,38 @@ "_originalHeight": 720, "_alignMode": 2, "_lockFlags": 0, - "_id": "13F6VuzFNFIp0QsSsuMSm/" + "_id": "" }, { "__type__": "cc.CompPrefabInfo", "fileId": "HluBcjZ7/jMY07j0jdhNkw" }, + { + "__type__": "1f765tAwURK4rGwe2upn+F6", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 323 + }, + "loginTitle": { + "__uuid__": "2896a7a8-b0d7-43a2-a6fb-a98624d63ea7@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "codeWaiting": { + "__uuid__": "1bf097b6-9c86-4afd-8740-94ae426fa0f8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "7dTa1uKnlGk5UDlFQf8K0X" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -7390,7 +7419,6 @@ }, "fileId": "HUPiVAu02owbfAK2LmJ9yg", "instance": null, - "targetOverrides": null, - "nestedPrefabInstanceRoots": null + "targetOverrides": null } -] +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/resources.meta b/cocoscreator_projects/YouleNexus/assets/resources.meta new file mode 100644 index 0000000..49f6aa2 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/resources.meta @@ -0,0 +1,14 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "1830368b-b039-44a1-8023-ede4c94e9dd3", + "files": [], + "subMetas": {}, + "userData": { + "isBundle": true, + "bundleConfigID": "default", + "bundleName": "resources", + "priority": 8 + } +} diff --git a/cocoscreator_projects/YouleNexus/assets/resources/dev-config.meta b/cocoscreator_projects/YouleNexus/assets/resources/dev-config.meta new file mode 100644 index 0000000..0bc269c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/resources/dev-config.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "2d251f16-3748-411c-88b6-6ef547bc63a5", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt b/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt new file mode 100644 index 0000000..df73461 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt @@ -0,0 +1,1500 @@ +{ + "showmessage": "", + "visitor_server_tcp": "115.159.146.131:51088", + "visitor_server_http": "115.159.146.131:51089", + "isclose": 1, + "scrollmsg": "官方郑重声明:近期有不法之徒以片面的游戏得分数据欺骗玩家购买外挂,已有玩家上当受骗,在此官方郑重声明,本游戏绝无外挂,凡自称有外挂的都是骗子,切勿上当受骗!", + "noticemsg": "", + "menunotice": { + "advert0": { + "type": 0, + "imgUrl": "http://tsgames.daoqi88.cn/image/adver0_youle2.png", + "imgAdUrl": "", + "height": 470, + "msg": "", + "url": "" + }, + "advert1": { + "msg": "", + "url": "" + }, + "advert2": { + "msg": "", + "url": "" + } + }, + "Ads": "本游戏仅为娱乐休闲使用,房卡所有#游戏通用", + "roomCardName": "房卡", + "nameImgFrame_1": 1, + "starName": "金币", + "nameImgFrame_2": 2, + "shieldShowRebate": 1, + "WechatPublicNo_ewm": "", + "behaviorList": [ + [ + 3233, + "rankList" + ], + [ + 604, + "rankList" + ], + [ + 76, + "ewm" + ], + [ + 3232, + "vipRank" + ], + [ + 3231, + "vipRank" + ], + [ + 679, + "match" + ], + [ + 1719, + "match" + ] + ], + "managerUrl": "http://dlweb.daoqi88.cn/white_list.html", + "service_qq": "", + "rankList": 0, + "wareHouse": 0, + "betaText": "Beta", + "ramSeat": 1, + "payCSS": "", + "payJS": "", + "cdKey": 2, + "agentlist": [ + { + "agentid": "veRa0qrBf0df2K1G4de2tgfmVxB2jxpv", + "agentname": "进贤", + "scrollmsg": "聚友棋牌仅限娱乐,禁止赌博!一经发现立即封号。客服号改为:jyqp181999", + "showmessage": "", + "xxroomdeduct": 1, + "audio_domain":"gameaudio.daoqi88.cn", + "audio_bucket":"gameaudio", + "html_buyroomcard": 2, + "html_applysales": 2, + "sale_wechat": "jyqp181999", + "agentmode": 2, + "WechatPublicNo_ewm": "http://tsgames.daoqi88.cn/image_ewm/ewm_jinxian2.png", + "service_wechat": "jyqp181999", + "service_tel": "", + "url_topup": "http://dlwebv3/wan_purchase_new.html", + "url_download": "http://sharegame/gameshare.html", + "logimage2": "http://op0g0jeqa.bkt.clouddn.com/log_jinxian.png", + "h5shareurl": "http://h5game1/gameLobby920/index2.html", + "game_share_image": "http://tsgames.daoqi88.cn/image_download/download_jinxian.jpg", + "shareh5title": "进贤聚友棋牌", + "shareh5desc": "一起来嗨~", + "gameNoticeShow": 0, + "shareType":1, + "shareImgUrl":"https://tsgames.daoqi88.cn/image/match.png", + "hallNoticeShow": 0, + "gameHallConfig": { + "topImgUrl": "http://tsgames.daoqi88.cn/image_xgamehall/gamehalllogo.png" + }, + "menunotice": { + "advert0": { + "type": 0, + "imgUrl": "http://tsgames.daoqi88.cn/image/adver0_jinxian2.png", + "imgAdUrl": "", + "height": 470, + "msg": "进贤本地玩法8年老平台##绿色平台,仅供娱乐!绝无外挂!##客服微信:jyqp181999", + "url": "" + } + }, + "rebateConfig": { + "from": 3, + "value": 0, + "rebateCharm": 1 + }, + "gamelist": [{ + "gameid": "G2hw0ubng0zcoI0r4mx3H2yr4GejidwO", + "gamename": "进贤聚友", + "game_server_tcp": "8.139.255.236:56072", + "game_server_http": "8.139.255.236:56073", + "game_version": 261, + "game_zip": "http://tsgames.daoqi88.cn/zip2/gamehall.zip", + "game_size": "4.61M", + "game_hall_dir": "gamehall", + "game_down_image": "http://tsgames.daoqi88.cn/image_download/download_jinxian.jpg", + "game_down_memo": "进贤聚友,让欢乐无限分享", + "hideSpid": [ + 478 + ], + + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 1, + "marketname": "App Store", + "app_version": 1, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 44, + "app_download": "itms-services://?action=download-manifest&url=https://daoqi.daoqi888.cn/plist/gamehall_jinxianv2.plist", + "app_size": "2.61M", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 50, + "app_download": "https://daoqi.daoqi888.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 2, + "app_download": "https://daoqi.daoqi888.cn/harmonyos/gamehall_jinxian_harmonyosv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "a52y0ajd106pKU1KIvF1zRii8h1gRem5", + "gamename": "三个老K", + "sharename": "【三个老K】", + "category": 1, + "game_server_tcp": "8.139.255.236:56084", + "game_server_http": "8.139.255.236:56085", + "game_version": 155, + "game_zip": "http://tsgames.daoqi88.cn/zip2/sangelaok.zip", + "game_size": "9.07M", + "game_hall_dir": "sangelaok", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/3glaoK.png", + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "rZDp0wfvC0ZaPT5iLqh2itGFsnJaDvzc", + "gamename": "进贤麻将", + "category": 2, + "game_server_tcp": "8.139.255.236:56056", + "game_server_http": "8.139.255.236:56057", + "sharename": "【进贤麻将】", + "game_version": 41, + "game_zip": "http://tsgames.daoqi88.cn/zip2/majiang_jx.zip", + "game_size": "10.59M", + "game_hall_dir": "majiang_jx", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/mj_jx.png", + "haverobot": 1, + "game_config": { + "test": 1, + "asetcount": [ + 8, + 16 + ], + "needroomcard": [ + [ + 1, + 3, + 3, + 3 + ], + [ + 2, + 6, + 6, + 6 + ] + ], + "needroomcard_joinroom": [ + [ + 1, + 3, + 3, + 3 + ], + [ + 2, + 6, + 6, + 6 + ] + ], + "Time_Quick": 70, + "Time_playcard": 60, + "Time_initial": 160 + }, + "system_room": [{ + "type": 2, + "title": "", + "roomtype": [ + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, [ + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + 0, + 2, + 1, [ + 1, + 2, + 400 + ], + 0, + 2 + ], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_1.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [ + 1, + 1, + 1, + 0, + 1, + 0, + 3, + 1, + 0, + 0, + 0, + 0, [ + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + 0, + 2, + 1, [ + 1, + 2, + 400 + ], + 0, + 2 + ], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_2.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [ + 1, + 1, + 1, + 0, + 1, + 0, + 3, + 1, + 0, + 0, + 0, + 0, [ + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + 0, + 2, + 1, [ + 2, + 100, + 20000 + ], + 0, + 2 + ], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_3.png", + "removeRobot": 1 + }, + { + "type": 2, + "title": "", + "roomtype": [ + 1, + 1, + 1, + 0, + 1, + 0, + 3, + 1, + 0, + 0, + 0, + 0, [ + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + 0, + 2, + 1, [ + 3, + 1000, + 200000 + ], + 0, + 2 + ], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_4.png", + "removeRobot": 1 + } + ], + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "sJgp0FArn0olVc3yitp3xThV036fG7n2", + "gamename": "跑得快", + "sharename": "【跑得快】", + "category": 1, + "game_server_tcp": "8.149.128.168:56024", + "game_server_http": "8.149.128.168:56025", + "game_version": 54, + "game_zip": "http://tsgames.daoqi88.cn/zip2/pdk_card_client-jinxian.zip", + "game_size": "9.8M", + "game_hall_dir": "pdk_card_client-jinxian", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/paodk-jx.png", + "haverobot": 1, + "game_config": { + "test": 1, + "star_beishu": [ + 1, + 10 + ], + "star_xianzhi": [ + 100, + 1000 + ], + "star_chashuifei": 300, + "needroomcard": [ + [ + 1, + 4, + 3, + 2 + ], + [ + 2, + 8, + 6, + 4 + ] + ], + "needroomcard_joinroom": [ + [ + 1, + 4, + 3, + 2 + ], + [ + 2, + 8, + 6, + 4 + ] + ], + "asetcount": [ + 8, + 16 + ], + "Time_Quick": 70, + "Time_playcard": 60, + "Time_initial": 160, + "paytype": "房卡", + "horse": "买马", + "horse_mun": [ + 1, + 2, + 3, + 4 + ], + "move_x": 20 + }, + "system_room": [{ + "type": 2, + "title": "4人初级场 10张", + "roomtype": [ + 1, + 1, + 1, + 2, + 1, -1, + 1, -1, -1, -1, -1, + 0, [ + [ + [ + [ + 2, + 3 + ], + [ + 1, + 5 + ] + ], + [ + [ + 3, + 4 + ], + [ + 3, + 4 + ] + ], + [ + [ + 1, + 2 + ], + [ + 1, + 4 + ] + ] + ], + [ + 8, + 1, + 1, + 0 + ], + 1, + 1, [ + 2, + 3 + ] + ], + 0, + 2, + 1, [ + 1, + 2, + 400 + ], + 0, + 2 + ], + "removeRobot": 1 + }, + { + "type": 2, + "title": "4人中级场 10张", + "roomtype": [ + 1, + 1, + 1, + 2, + 1, -1, + 1, -1, -1, -1, -1, + 0, [ + [ + [ + [ + 2, + 3 + ], + [ + 1, + 5 + ] + ], + [ + [ + 3, + 4 + ], + [ + 3, + 4 + ] + ], + [ + [ + 1, + 2 + ], + [ + 1, + 4 + ] + ] + ], + [ + 8, + 1, + 1, + 0 + ], + 1, + 1, [ + 2, + 3 + ] + ], + 0, + 2, + 1, [ + 2, + 100, + 20000 + ], + 0, + 2 + ], + "removeRobot": 1 + }, + { + "type": 2, + "title": "4人高级场 10张", + "roomtype": [ + 1, + 1, + 1, + 2, + 1, -1, + 1, -1, -1, -1, -1, + 0, [ + [ + [ + [ + 2, + 3 + ], + [ + 1, + 5 + ] + ], + [ + [ + 3, + 4 + ], + [ + 3, + 4 + ] + ], + [ + [ + 1, + 2 + ], + [ + 1, + 4 + ] + ] + ], + [ + 8, + 1, + 1, + 0 + ], + 1, + 1, [ + 2, + 3 + ] + ], + 0, + 2, + 1, [ + 3, + 1000, + 200000 + ], + 0, + 2 + ], + "removeRobot": 1 + } + ], + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "6ujc0IfoL0t1zS7J2dZ1eFh2u2t9682b", + "gamename": "找朋友", + "isdebugger": 0, + "category": 1, + "sharename": "【找朋友】", + "game_server_tcp": "8.149.128.168:56010", + "game_server_http": "8.149.128.168:56011", + "game_version": 7, + "game_zip": "http://tsgames.daoqi88.cn/zip2/zpy.zip", + "game_size": "11.16M", + "game_hall_dir": "zpy", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/zhaopy.png", + "game_config": { + "buckle": [ + 2, + 4 + ], + "personal": [ + 1, + 2 + ], + "numbergames": [ + 4, + 8 + ], + "mode":1, + "direction": 0, + "jiesuan": 1, + "touxiang": 0 + }, + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "sJgp0FArn0olVc8yitp0xThV036fG7n0", + "gamename": "关牌", + "sharename": "【关牌】", + "category": 1, + "game_server_tcp": "8.149.128.168:56016", + "game_server_http": "8.149.128.168:56017", + "isdebugger": 0, + "game_version": 54, + "game_zip": "http://tsgames.daoqi88.cn/zip2/guanpai-jx.zip", + "game_size": "11.0M", + "game_hall_dir": "guanpai-jx", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/guanpai.png", + "game_config": { + "buckle": [ + 1, + 2 + ], + "personal": [ + 1, + 2 + ], + "numbergames": [ + 6, + 12 + ], + "tiaoshi": 0, + "people": [ + 2, + 3 + ], + "difen": 10, + "zha": [ + 5, + 10, + 20 + ], + "zidongchu": 0, + "beishu": [ + 1, + 100 + ], + "xianzhi": [ + 1000, + 50000 + ], + "chxz": [ + 0, + 100 + ] + }, + "system_room": [{ + "type": 2, + "title": "练习场", + "roomtype": [ + 1, + 2, + 1, + 2, + 1, + 2, [ + 1, + 200 + ] + ] + }, + { + "type": 2, + "title": "初级场", + "roomtype": [ + 1, + 2, + 1, + 2, + 1, + 2, [ + 10, + 2000 + ] + ] + }, + { + "type": 2, + "title": "中级场", + "roomtype": [ + 1, + 2, + 1, + 2, + 1, + 2, [ + 20, + 4000 + ] + ] + }, + { + "type": 2, + "title": "高级场", + "roomtype": [ + 1, + 2, + 1, + 2, + 1, + 2, [ + 50, + 10000 + ] + ] + } + ], + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "FDtj0pGhT0Evjd3efAS5wkIlamiTh2oa", + "gamename": "斗地主", + "sharename": "【斗地主】", + "category": 1, + "game_server_tcp": "8.149.128.168:56008", + "game_server_http": "8.149.128.168:56009", + "isdebugger": 0, + "game_version": 30, + "game_zip": "http://tsgames.daoqi88.cn/zip2/doudizhu.zip", + "game_size": "13.3M", + "game_hall_dir": "doudizhu", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/doudizhu.png", + "game_config": { + "jushu": [ + 6, + 12 + ], + "kouka": [ + [ + 1, + 2 + ], + [ + 2, + 4 + ] + ], + "dizhu": [ + 1, + 5, + 10 + ], + "gold": [ + 1, + 50, + 1, + 1000 + ], + "tg_time": [ + 700, + 700, + 700, + 2500 + ], + "process": [ + [ + 1, + 50, + 0 + ], + [ + 2, + 15000, + 0 + ], + [ + 3, + 5000, -1 + ] + ], + "moshi": [ + 0, + 1, + 0 + ], + "renshu": [ + 2, + 3, + 4 + ], + "paixing": [ + [ + 1, [ + 1, + 1 + ] + ], + [ + 1, [ + 2, + 1 + ] + ], + [ + 1, [ + 1, + 5 + ] + ], + [ + 1, [ + 3, + 1 + ] + ], + [ + 1, [ + 3, + 1 + ] + ], + [ + 1, [ + 3, + 1 + ] + ], + [ + 0, [ + 3, + 1 + ] + ], + [ + 1, [ + 4, + 1 + ] + ], + [ + 1, [ + 4, + 1 + ] + ], + [ + 1, [ + 2, + 3 + ] + ], + [ + 1, [ + 3, + 2 + ] + ], + [ + 2, [ + 4, + 1 + ] + ], + [ + 3, [ + 1, + 2 + ] + ], + [ + 0, [ + 5, + 1 + ] + ] + ], + "playtime": [ + 15, + 5, + 15 + ], + "card_len": [ + [ + 17, + 13 + ], + [ + 20, + 14 + ] + ], + "del_cards": [ + [ + 3, [ + 1, + 2, + 3, + 4 + ] + ], + [ + 4, [ + 1, + 2, + 3, + 4 + ] + ], + [ + 5, [ + 1, + 2, + 3, + 4 + ] + ] + ], + "dipai": [ + 1, + 3, + 14 + ] + }, + "channellist": [{ + "channelid": "FtJf073aa0d6rI1xD8J1Y42fINTm0ziK", + "channelname": "进贤", + "game_version": 0, + "game_zip": "", + "game_size": "", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":true, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + } + ] + }, + { + "agentid": "1B2h0ccl205c390Y28m1Ajdplkuu4wgy", + "agentname": "测试", + "scrollmsg": "聚友棋牌仅限娱乐,禁止赌博!一经发现立即封号。客服号改为:jyqp181999", + "showmessage": "", + "xxroomdeduct": 1, + "audio_domain":"gameaudio.daoqi88.cn", + "audio_bucket":"gameaudio", + "html_buyroomcard": 2, + "html_applysales": 2, + "sale_wechat": "jyqp181999", + "agentmode": 2, + "WechatPublicNo_ewm": "http://tsgames.daoqi88.cn/image_ewm/ewm_jinxian2.png", + "service_wechat": "jyqp181999", + "service_tel": "", + "url_topup": "http://dlwebv3/wan_purchase_new.html", + "url_download": "http://sharegame/gameshare.html", + "logimage2": "http://op0g0jeqa.bkt.clouddn.com/log_jinxian.png", + "h5shareurl": "http://h5game1/gameLobby920/index2.html", + "game_share_image": "http://tsgames.daoqi88.cn/image_download/download_jinxian.jpg", + "shareh5title": "进贤聚友棋牌", + "shareh5desc": "一起来嗨~", + "gameNoticeShow": 0, + "shareType":1, + "showVisitor":1, + "shareImgUrl":"https://tsgames.daoqi88.cn/image/match.png", + "hallNoticeShow": 0, + "gameHallConfig": { + "topImgUrl": "http://tsgames.daoqi88.cn/image_xgamehall/gamehalllogo.png" + }, + "menunotice": { + "advert0": { + "type": 0, + "imgUrl": "http://tsgames.daoqi88.cn/image/adver0_jinxian2.png", + "imgAdUrl": "", + "height": 470, + "msg": " 【测试专用】 ##进贤本地玩法8年老平台##绿色平台,仅供娱乐!绝无外挂!##客服微信:jyqp181999", + "url": "" + } + }, + "rebateConfig": { + "from": 3, + "value": 0, + "rebateCharm": 1 + }, + "gamelist": [ + { + "gameid": "G2hw0ubng0zcoI0r4mx3H2yr4GejidwO", + "gamename": "进贤聚友", + "game_server_tcp": "8.139.255.236:56072", + "game_server_http": "8.139.255.236:56073", + "game_version": 261, + "game_zip": "http://tsgames.daoqi88.cn/zip2/gamehall.zip", + "game_size": "4.61M", + "game_hall_dir": "gamehall", + "game_down_image": "http://tsgames.daoqi88.cn/image_download/download_jinxian.jpg", + "game_down_memo": "进贤聚友,让欢乐无限分享", + "hideSpid": [ + 478 + ], + + "channellist": [{ + "channelid": "aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 1, + "marketname": "App Store", + "app_version": 1, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 45, + "app_download": "itms-services://?action=download-manifest&url=https://daoqi.daoqi888.cn/plist/gamehall_jinxianv2_test.plist", + "app_size": "2.61M", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 50, + "app_download": "https://daoqi.daoqi888.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":false, + "app_version": 2, + "app_download": "https://daoqi.daoqi888.cn/harmonyos/gamehall_jinxian_harmonyosv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "jzWe0kzmN0tAcZ5MLks3rFWcxNecogyu", + "gamename": "新进贤麻将", + "category": 2, + "game_server_tcp": "8.139.255.236:56030", + "game_server_http": "8.139.255.236:56031", + "sharename": "【新进贤麻将】", + "game_version": 7, + "game_zip": "http://tsgames.daoqi88.cn/zip2/jinxianmahjong.zip", + "game_size": "10.59M", + "game_hall_dir": "jinxianmahjong", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/mj_jx.png", + "haverobot": 1, + "game_config": { }, + "system_room": [{ + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_1.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_2.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_3.png", + "removeRobot": 1 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_4.png", + "removeRobot": 1 + } + ], + "channellist": [{ + "channelid": "aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":false, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + }, + { + "gameid": "J9r4022t80pxlb01zOK76DN1WD4QewFl", + "gamename": "二七王", + "category": 2, + "game_server_tcp": "8.139.255.236:56030", + "game_server_http": "8.139.255.236:56031", + "sharename": "【二七王】", + "game_version": 1, + "game_zip": "http://tsgames.daoqi88.cn/zip2/jinxianmahjong.zip", + "game_size": "10.59M", + "game_hall_dir": "jinxianmahjong", + "game_hall_image": "http://tsgames.daoqi88.cn/image_xxgamehall/mj_jx.png", + "haverobot": 1, + "game_config": { }, + "system_room": [{ + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_1.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_2.png", + "roomLeave": 1, + "removeRobot": 0 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_3.png", + "removeRobot": 1 + }, + { + "type": 2, + "title": "", + "roomtype": [], + "image": "http://tsgames.daoqi88.cn/image_system/mj_jx_4.png", + "removeRobot": 1 + } + ], + "channellist": [{ + "channelid": "aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG", + "channelname": "进贤", + "ios_defdownload_marketid": 2, + "and_defdownload_marketid": 3, + "game_version": 0, + "game_zip": "", + "game_size": "", + "marketlist": [{ + "marketid": 2, + "marketname": "苹果企业签", + "app_version": 0, + "app_download": "", + "app_size": "", + "game_version": 0, + "game_zip": "", + "game_size": "" + }, + { + "marketid": 3, + "marketname": "安卓", + "app_version": 0, + "app_download": "", + "app_size": "" + }, + { + "marketid": 4, + "marketname": "鸿蒙", + "deviceLogin":false, + "app_version": 1, + "app_download": "http://daoqi.daoqikj.cn/apk/gamehall_jinxianv2.apk", + "app_size": "6.61M" + } + ] + }] + } + ] + } + ] +} \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt.meta b/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt.meta new file mode 100644 index 0000000..918ea13 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.0.1", + "importer": "text", + "imported": true, + "uuid": "cf18eee8-2e2c-476f-85c7-7cf6bcb17aee", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene b/cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene deleted file mode 100644 index c591c27..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene +++ /dev/null @@ -1,1393 +0,0 @@ -[ - { - "__type__": "cc.SceneAsset", - "_name": "LocalPlatformLogin", - "_objFlags": 0, - "__editorExtras__": {}, - "_native": "", - "scene": { - "__id__": 1 - } - }, - { - "__type__": "cc.Scene", - "_name": "LocalPlatformLogin", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": null, - "_children": [ - { - "__id__": 2 - } - ], - "_active": true, - "_components": [], - "_prefab": { - "__id__": 36 - }, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 1073741824, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "autoReleaseAssets": false, - "_globals": { - "__id__": 37 - }, - "_id": "67e69aac-9982-4e78-a44c-1d9c42f016ae" - }, - { - "__type__": "cc.Node", - "_name": "Canvas", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 1 - }, - "_children": [ - { - "__id__": 3 - }, - { - "__id__": 5 - }, - { - "__id__": 7 - }, - { - "__id__": 17 - }, - { - "__id__": 19 - }, - { - "__id__": 24 - }, - { - "__id__": 29 - } - ], - "_active": true, - "_components": [ - { - "__id__": 34 - }, - { - "__id__": 35 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 800, - "y": 360, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "4fmPDGIcdPtKTxMtiB6nco" - }, - { - "__type__": "cc.Node", - "_name": "Camera", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [], - "_active": true, - "_components": [ - { - "__id__": 4 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 1000 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 1073741824, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "56FfrqrBlPOJEcpxhwztIB" - }, - { - "__type__": "cc.Camera", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 3 - }, - "_enabled": true, - "__prefab": null, - "_projection": 0, - "_priority": 0, - "_fov": 45, - "_fovAxis": 0, - "_orthoHeight": 360, - "_near": 0.1, - "_far": 2000, - "_color": { - "__type__": "cc.Color", - "r": 20, - "g": 29, - "b": 45, - "a": 255 - }, - "_depth": 1, - "_stencil": 0, - "_clearFlags": 7, - "_rect": { - "__type__": "cc.Rect", - "x": 0, - "y": 0, - "width": 1, - "height": 1 - }, - "_aperture": 19, - "_shutter": 7, - "_iso": 0, - "_screenScale": 1, - "_visibility": 1107296256, - "_targetTexture": null, - "_postProcess": null, - "_usePostProcess": false, - "_cameraType": -1, - "_trackingType": 0, - "_id": "5fJx8MoDNLDruFvKpvUqV3" - }, - { - "__type__": "cc.Node", - "_name": "Host", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [], - "_active": true, - "_components": [ - { - "__id__": 6 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 1073741824, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "273kGrcqlGnqgl7sJetcLH" - }, - { - "__type__": "25970YMrRZG8YntQr+ux0rZ", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 5 - }, - "_enabled": true, - "__prefab": null, - "loadingPrefab": { - "__uuid__": "3e5a41f3-b043-45a5-94f6-e299ea9acc61", - "__expectedType__": "cc.Prefab" - }, - "loginPrefab": { - "__uuid__": "b209cefb-b381-4c7c-b765-94a5ab75a89c", - "__expectedType__": "cc.Prefab" - }, - "loadingRoot": { - "__id__": 7 - }, - "loginRoot": { - "__id__": 17 - }, - "resultRoot": { - "__id__": 19 - }, - "statusOverlay": { - "__id__": 24 - }, - "statusLabel": { - "__id__": 27 - }, - "resultLabel": { - "__id__": 22 - }, - "stopButton": { - "__id__": 29 - }, - "_id": "94INvS4pdAnIe3wFZxabB7" - }, - { - "__type__": "cc.Node", - "_name": "LoadingRoot", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 8 - } - ], - "_active": true, - "_components": [ - { - "__id__": 16 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "40omEGvnFOKq2lXAwsqtP8" - }, - { - "__type__": "cc.Node", - "_objFlags": 0, - "_parent": { - "__id__": 7 - }, - "_prefab": { - "__id__": 9 - }, - "__editorExtras__": {} - }, - { - "__type__": "cc.PrefabInfo", - "root": { - "__id__": 8 - }, - "asset": { - "__uuid__": "3e5a41f3-b043-45a5-94f6-e299ea9acc61", - "__expectedType__": "cc.Prefab" - }, - "fileId": "D6NzGhQBg/6lYKr/OZsmJQ", - "instance": { - "__id__": 10 - }, - "targetOverrides": null, - "nestedPrefabInstanceRoots": null - }, - { - "__type__": "cc.PrefabInstance", - "fileId": "00Yyr3cfRNcZX1WLG7VLxz", - "prefabRootNode": null, - "mountedChildren": [], - "mountedComponents": [], - "propertyOverrides": [ - { - "__id__": 11 - }, - { - "__id__": 13 - }, - { - "__id__": 14 - }, - { - "__id__": 15 - } - ], - "removedComponents": [] - }, - { - "__type__": "CCPropertyOverrideInfo", - "targetInfo": { - "__id__": 12 - }, - "propertyPath": [ - "_name" - ], - "value": "Layer1_Logo" - }, - { - "__type__": "cc.TargetInfo", - "localID": [ - "D6NzGhQBg/6lYKr/OZsmJQ" - ] - }, - { - "__type__": "CCPropertyOverrideInfo", - "targetInfo": { - "__id__": 12 - }, - "propertyPath": [ - "_lpos" - ], - "value": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - } - }, - { - "__type__": "CCPropertyOverrideInfo", - "targetInfo": { - "__id__": 12 - }, - "propertyPath": [ - "_lrot" - ], - "value": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - } - }, - { - "__type__": "CCPropertyOverrideInfo", - "targetInfo": { - "__id__": 12 - }, - "propertyPath": [ - "_euler" - ], - "value": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - } - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 7 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1600, - "height": 720 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "12ZBzo5G9KBoOxPTXRKk/Q" - }, - { - "__type__": "cc.Node", - "_name": "LoginRoot", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [], - "_active": false, - "_components": [ - { - "__id__": 18 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "d6V5u3kY9ASbSWXectzlbS" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 17 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1600, - "height": 720 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "40Uk4k/ONMnJXSfDjQDunZ" - }, - { - "__type__": "cc.Node", - "_name": "ResultRoot", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 20 - } - ], - "_active": false, - "_components": [ - { - "__id__": 23 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "4e40f7pcFAipccXYiKt1LW" - }, - { - "__type__": "cc.Node", - "_name": "ResultLabel", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 19 - }, - "_children": [], - "_active": true, - "_components": [ - { - "__id__": 21 - }, - { - "__id__": 22 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "f9tsqXSTtDdqA0mcIgv5m8" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 20 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1200, - "height": 380 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "44DJyd4DhIlI4pbBdfv/QM" - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 20 - }, - "_enabled": true, - "__prefab": null, - "_customMaterial": null, - "_srcBlendFactor": 2, - "_dstBlendFactor": 4, - "_color": { - "__type__": "cc.Color", - "r": 245, - "g": 246, - "b": 250, - "a": 255 - }, - "_string": "登录结果", - "_horizontalAlign": 1, - "_verticalAlign": 1, - "_actualFontSize": 0, - "_fontSize": 36, - "_fontFamily": "Arial", - "_lineHeight": 48, - "_overflow": 0, - "_enableWrapText": true, - "_font": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_isItalic": false, - "_isBold": false, - "_isUnderline": false, - "_underlineHeight": 2, - "_cacheMode": 0, - "_enableOutline": false, - "_outlineColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_outlineWidth": 2, - "_enableShadow": false, - "_shadowColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_shadowOffset": { - "__type__": "cc.Vec2", - "x": 2, - "y": 2 - }, - "_shadowBlur": 2, - "_id": "01Oedc9UVL64Gj9z6SWzgN" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 19 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1600, - "height": 720 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "fcIBurW2VOWo3ZMkOtLxIu" - }, - { - "__type__": "cc.Node", - "_name": "StatusOverlay", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 25 - } - ], - "_active": true, - "_components": [ - { - "__id__": 28 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "a6kdhP/8ZLMLeFOXf1x03K" - }, - { - "__type__": "cc.Node", - "_name": "StatusLabel", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 24 - }, - "_children": [], - "_active": true, - "_components": [ - { - "__id__": 26 - }, - { - "__id__": 27 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": -265, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "13eN54GNpEHIFD+205kDRk" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 25 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 363.8892470046937, - "height": 47.879999999999995 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "b2FWK2DwBNLL7XlUTSI/Nw" - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 25 - }, - "_enabled": true, - "__prefab": null, - "_customMaterial": null, - "_srcBlendFactor": 2, - "_dstBlendFactor": 4, - "_color": { - "__type__": "cc.Color", - "r": 245, - "g": 246, - "b": 250, - "a": 255 - }, - "_string": "正在加载资源和连接本地服务…", - "_horizontalAlign": 1, - "_verticalAlign": 1, - "_actualFontSize": 27.408333333333335, - "_fontSize": 26, - "_fontFamily": "Arial", - "_lineHeight": 38, - "_overflow": 0, - "_enableWrapText": true, - "_font": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_isItalic": false, - "_isBold": false, - "_isUnderline": false, - "_underlineHeight": 2, - "_cacheMode": 0, - "_enableOutline": false, - "_outlineColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_outlineWidth": 2, - "_enableShadow": false, - "_shadowColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_shadowOffset": { - "__type__": "cc.Vec2", - "x": 2, - "y": 2 - }, - "_shadowBlur": 2, - "_id": "51RskzXn5Ata2xeyOVlNCY" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 24 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1600, - "height": 720 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "a2g6t0veZNVry/oIZkFXdJ" - }, - { - "__type__": "cc.Node", - "_name": "StopButton", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 30 - } - ], - "_active": true, - "_components": [ - { - "__id__": 33 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 665, - "y": 310, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "00faTTaCpK+6dsBhx902Te" - }, - { - "__type__": "cc.Node", - "_name": "StopLabel", - "_objFlags": 0, - "__editorExtras__": {}, - "_parent": { - "__id__": 29 - }, - "_children": [], - "_active": true, - "_components": [ - { - "__id__": 31 - }, - { - "__id__": 32 - } - ], - "_prefab": null, - "_lpos": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_lrot": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_lscale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "_mobility": 0, - "_layer": 33554432, - "_euler": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_id": "e4Tfs0ZLVBiaGDxp3kpgsI" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 30 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 103.96835628705533, - "height": 47.879999999999995 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "d0s+NCQGxLt5QFk5h0CZVa" - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 30 - }, - "_enabled": true, - "__prefab": null, - "_customMaterial": null, - "_srcBlendFactor": 2, - "_dstBlendFactor": 4, - "_color": { - "__type__": "cc.Color", - "r": 245, - "g": 246, - "b": 250, - "a": 255 - }, - "_string": "停止验证", - "_horizontalAlign": 1, - "_verticalAlign": 1, - "_actualFontSize": 27.408333333333335, - "_fontSize": 26, - "_fontFamily": "Arial", - "_lineHeight": 38, - "_overflow": 0, - "_enableWrapText": true, - "_font": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_isItalic": false, - "_isBold": false, - "_isUnderline": false, - "_underlineHeight": 2, - "_cacheMode": 0, - "_enableOutline": false, - "_outlineColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_outlineWidth": 2, - "_enableShadow": false, - "_shadowColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_shadowOffset": { - "__type__": "cc.Vec2", - "x": 2, - "y": 2 - }, - "_shadowBlur": 2, - "_id": "eewB3DGj9MzpyN67ez8juQ" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 29 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 220, - "height": 64 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "52C95shHpLdqYKR29izOA+" - }, - { - "__type__": "cc.UITransform", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 2 - }, - "_enabled": true, - "__prefab": null, - "_contentSize": { - "__type__": "cc.Size", - "width": 1600, - "height": 720 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_id": "ebjzNd6xRN6rCykuT0wir7" - }, - { - "__type__": "cc.Canvas", - "_name": "", - "_objFlags": 0, - "__editorExtras__": {}, - "node": { - "__id__": 2 - }, - "_enabled": true, - "__prefab": null, - "_cameraComponent": { - "__id__": 4 - }, - "_alignCanvasWithScreen": true, - "_id": "793QSuOMFHY6zVWL61HGbM" - }, - { - "__type__": "cc.PrefabInfo", - "root": null, - "asset": null, - "fileId": "67e69aac-9982-4e78-a44c-1d9c42f016ae", - "instance": null, - "targetOverrides": null, - "nestedPrefabInstanceRoots": [ - { - "__id__": 8 - } - ] - }, - { - "__type__": "cc.SceneGlobals", - "ambient": { - "__id__": 38 - }, - "shadows": { - "__id__": 39 - }, - "_skybox": { - "__id__": 40 - }, - "fog": { - "__id__": 41 - }, - "octree": { - "__id__": 42 - }, - "skin": { - "__id__": 43 - }, - "lightProbeInfo": { - "__id__": 44 - }, - "postSettings": { - "__id__": 45 - }, - "bakedWithStationaryMainLight": false, - "bakedWithHighpLightmap": false - }, - { - "__type__": "cc.AmbientInfo", - "_skyColorHDR": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.5, - "z": 0.8, - "w": 0.520833125 - }, - "_skyColor": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.5, - "z": 0.8, - "w": 0.520833125 - }, - "_skyIllumHDR": 20000, - "_skyIllum": 20000, - "_groundAlbedoHDR": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.2, - "z": 0.2, - "w": 1 - }, - "_groundAlbedo": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.2, - "z": 0.2, - "w": 1 - }, - "_skyColorLDR": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.5, - "z": 0.8, - "w": 1 - }, - "_skyIllumLDR": 20000, - "_groundAlbedoLDR": { - "__type__": "cc.Vec4", - "x": 0.2, - "y": 0.2, - "z": 0.2, - "w": 1 - } - }, - { - "__type__": "cc.ShadowsInfo", - "_enabled": false, - "_type": 0, - "_normal": { - "__type__": "cc.Vec3", - "x": 0, - "y": 1, - "z": 0 - }, - "_distance": 0, - "_planeBias": 1, - "_shadowColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 76 - }, - "_maxReceived": 4, - "_size": { - "__type__": "cc.Vec2", - "x": 1024, - "y": 1024 - } - }, - { - "__type__": "cc.SkyboxInfo", - "_envLightingType": 0, - "_envmapHDR": null, - "_envmap": null, - "_envmapLDR": null, - "_diffuseMapHDR": null, - "_diffuseMapLDR": null, - "_enabled": false, - "_useHDR": true, - "_editableMaterial": null, - "_reflectionHDR": null, - "_reflectionLDR": null, - "_rotationAngle": 0 - }, - { - "__type__": "cc.FogInfo", - "_type": 0, - "_fogColor": { - "__type__": "cc.Color", - "r": 200, - "g": 200, - "b": 200, - "a": 255 - }, - "_enabled": false, - "_fogDensity": 0.3, - "_fogStart": 0.5, - "_fogEnd": 300, - "_fogAtten": 5, - "_fogTop": 1.5, - "_fogRange": 1.2, - "_accurate": false - }, - { - "__type__": "cc.OctreeInfo", - "_enabled": false, - "_minPos": { - "__type__": "cc.Vec3", - "x": -1024, - "y": -1024, - "z": -1024 - }, - "_maxPos": { - "__type__": "cc.Vec3", - "x": 1024, - "y": 1024, - "z": 1024 - }, - "_depth": 8 - }, - { - "__type__": "cc.SkinInfo", - "_enabled": true, - "_blurRadius": 0.01, - "_sssIntensity": 3 - }, - { - "__type__": "cc.LightProbeInfo", - "_giScale": 1, - "_giSamples": 1024, - "_bounces": 2, - "_reduceRinging": 0, - "_showProbe": true, - "_showWireframe": true, - "_showConvex": false, - "_data": null, - "_lightProbeSphereVolume": 1 - }, - { - "__type__": "cc.PostSettingsInfo", - "_toneMappingType": 0 - } -] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene new file mode 100644 index 0000000..a1a46d8 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene @@ -0,0 +1,416 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "PlatformLoading", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": "PlatformLoading", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 7 + }, + "_id": "262ea1ac-58e5-4144-b79c-6e854d75d693" + }, + { + "__type__": "cc.Node", + "_name": "Canvas", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [ + { + "__id__": 3 + } + ], + "_active": true, + "_components": [ + { + "__id__": 5 + }, + { + "__id__": 6 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 800, + "y": 360, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "d9wH0yQuNFD6cIVq4RHwPA" + }, + { + "__type__": "cc.Node", + "_name": "Camera", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 2 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 4 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 1000 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "03SwPEvXVIrKlz1Ycix1/b" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 3 + }, + "_enabled": true, + "__prefab": null, + "_projection": 0, + "_priority": 0, + "_fov": 45, + "_fovAxis": 0, + "_orthoHeight": 360, + "_near": 1, + "_far": 2000, + "_color": { + "__type__": "cc.Color", + "r": 51, + "g": 51, + "b": 51, + "a": 255 + }, + "_depth": 1, + "_stencil": 0, + "_clearFlags": 7, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_aperture": 19, + "_shutter": 7, + "_iso": 0, + "_screenScale": 1, + "_visibility": 1107296256, + "_targetTexture": null, + "_postProcess": null, + "_usePostProcess": false, + "_cameraType": -1, + "_trackingType": 0, + "_id": "67kXVpE8xNtp7r60I1VAI2" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 1600, + "height": 720 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "adybdpE3VDW4/URT3c6gX8" + }, + { + "__type__": "cc.Canvas", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_cameraComponent": { + "__id__": 4 + }, + "_alignCanvasWithScreen": true, + "_id": "c6tdlDQhRJNIBJ8DjXiFQ/" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 8 + }, + "shadows": { + "__id__": 9 + }, + "_skybox": { + "__id__": 10 + }, + "fog": { + "__id__": 11 + }, + "octree": { + "__id__": 12 + }, + "skin": { + "__id__": 13 + }, + "lightProbeInfo": { + "__id__": 14 + }, + "postSettings": { + "__id__": 15 + }, + "bakedWithStationaryMainLight": false, + "bakedWithHighpLightmap": false + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_skyColorLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 1 + }, + "_skyIllumLDR": 20000, + "_groundAlbedoLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.ShadowsInfo", + "_enabled": false, + "_type": 0, + "_normal": { + "__type__": "cc.Vec3", + "x": 0, + "y": 1, + "z": 0 + }, + "_distance": 0, + "_planeBias": 1, + "_shadowColor": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 76 + }, + "_maxReceived": 4, + "_size": { + "__type__": "cc.Vec2", + "x": 1024, + "y": 1024 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLDR": null, + "_diffuseMapHDR": null, + "_diffuseMapLDR": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionLDR": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + }, + { + "__type__": "cc.SkinInfo", + "_enabled": true, + "_blurRadius": 0.01, + "_sssIntensity": 3 + }, + { + "__type__": "cc.LightProbeInfo", + "_giScale": 1, + "_giSamples": 1024, + "_bounces": 2, + "_reduceRinging": 0, + "_showProbe": true, + "_showWireframe": true, + "_showConvex": false, + "_data": null, + "_lightProbeSphereVolume": 1 + }, + { + "__type__": "cc.PostSettingsInfo", + "_toneMappingType": 0 + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene.meta b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene.meta similarity index 72% rename from cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene.meta rename to cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene.meta index a88364b..dd03581 100644 --- a/cocoscreator_projects/YouleNexus/assets/scenes/LocalPlatformLogin.scene.meta +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLoading.scene.meta @@ -2,7 +2,7 @@ "ver": "1.1.50", "importer": "scene", "imported": true, - "uuid": "67e69aac-9982-4e78-a44c-1d9c42f016ae", + "uuid": "262ea1ac-58e5-4144-b79c-6e854d75d693", "files": [ ".json" ], diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene new file mode 100644 index 0000000..54c01f4 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene @@ -0,0 +1,416 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "PlatformLogin", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": "PlatformLogin", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 7 + }, + "_id": "c6bf90e9-2e5e-4b7b-bfc0-ed8a4e243e4d" + }, + { + "__type__": "cc.Node", + "_name": "Canvas", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [ + { + "__id__": 3 + } + ], + "_active": true, + "_components": [ + { + "__id__": 5 + }, + { + "__id__": 6 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 800, + "y": 360, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "faTOCTLXRFdpZeZi7lNLQq" + }, + { + "__type__": "cc.Node", + "_name": "Camera", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 2 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 4 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 1000 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "6ayrdwHr9GLKXUxRdk2hH5" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 3 + }, + "_enabled": true, + "__prefab": null, + "_projection": 0, + "_priority": 0, + "_fov": 45, + "_fovAxis": 0, + "_orthoHeight": 360, + "_near": 1, + "_far": 2000, + "_color": { + "__type__": "cc.Color", + "r": 51, + "g": 51, + "b": 51, + "a": 255 + }, + "_depth": 1, + "_stencil": 0, + "_clearFlags": 7, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_aperture": 19, + "_shutter": 7, + "_iso": 0, + "_screenScale": 1, + "_visibility": 1107296256, + "_targetTexture": null, + "_postProcess": null, + "_usePostProcess": false, + "_cameraType": -1, + "_trackingType": 0, + "_id": "51tpdCan1JboRRHZOyhxuv" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 1600, + "height": 720 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "8ej3BZPwhAD6WQVYXaPCUB" + }, + { + "__type__": "cc.Canvas", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_cameraComponent": { + "__id__": 4 + }, + "_alignCanvasWithScreen": true, + "_id": "cduZE+wFRG1q4NzbTvVx8E" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 8 + }, + "shadows": { + "__id__": 9 + }, + "_skybox": { + "__id__": 10 + }, + "fog": { + "__id__": 11 + }, + "octree": { + "__id__": 12 + }, + "skin": { + "__id__": 13 + }, + "lightProbeInfo": { + "__id__": 14 + }, + "postSettings": { + "__id__": 15 + }, + "bakedWithStationaryMainLight": false, + "bakedWithHighpLightmap": false + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_skyColorLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 1 + }, + "_skyIllumLDR": 20000, + "_groundAlbedoLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.ShadowsInfo", + "_enabled": false, + "_type": 0, + "_normal": { + "__type__": "cc.Vec3", + "x": 0, + "y": 1, + "z": 0 + }, + "_distance": 0, + "_planeBias": 1, + "_shadowColor": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 76 + }, + "_maxReceived": 4, + "_size": { + "__type__": "cc.Vec2", + "x": 1024, + "y": 1024 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLDR": null, + "_diffuseMapHDR": null, + "_diffuseMapLDR": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionLDR": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + }, + { + "__type__": "cc.SkinInfo", + "_enabled": true, + "_blurRadius": 0.01, + "_sssIntensity": 3 + }, + { + "__type__": "cc.LightProbeInfo", + "_giScale": 1, + "_giSamples": 1024, + "_bounces": 2, + "_reduceRinging": 0, + "_showProbe": true, + "_showWireframe": true, + "_showConvex": false, + "_data": null, + "_lightProbeSphereVolume": 1 + }, + { + "__type__": "cc.PostSettingsInfo", + "_toneMappingType": 0 + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene.meta b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene.meta new file mode 100644 index 0000000..0dd43be --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformLogin.scene.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.1.50", + "importer": "scene", + "imported": true, + "uuid": "c6bf90e9-2e5e-4b7b-bfc0-ed8a4e243e4d", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene new file mode 100644 index 0000000..46e785b --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene @@ -0,0 +1,316 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "PlatformStartup", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": "PlatformStartup", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 4 + }, + "_id": "1c10c2ee-adc2-496f-9005-d3e0e6ebbb56" + }, + { + "__type__": "cc.Node", + "_name": "PlatformServices", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 3 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "edK1iknZ1EDKLpdkoVy5NZ" + }, + { + "__type__": "6dba8Keu9dNqL1ILyFO7MyT", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "loadingScene": { + "__uuid__": "262ea1ac-58e5-4144-b79c-6e854d75d693", + "__expectedType__": "cc.SceneAsset" + }, + "loginScene": { + "__uuid__": "c6bf90e9-2e5e-4b7b-bfc0-ed8a4e243e4d", + "__expectedType__": "cc.SceneAsset" + }, + "loadingPrefab": { + "__uuid__": "3e5a41f3-b043-45a5-94f6-e299ea9acc61", + "__expectedType__": "cc.Prefab" + }, + "loginPrefab": { + "__uuid__": "b209cefb-b381-4c7c-b765-94a5ab75a89c", + "__expectedType__": "cc.Prefab" + }, + "protocolPrefab": { + "__uuid__": "8fad42f6-6e14-4311-a344-e6ca87bc0087", + "__expectedType__": "cc.Prefab" + }, + "phonePrefab": { + "__uuid__": "b9ec1622-fe6d-4a07-8958-3a0732040e91", + "__expectedType__": "cc.Prefab" + }, + "_id": "09m0ERGqxLWKsJIBthaeXP" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 5 + }, + "shadows": { + "__id__": 6 + }, + "_skybox": { + "__id__": 7 + }, + "fog": { + "__id__": 8 + }, + "octree": { + "__id__": 9 + }, + "skin": { + "__id__": 10 + }, + "lightProbeInfo": { + "__id__": 11 + }, + "postSettings": { + "__id__": 12 + }, + "bakedWithStationaryMainLight": false, + "bakedWithHighpLightmap": false + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_skyColorLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 1 + }, + "_skyIllumLDR": 20000, + "_groundAlbedoLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.ShadowsInfo", + "_enabled": false, + "_type": 0, + "_normal": { + "__type__": "cc.Vec3", + "x": 0, + "y": 1, + "z": 0 + }, + "_distance": 0, + "_planeBias": 1, + "_shadowColor": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 76 + }, + "_maxReceived": 4, + "_size": { + "__type__": "cc.Vec2", + "x": 1024, + "y": 1024 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLDR": null, + "_diffuseMapHDR": null, + "_diffuseMapLDR": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionLDR": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + }, + { + "__type__": "cc.SkinInfo", + "_enabled": true, + "_blurRadius": 0.01, + "_sssIntensity": 3 + }, + { + "__type__": "cc.LightProbeInfo", + "_giScale": 1, + "_giSamples": 1024, + "_bounces": 2, + "_reduceRinging": 0, + "_showProbe": true, + "_showWireframe": true, + "_showConvex": false, + "_data": null, + "_lightProbeSphereVolume": 1 + }, + { + "__type__": "cc.PostSettingsInfo", + "_toneMappingType": 0 + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene.meta b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene.meta new file mode 100644 index 0000000..9aba202 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/PlatformStartup.scene.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.1.50", + "importer": "scene", + "imported": true, + "uuid": "1c10c2ee-adc2-496f-9005-d3e0e6ebbb56", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene b/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene new file mode 100644 index 0000000..0479cfb --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene @@ -0,0 +1,449 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "scene-2d", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": "scene-2d", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 8 + }, + "_id": "aa6aad6d-aef0-4b69-95a0-18ffda41f4c7" + }, + { + "__type__": "cc.Node", + "_name": "Canvas", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [ + { + "__id__": 3 + } + ], + "_active": true, + "_components": [ + { + "__id__": 5 + }, + { + "__id__": 6 + }, + { + "__id__": 7 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 800, + "y": 360.00000000000006, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "beI88Z2HpFELqR4T5EMHpg" + }, + { + "__type__": "cc.Node", + "_name": "Camera", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 2 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 4 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 1000 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "ebFwiq8gBFaYpqYbdoDODe" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 3 + }, + "_enabled": true, + "__prefab": null, + "_projection": 0, + "_priority": 0, + "_fov": 45, + "_fovAxis": 0, + "_orthoHeight": 360, + "_near": 0, + "_far": 2000, + "_color": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 255 + }, + "_depth": 1, + "_stencil": 0, + "_clearFlags": 7, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_aperture": 19, + "_shutter": 7, + "_iso": 0, + "_screenScale": 1, + "_visibility": 1108344832, + "_targetTexture": null, + "_postProcess": null, + "_usePostProcess": false, + "_cameraType": -1, + "_trackingType": 0, + "_id": "63WIch3o5BEYRlXzTT0oWc" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 1600, + "height": 720 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "d6rUX5yfhMlKoWX2bSbawx" + }, + { + "__type__": "cc.Canvas", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_cameraComponent": { + "__id__": 4 + }, + "_alignCanvasWithScreen": true, + "_id": "12O/ljcVlEqLmVm3U2gEOQ" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_alignFlags": 45, + "_target": null, + "_left": 0, + "_right": 0, + "_top": 5.684341886080802e-14, + "_bottom": 5.684341886080802e-14, + "_horizontalCenter": 0, + "_verticalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 0, + "_originalHeight": 0, + "_alignMode": 2, + "_lockFlags": 0, + "_id": "c5V1EV8IpMtrIvY1OE9t2u" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 9 + }, + "shadows": { + "__id__": 10 + }, + "_skybox": { + "__id__": 11 + }, + "fog": { + "__id__": 12 + }, + "octree": { + "__id__": 13 + }, + "skin": { + "__id__": 14 + }, + "lightProbeInfo": { + "__id__": 15 + }, + "postSettings": { + "__id__": 16 + }, + "bakedWithStationaryMainLight": false, + "bakedWithHighpLightmap": false + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0, + "y": 0, + "z": 0, + "w": 0.520833125 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0, + "y": 0, + "z": 0, + "w": 0.520833125 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0, + "y": 0, + "z": 0, + "w": 0 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0, + "y": 0, + "z": 0, + "w": 0 + }, + "_skyColorLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 1 + }, + "_skyIllumLDR": 20000, + "_groundAlbedoLDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.ShadowsInfo", + "_enabled": false, + "_type": 0, + "_normal": { + "__type__": "cc.Vec3", + "x": 0, + "y": 1, + "z": 0 + }, + "_distance": 0, + "_planeBias": 1, + "_shadowColor": { + "__type__": "cc.Color", + "r": 76, + "g": 76, + "b": 76, + "a": 255 + }, + "_maxReceived": 4, + "_size": { + "__type__": "cc.Vec2", + "x": 512, + "y": 512 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLDR": null, + "_diffuseMapHDR": null, + "_diffuseMapLDR": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionLDR": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + }, + { + "__type__": "cc.SkinInfo", + "_enabled": false, + "_blurRadius": 0.01, + "_sssIntensity": 3 + }, + { + "__type__": "cc.LightProbeInfo", + "_giScale": 1, + "_giSamples": 1024, + "_bounces": 2, + "_reduceRinging": 0, + "_showProbe": true, + "_showWireframe": true, + "_showConvex": false, + "_data": null, + "_lightProbeSphereVolume": 1 + }, + { + "__type__": "cc.PostSettingsInfo", + "_toneMappingType": 0 + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene.meta b/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene.meta new file mode 100644 index 0000000..15aa42c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scenes/scene-2d.scene.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.1.50", + "importer": "scene", + "imported": true, + "uuid": "aa6aad6d-aef0-4b69-95a0-18ffda41f4c7", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts deleted file mode 100644 index ba09187..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { _decorator, assetManager, Component, Label, Node, Prefab, ProgressBar, Sprite, instantiate, isValid, sys } from 'cc'; -import { DEBUG } from 'cc/env'; -import { HttpConfigFetcher } from '../../framework/config/remote-config-fetcher.ts'; -import { resolveLaunchWaitTime } from '../../framework/config/remote-config.ts'; -import type { NativeHost } from '../../framework/config/sources/native-settings.ts'; -import type { PageModel } from '../../framework/presentation/ui-contracts.ts'; -import { createLocalLoginHost, type LocalLoginHost } from './local-login-host.ts'; -import type { LocalLoginEvidence } from './local-login-transport.ts'; -import { CocosLocalLoginView, CocosLogoCountdownView, createLogoCountdown, createAssetLoadTask, createCocosFrameScheduler, positionDiagnosticStopButtonBelow, requireNode } from './local-login-view.ts'; - -const { ccclass, property } = _decorator; - -/** Explicit DEBUG browser scene entry. All business state belongs to its host Runtime. */ -@ccclass('LocalPlatformLogin') -export class LocalPlatformLogin extends Component { - @property(Prefab) loadingPrefab: Prefab | null = null; - @property(Prefab) loginPrefab: Prefab | null = null; - @property(Node) loadingRoot: Node | null = null; - @property(Node) loginRoot: Node | null = null; - @property(Node) resultRoot: Node | null = null; - @property(Node) statusOverlay: Node | null = null; - @property(Label) statusLabel: Label | null = null; - @property(Label) resultLabel: Label | null = null; - @property(Node) stopButton: Node | null = null; - - private host: LocalLoginHost | null = null; - private view: CocosLocalLoginView | null = null; - private resourceProgress: ProgressBar | null = null; - private loginInstance: Node | null = null; - private stopped = false; - private firstDrawAt: number | null = null; - private version: number | null = null; - private countdown: ReturnType | null = null; - private countdownView: CocosLogoCountdownView | null = null; - private readonly pending = new Set<() => void>(); - private readonly evidence: (LocalLoginEvidence & { at: number })[] = []; - private pageModel: PageModel | null = null; - private readonly stopTouch = () => this.stop(); - - async start(): Promise { - // This guard precedes reading browser storage, preparing account or creating socket. - if (!DEBUG || sys.isNative) throw new Error('LocalPlatformLogin requires DEBUG browser preview'); - try { - const loadingRoot = this.required(this.loadingRoot, 'loadingRoot'); - const loginRoot = this.required(this.loginRoot, 'loginRoot'); - const stopButton = this.required(this.stopButton, 'stopButton'); - this.view = new CocosLocalLoginView({ loadingRoot, loginRoot, - resultRoot: this.required(this.resultRoot, 'resultRoot'), - statusOverlay: this.required(this.statusOverlay, 'statusOverlay'), - statusLabel: this.required(this.statusLabel, 'statusLabel'), - resultLabel: this.required(this.resultLabel, 'resultLabel') }); - this.required(this.loadingPrefab, 'loadingPrefab'); - this.required(this.loginPrefab, 'loginPrefab'); - const logo = requireNode(loadingRoot, 'Layer1_Logo'); - this.resourceProgress = requireNode(logo, 'group-1/Spirit254').getComponent(ProgressBar); - if (this.resourceProgress === null) throw new Error('Layer1_Logo/group-1/Spirit254 requires ProgressBar'); - requireNode(logo, 'group-32').active = false; - const countdownLabels = logo.getComponentsInChildren(Label).filter(label => label.node.name === 'Spirit3104'); - if (countdownLabels.length !== 1) throw new Error('Logo requires exactly one Spirit3104 Label'); - const logoButton = requireNode(logo, 'group-1/Spirit3103'); - positionDiagnosticStopButtonBelow(stopButton, logoButton); - this.countdownView = new CocosLogoCountdownView(logoButton, countdownLabels[0].node, - () => this.countdown?.skip()); - this.countdown = createLogoCountdown({ now: () => performance.now(), - schedule: (callback, ms) => { - const timer = window.setTimeout(() => { - try { callback(); } catch (error) { this.fail(error); } - }, ms); - return () => window.clearTimeout(timer); - }, render: state => this.countdownView?.render(state) }); - this.validateSprites(loadingRoot); - stopButton.on(Node.EventType.TOUCH_END, this.stopTouch); - this.view.activate('loading'); - const firstDraw = this.nextDraw().then(() => { - this.firstDrawAt = performance.now(); - this.record({ kind: 'loading-first-draw', data: null }); - }); - // A configuration failure can cancel this frame before Runtime consumes its gate. - void firstDraw.catch(() => {}); - const view = this.view; - const browser = window as unknown as NativeHost & { returnCitySN?: unknown }; - this.host = createLocalLoginHost({ - configOptions: { hostKind: 'h5', search: window.location.search, - win: browser, fetcher: new HttpConfigFetcher() }, - storage: window.localStorage, random: Math.random, now: Date.now, - sampleHost: () => ({ location: null, - ...('returnCitySN' in browser ? { returnCitySN: browser.returnCitySN } : {}) }), - view: { - render: model => { this.pageModel = model; this.record({ kind: 'page-model', data: model }); view.render(model); }, - activate: page => { view.activate(page); this.record({ kind: 'page-active', data: page }); }, - showOverlay: model => view.showOverlay(model), - }, - scheduler: createCocosFrameScheduler(), - onConfigResolved: config => { - this.version = config.identity.version; - this.required(this.countdown, 'Logo countdown').start(resolveLaunchWaitTime(config.remoteConfig)); - }, - onResourcesReady: () => { - this.countdown?.resourcesReady(); - this.required(this.resourceProgress, 'resource ProgressBar').node.active = false; - }, - onSocketReady: ready => this.countdown?.socketReady(ready), - onStopped: () => { this.countdown?.cancel(); this.countdownView?.dispose(); }, - loadResources: () => this.loadResources(), - waitForMinimumDisplay: async () => { - await Promise.all([firstDraw, this.required(this.countdown, 'Logo countdown').promise]); - const drawnAt = this.required(this.firstDrawAt, 'loading first draw'); - this.record({ kind: 'minimum-display-ready', data: performance.now() - drawnAt }); - }, - createSocket: url => new WebSocket(url), record: event => this.record(event), - onBusy: busy => view.setBusy(busy), - onFailure: error => this.fail(error), - }); - await this.host.start(); - } catch (error) { this.fail(error); } - } - - private required(value: T | null, name: string): T { - if (value === null || value === undefined) throw new Error(`LocalPlatformLogin missing ${name}`); - return value; - } - - private record(event: LocalLoginEvidence): void { - this.evidence.push(Object.freeze({ ...event, at: performance.now() })); - } - - private validateSprites(root: Node): void { - for (const sprite of root.getComponentsInChildren(Sprite)) { - if (!sprite.node.activeInHierarchy) continue; - const frame = sprite.spriteFrame; - if (frame === null || !isValid(frame) || frame.texture === null || !isValid(frame.texture)) { - throw new Error(`Invalid SpriteFrame/texture dependency: ${sprite.node.name}`); - } - } - } - - private async loadResources(): Promise { - if (this.stopped) throw new Error('Local login resources cancelled'); - const serializedPrefab = this.required(this.loginPrefab, 'loginPrefab'); - const loading = this.required(this.loadingPrefab, 'loadingPrefab'); - if (!isValid(serializedPrefab) || !isValid(serializedPrefab.data) || !isValid(loading) || !isValid(loading.data)) { - throw new Error('Local login serialized Prefab dependency is invalid'); - } - const progress = this.required(this.resourceProgress, 'resource ProgressBar'); - const task = createAssetLoadTask((onProgress, onComplete) => { - assetManager.loadAny(serializedPrefab.uuid, onProgress, (error, asset) => onComplete(error, asset as Prefab)); - }, value => { progress.progress = value; }); - this.pending.add(task.cancel); - let prefab: Prefab; - try { prefab = await task.promise; } - finally { this.pending.delete(task.cancel); } - if (this.stopped) throw new Error('Local login resources cancelled'); - if (!isValid(prefab) || !isValid(prefab.data)) throw new Error('Loaded login Prefab dependency is invalid'); - const instance = instantiate(prefab); - this.loginInstance = instance; - instance.parent = this.required(this.loginRoot, 'loginRoot'); - const view = this.required(this.view, 'view'); - view.bindLogin(instance, () => { - const host = this.required(this.host, 'host'); - void host.login().catch(error => this.fail(error)); - }); - view.setVersion(this.required(this.version, 'resolved identity.version')); - // Validate active prefab content before the root is exposed as the login page. - for (const sprite of instance.getComponentsInChildren(Sprite)) { - if (!sprite.node.active) continue; - const frame = sprite.spriteFrame; - if (frame === null || !isValid(frame) || frame.texture === null || !isValid(frame.texture)) { - throw new Error(`Invalid login SpriteFrame/texture dependency: ${sprite.node.name}`); - } - } - await this.nextDraw(); - this.record({ kind: 'resources-ready', data: { login: prefab.uuid, loading: loading.uuid } }); - } - - private nextDraw(): Promise { - return new Promise((resolve, reject) => { - if (this.stopped) { reject(new Error('Local login frame cancelled')); return; } - const cancelFrame = createCocosFrameScheduler().request(() => { - this.pending.delete(cancel); resolve(); - }); - const cancel = () => { cancelFrame(); this.pending.delete(cancel); reject(new Error('Local login frame cancelled')); }; - this.pending.add(cancel); - }); - } - - private delay(milliseconds: number): Promise { - return new Promise((resolve, reject) => { - if (this.stopped) { reject(new Error('Local login timer cancelled')); return; } - const timer = window.setTimeout(() => { this.pending.delete(cancel); resolve(); }, milliseconds); - const cancel = () => { window.clearTimeout(timer); this.pending.delete(cancel); reject(new Error('Local login timer cancelled')); }; - this.pending.add(cancel); - }); - } - - private release(): void { - this.stopped = true; - this.countdown?.cancel(); - this.countdownView?.dispose(); - for (const cancel of [...this.pending]) cancel(); - this.stopButton?.off(Node.EventType.TOUCH_END, this.stopTouch); - this.view?.dispose(); - this.host?.stop(); - } - - private fail(error: unknown): void { - if (this.stopped) return; - this.record({ kind: 'component-failure', data: error }); - try { this.release(); } - catch (cleanup) { - this.record({ kind: 'cleanup-failure', data: cleanup }); - this.view?.showFailure({ error, cleanup }); - return; - } - this.view?.showFailure(error); - if (this.view === null && this.statusLabel !== null) this.statusLabel.string = `验证失败:${String(error)}`; - } - - stop(): void { - if (this.stopped) return; - this.view?.showStopped(); - try { this.release(); } - catch (error) { this.view?.showFailure(error); } - this.record({ kind: 'stopped', data: null }); - } - - onDestroy(): void { - this.stop(); - if (this.loginInstance !== null && isValid(this.loginInstance)) this.loginInstance.destroy(); - } - - /** Local memory only: callers must redact raw account/device IDs before sharing. */ - getDiagnostics() { - return Object.freeze({ events: Object.freeze([...this.evidence]), snapshot: this.host?.getSnapshot(), - pageModel: this.pageModel, stopped: this.stopped, pendingCount: this.pending.size, - firstDrawAt: this.firstDrawAt, resourceProgress: this.resourceProgress?.progress }); - } -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts deleted file mode 100644 index 68cda18..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { resolveLocalStartup } from '../../framework/config/local-startup.ts'; -import type { ResolveRuntimeConfigOptions, RuntimeConfig } from '../../framework/config/runtime-config.ts'; -import { createLocalLoginSources, type LocalHostSnapshot, type LocalLoginSources } from '../../framework/adapters/local/login-sources.ts'; -import type { LocalStoragePort } from '../../framework/adapters/local/local-storage.ts'; -import { PlatformRuntime } from '../../framework/platform/runtime.ts'; -import type { PlatformState } from '../../framework/platform/stores/platform-types.ts'; -import { PlatformUiController } from '../../framework/presentation/platform-ui-controller.ts'; -import type { PlatformViewPort, FrameScheduler } from '../../framework/presentation/ui-contracts.ts'; -import { createDiagnosticGameEntry } from './local-login-game-entry.ts'; -import { createLocalLoginWire } from './local-login-wire.ts'; -import { LocalLoginTransport, type EvidenceSink } from './local-login-transport.ts'; - -export interface LocalLoginHostOptions { - configOptions: ResolveRuntimeConfigOptions; - storage: LocalStoragePort; - random: () => number; - now: () => number; - sampleHost: () => LocalHostSnapshot; - view: PlatformViewPort; - scheduler: FrameScheduler; - loadResources: (config: RuntimeConfig) => Promise; - waitForMinimumDisplay: (config: RuntimeConfig) => Promise; - onConfigResolved?: (config: RuntimeConfig) => void; - onResourcesReady?: () => void; - onSocketReady?: (ready: boolean) => void; - onStopped?: () => void; - createSocket: (url: string) => WebSocket; - record: EvidenceSink; - onBusy: (busy: boolean) => void; - onFailure: (error: unknown) => void; -} - -export interface LocalLoginHost { - start(): Promise; - login(): Promise; - stop(): void; - getSnapshot(): { state: PlatformState | null; ready: boolean; busy: boolean }; -} - -class LocalLoginHostFault extends Error { - readonly errors: readonly unknown[]; - constructor(errors: readonly unknown[]) { - super('Local login host failed with additional observer or cleanup errors'); - this.name = 'LocalLoginHostFault'; - this.errors = Object.freeze([...errors]); - } -} - -function collected(errors: readonly unknown[]): unknown { - return errors.length === 1 ? errors[0] : new LocalLoginHostFault(errors); -} - -/** Development composition only; Runtime remains the sole business state owner. */ -export function createLocalLoginHost(options: LocalLoginHostOptions): LocalLoginHost { - let runtime: PlatformRuntime | null = null; - let controller: PlatformUiController | null = null; - let sources: LocalLoginSources | null = null; - let startup: Promise | null = null; - let loginOperation: Promise | null = null; - let terminal = false; - let busy = false; - let cleanupErrors: unknown[] | null = null; - let serverFailure: { data: unknown } | null = null; - let unsubscribeState: (() => void) | null = null; - - function requireActive(): void { - if (terminal) throw new Error('Local login host is stopped or terminal'); - } - - function setBusy(value: boolean): void { - if (busy === value) return; - busy = value; - options.onBusy(value); - } - - function shutdown(errors: unknown[]): void { - terminal = true; - cleanupErrors = errors; - try { options.onStopped?.(); } catch (error) { errors.push(error); } - try { unsubscribeState?.(); unsubscribeState = null; } catch (error) { errors.push(error); } - // Disable input before releasing either owner; dispose preserves drawn output. - try { setBusy(false); } catch (error) { errors.push(error); } - try { controller?.dispose(); } catch (error) { errors.push(error); } - try { runtime?.stop(); } catch (error) { errors.push(error); } - cleanupErrors = null; - } - - function fail(error: unknown): void { - if (cleanupErrors !== null) { - if (!cleanupErrors.includes(error)) cleanupErrors.push(error); - return; - } - if (terminal) return; - const errors = [error]; - shutdown(errors); - options.onFailure(collected(errors)); - } - - function wireFault(error: unknown): void { - if (serverFailure === null) { fail(error); return; } - // The Wire scope marker follows the existing denial/kick handler. Keep the - // original server payload, and retain any independent Wire cleanup errors. - const extras = error instanceof Error && 'errors' in error - ? (error as Error & { errors: readonly unknown[] }).errors.slice(1) : []; - fail(collected([serverFailure.data, ...extras])); - } - - function transportFault(error: unknown): void { - fail(serverFailure === null ? error : collected([serverFailure.data, error])); - } - - async function readyTask(task: () => Promise): Promise { - try { requireActive(); await task(); requireActive(); } - catch (error) { fail(error); throw error; } - } - - function start(): Promise { - if (startup !== null) return startup; - if (terminal) return Promise.reject(new Error('Local login host is stopped or terminal')); - startup = Promise.resolve().then(async () => { - requireActive(); - const context = await resolveLocalStartup(options.configOptions); - requireActive(); - options.onConfigResolved?.(context.config); - requireActive(); - const { mode, isDebugger, identity, servers, source, gameserver } = context.config; - options.record({ kind: 'startup-config', data: { mode, isDebugger, identity, servers, source, gameserver } }); - requireActive(); - const entry = createDiagnosticGameEntry(context.config.identity.gameid, options.record); - sources = createLocalLoginSources({ context, storage: options.storage, random: options.random, - now: options.now, sampleHost: options.sampleHost, - isSessionActive: () => busy || runtime?.state.app.phase === 'logged-in' }); - controller = new PlatformUiController({ - render: model => { - if (terminal) return; - try { options.view.render(model); } - catch (error) { fail(error); throw error; } - }, - activate: page => { - if (terminal) return; - try { - options.view.activate(page); - if (!terminal && page === 'lobby') setBusy(false); - } catch (error) { fail(error); throw error; } - }, - showOverlay: model => { - if (terminal) return; - if (model.kind === 'denial') serverFailure = { data: model.outcome.data }; - if (model.kind === 'kicked') serverFailure = { data: model.data }; - try { options.view.showOverlay(model); } - catch (error) { - if (model.kind === 'fatal') fail(collected([model.error, error])); - else transportFault(error); - throw error; - } - if (model.kind === 'fatal') fail(model.error); - }, - }, { - request: callback => { - try { - const cancel = options.scheduler.request(callback); - return () => { - try { cancel(); } - catch (error) { fail(error); throw error; } - }; - } catch (error) { fail(error); throw error; } - }, - }, fail); - runtime = new PlatformRuntime({ gameEntry: entry, - resolveRuntimeConfig: async () => context.config, - createWireClient: () => createLocalLoginWire({ context, record: options.record, onFault: wireFault, - transportFactory: () => new LocalLoginTransport({ context, createSocket: options.createSocket, - record: options.record, onFault: transportFault }) }), - scene: controller, loadResources: () => readyTask(async () => { - await options.loadResources(context.config); - requireActive(); options.onResourcesReady?.(); - }), - waitForMinimumDisplay: () => readyTask(() => options.waitForMinimumDisplay(context.config)), - getLoginDeviceSnapshot: sources.getLoginDeviceSnapshot, - }); - unsubscribeState = runtime.subscribeState(state => { - if (!terminal) options.onSocketReady?.(state.app.phase === 'connected' || state.app.phase === 'logged-in'); - }, fail); - controller.connect(runtime); - requireActive(); - await runtime.start(); - requireActive(); - }).catch(error => { fail(error); throw error; }); - return startup; - } - - function login(): Promise { - if (terminal) return Promise.reject(new Error('Local login host is stopped or terminal')); - if (busy && loginOperation !== null) return loginOperation; - if (runtime === null || controller === null || sources === null || !runtime.ready) { - return Promise.reject(new Error('Local login host is not ready for login')); - } - const activeSources = sources; - const activeController = controller; - loginOperation = Promise.resolve().then(async () => { - requireActive(); - const account = await activeSources.prepareLogin(); - requireActive(); - activeController.login(account); - }).catch(error => { fail(error); throw error; }); - try { setBusy(true); } catch (error) { fail(error); } - return loginOperation; - } - - return { - start, login, - stop() { - if (terminal) return; - const errors: unknown[] = []; - shutdown(errors); - if (errors.length > 0) { - const error = collected(errors); - options.onFailure(error); - throw error; - } - }, - getSnapshot: () => ({ state: runtime === null ? null : runtime.state, - ready: !terminal && runtime !== null && runtime.ready, busy }), - }; -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts.meta deleted file mode 100644 index 53c7288..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-host.ts.meta +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "2c6ce55f-b5a3-4503-bc65-ca12c5537c8f", - "files": [], - "subMetas": {}, - "userData": {} -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts deleted file mode 100644 index 444026d..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - assertLocalConnectionTarget, - type LocalStartupContext, -} from '../../framework/config/local-startup.ts'; -import type { Transport } from '../../framework/net/transport.ts'; - -export type LocalLoginEvidence = { kind: string; data: unknown }; -export type EvidenceSink = (event: LocalLoginEvidence) => void; - -interface LocalLoginTransportOptions { - context: LocalStartupContext; - createSocket: (url: string) => WebSocket; - onFault: (error: unknown) => void; - record: EvidenceSink; -} - -/** ES2020 aggregate: retain originals before notifying a terminal observer once. */ -class LocalLoginTransportFault extends Error { - readonly errors: readonly unknown[]; - constructor(errors: readonly unknown[]) { - super('Local login transport failed with an additional cleanup error'); - this.name = 'LocalLoginTransportFault'; - this.errors = Object.freeze([...errors]); - } -} - -/** Local-only socket boundary; raw evidence precedes protocol decoding. */ -export class LocalLoginTransport implements Transport { - private socket: WebSocket | null = null; - private generation = 0; - private openCallback?: () => void; - private messageCallback?: (frame: string) => void; - private closeCallback?: () => void; - - constructor(private readonly options: LocalLoginTransportOptions) {} - - connect(url: string): void { - try { - assertLocalConnectionTarget(this.options.context, url); - } catch (error) { - this.options.onFault(error); - throw error; - } - this.close(); - let socket: WebSocket; - try { - socket = this.options.createSocket(url); - } catch (error) { - this.options.onFault(error); - throw error; - } - this.socket = socket; - const generation = this.generation; - socket.onopen = () => { - if (this.socket === socket) this.openCallback?.(); - }; - socket.onmessage = (event) => { - if (this.socket !== socket) return; - if (typeof event.data !== 'string') { - this.stopWithFault(new TypeError('Local login WebSocket requires text frames')); - return; - } - try { - this.options.record({ kind: 'transport-receive', data: event.data }); - } catch (error) { - throw this.stopWithFault(error); - } - if (this.socket === socket) this.messageCallback?.(event.data); - }; - socket.onclose = () => { - if (this.socket !== socket) return; - this.socket = null; - this.closeCallback?.(); - }; - socket.onerror = (event) => { - if (this.socket !== socket) return; - try { - this.options.record({ kind: 'transport-error', data: event }); - } catch (error) { - throw this.stopWithFault(error); - } - if (this.socket !== socket || this.generation !== generation) return; - // Invalidate before close: browsers may dispatch close after error. - this.socket = null; - try { - socket.close(); - } catch (error) { - this.options.onFault(error); - throw error; - } - if (this.generation === generation) this.closeCallback?.(); - }; - } - - send(frame: string): void { - const socket = this.socket; - if (!socket || socket.readyState !== 1) { - throw new Error('Local login WebSocket must be OPEN before send'); - } - try { - socket.send(frame); - this.options.record({ kind: 'transport-send', data: frame }); - } catch (error) { - throw this.stopWithFault(error); - } - } - - close(): void { - this.generation++; - const socket = this.socket; - this.socket = null; - if (!socket) return; - try { - socket.close(); - } catch (error) { - this.options.onFault(error); - throw error; - } - } - - onOpen(callback: () => void): void { this.openCallback = callback; } - onMessage(callback: (frame: string) => void): void { this.messageCallback = callback; } - onClose(callback: () => void): void { this.closeCallback = callback; } - - private stopWithFault(error: unknown): unknown { - const socket = this.socket; - this.socket = null; - const errors: unknown[] = [error]; - try { - socket?.close(); - } catch (cleanupError) { - errors.push(cleanupError); - } - const fault = errors.length === 1 ? error : new LocalLoginTransportFault(errors); - this.options.onFault(fault); - return fault; - } -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts.meta deleted file mode 100644 index dbeb6af..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts.meta +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "2bbf94d8-6aa1-4b5f-a94c-c3f12205f46c", - "files": [], - "subMetas": {}, - "userData": {} -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts deleted file mode 100644 index 9486a02..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { Director, Label, Node, Sprite, UITransform, Vec3, Widget, director } from 'cc'; -import type { FrameScheduler, OverlayModel, PageId, PageModel, PlatformViewPort } from '../../framework/presentation/ui-contracts.ts'; - -interface ViewReferences { - loadingRoot: Node; loginRoot: Node; resultRoot: Node; - statusOverlay: Node; statusLabel: Label; resultLabel: Label; -} - -export interface LogoCountdownState { readonly visible: boolean; readonly text: string; } - -const DIAGNOSTIC_STOP_GAP = 12; - -/** Keeps the scene diagnostic control clear of the native Logo click region. */ -export function positionDiagnosticStopButtonBelow(stopButton: Node, logoButton: Node): void { - const stopTransform = stopButton.getComponent(UITransform); - const logoTransform = logoButton.getComponent(UITransform); - const stopParentTransform = stopButton.parent?.getComponent(UITransform); - if (!stopTransform || !logoTransform || !stopParentTransform) { - throw new Error('Diagnostic stop layout requires native UITransform bindings'); - } - const widget = stopButton.getComponent(Widget); - if (widget) { - widget.updateAlignment(); - widget.enabled = false; - } - const logoBottom = logoTransform.convertToWorldSpaceAR( - new Vec3((0.5 - logoTransform.anchorX) * logoTransform.width, -logoTransform.anchorY * logoTransform.height, 0), - ); - const bottomInStopParent = stopParentTransform.convertToNodeSpaceAR(logoBottom); - const current = stopButton.position; - stopButton.setPosition(new Vec3(current.x, - bottomInStopParent.y - DIAGNOSTIC_STOP_GAP - (1 - stopTransform.anchorY) * stopTransform.height, - current.z)); -} - -/** Native input and measured label layout, independent of serialized label parentage. */ -export class CocosLogoCountdownView { - private readonly label: Label; - private readonly buttonTransform: UITransform; - private readonly textTransform: UITransform; - private readonly parentTransform: UITransform; - private disposed = false; - - constructor(private readonly button: Node, private readonly text: Node, private readonly onSkip: () => void) { - const label = text.getComponent(Label), buttonTransform = button.getComponent(UITransform); - const textTransform = text.getComponent(UITransform), parentTransform = text.parent?.getComponent(UITransform); - if (!label || !buttonTransform || !textTransform || !parentTransform) { - throw new Error('Logo countdown requires Label and native UITransform bindings'); - } - this.label = label; this.buttonTransform = buttonTransform; - this.textTransform = textTransform; this.parentTransform = parentTransform; - // The label follows the actual button center, so its old absolute Widget must not fight layout. - const widget = text.getComponent(Widget); if (widget) widget.enabled = false; - this.label.overflow = Label.Overflow.NONE; - button.active = false; text.active = false; - button.on(Node.EventType.TOUCH_END, onSkip); - } - - render(state: LogoCountdownState): void { - if (this.disposed) return; - this.button.active = state.visible; this.text.active = state.visible; - this.label.string = state.text; - if (!state.visible) return; - this.button.getComponent(Widget)?.updateAlignment(); - this.label.updateRenderData(true); - const button = this.buttonTransform, text = this.textTransform; - const center = button.convertToWorldSpaceAR(new Vec3((0.5 - button.anchorX) * button.width, (0.5 - button.anchorY) * button.height, 0)); - const position = this.parentTransform.convertToNodeSpaceAR(center); - position.x += (text.anchorX - 0.5) * text.width; - position.y += (text.anchorY - 0.5) * text.height; - this.text.setPosition(position); - } - - dispose(): void { - if (this.disposed) return; - this.disposed = true; - this.button.off(Node.EventType.TOUCH_END, this.onSkip); - this.button.active = false; this.text.active = false; - } -} - -/** Display gate only. Runtime still independently owns resource/config/socket gates. */ -export function createLogoCountdown(options: { - now(): number; - schedule(callback: () => void, milliseconds: number): () => void; - render(state: LogoCountdownState): void; -}) { - let started = false, finished = false, resources = false, socket = false; - let remaining = 0, deadline = 0; - let cancelDeadline: (() => void) | null = null, cancelTick: (() => void) | null = null; - let resolve!: () => void, reject!: (error: unknown) => void; - const promise = new Promise((yes, no) => { resolve = yes; reject = no; }); - // Cancellation may precede the host attaching the display gate. - void promise.catch(() => {}); - const render = () => options.render({ visible: started && !finished && remaining > 0, - text: `倒计时${Math.ceil(Math.max(0, remaining) / 1000)}s${resources ? ' 跳过' : ''}` }); - const finish = (error?: unknown) => { - if (finished) return; - finished = true; - const failures: unknown[] = error === undefined ? [] : [error]; - for (const cancel of [cancelDeadline, cancelTick]) { - try { cancel?.(); } catch (failure) { failures.push(failure); } - } - cancelDeadline = null; cancelTick = null; - try { render(); } catch (failure) { failures.push(failure); } - if (failures.length > 0) reject(failures[0]); else resolve(); - }; - const guard = (operation: () => void) => { - if (finished) return; - try { operation(); } catch (error) { finish(error); } - }; - const checkDeadline = () => { - cancelDeadline = null; - if (finished) return; - const wait = deadline - options.now(); - if (wait <= 0) { finish(); return; } - guard(() => { cancelDeadline = options.schedule(checkDeadline, wait); }); - }; - const tick = () => { - cancelTick = null; - if (finished) return; - guard(() => { - remaining -= 1000; - render(); - if (remaining > 1000) cancelTick = options.schedule(tick, 1000); - }); - }; - render(); - return { - promise, - start(milliseconds: number) { - if (finished) return; - if (started) throw new Error('Logo countdown already started'); - if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new Error('Invalid launchWaitTime'); - started = true; remaining = milliseconds; deadline = options.now() + milliseconds; - guard(() => { - render(); checkDeadline(); - if (!finished && remaining > 1000) cancelTick = options.schedule(tick, 1000); - }); - }, - resourcesReady() { guard(() => { resources = true; render(); }); }, - socketReady(value: boolean) { if (!finished) socket = value; }, - skip() { if (!finished && started && resources && socket) finish(); }, - cancel() { finish(new Error('Logo countdown cancelled')); }, - }; -} - -export interface AssetLoadTask { - readonly promise: Promise; - cancel(): void; -} - -type AssetLoadStarter = ( - onProgress: (completed: number, total: number) => void, - onComplete: (error: unknown, asset?: T) => void, -) => void; - -/** Owns loader callback lifetime so stop/failure cannot revive the login flow. */ -export function createAssetLoadTask(start: AssetLoadStarter, setProgress: (progress: number) => void): AssetLoadTask { - let settled = false; - let resolveTask!: (asset: T) => void; - let rejectTask!: (error: unknown) => void; - const promise = new Promise((resolve, reject) => { resolveTask = resolve; rejectTask = reject; }); - const reject = (error: unknown) => { - if (settled) return; - settled = true; - rejectTask(error); - }; - try { - setProgress(0); - start((completed, total) => { - if (settled) return; - if (!Number.isInteger(completed) || !Number.isInteger(total) || completed < 0 || total <= 0 || completed > total) { - reject(new Error(`Invalid asset load progress: completed=${completed}, total=${total}`)); - return; - } - try { setProgress(completed / total); } - catch (error) { reject(error); } - }, (error, asset) => { - if (settled) return; - if (error !== null && error !== undefined) { reject(error); return; } - if (asset === null || asset === undefined) { reject(new Error('Asset loader completed without an asset')); return; } - settled = true; - resolveTask(asset); - }); - } catch (error) { reject(error); } - return { promise, cancel() { reject(new Error('Asset load cancelled')); } }; -} - -export function requireNode(root: Node, path: string): Node { - const node = root.getChildByPath(path); - if (node === null) throw new Error(`Local login binding missing node: ${path}`); - return node; -} - -function describeFailure(error: unknown): string { - if (error instanceof Error) { - const details = 'errors' in error ? (error as Error & { errors: unknown[] }).errors : []; - return [error.message, ...details.map(describeFailure)].join('\n'); - } - if (typeof error === 'string') return error; - return JSON.stringify(error, (_key, value: unknown) => value instanceof Error ? describeFailure(value) : value); -} - -/** Projection of Controller models; owns only Cocos bindings and input state. */ -export class CocosLocalLoginView implements PlatformViewPort { - private visitor: Node | null = null; - private version: Label | null = null; - private mask: Node | null = null; - private touch: (() => void) | null = null; - private canLogin = false; - private busy = false; - private terminal = false; - private page: PageId = 'loading'; - - constructor(private readonly refs: ViewReferences) { - for (const [name, value] of Object.entries(refs)) { - if (value === null || value === undefined) throw new Error(`Local login binding missing ${name}`); - } - } - - bindLogin(instance: Node, onLogin: () => void): void { - if (this.visitor !== null || this.terminal) throw new Error('Local login view cannot bind twice or after disposal'); - const visitor = requireNode(instance, 'group-2/游客登录'); - if (visitor.getComponent(Sprite) === null) throw new Error('游客登录 requires Sprite'); - if (visitor.getComponent(UITransform) === null) throw new Error('游客登录 requires UITransform'); - const version = requireNode(instance, 'group-2/版本显示').getComponent(Label); - if (version === null) throw new Error('版本显示 requires Label'); - const mask = requireNode(instance, 'group-2/登录中遮罩'); - const hidden = ['微信登录', '手机登录', 'qq登录', '快速登录', '隐藏快速登录-1', '隐藏快速登录-2', - '登录头像背景框', '登录中头像', '登录中昵称', '登录中动画', '登录中头像框'] - .map(name => requireNode(instance, `group-2/${name}`)); - hidden.forEach(node => { node.active = false; }); - this.visitor = visitor; this.version = version; this.mask = mask; - this.touch = () => { - if (!this.terminal && !this.busy && this.canLogin && this.page === 'login') onLogin(); - }; - visitor.on(Node.EventType.TOUCH_END, this.touch); - mask.active = this.busy; - } - - setVersion(version: number): void { - if (this.version === null) throw new Error('Login instance must be bound before version display'); - this.version.string = String(version); - } - - render(model: PageModel): void { - if (this.terminal) return; - switch (model.page) { - case 'loading': this.refs.statusLabel.string = '正在加载资源和连接本地服务…'; break; - case 'login': this.canLogin = model.canLogin; break; - case 'lobby': - this.refs.resultLabel.string = `登录成功\n玩家 ID:${model.self.playerid}\n昵称:${model.self.nickname}\n豆豆:${model.self.bean}`; - break; - case 'room': throw new Error('Local login view cannot present room'); - } - } - - activate(page: PageId): void { - if (this.terminal) return; - if (page === 'room') throw new Error('Local login view cannot activate room'); - this.page = page; - this.refs.loadingRoot.active = page === 'loading'; - this.refs.loginRoot.active = page === 'login'; - this.refs.resultRoot.active = page === 'lobby'; - this.refs.statusOverlay.active = page === 'loading' || this.busy; - } - - setBusy(busy: boolean): void { - if (this.terminal) return; - this.busy = busy; - if (this.mask !== null) this.mask.active = busy; - this.refs.statusOverlay.active = busy || this.page === 'loading'; - if (busy) this.refs.statusLabel.string = '正在登录,请稍候…'; - } - - showOverlay(model: OverlayModel): void { - if (this.terminal) return; - switch (model.kind) { - case 'none': this.refs.statusOverlay.active = this.busy || this.page === 'loading'; break; - case 'reconnect': - this.refs.statusOverlay.active = true; - this.refs.statusLabel.string = '连接中断,正在重连…'; break; - case 'denial': this.showFailure(model.outcome.data); break; - case 'kicked': this.showFailure(model.data); break; - case 'fatal': this.showFailure(model.error); break; - } - } - - showFailure(error: unknown): void { - this.dispose(); - this.refs.statusOverlay.active = true; - this.refs.statusLabel.string = `验证失败:${describeFailure(error)}`; - } - - showStopped(): void { - if (this.terminal) return; - this.dispose(); - this.refs.statusOverlay.active = true; - this.refs.statusLabel.string = '已停止本次验证'; - } - - dispose(): void { - this.terminal = true; this.canLogin = false; this.busy = false; - if (this.visitor !== null && this.touch !== null) this.visitor.off(Node.EventType.TOUCH_END, this.touch); - this.touch = null; - if (this.mask !== null) this.mask.active = false; - } -} - -export function createCocosFrameScheduler(): FrameScheduler { - return { request(callback) { - const onDraw = () => { director.off(Director.EVENT_AFTER_DRAW, onDraw); callback(); }; - director.on(Director.EVENT_AFTER_DRAW, onDraw); - return () => { director.off(Director.EVENT_AFTER_DRAW, onDraw); }; - } }; -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts.meta deleted file mode 100644 index 3eb9eab..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts.meta +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "5af2497c-15f2-4493-be9b-a6391d25b878", - "files": [], - "subMetas": {}, - "userData": {} -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts deleted file mode 100644 index d4e1f67..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts +++ /dev/null @@ -1,120 +0,0 @@ -import type { LocalStartupContext } from '../../framework/config/local-startup.ts'; -import type { Transport } from '../../framework/net/transport.ts'; -import { WireClient, type WireEvent } from '../../framework/net/wire-client.ts'; -import type { PlatformWireClient } from '../../framework/platform/runtime.ts'; -import { parseLoginResponse } from '../../framework/protocol/contracts/index.ts'; -import type { EvidenceSink } from './local-login-transport.ts'; - -interface LocalLoginWireOptions { - context: LocalStartupContext; - transportFactory: () => Transport; - onFault: (error: unknown) => void; - record: EvidenceSink; -} - -/** ES2020-compatible aggregate retaining original errors, including non-Error values. */ -class LocalLoginWireFault extends Error { - readonly errors: readonly unknown[]; - constructor(errors: readonly unknown[]) { - super('Local login Wire failed with additional cleanup or evidence errors'); - this.name = 'LocalLoginWireFault'; - this.errors = Object.freeze([...errors]); - } -} - -export function createLocalLoginWire(options: LocalLoginWireOptions): PlatformWireClient { - const wire = new WireClient({ servers: options.context.config.servers, transportFactory: options.transportFactory }); - const listeners = new Set<(event: WireEvent) => void>(); - let stopped = false; - let faulted = false; - let terminalDelivery = false; - - function stop(): void { - if (stopped) return; - stopped = true; - unsubscribe(); - listeners.clear(); - wire.stop(); - } - - function fail(error: unknown, operation?: string, event?: WireEvent): unknown { - if (faulted) return error; - faulted = true; - const errors = [error]; - try { stop(); } catch (cleanupError) { errors.push(cleanupError); } - if (operation !== undefined) { - try { options.record({ kind: 'scope-violation', data: { operation, event } }); } - catch (recordError) { errors.push(recordError); } - } - const fault = errors.length === 1 ? error : new LocalLoginWireFault(errors); - options.onFault(fault); - return fault; - } - - function scopeError(operation: string): Error { - return new Error(`Local platform login scope forbids ${operation}`); - } - - function assertActive(): void { - if (stopped || faulted || terminalDelivery) throw new Error('Local login Wire is stopped or terminal'); - } - - function deliver(event: WireEvent): void { - for (const listener of [...listeners]) { - if (stopped || faulted) break; - listener(event); - } - } - - const unsubscribe = wire.subscribe(event => { - if (stopped || faulted || terminalDelivery) return; - try { - options.record({ kind: 'wire-event', data: event }); - if (stopped || faulted) return; - if (event.type !== 'message') { deliver(event); return; } - const { route, rpc, data } = event.message; - if (rpc === 'connect_agentserver' || rpc === 'connect_roomserver' || (route !== 'agent' && route !== 'platform')) { - fail(scopeError(`${route}/${rpc}`), `${route}/${rpc}`, event); - return; - } - let terminalOperation: string | undefined; - if (route === 'agent' && rpc === 'player_login') { - const parsed = parseLoginResponse(data); - if (parsed.state === 0 && parsed.room !== null) { - fail(scopeError('login room recovery'), 'login room recovery', event); - return; - } - if (parsed.state !== 0) terminalOperation = 'login rejected'; - } - if (route === 'agent' && rpc === 'kick_server') terminalOperation = 'kick_server'; - if (terminalOperation !== undefined) { - // Existing runtime failure display runs first, but cannot initiate more traffic. - terminalDelivery = true; - try { deliver(event); } - catch (error) { fail(error); return; } - fail(scopeError(terminalOperation), terminalOperation, event); - return; - } - deliver(event); - } catch (error) { fail(error); } - }); - - return { - subscribe(listener) { - if (stopped || faulted) return () => {}; - listeners.add(listener); - return () => { listeners.delete(listener); }; - }, - start() { assertActive(); wire.start(); }, - send(envelope) { - assertActive(); - if (envelope.route !== 'agent' || envelope.rpc !== 'player_login') { - throw fail(scopeError(`outbound ${envelope.route}/${envelope.rpc}`), `outbound ${envelope.route}/${envelope.rpc}`); - } - wire.send(envelope); - }, - switchServer() { assertActive(); throw fail(scopeError('switchServer'), 'switchServer'); }, - reconnectCurrent() { assertActive(); wire.reconnectCurrent(); }, - stop, - }; -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts.meta deleted file mode 100644 index 5db90f4..0000000 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts.meta +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "96a339e3-f954-4e96-b07f-9fe124d457f0", - "files": [], - "subMetas": {}, - "userData": {} -} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login.meta new file mode 100644 index 0000000..c71ae98 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "2d0af0de-962c-48db-a658-3f0396048806", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts new file mode 100644 index 0000000..16efd52 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts @@ -0,0 +1,9 @@ +import { _decorator, Component, SpriteFrame } from 'cc'; +const {ccclass,property}=_decorator; + +/** Serialized frame references keep both original phone-panel modes in the build. */ +@ccclass('PhoneLoginAssets') +export class PhoneLoginAssets extends Component { + @property(SpriteFrame) loginTitle:SpriteFrame|null=null; + @property(SpriteFrame) codeWaiting:SpriteFrame|null=null; +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts.meta new file mode 100644 index 0000000..107e83d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PhoneLoginAssets.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "1f765b40-c144-4ae2-b1b0-7b6ba99fe17a", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts new file mode 100644 index 0000000..0d6a78d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts @@ -0,0 +1,217 @@ +import { _decorator, assetManager, Component, Director, Label, Node, Prefab, ProgressBar, SceneAsset, + UITransform, Widget, director, instantiate, isValid } from 'cc'; +import { resolveRuntimeConfig, type RuntimeConfig } from '../../framework/config/runtime-config.ts'; +import { CocosLocalConfigFetcher } from './local-config-fetcher.ts'; +import { HttpConfigFetcher } from '../../framework/config/remote-config-fetcher.ts'; +import { resolveLaunchWaitTime } from '../../framework/config/remote-config.ts'; +import { resolveLoginPolicy, type LoginPolicy } from '../../framework/presentation/login-policy.ts'; +import { LoginAccounts, type LoginNativeSettings } from '../../framework/adapters/native/login-accounts.ts'; +import { WebViewJavascriptBridgeAdapter, type WebViewJavascriptBridgeEnvironment } from '../../framework/adapters/native/webview-javascript-bridge.ts'; +import { PlatformRuntime } from '../../framework/platform/runtime.ts'; +import { WireClient } from '../../framework/net/wire-client.ts'; +import { CocosWebSocketTransport } from '../../framework/net/cocos-transport.ts'; +import type { DeviceLoginMode, LoginAccountIdentity, LoginDeviceSnapshot } from '../../framework/protocol/contracts/login-contract.ts'; +import { createLoginStageGameEntry } from './login-stage-game-entry.ts'; +import { CocosLogoCountdownView, createLogoCountdown } from './logo-countdown.ts'; +import { requireNode } from './node-bindings.ts'; +import { SceneFlow } from './scene-flow.ts'; +import { LoginPanel } from './login-panel.ts'; + +const {ccclass,property}=_decorator; +function required(value:T|null|undefined,name:string):T{if(value==null)throw Error(`PlatformStartup missing ${name}`);return value;} + +/** One persistent owner for config/socket/account; scenes own their own visual trees. */ +@ccclass('PlatformStartup') +export class PlatformStartup extends Component { + @property(SceneAsset) loadingScene:SceneAsset|null=null; + @property(SceneAsset) loginScene:SceneAsset|null=null; + @property(Prefab) loadingPrefab:Prefab|null=null; + @property(Prefab) loginPrefab:Prefab|null=null; + @property(Prefab) protocolPrefab:Prefab|null=null; + @property(Prefab) phonePrefab:Prefab|null=null; + private runtime:PlatformRuntime|null=null; + private config:RuntimeConfig|null=null; + private policy:LoginPolicy|null=null; + private accounts:LoginAccounts|null=null; + private panel:LoginPanel|null=null; + private bridge:WebViewJavascriptBridgeAdapter|null=null; + private restoreShareLogin:(()=>void)|null=null; + private restoreLocation:(()=>void)|null=null; + private startLocation:(()=>void)|null=null; + private location:unknown=null; + private loginResponse:unknown=null; + private unsubscribe:(()=>void)|null=null; + private countdown:ReturnType|null=null; + private countdownView:CocosLogoCountdownView|null=null; + private progress:ProgressBar|null=null; + private status:Label|null=null; + private device:LoginDeviceSnapshot|null=null; + private busy=false; + private terminal=false; + private readonly events:Array<{kind:string;data:unknown}>=[]; + private readonly flow=new SceneFlow(name=>new Promise((resolve,reject)=>{ + if(!director.loadScene(name,error=>error?reject(error):resolve()))reject(Error(`Cannot load scene ${name}`)); + })); + + /** 常驻服务按加载场景、配置与桥接、连接、登录场景顺序启动;场景仅拥有自己的 UI。 */ + async start():Promise{ + try{ + required(this.loadingScene,'loadingScene');required(this.loginScene,'loginScene'); + required(this.loadingPrefab,'loadingPrefab');required(this.loginPrefab,'loginPrefab'); + required(this.protocolPrefab,'protocolPrefab');required(this.phonePrefab,'phonePrefab'); + director.addPersistRootNode(this.node); + await this.flow.enter(this.loadingScene!.name,()=>this.bindLoading()); + if(this.terminal)return; + const injected=window as unknown as {settings?:LoginNativeSettings & {startlocation(type:number):void};app_version?:number|string;app_appversion?:number|string}; + const bridgeHost=injected.app_version!==undefined&&Number(injected.app_version)!==0; + const native=bridgeHost?{ + getcompareCode:()=>Number(injected.app_appversion), + // uAgent_3 passes the original argument, not its unused local tp variable. + accreditlogin:(provider:number)=>required(this.bridge,'native bridge').call('accreditlogin',provider===2?undefined:provider,()=>{}), + }:injected.settings; + this.config=await resolveRuntimeConfig({hostKind:bridgeHost?'uAgent_3':native?'native-settings':'h5',win:window as any, + search:window.location.search,fetcher:new HttpConfigFetcher(),localFetcher:new CocosLocalConfigFetcher()}); + if(this.terminal)return; + this.policy=resolveLoginPolicy(this.config.remoteConfig); + const loginConfig={source:this.config.source,gameserver:this.config.gameserver,identity:this.config.identity, + showVisitor:this.policy.showVisitor,deviceLogin:this.policy.deviceLogin,qqLogin:this.policy.qqLogin}; + this.events.push({kind:'login-config',data:loginConfig}); + if(this.config.isDebugger)console.info('[PlatformStartup] login-config',JSON.stringify(loginConfig)); + this.accounts=new LoginAccounts({storage:window.localStorage,settings:native,random:Math.random,now:Date.now, + gameid:this.config.identity.gameid,agentid:this.config.identity.agentid,policy:this.policy, + submit:(account,mode)=>this.login(account,mode),tip:text=>this.tip(text)}); + const previous=(window as any).sharelogin; + const callback=(openid:unknown,headimgurl:unknown,nickname:unknown,sex:unknown,city:unknown,Province:unknown,unionid:unknown)=>{ + if(!this.terminal)this.acceptNative({openid,headimgurl,nickname,sex,city,Province,unionid},'global'); + }; + (window as any).sharelogin=callback; + this.restoreShareLogin=()=>{if((window as any).sharelogin===callback){if(previous===undefined)delete (window as any).sharelogin;else (window as any).sharelogin=previous;}}; + const oldLocation=(window as any).getlocationinfo; + const locationCallback=(data:unknown)=>{ + if(this.terminal)return; + try{ + const value=typeof data==='string'?JSON.parse(data):data; + if(value===null||typeof value!=='object'||Array.isArray(value))throw Error('Invalid getlocationinfo payload'); + this.location=value; + }catch(error){this.tip(error instanceof Error?error.message:String(error));} + }; + (window as any).getlocationinfo=locationCallback; + this.restoreLocation=()=>{if((window as any).getlocationinfo===locationCallback){if(oldLocation===undefined)delete (window as any).getlocationinfo;else (window as any).getlocationinfo=oldLocation;}}; + if(native){ + this.bridge=new WebViewJavascriptBridgeAdapter(window as unknown as WebViewJavascriptBridgeEnvironment); + this.bridge.register('sharelogin',data=>this.acceptNative(data,'bridge')); + this.bridge.register('getlocationinfo',locationCallback); + this.startLocation=()=>{if(bridgeHost)required(this.bridge,'native bridge').call('startlocation',2,()=>{}); + else required(injected.settings,'settings').startlocation(2);}; + } + required(this.countdown,'countdown').start(resolveLaunchWaitTime(this.config.remoteConfig)); + this.runtime=new PlatformRuntime({gameEntry:createLoginStageGameEntry(this.config.identity.gameid,event=>this.events.push(event)), + resolveRuntimeConfig:async()=>required(this.config,'config'), + createWireClient:config=>{ + const wire=new WireClient({servers:config.servers,transportFactory:()=>new CocosWebSocketTransport()}); + wire.subscribe(event=>{this.events.push({kind:'wire',data:event.type}); + if(event.type==='message'&&event.message.route==='agent'&&event.message.rpc==='player_login')this.loginResponse=event.message.data; + });return wire; + }, + scene:{showLoading:()=>{},showLogin:()=>{void this.showLogin().catch(error=>this.fail(error));}, + showLobby:()=>{try{required(this.accounts,'accounts').loginSucceeded(required(this.loginResponse,'login response'),required(this.device,'prepared login device').deviceLogin); + this.busy=false;this.panel?.setBusy(false);this.panel?.close();this.tip('登录成功');}catch(error){this.fail(error);}}, + showRoom:()=>this.fail(Error('当前入口尚未接入子游戏场景')), + showReconnect:()=>this.tip('连接中断,正在重连…'), + showKicked:data=>this.fail(Error(`账号退出:${JSON.stringify(data)}`)), + showServerDenial:outcome=>{this.busy=false;this.panel?.setBusy(false);this.tip(JSON.stringify(outcome.data));}, + showFatal:error=>this.fail(error)}, + loadResources:()=>this.loadLoginResources(), + waitForMinimumDisplay:()=>required(this.countdown,'countdown').promise, + getLoginDeviceSnapshot:()=>required(this.device,'prepared login device'), + }); + this.unsubscribe=this.runtime.subscribeState(state=>this.countdown?.socketReady(state.app.phase==='connected'||state.app.phase==='logged-in'),error=>this.fail(error)); + await this.runtime.start(); + }catch(error){this.fail(error);} + } + private canvas():Node{return required(director.getScene(),'scene').getChildByName('Canvas') ?? (()=>{throw Error('Scene requires Canvas');})();} + private createStatus():void{ + const canvas=this.canvas();const node=new Node('Status');node.parent=canvas;node.layer=canvas.layer; + node.setPosition(0,315,0);node.addComponent(UITransform).setContentSize(1200,60); + this.status=node.addComponent(Label);this.status.string='';this.status.fontSize=24;this.status.lineHeight=28; + } + private async bindLoading():Promise{ + const logo=instantiate(required(this.loadingPrefab,'loadingPrefab'));logo.parent=this.canvas();logo.setPosition(0,0,0); + this.createStatus();requireNode(logo,'group-32').active=false; + this.progress=required(requireNode(logo,'group-1/Spirit254').getComponent(ProgressBar),'Logo ProgressBar'); + const labels=logo.getComponentsInChildren(Label).filter(label=>label.node.name==='Spirit3104'); + if(labels.length!==1)throw Error('Logo requires one Spirit3104'); + this.countdownView=new CocosLogoCountdownView(requireNode(logo,'group-1/Spirit3103'),labels[0].node,()=>this.countdown?.skip()); + this.countdown=createLogoCountdown({now:()=>performance.now(),schedule:(callback,ms)=>{const id=window.setTimeout(callback,ms);return ()=>window.clearTimeout(id);}, + render:state=>this.countdownView?.render(state)}); + await new Promise(resolve=>director.once(Director.EVENT_AFTER_DRAW,resolve)); + this.events.push({kind:'scene',data:'Loading'}); + } + private async loadLoginResources():Promise{ + const prefab=required(this.loginPrefab,'loginPrefab'); + await new Promise((resolve,reject)=>assetManager.loadAny(prefab.uuid,(done,total)=>{ + if(!this.terminal&&this.progress&&total>0)this.progress.progress=done/total; + },error=>error?reject(error):resolve())); + if(this.terminal)throw Error('Startup stopped'); + this.countdown?.resourcesReady();required(this.progress,'progress').node.active=false; + } + /** 完成真实场景切换后再绑定按钮和尝试缓存登录;弹窗挂在独立 PopupRoot。 */ + private async showLogin():Promise{ + this.countdownView?.dispose();this.countdownView=null; + await this.flow.enter(required(this.loginScene,'loginScene').name,()=>{ + const root=instantiate(required(this.loginPrefab,'loginPrefab'));root.parent=this.canvas();root.setPosition(0,0,0); + const popup=new Node('PopupRoot');popup.parent=this.canvas();popup.layer=root.layer;popup.addComponent(UITransform).setContentSize(1600,720); + this.createStatus();this.panel=new LoginPanel(root,popup,required(this.protocolPrefab,'protocolPrefab'),required(this.phonePrefab,'phonePrefab'), + required(this.accounts,'accounts'),required(this.policy,'policy'),message=>this.tip(message), + ()=>!this.busy&&!this.terminal&&this.runtime?.state.app.phase==='connected'); + const version=required(requireNode(root,'group-2/版本显示').getComponent(Label),'version Label'); + // Beta 展示格式使用当前渠道版本号;只改变显示,不改变登录协议中的数值版本。 + version.string=`v${required(this.config,'config').identity.version}.0.0 Beta`; + // Label 自动尺寸更新后重新计算左上角 Widget,避免长版本文字向屏幕外扩展。 + required(version.node.getComponent(UITransform),'version UITransform').setAnchorPoint(0,1); + version.updateRenderData(true); + const versionWidget=required(version.node.getComponent(Widget),'version Widget'); + versionWidget.target=this.canvas(); + versionWidget.updateAlignment(); + this.events.push({kind:'scene',data:'Login'}); + // Legacy location acquisition is independent of login. Keep the source null + // until a real callback arrives; report host failure without stopping the socket. + try{this.startLocation?.();}catch(error){this.tip(`定位接口调用失败:${error instanceof Error?error.message:String(error)}`);} + if(this.accounts!.cached())this.accounts!.autoLogin(); + }); + } + private acceptNative(data:unknown,channel:'global'|'bridge'):void{ + if(this.terminal)return; + try{required(this.accounts,'accounts').acceptNative(data,channel);}catch(error){this.tip(error instanceof Error?error.message:String(error));} + } + /** 所有登录最终收口:再次校验协议与就绪状态,保存本次模式供登录响应使用。 */ + private login(account:LoginAccountIdentity,mode:DeviceLoginMode):void{ + const runtime=required(this.runtime,'runtime'); + if(this.busy||this.terminal)return; + if(!required(this.accounts,'accounts').requireAgreement())return; + if(!runtime.ready)throw Error('登录资源或连接尚未就绪'); + const storage=window.localStorage; + let machineid=storage.getItem('tsgame_machineId_'); + if(machineid===null){machineid=`${Date.now()}-${1000+Math.floor(Math.random()*8999)}`;storage.setItem('tsgame_machineId_',machineid);} + const host=window as unknown as {returnCitySN?:{ip:string;province:string;city:string}}; + const city=host.returnCitySN; + if(city!==undefined&&(!city||typeof city.ip!=='string'||typeof city.province!=='string'||typeof city.city!=='string'))throw Error('Invalid returnCitySN'); + this.device={location:this.location,machineid,machineroom:'',deviceLogin:mode,loginPlayerId:{enabled:false},...(city?{ip:city.ip}:{})}; + this.busy=true;this.panel?.setBusy(true); + try{runtime.login(city?{...account,province:city.province,city:city.city}:account);} + catch(error){this.busy=false;this.panel?.setBusy(false);throw error;} + } + private tip(text:string):void{if(this.status&&isValid(this.status))this.status.string=text;} + private fail(error:unknown):void{ + this.tip(error instanceof Error?error.message:String(error));console.error('[PlatformStartup]',error); + this.release(); + } + /** 幂等释放计时器、订阅和宿主回调,阻止销毁后的迟到消息重新激活流程。 */ + private release():void{ + if(this.terminal)return;this.terminal=true;this.flow.dispose();this.panel?.dispose(); + this.countdown?.cancel();this.countdownView?.dispose();this.unsubscribe?.();this.bridge?.dispose(); + this.restoreShareLogin?.();this.restoreLocation?.();this.runtime?.stop(); + } + onDestroy():void{this.release();} + getDiagnostics(){return {events:[...this.events],ready:this.runtime?.ready,busy:this.busy,terminal:this.terminal};} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts.meta new file mode 100644 index 0000000..910d262 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/PlatformStartup.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "6dba829e-bbd7-4da8-bd48-2f214eeccc93", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts new file mode 100644 index 0000000..5cdd702 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts @@ -0,0 +1,29 @@ +import { resources, TextAsset } from 'cc'; +import { DEBUG } from 'cc/env'; +import { ConfigFetchError, ConfigParseError, type ConfigFetcher } from '../../framework/config/remote-config.ts'; + +/** + * 开发专用配置读取器。使用 Cocos 资源数据库读取项目 txt,支持预览和调试构建。 + * 路径由 profiles.ts 唯一指定;读取失败绝不改用远程,以免测试了错误的数据。 + */ +export class CocosLocalConfigFetcher implements ConfigFetcher { + async fetch(resourcePath: string): Promise { + // 除启动配置的 debug 限制外,发布构建也在引擎边界禁止加载开发配置。 + if (!DEBUG) throw new ConfigFetchError('本地配置仅允许开发预览或调试构建'); + const asset = await new Promise((resolve, reject) => { + resources.load(resourcePath, TextAsset, (error, result) => { + if (error) reject(new ConfigFetchError(`本地配置读取失败: ${resourcePath}.txt: ${error.message}`)); + else if (!result) reject(new ConfigFetchError(`本地配置资源为空: ${resourcePath}.txt`)); + else resolve(result); + }); + }); + try { + return JSON.parse(asset.text) as unknown; + } catch { + throw new ConfigParseError(`本地配置 JSON 解析失败: ${resourcePath}.txt`); + } finally { + // 内容已转为普通数据;下次启动重新加载,避免沿用旧 TextAsset 缓存。 + resources.release(resourcePath, TextAsset); + } + } +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts.meta new file mode 100644 index 0000000..20adc89 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "97325d47-c809-4a38-9e1a-520ee2a3d863", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts new file mode 100644 index 0000000..3b0d919 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts @@ -0,0 +1,166 @@ +import { Animation, assetManager, BlockInputEvents, Color, EditBox, ImageAsset, Label, Node, Prefab, ScrollView, Sprite, SpriteFrame, Texture2D, UITransform, Widget, instantiate, isValid } from 'cc'; +import type { LoginAccounts } from '../../framework/adapters/native/login-accounts.ts'; +import { loginButtons, type LoginPolicy } from '../../framework/presentation/login-policy.ts'; +import { requireNode } from './node-bindings.ts'; +import { PhoneLoginAssets } from './PhoneLoginAssets.ts'; + +/** 登录场景节点与弹窗的生命周期管理;协议状态和账户行为由 LoginAccounts 唯一持有。 */ +export class LoginPanel { + private readonly releases:Array<()=>void>=[]; + private modal:Node|null=null; + private modalReleases:Array<()=>void>=[]; + private disposed=false; + private readonly loadingAnimation:Animation; + constructor(readonly root:Node,private readonly popupRoot:Node, + private readonly protocol:Prefab,private readonly phone:Prefab, + private readonly accounts:LoginAccounts,policy:LoginPolicy, + private readonly tip:(message:string)=>void,private readonly canAct:()=>boolean){ + const group=requireNode(root,'group-2'); + const animation=requireNode(group,'Logining/登录中动画').getComponent(Animation); + if(!animation||!animation.defaultClip)throw Error('登录中动画 requires Animation and default clip'); + this.loadingAnimation=animation; + const state=loginButtons(policy,accounts.versionState,accounts.cached()!==null); + const names={wechat:'微信登录',visitor:'游客登录',phone:'手机登录',quick:'快速登录',qq:'qq登录'}; + for(const key of Object.keys(names) as Array)requireNode(group,names[key]).active=state[key]; + // JumpWxAuth uses legacy 1280-wide coordinates; prefab coordinates are centered. + const left=(name:string,x:number,width?:number,height?:number)=>{ + const node=requireNode(group,name),ui=node.getComponent(UITransform);if(!ui)throw Error(`${name} requires UITransform`); + const widget=node.getComponent(Widget);if(widget)widget.enabled=false; + if(width!==undefined&&height!==undefined)ui.setContentSize(width,height); + node.setPosition(x-640+ui.width*ui.anchorX,node.position.y,node.position.z); + }; + if(policy.deviceLogin)left('手机登录',490); + if(accounts.versionState===1){requireNode(group,'渠道logo').active=false;left('微信登录',113,320,98);left('游客登录',488);} + else left('微信登录',415); + // Both switches may be enabled by the remote hierarchy. Keep both actions + // visible, with the user-requested side-by-side layout instead of overlap. + if(state.visitor&&state.phone){ + for(const [name,x] of [['游客登录',-210],['手机登录',210]] as const){ + const node=requireNode(group,name),widget=node.getComponent(Widget);if(widget)widget.enabled=false; + node.setPosition(x,node.position.y,node.position.z); + } + } + // 登录中展示节点统一位于 prefab 的 Logining 分组,路径必须与资源一致。 + const logining=requireNode(group,'Logining'); + for(const name of ['登录头像背景框','登录中头像','登录中昵称','登录中动画','登录中头像框'])requireNode(logining,name).active=state.account; + this.setBusy(state.account); + const cached=accounts.cached();if(cached){const label=requireNode(logining,'登录中昵称').getComponent(Label);if(!label)throw Error('登录中昵称 requires Label');label.string=cached.nickname;} + if(cached?.avatar){ + const sprite=requireNode(logining,'登录中头像').getComponent(Sprite);if(!sprite)throw Error('登录中头像 requires Sprite'); + assetManager.loadRemote(cached.avatar,{ext:'.png'},(error,image)=>{ + if(this.disposed||!isValid(sprite))return; + if(error){this.tip(`登录头像加载失败:${error.message}`);return;} + const texture=new Texture2D();texture.image=image;const frame=new SpriteFrame();frame.texture=texture;sprite.spriteFrame=frame; + this.releases.push(()=>{if(isValid(sprite))sprite.spriteFrame=null;frame.destroy();texture.destroy();}); + }); + } + const check=requireNode(group,'协议确认/check');check.active=accounts.agreed; + this.bind(requireNode(group,'协议确认'),()=>{accounts.setAgreement(!accounts.agreed);check.active=accounts.agreed;}); + this.bind(requireNode(group,'协议内容'),()=>this.openProtocol()); + this.bind(requireNode(group,'微信登录'),()=>accounts.authorize('wechat')); + this.bind(requireNode(group,'qq登录'),()=>accounts.authorize('qq')); + this.bind(requireNode(group,'游客登录'),()=>accounts.visitor()); + this.bind(requireNode(group,'快速登录'),()=>accounts.device()); + this.bind(requireNode(group,'手机登录'),()=>this.openPhone()); + this.bind(requireNode(group,'隐藏快速登录-1'),()=>accounts.hiddenFirst()); + this.bind(requireNode(group,'隐藏快速登录-2'),()=>accounts.hiddenSecond()); + } + private bind(node:Node,action:()=>void,modal=false){ + const listener=()=>{if(this.disposed||!this.canAct())return;try{action();}catch(error){this.tip(error instanceof Error?error.message:String(error));}}; + node.on(Node.EventType.TOUCH_END,listener); + (modal?this.modalReleases:this.releases).push(()=>node.off(Node.EventType.TOUCH_END,listener)); + } + private open(prefab:Prefab):Node|null{ + if(this.modal!==null)return null; + const modal=instantiate(prefab);modal.parent=this.popupRoot;modal.setPosition(0,0,0);modal.active=true; + this.modal=modal;return modal; + } + /** 协议弹窗独立实例化,每次打开回到顶部,关闭时解除监听并销毁实例。 */ + openProtocol():void{ + const modal=this.open(this.protocol);if(!modal)return; + const scroll=requireNode(modal,'group-37/协议滚动').getComponent(ScrollView); + if(!scroll)throw Error('协议滚动 requires ScrollView');scroll.scrollToTop(0); + for(const name of ['Spirit253','协议遮罩'])this.bind(requireNode(modal,`group-37/${name}`),()=>this.close(),true); + } + /** 沿用原工程输入与验证码倒计时规则;弹窗不另存协议勾选状态。 */ + openPhone():void{ + if(!this.accounts.requireAgreement())return; + const modal=this.open(this.phone);if(!modal)return; + const group=requireNode(modal,'group-106'); + requireNode(modal,'group-108').active=false; + const frames=modal.getComponent(PhoneLoginAssets); + if(!frames?.loginTitle||!frames.codeWaiting)throw Error('PhoneLoginAssets requires loginTitle/codeWaiting'); + const title=requireNode(group,'Spirit3252').getComponent(Sprite);if(!title)throw Error('Spirit3252 requires Sprite');title.spriteFrame=frames.loginTitle; + const saved=this.accounts.phonePanelState(); + const input=(name:string,textName:string,placeholderName:string)=>{ + const node=requireNode(group,name),labelNode=requireNode(group,textName); + const placeholder=requireNode(group,placeholderName); + const glyph=labelNode.getComponent(UITransform);if(!glyph)throw Error(`${textName} requires UITransform`); + // Existing art remains the input background; native EditBox owns text entry. + const ui=node.getComponent(UITransform);if(!ui)throw Error(`${name} requires UITransform`); + const text=new Node('InputText');text.parent=node;text.layer=node.layer; + const textUi=text.addComponent(UITransform);textUi.setAnchorPoint(0,0.5);textUi.setContentSize(ui.width-85,ui.height); + text.setPosition(-ui.width/2+65,0,0); + const inputLabel=text.addComponent(Label);inputLabel.string='';inputLabel.fontSize=glyph.height; + inputLabel.horizontalAlign=Label.HorizontalAlign.LEFT;inputLabel.overflow=Label.Overflow.CLAMP;inputLabel.color=Color.BLACK; + const hint=new Node('Placeholder');hint.parent=node;hint.layer=node.layer; + hint.addComponent(UITransform).setContentSize(ui.width-20,ui.height); + const hintLabel=hint.addComponent(Label);hintLabel.string='';hintLabel.fontSize=glyph.height; + // Configure before onLoad so EditBox does not create orphan default labels. + labelNode.active=false;node.active=false; + const edit=node.addComponent(EditBox);edit.textLabel=inputLabel;edit.placeholderLabel=hintLabel; + edit.string='';edit.placeholder=''; + edit.inputMode=EditBox.InputMode.NUMERIC;edit.maxLength=18; + const changed=()=>{edit.string=edit.string.replace(/\D/g,'');placeholder.active=edit.string.length===0;}; + node.on(EditBox.EventType.TEXT_CHANGED,changed);this.modalReleases.push(()=>node.off(EditBox.EventType.TEXT_CHANGED,changed)); + changed();node.active=true; + const alignText=()=>{ + textUi.setAnchorPoint(0,1);textUi.setContentSize(ui.width-85,ui.height); + text.setPosition(-ui.width/2+65,ui.height/2,0);inputLabel.verticalAlign=Label.VerticalAlign.CENTER; + }; + alignText();node.on(Node.EventType.SIZE_CHANGED,alignText); + this.modalReleases.push(()=>node.off(Node.EventType.SIZE_CHANGED,alignText));return edit; + }; + const phone=input('Spirit3243','Spirit3247','Spirit3249'); + const code=input('Spirit3244','Spirit3248','Spirit3250'); + if(saved?.phoneNumber){phone.string=saved.phoneNumber;requireNode(group,'Spirit3249').active=false;} + const codeButton=requireNode(group,'Spirit3245'),codeSprite=codeButton.getComponent(Sprite); + if(!codeSprite||!codeSprite.spriteFrame)throw Error('Spirit3245 requires SpriteFrame'); + const idleFrame=codeSprite.spriteFrame; + const timerNode=requireNode(group,'Spirit3246'),timerSprite=timerNode.getComponent(Sprite); + if(!timerSprite)throw Error('Spirit3246 requires Sprite');timerSprite.enabled=false; + const timerWidget=timerNode.getComponent(Widget);if(timerWidget)timerWidget.enabled=false; + timerNode.setPosition(codeButton.position);timerNode.getComponent(UITransform)!.setContentSize(148,28); + const timerText=new Node('CountdownText');timerText.parent=timerNode;timerText.layer=timerNode.layer; + timerText.addComponent(UITransform).setContentSize(148,28); + const timerLabel=timerText.addComponent(Label);timerLabel.fontSize=28;timerLabel.color=Color.BLACK; + let restTime=saved===null?0:saved.restTime; + let waiting=restTime>0; + const renderTimer=()=>{timerNode.active=waiting;timerLabel.string=`${restTime}秒`;codeSprite.spriteFrame=waiting?frames.codeWaiting:idleFrame;}; + renderTimer(); + if(waiting){const timer=window.setInterval(()=>{restTime--;if(restTime<0){waiting=false;window.clearInterval(timer);}renderTimer();},1000); + this.modalReleases.push(()=>window.clearInterval(timer));} + this.bind(codeButton,()=>{if(restTime<=0&&phone.string)this.tip('请通过相关渠道获取');},true); + this.bind(requireNode(group,'Spirit3253'),()=>{if(!this.accounts.requireAgreement())return; + if(!phone.string||!code.string){this.tip('手机号与验证码均不能为空!');return;} + this.accounts.phone(phone.string,code.string);this.close();},true); + for(const name of ['Spirit3251','Spirit3241'])this.bind(requireNode(group,name),()=>this.close(),true); + const background=requireNode(group,'Spirit3242');if(!background.getComponent(BlockInputEvents))background.addComponent(BlockInputEvents); + } + /** 帧切换由 prefab 的 AnimationClip 驱动;登录状态只负责显隐及播放生命周期。 */ + setBusy(busy:boolean):void{ + const group=requireNode(this.root,'group-2/Logining'); + group.active=busy; + for(const name of ['登录中遮罩','登录头像背景框','登录中头像','登录中昵称','登录中动画','登录中头像框'])requireNode(group,name).active=busy; + if(busy){ + const clip=this.loadingAnimation.defaultClip!; + if(!this.loadingAnimation.getState(clip.name)?.isPlaying)this.loadingAnimation.play(clip.name); + }else this.loadingAnimation.stop(); + } + /** 清理弹窗事件和验证码计时器,避免反复打开时累积回调。 */ + close():void{ + this.modalReleases.splice(0).forEach(release=>release()); + if(this.modal&&isValid(this.modal))this.modal.destroy();this.modal=null; + } + dispose():void{this.setBusy(false);this.disposed=true;this.close();this.releases.splice(0).forEach(release=>release());} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts.meta new file mode 100644 index 0000000..0df7bbf --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-panel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e79a22bd-cbb6-41d0-a2ca-6ffb7e340f38", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts similarity index 71% rename from cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts rename to cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts index 6e39db3..1351b0b 100644 --- a/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts @@ -1,8 +1,12 @@ import type { GameEntry } from '../../framework/sdk/contracts/game-entry.ts'; -import type { EvidenceSink } from './local-login-transport.ts'; +/** 登录阶段的诊断事件,仅供入口记录生命周期和越界操作。 */ +export type LoginEntryEvent = { kind: string; data: unknown }; +type EvidenceSink = (event: LoginEntryEvent) => void; -/** Satisfies entry validation while making every actual game operation a scope error. */ -export function createDiagnosticGameEntry(gameId: string | number, record: EvidenceSink): GameEntry { +/** 登录阶段尚未接入子游戏:满足运行时入口契约,但任何游戏操作均显式拒绝。 + * 接入真实子游戏时应替换此入口,不能把占位实现当作大厅或房间支持。 + */ +export function createLoginStageGameEntry(gameId: string | number, record: EvidenceSink): GameEntry { let nextInstanceId = 0; function violation(operation: string, instanceId?: number): never { record({ kind: 'scope-violation', data: { operation, instanceId } }); diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts.meta new file mode 100644 index 0000000..a90a03d --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "57db0201-3b1b-482e-b783-f19e5e9a47a7", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts new file mode 100644 index 0000000..61df5c2 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts @@ -0,0 +1,116 @@ +import { Label, Node, UITransform, Vec3, Widget } from 'cc'; + +/** Logo 展示状态;倒计时只控制最短展示时间,资源和连接就绪由运行时独立管理。 */ +export interface LogoCountdownState { readonly visible: boolean; readonly text: string; } + +/** Native input and measured label layout, independent of serialized label parentage. */ +export class CocosLogoCountdownView { + private readonly label: Label; + private readonly buttonTransform: UITransform; + private readonly textTransform: UITransform; + private readonly parentTransform: UITransform; + private disposed = false; + + constructor(private readonly button: Node, private readonly text: Node, private readonly onSkip: () => void) { + const label = text.getComponent(Label), buttonTransform = button.getComponent(UITransform); + const textTransform = text.getComponent(UITransform), parentTransform = text.parent?.getComponent(UITransform); + if (!label || !buttonTransform || !textTransform || !parentTransform) { + throw new Error('Logo countdown requires Label and native UITransform bindings'); + } + this.label = label; this.buttonTransform = buttonTransform; + this.textTransform = textTransform; this.parentTransform = parentTransform; + // The label follows the actual button center, so its old absolute Widget must not fight layout. + const widget = text.getComponent(Widget); if (widget) widget.enabled = false; + this.label.overflow = Label.Overflow.NONE; + button.active = false; text.active = false; + button.on(Node.EventType.TOUCH_END, onSkip); + } + + render(state: LogoCountdownState): void { + if (this.disposed) return; + this.button.active = state.visible; this.text.active = state.visible; + this.label.string = state.text; + if (!state.visible) return; + this.button.getComponent(Widget)?.updateAlignment(); + this.label.updateRenderData(true); + const button = this.buttonTransform, text = this.textTransform; + const center = button.convertToWorldSpaceAR(new Vec3((0.5 - button.anchorX) * button.width, (0.5 - button.anchorY) * button.height, 0)); + const position = this.parentTransform.convertToNodeSpaceAR(center); + position.x += (text.anchorX - 0.5) * text.width; + position.y += (text.anchorY - 0.5) * text.height; + this.text.setPosition(position); + } + + dispose(): void { + if (this.disposed) return; + this.disposed = true; + this.button.off(Node.EventType.TOUCH_END, this.onSkip); + this.button.active = false; this.text.active = false; + } +} + +/** Display gate only. Runtime still independently owns resource/config/socket gates. */ +export function createLogoCountdown(options: { + now(): number; + schedule(callback: () => void, milliseconds: number): () => void; + render(state: LogoCountdownState): void; +}) { + let started = false, finished = false, resources = false, socket = false; + let remaining = 0, deadline = 0; + let cancelDeadline: (() => void) | null = null, cancelTick: (() => void) | null = null; + let resolve!: () => void, reject!: (error: unknown) => void; + const promise = new Promise((yes, no) => { resolve = yes; reject = no; }); + // Cancellation may precede the host attaching the display gate. + void promise.catch(() => {}); + const render = () => options.render({ visible: started && !finished && remaining > 0, + text: `倒计时${Math.ceil(Math.max(0, remaining) / 1000)}s${resources ? ' 跳过' : ''}` }); + const finish = (error?: unknown) => { + if (finished) return; + finished = true; + const failures: unknown[] = error === undefined ? [] : [error]; + for (const cancel of [cancelDeadline, cancelTick]) { + try { cancel?.(); } catch (failure) { failures.push(failure); } + } + cancelDeadline = null; cancelTick = null; + try { render(); } catch (failure) { failures.push(failure); } + if (failures.length > 0) reject(failures[0]); else resolve(); + }; + const guard = (operation: () => void) => { + if (finished) return; + try { operation(); } catch (error) { finish(error); } + }; + const checkDeadline = () => { + cancelDeadline = null; + if (finished) return; + const wait = deadline - options.now(); + if (wait <= 0) { finish(); return; } + guard(() => { cancelDeadline = options.schedule(checkDeadline, wait); }); + }; + const tick = () => { + cancelTick = null; + if (finished) return; + guard(() => { + remaining -= 1000; + render(); + if (remaining > 1000) cancelTick = options.schedule(tick, 1000); + }); + }; + render(); + return { + promise, + start(milliseconds: number) { + if (finished) return; + if (started) throw new Error('Logo countdown already started'); + if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new Error('Invalid launchWaitTime'); + started = true; remaining = milliseconds; deadline = options.now() + milliseconds; + guard(() => { + render(); checkDeadline(); + if (!finished && remaining > 1000) cancelTick = options.schedule(tick, 1000); + }); + }, + resourcesReady() { guard(() => { resources = true; render(); }); }, + socketReady(value: boolean) { if (!finished) socket = value; }, + skip() { if (!finished && started && resources && socket) finish(); }, + cancel() { finish(new Error('Logo countdown cancelled')); }, + }; +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts.meta new file mode 100644 index 0000000..6d32de9 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/logo-countdown.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "1baca79a-2862-41de-b01e-616eb90baf04", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts new file mode 100644 index 0000000..df81645 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts @@ -0,0 +1,8 @@ +import { Node } from 'cc'; + +/** 按迁移资源的固定路径取节点;绑定缺失直接报错,避免把资源错误掩盖成按钮无响应。 */ +export function requireNode(root: Node, path: string): Node { + const node = root.getChildByPath(path); + if (node === null) throw new Error(`Platform login binding missing node: ${path}`); + return node; +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts.meta new file mode 100644 index 0000000..934100a --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/node-bindings.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "a7ceb35e-66c2-4ecf-ba76-3e8ee1aac9a0", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts new file mode 100644 index 0000000..eb87515 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts @@ -0,0 +1,18 @@ +/** Serializes scene lifetime; services are owned by the persistent startup component. */ +export class SceneFlow { + private pending:Promise=Promise.resolve(); + private disposed=false; + constructor(private readonly load:(name:string)=>Promise){} + /** 串行加载后绑定新场景;等待期间已销毁则禁止继续绑定。 */ + enter(name:string,bind:()=>void|Promise):Promise{ + const next=this.pending.then(async()=>{ + if(this.disposed)throw Error('SceneFlow disposed'); + await this.load(name); + if(this.disposed)throw Error('SceneFlow disposed'); + await bind(); + }); + this.pending=next; + return next; + } + dispose():void{this.disposed=true;} +} diff --git a/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts.meta b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts.meta new file mode 100644 index 0000000..65b787c --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/scripts/platform-login/scene-flow.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "5fb154af-f704-44d3-9786-640db8ac3051", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/out/legacy-migration/BackHall_Layer.prefab b/cocoscreator_projects/YouleNexus/out/legacy-migration/BackHall_Layer.prefab index 5b599e6..7c1d49f 100644 --- a/cocoscreator_projects/YouleNexus/out/legacy-migration/BackHall_Layer.prefab +++ b/cocoscreator_projects/YouleNexus/out/legacy-migration/BackHall_Layer.prefab @@ -196,7 +196,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "yuLUeDXKPyDesn2Q1qhhWq" + "fileId": "b32JxFEuROt3TrHJPbNqtv" }, { "__type__": "cc.ProgressBar", @@ -234,7 +234,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "qc3XCkoNQIDpH6wTCxQi7x" + "fileId": "ObM6lyV6YYtGvMYs4ei9VA" }, { "__type__": "cc.Widget", @@ -266,7 +266,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "U5IlbCYg18o2PRDG5LmLi9" + "fileId": "pPsz4F8UHqSoNaFwhIjyrY" }, { "__type__": "cc.PrefabInfo", @@ -276,7 +276,7 @@ "asset": { "__id__": 0 }, - "fileId": "6fepRbLq0GfRc6IezdSOgw", + "fileId": "zZDIED9SXJMz2T6Q5o8Vj8", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -307,7 +307,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "Ez6HTW5UovXUbxsmsQt1FE" + "fileId": "XD0CiaJTxW9sdG0s7m9lCi" }, { "__type__": "cc.Widget", @@ -343,7 +343,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "8489KtK9YDJpc9pM2rJnQ5" + "fileId": "G4b7x02HDKQduoMmoiOfAy" }, { "__type__": "cc.PrefabInfo", @@ -353,7 +353,7 @@ "asset": { "__id__": 0 }, - "fileId": "o1NkKWcu9BPc5XrLksvN6B", + "fileId": "oMLH0QNQy0BmAyi3EE6Kjt", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -384,7 +384,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "SehSZqVEb085kMOfoTcvYa" + "fileId": "n3vCuv72ApZ1uVw93PRiHd" }, { "__type__": "cc.Widget", @@ -420,7 +420,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "0ztNT7zoYHcqwxEE1gkqxB" + "fileId": "tl0l6z7daFX50685hlfgr9" }, { "__type__": "cc.PrefabInfo", @@ -430,7 +430,7 @@ "asset": { "__id__": 0 }, - "fileId": "6LFTIo7Z6sUOfqsJgVpz04", + "fileId": "RWU14cHHVlX2UDhd9X43g7", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null diff --git a/cocoscreator_projects/YouleNexus/out/legacy-migration/Protol_Layer.prefab b/cocoscreator_projects/YouleNexus/out/legacy-migration/Protol_Layer.prefab index a30ac51..ca59b1f 100644 --- a/cocoscreator_projects/YouleNexus/out/legacy-migration/Protol_Layer.prefab +++ b/cocoscreator_projects/YouleNexus/out/legacy-migration/Protol_Layer.prefab @@ -205,7 +205,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "u6iFoyVSsDCf7O8mOeAVKS" + "fileId": "ttLMdGQNfMqWrYZynHkGDH" }, { "__type__": "cc.Sprite", @@ -250,7 +250,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "RQ15R9kItMeM8jj3220l4X" + "fileId": "fRZkGOv1urgFw8Ak0fzL1F" }, { "__type__": "cc.Widget", @@ -284,7 +284,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "psTpVYM4XYJKhMfhS1ruMB" + "fileId": "LkaxLguQityt3Sivh9g3Pc" }, { "__type__": "cc.PrefabInfo", @@ -294,7 +294,7 @@ "asset": { "__id__": 0 }, - "fileId": "1puOtXKPT4nBm2e11y9lbT", + "fileId": "ue5AFKfj6nsyEkZBJCGnrM", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -378,7 +378,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "D5HWHcLEAQZsuXVmhu0yys" + "fileId": "Yd7mhmnggPie0AWlWrmMA5" }, { "__type__": "cc.Sprite", @@ -423,7 +423,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "Nb5FFb8adxEmwmCefz9pot" + "fileId": "EEb1vzbh4AB9FnhcIzFso8" }, { "__type__": "cc.Widget", @@ -455,7 +455,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "4qE04DTORykivqdU4AIv5f" + "fileId": "IMZTDkHZUKD3FsYxtxtY7L" }, { "__type__": "cc.PrefabInfo", @@ -465,7 +465,7 @@ "asset": { "__id__": 0 }, - "fileId": "PZ6oONqr7RjodZhhV8RT0l", + "fileId": "mEacoNsXyscsTiWF2N751I", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -549,7 +549,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "iC90mAI0q6RTSEFGVSXgvc" + "fileId": "16xxKHWp4EIgjez58oy1eo" }, { "__type__": "cc.Sprite", @@ -594,7 +594,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "haYjXlRPtYOKIGE64TSyVl" + "fileId": "buLpw6lDAfLS5iuacaACLg" }, { "__type__": "cc.Widget", @@ -626,7 +626,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "LKL3T0bw27UdoONwSWyYGW" + "fileId": "EGkAfOvvadMRdZKHNe3MX0" }, { "__type__": "cc.PrefabInfo", @@ -636,7 +636,7 @@ "asset": { "__id__": 0 }, - "fileId": "7yknDa8VbX4Ix6OJ1i6nkq", + "fileId": "Pn8ZX71OXWrBUXXzKkISLh", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -720,7 +720,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "zzra4CkCzqFKe69JdBWdeK" + "fileId": "qM6ForTwIpD6MOJOhnZQeS" }, { "__type__": "cc.Sprite", @@ -765,7 +765,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "BK9E81FtDCVn75b59au9pF" + "fileId": "WBC4EEA2yC3sCRi5vqEMur" }, { "__type__": "cc.Widget", @@ -797,7 +797,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "ECPEIRe4c5WnccAUfGjkqY" + "fileId": "jksMjvJCiFFc6fk1oDe59n" }, { "__type__": "cc.PrefabInfo", @@ -807,7 +807,7 @@ "asset": { "__id__": 0 }, - "fileId": "uhRHwknn4SFHqdPSyMR5uD", + "fileId": "mjKGFdFaqaHZOP6vASvm2T", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -838,7 +838,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "43IfomALQcOTipLcyDwTs1" + "fileId": "NEUDScLXeOyUaRYpzzSdwS" }, { "__type__": "cc.Widget", @@ -874,7 +874,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "Pw8AVmZUrybxMOrLHKgSSP" + "fileId": "yU7fWEXjloqkyHdJTNuOO5" }, { "__type__": "cc.PrefabInfo", @@ -884,7 +884,7 @@ "asset": { "__id__": 0 }, - "fileId": "IlNaN5A8P0dQhF8E9Yx7nt", + "fileId": "NmigEGjkGY32J4dwShFP7R", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null @@ -915,7 +915,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "ZMnfGBzF5DGII4RMALE6el" + "fileId": "ERoBjCusgKp8Xnv3l66PIE" }, { "__type__": "cc.Widget", @@ -951,7 +951,7 @@ }, { "__type__": "cc.CompPrefabInfo", - "fileId": "i8q8guTJmnghedToQD3G7G" + "fileId": "Ey8iM8NbAYWmikqFZ8DdmT" }, { "__type__": "cc.PrefabInfo", @@ -961,7 +961,7 @@ "asset": { "__id__": 0 }, - "fileId": "Z8ImfsM2tYFs5phLftN067", + "fileId": "S3ZUouTGopxL6K9LuhUne2", "instance": null, "targetOverrides": null, "nestedPrefabInstanceRoots": null diff --git a/cocoscreator_projects/YouleNexus/tsconfig.json b/cocoscreator_projects/YouleNexus/tsconfig.json index 7dc649a..1547c4c 100644 --- a/cocoscreator_projects/YouleNexus/tsconfig.json +++ b/cocoscreator_projects/YouleNexus/tsconfig.json @@ -4,6 +4,7 @@ /* Add your custom configuration here. */ "compilerOptions": { - "strict": false + "strict": false, + "allowImportingTsExtensions": true } } diff --git a/cocoscreator_projects/framework-tests/adapters/local/login-sources.test.ts b/cocoscreator_projects/framework-tests/adapters/local/login-sources.test.ts index f6ee075..c46ac44 100644 --- a/cocoscreator_projects/framework-tests/adapters/local/login-sources.test.ts +++ b/cocoscreator_projects/framework-tests/adapters/local/login-sources.test.ts @@ -2,7 +2,9 @@ import { remoteFixture } from '../../fixtures/remote-config.ts'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { BUILD_IDENTITY } from '../../../YouleNexus/assets/framework/config/sources/defaults.ts'; -import { STARTUP_CONFIG } from '../../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import type { LocalStartupContext } from '../../../YouleNexus/assets/framework/config/local-startup.ts'; import { LOCAL_MACHINE_KEY, localAccountKey, type LocalStoragePort } from '../../../YouleNexus/assets/framework/adapters/local/local-storage.ts'; import { createLocalLoginSources, type LocalHostSnapshot } from '../../../YouleNexus/assets/framework/adapters/local/login-sources.ts'; diff --git a/cocoscreator_projects/framework-tests/adapters/local/visitor-account.test.ts b/cocoscreator_projects/framework-tests/adapters/local/visitor-account.test.ts index cda6f2f..5b6a06e 100644 --- a/cocoscreator_projects/framework-tests/adapters/local/visitor-account.test.ts +++ b/cocoscreator_projects/framework-tests/adapters/local/visitor-account.test.ts @@ -2,7 +2,9 @@ import { remoteFixture } from '../../fixtures/remote-config.ts'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { BUILD_IDENTITY } from '../../../YouleNexus/assets/framework/config/sources/defaults.ts'; -import { STARTUP_CONFIG } from '../../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import type { LocalStartupContext } from '../../../YouleNexus/assets/framework/config/local-startup.ts'; import { LOCAL_MACHINE_KEY, localAccountKey, type LocalStoragePort } from '../../../YouleNexus/assets/framework/adapters/local/local-storage.ts'; import { createLocalVisitorProvider } from '../../../YouleNexus/assets/framework/adapters/local/visitor-account.ts'; @@ -121,7 +123,7 @@ test('scope isolates identity types, ignores version, and serializes the server assert.notEqual(localAccountKey(base), localAccountKey(context({ marketid: '7', version: 1 }))); assert.equal(localAccountKey(base), localAccountKey(context({ marketid: 7, version: 999 }))); assert.equal(localAccountKey(base), - 'youle.local-test.account.v1:["local",["ws://127.0.0.1:3088"],"veRa0qrBf0df2K1G4de2tgfmVxB2jxpv","J9r4022t80pxlb01zOK76DN1WD4QewFl","FtJf073aa0d6rI1xD8J1Y42fINTm0ziK",7]'); + 'youle.local-test.account.v1:["local",["ws://127.0.0.1:3088"],"1B2h0ccl205c390Y28m1Ajdplkuu4wgy","J9r4022t80pxlb01zOK76DN1WD4QewFl","aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG",7]'); }); test('reset is isolated, refuses active sessions, and refuses overlap with getAccount', async () => { diff --git a/cocoscreator_projects/framework-tests/config/bootstrap.test.ts b/cocoscreator_projects/framework-tests/config/bootstrap.test.ts index 378eeda..e1dfc96 100644 --- a/cocoscreator_projects/framework-tests/config/bootstrap.test.ts +++ b/cocoscreator_projects/framework-tests/config/bootstrap.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { test } from 'node:test'; import assert from 'node:assert/strict'; import { resolveBootstrap } from '../../YouleNexus/assets/framework/config/bootstrap.ts'; @@ -9,7 +11,7 @@ test('resolveBootstrap:显式 hostKind 透传严格 RuntimeConfig,并保留 async fetch() { return { player_server_tcp: '5.5.5.5:5000' }; }, }; const result = await resolveBootstrap({ - hostKind: 'h5', win: {}, search: '', fetcher, + startupConfig:STARTUP_CONFIG, hostKind: 'h5', win: {}, search: '', fetcher, }); assert.equal(result.source, 'remote'); assert.equal(result.profile.name, 'local'); diff --git a/cocoscreator_projects/framework-tests/config/local-config-fetcher.test.ts b/cocoscreator_projects/framework-tests/config/local-config-fetcher.test.ts new file mode 100644 index 0000000..4655aa7 --- /dev/null +++ b/cocoscreator_projects/framework-tests/config/local-config-fetcher.test.ts @@ -0,0 +1,47 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { runInNewContext } from 'node:vm'; +import ts from 'typescript'; +import { ConfigFetchError, ConfigParseError } from '../../YouleNexus/assets/framework/config/remote-config.ts'; + +// 在引擎资源边界验证错误、JSON 解析和缓存释放,不以 HTTP 模拟项目资源。 +function fixture(debug: boolean, text: string, error?: Error) { + const calls: string[] = []; + const exports: any = {}; + class TextAsset {} + const resources = { + load(path: string, type: unknown, done: Function) { + assert.equal(type, TextAsset); calls.push(`load:${path}`); done(error, { text }); + }, + release(path: string, type: unknown) { assert.equal(type, TextAsset); calls.push(`release:${path}`); }, + }; + const source = readFileSync(new URL('../../YouleNexus/assets/scripts/platform-login/local-config-fetcher.ts', import.meta.url), 'utf8'); + runInNewContext(ts.transpileModule(source, { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, + }).outputText, { exports, require(name: string) { + if (name === 'cc') return { resources, TextAsset }; + if (name === 'cc/env') return { DEBUG: debug }; + if (name.endsWith('/remote-config.ts')) return { ConfigFetchError, ConfigParseError }; + throw Error(`Unexpected import: ${name}`); + } }); + return { fetcher: new exports.CocosLocalConfigFetcher(), calls }; +} + +test('project txt parses JSON and releases the asset after every load', async () => { + const f = fixture(true, '{"showVisitor":1}'); + assert.equal((await f.fetcher.fetch('dev-config/test')).showVisitor, 1); + await f.fetcher.fetch('dev-config/test'); + assert.deepEqual(f.calls, ['load:dev-config/test', 'release:dev-config/test', 'load:dev-config/test', 'release:dev-config/test']); +}); + +test('release build never loads local txt; missing or malformed assets expose source errors', async () => { + const release = fixture(false, '{}'); + await assert.rejects(release.fetcher.fetch('dev-config/test'), ConfigFetchError); + assert.deepEqual(release.calls, []); + const missing = fixture(true, '', Error('not found')); + await assert.rejects(missing.fetcher.fetch('dev-config/test'), /test.txt.*not found/); + const malformed = fixture(true, 'not json'); + await assert.rejects(malformed.fetcher.fetch('dev-config/test'), ConfigParseError); + assert.deepEqual(malformed.calls, ['load:dev-config/test', 'release:dev-config/test']); +}); diff --git a/cocoscreator_projects/framework-tests/config/local-startup.test.ts b/cocoscreator_projects/framework-tests/config/local-startup.test.ts index 3270bf3..611c2a4 100644 --- a/cocoscreator_projects/framework-tests/config/local-startup.test.ts +++ b/cocoscreator_projects/framework-tests/config/local-startup.test.ts @@ -7,7 +7,9 @@ import { resolveLocalStartup, type LocalStartupContext, } from '../../YouleNexus/assets/framework/config/local-startup.ts'; -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import type { ResolveRuntimeConfigOptions, RuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts'; import type { ConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config.ts'; import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts'; @@ -22,7 +24,7 @@ function options(search = ''): ResolveRuntimeConfigOptions & { return { player_server_tcp: 'remote.example:3088' }; }, }; - return { hostKind: 'h5', win: {}, search, fetcher }; + return { startupConfig:STARTUP_CONFIG, hostKind: 'h5', win: {}, search, fetcher }; } test('local debug fetches remote config before choosing authoritative local servers', async () => { @@ -174,3 +176,12 @@ test('local consumers validate the injected source snapshot and its exact target assert.throws(()=>assertLocalStartupContext({...context,config:{...context.config,startupConfig:{...context.config.startupConfig,...patch}}} as any),/mode|useLocalServer|localServers|gameserver/); } }); + +test('local startup validates the selected project txt source', async () => { + const localFetcher={async fetch(){return {showVisitor:1};}}; + const context=await resolveLocalStartup({startupConfig:{...STARTUP_CONFIG,configSource:'local'},hostKind:'h5',win:{},search:'', + fetcher:{async fetch(){throw Error('remote forbidden');}},localFetcher}); + assert.equal(context.config.source,'local'); + assert.equal(context.config.gameserver,STARTUP_CONFIG.localConfigResource); + assert.doesNotThrow(()=>assertLocalStartupContext(context)); +}); diff --git a/cocoscreator_projects/framework-tests/config/profiles.test.ts b/cocoscreator_projects/framework-tests/config/profiles.test.ts index 1afbea5..5a4ca7d 100644 --- a/cocoscreator_projects/framework-tests/config/profiles.test.ts +++ b/cocoscreator_projects/framework-tests/config/profiles.test.ts @@ -1,11 +1,13 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { - STARTUP_CONFIG, + STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG, validateStartupConfig, decodeGameConfig, resolveGameServer, } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); test('decodeGameConfig:按旧协议把 -/# 解码为远程 txt URL', () => { assert.equal(decodeGameConfig('host-path#8080-config'), 'http://host/path:8080/config.txt'); @@ -23,7 +25,7 @@ test('resolveGameServer:空 gameconfig 使用配置源,非空值使用解码 }); test('one frozen source defines the complete default startup configuration', () => { - assert.deepEqual(STARTUP_CONFIG, { mode: 'debug', gameserver: 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt', useLocalServer: true, localServers: ['ws://127.0.0.1:3088'] }); + assert.deepEqual(STARTUP_CONFIG, { mode: 'debug', configSource: 'remote', localConfigResource: 'dev-config/update_jsonv2', gameserver: 'https://tsgames.daoqi88.cn/config_test/update_jsonv2.txt', useLocalServer: true, localServers: ['ws://127.0.0.1:3088'] }); assert.ok(Object.isFrozen(STARTUP_CONFIG)); assert.ok(Object.isFrozen(STARTUP_CONFIG.localServers)); }); test('source validation rejects malformed configuration without defaults', () => { @@ -32,3 +34,11 @@ test('source validation rejects malformed configuration without defaults', () => } for (const url of ['ws://127.4.3.2:3088','wss://localhost:3088','ws://[::1]:3088']) assert.deepEqual(validateStartupConfig({...STARTUP_CONFIG,localServers:[url]}).localServers,[url]); }); + +test('config source and local resource paths are explicit and local is debug only', () => { + for (const patch of [{configSource:undefined},{configSource:'other'},{configSource:'local',mode:'release'}, + {localConfigResource:''},{localConfigResource:'../outside'},{localConfigResource:'dev-config/a.txt'},{localConfigResource:'/absolute'}]) { + assert.throws(()=>validateStartupConfig({...STARTUP_CONFIG,...patch} as any), /startup config/); + } + assert.equal(validateStartupConfig({...STARTUP_CONFIG,configSource:'local'}).configSource,'local'); +}); diff --git a/cocoscreator_projects/framework-tests/config/remote-config-fetcher.test.ts b/cocoscreator_projects/framework-tests/config/remote-config-fetcher.test.ts index 01f2382..367a8fc 100644 --- a/cocoscreator_projects/framework-tests/config/remote-config-fetcher.test.ts +++ b/cocoscreator_projects/framework-tests/config/remote-config-fetcher.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { test } from 'node:test'; import assert from 'node:assert/strict'; import { HttpConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config-fetcher.ts'; @@ -36,7 +38,7 @@ test('resolveRuntimeConfig:把 cacheBust 注入唯一的 HTTP 请求构造点' }); assert.equal(calls.length, 1); - assert.equal(calls[0]?.input, 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt?123'); + assert.equal(calls[0]?.input, `${STARTUP_CONFIG.gameserver}?123`); assert.deepEqual(calls[0]?.init, { method: 'POST', body: '' }); }); diff --git a/cocoscreator_projects/framework-tests/config/remote-config.test.ts b/cocoscreator_projects/framework-tests/config/remote-config.test.ts index 29e977e..a5c9a80 100644 --- a/cocoscreator_projects/framework-tests/config/remote-config.test.ts +++ b/cocoscreator_projects/framework-tests/config/remote-config.test.ts @@ -17,6 +17,19 @@ function legacy(raw: unknown, name: string, selected = identity) { GameData: { AgentId: selected.agentid, GameId: selected.gameid, ChannelId: selected.channelid, marketID: selected.marketid }, }); } + +test('get_paravalue is the shared legacy-compatible reader, including an absent data wrapper', () => { + assert.equal(typeof api.get_paravalue, 'function'); + const raw = {showVisitor:1,agentlist:[ + {agentid:11,showVisitor:0,gamelist:[{gameid:22,deviceLogin:true}]}, + {agentid:11,showVisitor:2}, + ]}; + for(const name of ['showVisitor','deviceLogin','missing']) { + assert.deepEqual(api.get_paravalue({data:raw},name,identity),legacy(raw,name)); + assert.deepEqual(view(raw).getValue(name),api.get_paravalue({data:raw},name,identity)); + } + assert.equal(api.get_paravalue({},'showVisitor',identity),legacy(undefined,'showVisitor')); +}); test('memory view agrees with actual legacy function across all five scopes and preserves raw objects', () => { const scopes: Record[] = [ { hall_config: { root: true }, rootOnly: 'root', falsy: 'inherited' }, diff --git a/cocoscreator_projects/framework-tests/config/runtime-config.test.ts b/cocoscreator_projects/framework-tests/config/runtime-config.test.ts index 32393d3..6f85ecf 100644 --- a/cocoscreator_projects/framework-tests/config/runtime-config.test.ts +++ b/cocoscreator_projects/framework-tests/config/runtime-config.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { test } from 'node:test'; import assert from 'node:assert/strict'; import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts'; @@ -30,9 +32,9 @@ test('h5:只读 query 身份,不访问原生宿主,并生成严格 remote assert.deepEqual(result.identity, { ...BUILD_IDENTITY, agentid: 'HA', channelid: 'HC' }); assert.deepEqual(result.servers, ['ws://5.5.5.5:5000', 'ws://5.5.5.5:5000']); assert.equal(result.source, 'remote'); - assert.equal(result.gameserver, 'https://tsgames.daoqi88.cn/config/update_jsonv2.txt'); + assert.equal(result.gameserver, STARTUP_CONFIG.gameserver); assert.equal(result.rawConfig, REMOTE); - assert.deepEqual(fetcher.calls, ['https://tsgames.daoqi88.cn/config/update_jsonv2.txt']); + assert.deepEqual(fetcher.calls, [STARTUP_CONFIG.gameserver]); assert.equal(Object.isFrozen(result), true); assert.equal(Object.isFrozen(result.identity), true); assert.equal(Object.isFrozen(result.servers), true); @@ -201,7 +203,7 @@ for (const mode of ['debug','release'] as const) for (const useLocalServer of [t source.localServers=['ws://localhost:9999']; assert.deepEqual(result.startupConfig.localServers,STARTUP_CONFIG.localServers); }); test('default runtime starts without query configuration', async () => { - const result=await resolveRuntimeConfig({hostKind:'h5',win:{},search:'',fetcher:returning({notice:'kept'})}); + const result=await resolveRuntimeConfig({startupConfig:STARTUP_CONFIG,hostKind:'h5',win:{},search:'',fetcher:returning({notice:'kept'})}); assert.deepEqual(result.startupConfig,STARTUP_CONFIG); assert.deepEqual(result.servers,STARTUP_CONFIG.localServers); }); @@ -216,3 +218,38 @@ for (const mode of ['debug','release'] as const) for (const useLocalServer of [t const fetcher={async fetch(){throw new Error('remote unavailable');}}; await assert.rejects(resolveRuntimeConfig({startupConfig:{...STARTUP_CONFIG,mode,useLocalServer},hostKind:'h5',win:{},search:'',fetcher}),/远程配置抓取失败/); }); + +// 本地来源必须在运行时边界隔离网络读取,并复用正式配置的层级取值规则。 +test('local txt selects one source, ignores gameconfig and preserves scoped values', async () => { + const remote = returning(REMOTE); + const local = returning({showVisitor: 1, agentlist: [{agentid: BUILD_IDENTITY.agentid, deviceLogin: true}]}); + const result = await resolveRuntimeConfig({startupConfig:{...STARTUP_CONFIG, configSource:'local'}, + hostKind:'h5',win:{},search:'?gameconfig=unwanted-host',fetcher:remote,localFetcher:local}); + assert.equal(result.source,'local'); + assert.deepEqual(remote.calls,[]); + assert.deepEqual(local.calls,[STARTUP_CONFIG.localConfigResource]); + assert.equal(result.remoteConfig.getValue('showVisitor'),1); + assert.equal(result.remoteConfig.getValue('deviceLogin'),true); + assert.deepEqual(result.servers,STARTUP_CONFIG.localServers); +}); + +test('local txt errors never fall back to remote, release rejects before I/O', async () => { + const remote=returning(REMOTE); let localCalls=0; + const localFetcher={async fetch(){localCalls++;throw Error('missing txt');}}; + const options={startupConfig:{...STARTUP_CONFIG,configSource:'local' as const},hostKind:'h5' as const,win:{},search:'',fetcher:remote,localFetcher}; + await assert.rejects(resolveRuntimeConfig(options),/本地配置读取失败/); + assert.equal(localCalls,1);assert.equal(remote.calls.length,0); + await assert.rejects(resolveRuntimeConfig({...options,startupConfig:{...options.startupConfig,mode:'release'}}),/configSource/); + assert.equal(localCalls,1); + await assert.rejects(resolveRuntimeConfig({...options,localFetcher:undefined}),/localFetcher/); +}); + +test('local txt and socket target are independent; remote mode never touches local loader', async () => { + const remote=returning(REMOTE),local=returning(REMOTE); + const options={hostKind:'h5' as const,win:{},search:'',fetcher:remote,localFetcher:local}; + const result=await resolveRuntimeConfig({...options,startupConfig:{...STARTUP_CONFIG,configSource:'local',useLocalServer:false}}); + assert.deepEqual(result.servers,['ws://5.5.5.5:5000','ws://5.5.5.5:5000']); + assert.equal(remote.calls.length,0);assert.equal(local.calls.length,1); + await resolveRuntimeConfig({...options,startupConfig:{...STARTUP_CONFIG,configSource:'remote'}}); + assert.equal(remote.calls.length,1);assert.equal(local.calls.length,1); +}); diff --git a/cocoscreator_projects/framework-tests/integration/local-login-sources.test.ts b/cocoscreator_projects/framework-tests/integration/local-login-sources.test.ts index 12d3543..498d237 100644 --- a/cocoscreator_projects/framework-tests/integration/local-login-sources.test.ts +++ b/cocoscreator_projects/framework-tests/integration/local-login-sources.test.ts @@ -1,3 +1,4 @@ +import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { resolveLocalStartup } from '../../YouleNexus/assets/framework/config/local-startup.ts'; @@ -13,7 +14,7 @@ class MapStorage implements LocalStoragePort { } function resolve(search = '?profile=local') { - return resolveLocalStartup({ hostKind: 'h5', win: {}, search, + return resolveLocalStartup({ startupConfig:{...STARTUP_CONFIG,configSource:'remote'}, hostKind: 'h5', win: {}, search, fetcher: { async fetch() { return { player_server_tcp: 'remote.example:3088' }; } } }); } diff --git a/cocoscreator_projects/framework-tests/integration/login-flow.test.ts b/cocoscreator_projects/framework-tests/integration/login-flow.test.ts index 174014a..29b286d 100644 --- a/cocoscreator_projects/framework-tests/integration/login-flow.test.ts +++ b/cocoscreator_projects/framework-tests/integration/login-flow.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; diff --git a/cocoscreator_projects/framework-tests/integration/platform-vertical-slice.test.ts b/cocoscreator_projects/framework-tests/integration/platform-vertical-slice.test.ts index db91afe..41c451a 100644 --- a/cocoscreator_projects/framework-tests/integration/platform-vertical-slice.test.ts +++ b/cocoscreator_projects/framework-tests/integration/platform-vertical-slice.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { test } from 'node:test'; diff --git a/cocoscreator_projects/framework-tests/local-platform-login/host.test.ts b/cocoscreator_projects/framework-tests/local-platform-login/host.test.ts deleted file mode 100644 index 5c55540..0000000 --- a/cocoscreator_projects/framework-tests/local-platform-login/host.test.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; -import assert from 'node:assert/strict'; -import { readFileSync } from 'node:fs'; -import { test } from 'node:test'; -import type { PageModel, OverlayModel, PageId } from '../../YouleNexus/assets/framework/presentation/ui-contracts.ts'; -import type { ResolveRuntimeConfigOptions, RuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts'; - -function deferred() { - let resolve!: () => void; - let reject!: (error: unknown) => void; - const promise = new Promise((yes, no) => { resolve = yes; reject = no; }); - return { promise, resolve, reject }; -} -async function flush() { for (let i = 0; i < 30; i++) await Promise.resolve(); } -function fixture(name = 'player-login-success.json') { - return JSON.parse(readFileSync(new URL(`../fixtures/contracts/${name}`, import.meta.url), 'utf8')); -} -class Socket { - readyState = 0; - sent: string[] = []; - closes = 0; - closeError?: Error; - onopen: (() => void) | null = null; - onclose: (() => void) | null = null; - onmessage: ((event: { data: string }) => void) | null = null; - onerror: (() => void) | null = null; - send(frame: string) { this.sent.push(frame); } - close() { this.closes++; if (this.closeError) throw this.closeError; this.readyState = 3; this.onclose?.(); } - open() { this.readyState = 1; this.onopen?.(); } - push(message: unknown) { this.onmessage?.({ data: JSON.stringify(message) }); } -} -async function setup(overrides: { config?: Partial; render?: (model: PageModel) => void; - cancelFrame?: () => void; showOverlay?: (model: OverlayModel) => void; - record?: (event: { kind: string; data: unknown }) => void; onBusy?: (busy: boolean) => void; - onConfigResolved?: (config: RuntimeConfig) => void; onResourcesReady?: () => void; - onSocketReady?: (ready: boolean) => void; onStopped?: () => void } = {}) { - const api = await import('../../YouleNexus/assets/scripts/local-platform-login/local-login-host.ts') - .catch(error => assert.fail(`Local login host implementation must exist: ${String(error)}`)); - const resources = deferred(), minimum = deferred(); - const sockets: Socket[] = [], pages: PageModel[] = [], overlays: OverlayModel[] = [], activations: PageId[] = []; - const failures: unknown[] = [], busy: boolean[] = [], evidence: { kind: string; data: unknown }[] = []; - const values = new Map(), frames = new Set<() => void>(); - let fetches = 0; - const host = api.createLocalLoginHost({ - onConfigResolved: overrides.onConfigResolved, onResourcesReady: overrides.onResourcesReady, - onSocketReady: overrides.onSocketReady, onStopped: overrides.onStopped, - configOptions: { hostKind: 'h5', win: {}, search: '?profile=local&version=731', - fetcher: { async fetch() { fetches++; return { player_server_tcp: 'remote.example:3088' }; } }, ...overrides.config }, - storage: { getItem: key => values.get(key) ?? null, setItem: (key, value) => { values.set(key, value); } }, - random: () => .5, now: () => 123456, sampleHost: () => ({ location: null }), - view: { render: model => { overrides.render?.(model); pages.push(model); }, - activate: page => { activations.push(page); }, showOverlay: model => { overlays.push(model); overrides.showOverlay?.(model); } }, - scheduler: { request: callback => { frames.add(callback); return () => { frames.delete(callback); overrides.cancelFrame?.(); }; } }, - loadResources: () => resources.promise, waitForMinimumDisplay: () => minimum.promise, - createSocket: () => { const socket = new Socket(); sockets.push(socket); return socket as unknown as WebSocket; }, - record: event => { evidence.push(event); overrides.record?.(event); }, - onBusy: value => { busy.push(value); overrides.onBusy?.(value); }, onFailure: error => { failures.push(error); }, - }); - const draw = () => { for (const frame of [...frames]) { frames.delete(frame); frame(); } }; - const ready = async () => { const starting = host.start(); resources.resolve(); minimum.resolve(); await flush(); sockets[0]!.open(); await starting; draw(); }; - return { host, resources, minimum, sockets, pages, overlays, activations, failures, busy, evidence, values, frames, draw, ready, fetches: () => fetches }; -} - -test('host exposes resolved config and independent socket/resources readiness before display, and stops late events', async () => { - const configs: RuntimeConfig[] = [], socket: boolean[] = []; let resources = 0, stopped = 0; - const h = await setup({ onConfigResolved: config => configs.push(config), onResourcesReady: () => resources++, - onSocketReady: ready => socket.push(ready), onStopped: () => stopped++ }); - const starting = h.host.start(); await flush(); - assert.equal(configs.length, 1); assert.equal(configs[0].identity.version, 731); - assert.equal(configs[0].remoteConfig.getValue('player_server_tcp'), 'remote.example:3088'); - h.sockets[0].open(); await flush(); assert.equal(socket[socket.length - 1], true); - assert.equal(h.host.getSnapshot().ready, false); - h.resources.resolve(); await flush(); assert.equal(resources, 1); - assert.equal(h.host.getSnapshot().ready, false); - h.minimum.resolve(); await starting; h.host.stop(); assert.equal(stopped, 1); - const events = socket.length; h.sockets[0].open(); await flush(); assert.equal(socket.length, events); -}); - -test('host stop cancels presentation and ignores a resource completion still pending', async () => { - let resources = 0, stopped = 0; - const h = await setup({ onResourcesReady: () => resources++, onStopped: () => stopped++ }); - const starting = h.host.start(); const rejection = assert.rejects(starting, /stop|terminal/); - await flush(); h.host.stop(); h.resources.resolve(); h.minimum.resolve(); - await rejection; assert.equal(resources, 0); assert.equal(stopped, 1); -}); - -for (const order of [ - ['resources', 'socket', 'minimum'], ['resources', 'minimum', 'socket'], - ['socket', 'resources', 'minimum'], ['socket', 'minimum', 'resources'], - ['minimum', 'resources', 'socket'], ['minimum', 'socket', 'resources'], -] as const) test(`all startup gates remain independent in ${order.join(' -> ')} order`, async t => { - const h = await setup(); t.after(() => h.host.stop()); - const starting = h.host.start(); await flush(); - for (let index = 0; index < order.length; index++) { - const gate = order[index]; - if (gate === 'socket') h.sockets[0].open(); else h[gate].resolve(); - await flush(); - assert.equal(h.host.getSnapshot().ready, index === order.length - 1); - assert.equal(h.activations.includes('login'), index === order.length - 1); - } - await starting; h.draw(); assert.equal(h.activations.includes('login'), true); -}); - -test('pending config cannot be bypassed by previously completed resource/display tasks', async t => { - const config = deferred(); let resolved = 0; - const h = await setup({ config: { fetcher: { async fetch() { await config.promise; return {}; } } }, - onConfigResolved: () => resolved++ }); - t.after(() => h.host.stop()); const starting = h.host.start(); - h.resources.resolve(); h.minimum.resolve(); await flush(); - assert.equal(resolved, 0); assert.equal(h.sockets.length, 0); assert.equal(h.host.getSnapshot().ready, false); - config.resolve(); await flush(); assert.equal(resolved, 1); assert.equal(h.host.getSnapshot().ready, false); - h.sockets[0].open(); await starting; h.draw(); assert.equal(h.host.getSnapshot().ready, true); -}); - -for (const missing of ['resources', 'minimum', 'socket'] as const) test(`login waits for ${missing}, and start composes only once`, async t => { - const h = await setup(); t.after(() => h.host.stop()); - const starting = h.host.start(); const repeated = h.host.start(); - assert.strictEqual(starting, repeated); - await flush(); - if (missing !== 'resources') h.resources.resolve(); - if (missing !== 'minimum') h.minimum.resolve(); - if (missing !== 'socket') h.sockets[0]!.open(); - await flush(); - assert.equal(h.host.getSnapshot().ready, false); - await assert.rejects(h.host.login(), /ready/i); - assert.equal(h.values.size, 0); assert.deepEqual(h.sockets[0]!.sent, []); - h.resources.resolve(); h.minimum.resolve(); if (missing === 'socket') h.sockets[0]!.open(); - await starting; - assert.equal(h.host.getSnapshot().ready, true); assert.equal(h.fetches(), 1); - assert.equal(h.sockets.length, 1); - assert.equal(h.evidence.filter(event => event.kind === 'startup-config').length, 1); - await h.host.login(); - assert.equal(JSON.parse(h.sockets[0]!.sent[0]!).data.version, 731); -}); - -test('duplicate clicks send once; response updates the sole Store and lobby projection and clears busy', async t => { - const h = await setup(); t.after(() => h.host.stop()); await h.ready(); - const first = h.host.login(), second = h.host.login(); assert.strictEqual(first, second); - assert.equal(h.host.getSnapshot().busy, true); - await first; await h.host.login(); assert.equal(h.sockets[0]!.sent.length, 1); - assert.deepEqual(h.busy, [true]); - h.sockets[0]!.push(fixture()); h.draw(); - assert.equal(h.host.getSnapshot().state?.app.phase, 'logged-in'); - assert.equal(h.host.getSnapshot().state?.room.kind, 'outside'); - assert.deepEqual(h.pages[h.pages.length - 1], { page: 'lobby', self: { playerid: 430511, nickname: '测试号', avatar: 'http://a', bean: 0 } }); - assert.deepEqual(h.busy, [true, false]); - const before = h.pages.length; h.host.stop(); h.draw(); assert.equal(h.pages.length, before); -}); - -for (const rpc of ['player_login', 'kick_server']) test(`${rpc} retains original server reason through terminal cleanup`, async t => { - const h = await setup(); t.after(() => h.host.stop()); await h.ready(); await h.host.login(); - const data = { state: 17, msg: '原始服务器原因' }; - h.sockets[0]!.push({ route: 'agent', rpc, data }); - assert.equal(h.failures.length, 1); assert.deepEqual(h.failures[0], data); - assert.equal(h.host.getSnapshot().ready, false); assert.equal(h.host.getSnapshot().busy, false); - assert.equal(h.sockets[0]!.closes, 1); - await assert.rejects(h.host.login(), /stop|terminal/i); -}); - -test('room recovery is refused before any player or room state enters Store', async t => { - const h = await setup(); t.after(() => h.host.stop()); await h.ready(); await h.host.login(); - h.sockets[0]!.push(fixture('player-login-room.json')); - assert.match(String(h.failures[0]), /room recovery/); - assert.equal(h.host.getSnapshot().state?.players.selfPlayerId, null); - assert.equal(h.host.getSnapshot().state?.room.kind, 'outside'); - assert.equal(h.pages.some(model => model.page === 'lobby'), false); -}); - -test('corrupt account cache prevents sending and preserves the cache', async t => { - const h = await setup(); t.after(() => h.host.stop()); await h.ready(); - // Discover the authority-owned key from a prior independent local login. - const prior = await setup(); t.after(() => prior.host.stop()); await prior.ready(); await prior.host.login(); - for (const [key, value] of prior.values) h.values.set(key, value); - const accountKey = [...h.values].find(([, value]) => value.includes('account'))![0]; - h.values.set(accountKey, '{broken'); - await assert.rejects(h.host.login(), /invalid JSON/); - assert.equal(h.sockets[0]!.sent.length, 0); assert.equal(h.values.get(accountKey), '{broken'); - assert.equal(h.failures.length, 1); -}); - -for (const phase of ['config', 'resources', 'prepare'] as const) test(`stop during ${phase} suppresses late connection, send and render`, async () => { - const h = await setup(); - let operation: Promise; - if (phase === 'prepare') { await h.ready(); operation = h.host.login(); } - else { operation = h.host.start(); if (phase === 'resources') await flush(); } - const settled = operation.catch(() => {}); - // Let config resolution / account preparation begin before terminal stop. - if (phase !== 'resources') await Promise.resolve(); - h.host.stop(); const pageCount = h.pages.length; - h.resources.resolve(); h.minimum.resolve(); await settled; await flush(); h.draw(); - assert.equal(h.pages.length, pageCount); assert.equal(h.frames.size, 0); - assert.equal(h.sockets.flatMap(socket => socket.sent).length, 0); - if (phase === 'config') assert.equal(h.sockets.length, 0); - assert.deepEqual(h.failures, []); -}); - -for (const config of [{ startupConfig: {...STARTUP_CONFIG,mode:'release' as const} }, { startupConfig: {...STARTUP_CONFIG,useLocalServer:false} }, { hostKind: 'native-settings' as const }]) test('invalid local input cannot create an account or connection', async () => { - const h = await setup({ config }); - await assert.rejects(h.host.start(), /local startup/); - assert.equal(h.values.size, 0); assert.equal(h.sockets.length, 0); assert.equal(h.failures.length, 1); -}); - -test('resource failure is reported with original object after shutdown', async () => { - const h = await setup(); const error = new Error('resource dependency missing'); - const starting = h.host.start(); await flush(); h.resources.reject(error); h.minimum.resolve(); - await assert.rejects(starting); - assert.strictEqual(h.failures[0], error); assert.equal(h.sockets[0]!.closes, 1); -}); - -test('observer and socket cleanup failures both survive terminal reporting', async t => { - const observer = new Error('render observer failed'), cleanup = new Error('socket close failed'); - const h = await setup({ render: model => { if (model.page === 'lobby') throw observer; } }); - t.after(() => h.host.stop()); await h.ready(); await h.host.login(); h.sockets[0]!.closeError = cleanup; - h.sockets[0]!.push(fixture()); - const contains = (value: unknown, target: unknown): boolean => value === target || - (!!value && typeof value === 'object' && 'errors' in value && (value.errors as unknown[]).some(error => contains(error, target))); - assert.equal(contains(h.failures[0], observer), true); assert.equal(contains(h.failures[0], cleanup), true); -}); - -test('resource error remains primary when closing its socket also fails', async () => { - const h = await setup(); const primary = new Error('resource failed'), cleanup = new Error('close failed'); - const starting = h.host.start(); await flush(); h.sockets[0]!.closeError = cleanup; - h.resources.reject(primary); h.minimum.resolve(); await assert.rejects(starting); - const errors = (h.failures[0] as { errors: unknown[] }).errors; - assert.strictEqual(errors[0], primary); assert.ok(errors.includes(cleanup)); -}); - -test('kick data remains primary when closing its socket also fails', async () => { - const h = await setup(); await h.ready(); await h.host.login(); - const cleanup = new Error('kick close failed'); h.sockets[0]!.closeError = cleanup; - const data = { msg: 'original kick reason' }; - h.sockets[0]!.push({ route: 'agent', rpc: 'kick_server', data }); - const errors = (h.failures[0] as { errors: unknown[] }).errors; - assert.deepEqual(errors[0], data); assert.ok(errors.includes(cleanup)); -}); - -test('pending frame cancellation retains its primary error when socket cleanup also fails', async () => { - const primary = new Error('pending frame cancel failed'), cleanup = new Error('socket close failed'); - let armed = false; - const h = await setup({ cancelFrame: () => { if (armed) throw primary; } }); - await h.ready(); await h.host.login(); - armed = true; h.sockets[0]!.closeError = cleanup; - // The real login response schedules a Store frame, then showLobby flush cancels it. - h.sockets[0]!.push(fixture()); - assert.equal(h.failures.length, 1); - const fault = h.failures[0] as { errors?: unknown[] }; - assert.ok(fault.errors, 'primary and cleanup failures must both be retained'); - assert.strictEqual(fault.errors[0], primary); - assert.ok(fault.errors.includes(cleanup)); - assert.equal(h.host.getSnapshot().ready, false); assert.equal(h.frames.size, 0); - assert.equal(h.sockets[0]!.closes, 1); -}); - -test('fatal overlay observer failure retains the original Runtime error object', async () => { - const observer = new Error('fatal overlay observer failed'); - let primary: Error | undefined; - const h = await setup({ showOverlay: model => { - if (model.kind === 'fatal') { primary = model.error; throw observer; } - } }); - await h.ready(); - h.sockets[0]!.push({ route: 'agent', rpc: 'unsupported_test_rpc', data: {} }); - assert.ok(primary); assert.match(primary.message, /Unsupported platform rpc/); - assert.equal(h.failures.length, 1); - const fault = h.failures[0] as { errors?: unknown[] }; - assert.ok(fault.errors, 'Runtime error and overlay observer error must both be retained'); - assert.strictEqual(fault.errors[0], primary); assert.strictEqual(fault.errors[1], observer); - assert.equal(h.host.getSnapshot().ready, false); assert.equal(h.sockets[0]!.closes, 1); -}); - -test('remote fetch and parse failures leave local sockets and account storage untouched', async () => { - for (const fetch of [async () => { throw new Error('network down'); }, async () => ({ agentlist: {} }), async () => null]) { - const h = await setup({ config: { fetcher: { fetch } } }); - const result = h.host.start().catch(error => error); - await flush(); - assert.equal(h.sockets.length, 0); - assert.equal(h.values.size, 0); - assert.ok(await result instanceof Error); - assert.equal(h.failures.length, 1); - } -}); - -test('stop while remote fetch is pending prevents late socket creation', async () => { - const pending = deferred(); - let fetched = false; - const h = await setup({ config: { fetcher: { async fetch() { - fetched = true; await pending.promise; return { player_server_tcp: 'remote.example:3088' }; - } } } }); - const result = h.host.start().catch(error => error); - await flush(); assert.equal(fetched, true); assert.equal(h.sockets.length, 0); - h.host.stop(); pending.resolve(); await result; - assert.equal(h.sockets.length, 0); assert.equal(h.values.size, 0); -}); - -test('startup diagnostics exclude raw remote fields', async t => { - const h = await setup({ config: { fetcher: { async fetch() { - return { player_server_tcp: 'remote.example:3088', privateConfig: 'DO_NOT_RECORD_REMOTE_SECRET' }; - } } } }); - t.after(() => h.host.stop()); await h.ready(); - assert.equal(JSON.stringify(h.evidence).includes('DO_NOT_RECORD_REMOTE_SECRET'), false); -}); - -test('default no-query host reaches login ready and ignores conflicting selectors', async t => { - for (const search of ['', '?mode=release&mode=other&profile=prod&profile=unknown']) { - const h=await setup({config:{search}}); t.after(()=>h.host.stop()); await h.ready(); - assert.equal(h.fetches(),1); assert.equal(h.host.getSnapshot().ready,true); assert.ok(h.activations.includes('login')); assert.equal(h.failures.length,0); - } -}); diff --git a/cocoscreator_projects/framework-tests/local-platform-login/transport.test.ts b/cocoscreator_projects/framework-tests/local-platform-login/transport.test.ts deleted file mode 100644 index 752fbab..0000000 --- a/cocoscreator_projects/framework-tests/local-platform-login/transport.test.ts +++ /dev/null @@ -1,323 +0,0 @@ -import assert from 'node:assert/strict'; -import { test } from 'node:test'; -import { resolveLocalStartup } from '../../YouleNexus/assets/framework/config/local-startup.ts'; -import { WireClient } from '../../YouleNexus/assets/framework/net/wire-client.ts'; -import type { Clock } from '../../YouleNexus/assets/framework/net/heartbeat.ts'; - -class FakeSocket { - readyState = 0; - sent: string[] = []; - closes = 0; - sendError?: Error; - closeError?: Error; - onopen: ((event: Event) => void) | null = null; - onmessage: ((event: MessageEvent) => void) | null = null; - onclose: ((event: CloseEvent) => void) | null = null; - onerror: ((event: Event) => void) | null = null; - send(frame: string): void { - if (this.sendError) throw this.sendError; - this.sent.push(frame); - } - close(): void { - this.closes++; - if (this.closeError) throw this.closeError; - this.readyState = 3; - this.onclose?.({} as CloseEvent); - } - open(): void { this.readyState = 1; this.onopen?.({} as Event); } - message(data: unknown): void { this.onmessage?.({ data } as MessageEvent); } -} - -async function setup(overrides: { - createSocket?: (url: string) => WebSocket; - record?: (event: { kind: string; data: unknown }) => void; - onFault?: (error: unknown) => void; -} = {}) { - const module = await import('../../YouleNexus/assets/scripts/local-platform-login/local-login-transport.ts') - .catch((error: unknown) => assert.fail(`LocalLoginTransport implementation must exist: ${String(error)}`)); - const context = await resolveLocalStartup({ - hostKind: 'h5', win: {}, search: '?profile=local', - fetcher: { async fetch() { return { player_server_tcp: 'remote.example:3088' }; } }, - }); - const sockets: FakeSocket[] = []; - const urls: string[] = []; - const faults: unknown[] = []; - const evidence: { kind: string; data: unknown }[] = []; - const transport = new module.LocalLoginTransport({ - context, - createSocket: overrides.createSocket ?? ((url) => { - urls.push(url); - const socket = new FakeSocket(); - sockets.push(socket); - return socket as unknown as WebSocket; - }), - record: overrides.record ?? ((event) => evidence.push(event)), - onFault: (error) => { faults.push(error); overrides.onFault?.(error); }, - }); - return { transport, context, sockets, urls, faults, evidence, url: context.config.servers[0]! }; -} - -test('requires OPEN and records only frames successfully sent', async () => { - const h = await setup(); - assert.throws(() => h.transport.send('{}'), /open/i); - h.transport.connect(h.url); - assert.throws(() => h.transport.send('{}'), /open/i); - const socket = h.sockets[0]!; - socket.open(); - h.transport.send('frame'); - assert.deepEqual(socket.sent, ['frame']); - assert.deepEqual(h.evidence, [{ kind: 'transport-send', data: 'frame' }]); - const error = new Error('send failed'); - socket.sendError = error; - assert.throws(() => h.transport.send('unsent'), (caught) => caught === error); - assert.equal(h.evidence.length, 1); - assert.equal(h.faults[h.faults.length - 1], error); -}); - -test('validates every connection target before constructing and reports the thrown error', async () => { - const h = await setup(); - h.transport.connect(h.url); - assert.throws(() => h.transport.connect('wss://remote.example/'), (error) => { - assert.equal(h.faults[h.faults.length - 1], error); - return /loopback/i.test(String(error)); - }); - assert.deepEqual(h.urls, [h.url]); -}); - -test('socket constructor failure is reported before rethrowing the same object and cause', async () => { - const cause = new Error('root'); - const error = Object.assign(new Error('constructor failed'), { cause }); - const h = await setup({ createSocket: () => { throw error; } }); - assert.throws(() => h.transport.connect(h.url), (caught) => { - assert.equal(h.faults[0], error); - return caught === error && error.cause === cause; - }); -}); - -test('records raw handshake before message delivery', async () => { - const order: unknown[] = []; - const h = await setup({ record: (event) => order.push(event) }); - h.transport.onMessage((frame) => order.push(frame)); - h.transport.connect(h.url); - h.sockets[0]!.open(); - h.sockets[0]!.message('@toconcon raw handshake'); - assert.deepEqual(order, [ - { kind: 'transport-receive', data: '@toconcon raw handshake' }, '@toconcon raw handshake', - ]); -}); - -test('non-text frames stop, report TypeError and never reach consumers or reconnect callback', async () => { - const h = await setup(); - let delivered = 0; - let closed = 0; - h.transport.onMessage(() => delivered++); - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - socket.open(); - socket.message(new ArrayBuffer(2)); - socket.message('late frame'); - assert.equal(delivered, 0); - assert.equal(closed, 0); - assert.equal(socket.closes, 1); - assert.equal(h.faults.length, 1); - assert.ok(h.faults[0] instanceof TypeError); - assert.deepEqual(h.evidence, []); - assert.throws(() => h.transport.send('late'), /open/i); -}); - -for (const direction of ['receive', 'send']) { - test(`${direction} evidence failure preserves original error and stops without reconnect`, async () => { - const error = new Error('evidence storage failed'); - const h = await setup({ record: () => { throw error; } }); - let delivered = 0; - let closed = 0; - h.transport.onMessage(() => delivered++); - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - socket.open(); - const run = () => direction === 'receive' ? socket.message('frame') : h.transport.send('frame'); - assert.throws(run, (caught) => caught === error); - assert.equal(h.faults[0], error); - assert.equal(socket.closes, 1); - assert.equal(delivered, 0); - assert.equal(closed, 0); - assert.throws(() => h.transport.send('late'), /open/i); - }); -} - -test('error followed by close notifies once and retains original error event', async () => { - const h = await setup(); - let closed = 0; - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - const error = {} as Event; - socket.onerror?.(error); - socket.onclose?.({} as CloseEvent); - assert.equal(closed, 1); - assert.deepEqual(h.faults, []); - assert.deepEqual(h.evidence, [{ kind: 'transport-error', data: error }]); -}); - -test('explicit close and superseded sockets invalidate every old callback', async () => { - const h = await setup(); - const calls: string[] = []; - h.transport.onOpen(() => calls.push('open')); - h.transport.onMessage(() => calls.push('message')); - h.transport.onClose(() => calls.push('close')); - h.transport.connect(h.url); - const old = h.sockets[0]!; - h.transport.connect(h.url); - old.open(); old.message('old'); old.onerror?.({} as Event); old.onclose?.({} as CloseEvent); - assert.equal(old.closes, 1); - const current = h.sockets[1]!; - current.open(); - h.transport.close(); - current.open(); current.message('late'); current.onerror?.({} as Event); - assert.deepEqual(calls, ['open']); - assert.deepEqual(h.faults, []); - assert.deepEqual(h.evidence, []); -}); - -test('close failure is reported and thrown after invalidation', async () => { - const h = await setup(); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - const error = new Error('close failed'); - socket.closeError = error; - assert.throws(() => h.transport.close(), (caught) => caught === error); - assert.equal(h.faults[0], error); - assert.throws(() => h.transport.send('late'), /open/i); -}); - -test('explicit stop from error evidence observer suppresses the pending error close callback', async () => { - const h = await setup({ record: () => h.transport.close() }); - let closed = 0; - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - h.sockets[0]!.onerror?.({} as Event); - assert.equal(closed, 0); -}); - -test('a replacement connection from error evidence observer cannot receive the old close callback', async () => { - const h = await setup({ record: () => h.transport.connect(h.url) }); - let closed = 0; - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - h.sockets[0]!.onerror?.({} as Event); - assert.equal(h.sockets.length, 2); - assert.equal(closed, 0); -}); - -test('natural close lets real WireClient reconnect while explicit stop cancels it', async () => { - const h = await setup(); - const timers = new Map void>(); - const clock: Clock = { - setTimeout: (fn) => { const key = {}; timers.set(key, fn); return key; }, - clearTimeout: (key) => { timers.delete(key); }, - }; - const wire = new WireClient({ servers: h.context.config.servers, transportFactory: () => h.transport, clock }); - wire.start(); - h.sockets[0]!.open(); - h.sockets[0]!.close(); - assert.equal(timers.size, 1); - const retry = [...timers.values()][0]!; - timers.clear(); retry(); - assert.equal(h.sockets.length, 2); - wire.stop(); - assert.equal(timers.size, 0); -}); - -test('natural error reconnects real WireClient even when terminal onFault stops the host', async () => { - const h = await setup({ onFault: () => wire.stop() }); - const timers = new Map void>(); - const clock: Clock = { - setTimeout: (fn) => { const key = {}; timers.set(key, fn); return key; }, - clearTimeout: (key) => { timers.delete(key); }, - }; - const wire = new WireClient({ servers: h.context.config.servers, transportFactory: () => h.transport, clock }); - wire.start(); - h.sockets[0]!.open(); - const error = {} as Event; - h.sockets[0]!.onerror?.(error); - h.sockets[0]!.onclose?.({} as CloseEvent); - assert.deepEqual(h.faults, []); - assert.deepEqual(h.evidence, [{ kind: 'transport-error', data: error }]); - assert.equal(timers.size, 1); - const retry = [...timers.values()][0]!; - timers.clear(); retry(); - assert.equal(h.sockets.length, 2); - wire.stop(); - assert.equal(timers.size, 0); -}); - -for (const failure of ['record', 'close']) { - test(`socket error ${failure} failure reports terminal original and suppresses reconnect`, async () => { - const error = new Error(`${failure} failed`); - const h = await setup(failure === 'record' ? { record: () => { throw error; } } : {}); - let closed = 0; - h.transport.onClose(() => closed++); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - if (failure === 'close') socket.closeError = error; - const event = {} as Event; - assert.throws(() => socket.onerror?.(event), (caught) => caught === error); - assert.deepEqual(h.faults, [error]); - assert.equal(closed, 0); - socket.onclose?.({} as CloseEvent); - assert.equal(closed, 0); - assert.throws(() => h.transport.send('late'), /open/i); - }); -} - -test('non-text and cleanup failures reach a single terminal observer together', async () => { - const observed: unknown[] = []; - const h = await setup({ onFault: (error) => { if (observed.length === 0) observed.push(error); } }); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - const cleanupError = new Error('close failed'); - socket.closeError = cleanupError; - socket.message(new ArrayBuffer(1)); - assert.equal(h.faults.length, 1); - assert.equal(observed[0], h.faults[0]); - const fault = observed[0] as Error & { errors: unknown[] }; - assert.ok(fault instanceof Error); - assert.equal(fault.errors.length, 2); - assert.ok(fault.errors[0] instanceof TypeError); - assert.equal(fault.errors[1], cleanupError); - assert.throws(() => h.transport.send('late'), /open/i); -}); - -for (const operation of ['receive-record', 'error-record', 'send-record', 'send']) { - test(`${operation} and close failures are reported once and thrown as the same combined fault`, async () => { - const primary = new Error('primary failed'); - const cleanup = new Error('close failed'); - const observed: unknown[] = []; - const h = await setup({ - ...(operation === 'send' ? {} : { record: () => { throw primary; } }), - onFault: (error) => { if (observed.length === 0) observed.push(error); }, - }); - h.transport.connect(h.url); - const socket = h.sockets[0]!; - socket.open(); - socket.closeError = cleanup; - if (operation === 'send') socket.sendError = primary; - let thrown: unknown; - try { - if (operation === 'receive-record') socket.message('frame'); - else if (operation === 'error-record') socket.onerror?.({} as Event); - else h.transport.send('frame'); - } catch (error) { thrown = error; } - assert.equal(h.faults.length, 1); - assert.equal(thrown, observed[0]); - assert.equal(thrown, h.faults[0]); - assert.ok(thrown instanceof Error); - const errors = (thrown as Error & { errors: unknown[] }).errors; - assert.equal(errors.length, 2); - assert.equal(errors[0], primary); - assert.equal(errors[1], cleanup); - assert.throws(() => h.transport.send('late'), /open/i); - }); -} diff --git a/cocoscreator_projects/framework-tests/local-platform-login/view.test.ts b/cocoscreator_projects/framework-tests/local-platform-login/view.test.ts deleted file mode 100644 index 2272c98..0000000 --- a/cocoscreator_projects/framework-tests/local-platform-login/view.test.ts +++ /dev/null @@ -1,391 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { readFileSync, existsSync } from 'node:fs'; -import { runInNewContext } from 'node:vm'; -import ts from 'typescript'; -import { EventEmitter } from 'node:events'; -import { resolveLaunchWaitTime } from '../../YouleNexus/assets/framework/config/remote-config.ts'; - -class Sprite {} -class Vec3 { constructor(public x = 0, public y = 0, public z = 0) {} } -class Widget { enabled = true; updateAlignment() {} } -class UITransform { - width = 100; height = 30; anchorX = 0.5; anchorY = 0.5; - convertToWorldSpaceAR(value: Vec3) { return new Vec3(value.x + 250, value.y + 70, value.z); } - convertToNodeSpaceAR(value: Vec3) { return new Vec3(value.x - 20, value.y - 10, value.z); } -} -class ProgressBar { progress = 0; } -class Label { - static Overflow = { NONE: 0 }; overflow = 1; - string = ''; updateRenderData() {} -} -class Node extends EventEmitter { - static EventType = { TOUCH_END: 'touch-end' }; - active = true; - children: Node[] = []; - parent: Node | null = null; - position = new Vec3(); - setPosition(position: Vec3) { this.position = position; } - components = new Map(); - constructor(public name = '') { super(); } - getComponent(type: unknown) { return this.components.get(type) ?? null; } - getComponentsInChildren(type: unknown): any[] { - const own = this.getComponent(type); - return [...(own ? [own] : []), ...this.children.flatMap(child => child.getComponentsInChildren(type))]; - } - getChildByPath(path: string): Node | null { - const [head, ...tail] = path.split('/'); - const child = this.children.find(node => node.name === head); - return child ? (tail.length ? child.getChildByPath(tail.join('/')) : child) : null; - } -} -const director = new EventEmitter(); -function countdownFixture() { - const { createLogoCountdown } = load(); - assert.equal(typeof createLogoCountdown, 'function'); - let now = 0; let id = 0; - const timers = new Map void }>(); - const states: { visible: boolean; text: string }[] = []; - const gate = createLogoCountdown({ now: () => now, - schedule: (callback: () => void, ms: number) => { - const key = ++id; timers.set(key, { at: now + ms, callback }); return () => timers.delete(key); - }, render: (state: { visible: boolean; text: string }) => states.push(state) }); - const advance = (ms: number) => { - const end = now + ms; - while (true) { - const next = [...timers].filter(([, timer]) => timer.at <= end).sort((a, b) => a[1].at - b[1].at)[0]; - if (!next) break; - now = next[1].at; timers.delete(next[0]); next[1].callback(); - } - now = end; - }; - return { gate, timers, states, advance, last: () => states[states.length - 1]! }; -} - -test('Logo countdown ticks legacy milliseconds and skip requires config, resource and socket readiness', async () => { - const f = countdownFixture(); let complete = false; void f.gate.promise.then(() => { complete = true; }); - assert.equal(f.last().visible, false); - f.gate.skip(); f.gate.start(2500); - assert.equal(f.last().text, '倒计时3s'); - f.advance(1000); assert.equal(f.last().text, '倒计时2s'); - f.gate.resourcesReady(); assert.equal(f.last().text, '倒计时2s 跳过'); - f.gate.skip(); await Promise.resolve(); assert.equal(complete, false); - f.gate.socketReady(true); await Promise.resolve(); assert.equal(complete, false, 'premature click must not queue'); - f.gate.skip(); await f.gate.promise; - assert.equal(f.last().visible, false); assert.equal(f.timers.size, 0); -}); - -test('Logo exact deadline is independent from resources and socket; terminal cancellation ignores late events', async () => { - const f = countdownFixture(); f.gate.start(10); f.advance(9); - assert.equal(f.last().visible, true); f.advance(1); await f.gate.promise; - assert.equal(f.last().visible, false); assert.equal(f.timers.size, 0); - const cancelled = countdownFixture(); cancelled.gate.start(5000); - const rejection = assert.rejects(cancelled.gate.promise, /cancelled/); - cancelled.gate.cancel(); await rejection; - const count = cancelled.states.length; - cancelled.gate.start(9999); cancelled.gate.resourcesReady(); cancelled.gate.socketReady(true); cancelled.gate.skip(); cancelled.advance(10000); - assert.equal(cancelled.states.length, count); assert.equal(cancelled.timers.size, 0); -}); - -test('Logo render failure at completion rejects the gate and clears all timers', async () => { - const { createLogoCountdown } = load(); let now = 0; let failRender = false; - const callbacks = new Set<() => void>(); - const gate = createLogoCountdown({ now: () => now, - schedule: (callback: () => void) => { callbacks.add(callback); return () => callbacks.delete(callback); }, - render: () => { if (failRender) throw new Error('label rendering failed'); } }); - gate.start(2500); failRender = true; now = 2500; - const rejection = assert.rejects(gate.promise, /label rendering failed/); - assert.doesNotThrow(() => { const callback = [...callbacks][0]; callbacks.delete(callback); callback(); }); - await rejection; assert.equal(callbacks.size, 0); -}); - -test('Logo scheduling and tick render failures settle pending display promises', async () => { - const { createLogoCountdown } = load(); - const failed = createLogoCountdown({ now: () => 0, schedule: () => { throw new Error('timer failed'); }, render() {} }); - const scheduling = assert.rejects(failed.promise, /timer failed/); - assert.doesNotThrow(() => failed.start(2500)); await scheduling; - let failRender = false; const callbacks = new Set<() => void>(); - const gate = createLogoCountdown({ now: () => 0, - schedule: (callback: () => void) => { callbacks.add(callback); return () => callbacks.delete(callback); }, - render: () => { if (failRender) throw new Error('tick rendering failed'); } }); - gate.start(2500); failRender = true; - const rendering = assert.rejects(gate.promise, /tick rendering failed/); - assert.doesNotThrow(() => { const callback = [...callbacks][1]; callbacks.delete(callback); callback(); }); - await rendering; assert.equal(callbacks.size, 0); -}); -const source = new URL('../../YouleNexus/assets/scripts/local-platform-login/local-login-view.ts', import.meta.url); -function load() { - assert.ok(existsSync(source), 'Cocos local login view implementation must exist'); - const exports = {}; - runInNewContext(ts.transpileModule(readFileSync(source, 'utf8'), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText, { exports, require: (name: string) => { - assert.equal(name, 'cc'); - return { Node, Sprite, UITransform, Label, Widget, Vec3, director, Director: { EVENT_AFTER_DRAW: 'draw' } }; - }, Error }); - return exports as any; -} - -test('Logo native binding measures label around button center and releases its touch listener', () => { - const { CocosLogoCountdownView } = load(); assert.equal(typeof CocosLogoCountdownView, 'function'); - const button = new Node('Spirit3103'), text = new Node('Spirit3104'), parent = new Node('group'); - text.parent = parent; - for (const node of [button, text, parent]) node.components.set(UITransform, new UITransform()); - const label = new Label(); text.components.set(Label, label); - const widget = new Widget(); text.components.set(Widget, widget); - let clicks = 0; const view = new CocosLogoCountdownView(button, text, () => clicks++); - assert.equal(button.active, false); assert.equal(text.active, false); - view.render({ visible: true, text: '倒计时3s 跳过' }); - assert.equal(label.string, '倒计时3s 跳过'); assert.equal(label.overflow, Label.Overflow.NONE); - assert.equal(widget.enabled, false); assert.equal(text.position.x, 230); assert.equal(text.position.y, 60); - button.emit('touch-end'); assert.equal(clicks, 1); - view.dispose(); assert.equal(button.listenerCount('touch-end'), 0); assert.equal(button.active, false); -}); - -test('diagnostic stop button is placed below Logo across different parent coordinates without changing its right-side x', () => { - const { positionDiagnosticStopButtonBelow } = load(); - assert.equal(typeof positionDiagnosticStopButtonBelow, 'function'); - const logo = new Node('Spirit3103'), logoParent = new Node('logo-parent'); - const stop = new Node('StopButton'), canvas = new Node('Canvas'); - logo.parent = logoParent; stop.parent = canvas; stop.position = new Vec3(665, 310, 7); - const logoTransform = new UITransform(); - Object.assign(logoTransform, { width: 208, height: 59, anchorX: 0.5, anchorY: 0.5 }); - logoTransform.convertToWorldSpaceAR = value => new Vec3(value.x + 671.37, value.y + 311.5, value.z); - const stopTransform = new UITransform(); - Object.assign(stopTransform, { width: 220, height: 64, anchorX: 0.5, anchorY: 0.5 }); - const canvasTransform = new UITransform(); - canvasTransform.convertToNodeSpaceAR = value => new Vec3(value.x - 100, value.y - 40, value.z); - logo.components.set(UITransform, logoTransform); stop.components.set(UITransform, stopTransform); - canvas.components.set(UITransform, canvasTransform); - const widget = new Widget(); stop.components.set(Widget, widget); - - positionDiagnosticStopButtonBelow(stop, logo); - - assert.equal(widget.enabled, false, 'runtime position must not be overwritten by the native Widget'); - assert.equal(stop.position.x, 665); assert.equal(stop.position.z, 7); - assert.equal(stop.position.y, 198, 'button top is 12 px below the logo bottom in Canvas coordinates'); -}); -function fixture() { - const refs = { loadingRoot: new Node(), loginRoot: new Node(), resultRoot: new Node(), - statusOverlay: new Node(), statusLabel: new Label(), resultLabel: new Label() }; - const instance = new Node(); const group = new Node('group-2'); instance.children.push(group); - for (const name of ['游客登录', '版本显示', '微信登录', '手机登录', 'qq登录', '快速登录', - '隐藏快速登录-1', '隐藏快速登录-2', '登录中遮罩', '登录头像背景框', '登录中头像', - '登录中昵称', '登录中动画', '登录中头像框']) { - const node = new Node(name); group.children.push(node); - node.components.set(Sprite, new Sprite()); node.components.set(UITransform, new UITransform()); - node.components.set(Label, new Label()); - } - return { refs, instance, group, visitor: instance.getChildByPath('group-2/游客登录')! }; -} -test('required binding fails explicitly, including missing Sprite', () => { - const { CocosLocalLoginView } = load(); const f = fixture(); - const view = new CocosLocalLoginView(f.refs); - assert.throws(() => view.bindLogin(new Node(), () => {}), /游客登录/); - f.visitor.components.delete(Sprite); - assert.throws(() => view.bindLogin(f.instance, () => {}), /Sprite/); -}); -test('PageModel drives result labels and blocks room presentation', () => { - const { CocosLocalLoginView } = load(); const f = fixture(); const view = new CocosLocalLoginView(f.refs); - view.render({ page: 'lobby', self: { playerid: 19, nickname: '测试玩家', bean: 72, avatar: 'unused' } }); - view.activate('lobby'); - assert.equal(f.refs.resultLabel.string, '登录成功\n玩家 ID:19\n昵称:测试玩家\n豆豆:72'); - assert.equal(f.refs.resultRoot.active, true); assert.equal(f.refs.loginRoot.active, false); - assert.throws(() => view.activate('room'), /room/); -}); -test('busy mask and readiness gate touch, terminal disposal removes ownership', () => { - const { CocosLocalLoginView } = load(); const f = fixture(); const view = new CocosLocalLoginView(f.refs); - let clicks = 0; view.bindLogin(f.instance, () => clicks++); view.setVersion(123); - assert.equal((f.instance.getChildByPath('group-2/版本显示')!.getComponent(Label) as Label).string, '123'); - assert.equal(f.instance.getChildByPath('group-2/微信登录')!.active, false); - view.render({ page: 'login', canLogin: false }); view.activate('login'); f.visitor.emit('touch-end'); - assert.equal(clicks, 0); - view.render({ page: 'login', canLogin: true }); view.setBusy(true); f.visitor.emit('touch-end'); - assert.equal(clicks, 0); assert.equal(f.instance.getChildByPath('group-2/登录中遮罩')!.active, true); - view.setBusy(false); f.visitor.emit('touch-end'); assert.equal(clicks, 1); - view.showFailure({ state: 7, msg: '服务器拒绝' }); view.showOverlay({ kind: 'none' }); - assert.match(f.refs.statusLabel.string, /服务器拒绝/); - f.visitor.emit('touch-end'); assert.equal(clicks, 1); - view.dispose(); assert.equal(f.visitor.listenerCount('touch-end'), 0); -}); -test('frame request is one shot and real cancellation removes pending draw callback', () => { - const { createCocosFrameScheduler } = load(); const scheduler = createCocosFrameScheduler(); let calls = 0; - const cancel = scheduler.request(() => calls++); cancel(); director.emit('draw'); assert.equal(calls, 0); - scheduler.request(() => calls++); director.emit('draw'); director.emit('draw'); assert.equal(calls, 1); - assert.equal(director.listenerCount('draw'), 0); -}); - -test('asset load task reports validated native loader progress from zero through completion', async () => { - const { createAssetLoadTask } = load(); - const progress: number[] = []; - let onProgress!: (completed: number, total: number) => void; - let onComplete!: (error: unknown, asset?: unknown) => void; - const task = createAssetLoadTask((progressCallback: typeof onProgress, completeCallback: typeof onComplete) => { - onProgress = progressCallback; onComplete = completeCallback; - }, (value: number) => progress.push(value)); - - assert.deepEqual(progress, [0]); - onProgress(1, 2); onProgress(2, 2); onComplete(null, { uuid: 'login' }); - assert.deepEqual(progress, [0, 0.5, 1]); - assert.deepEqual(await task.promise, { uuid: 'login' }); -}); - -test('asset load task rejects invalid counts and loader failures without reporting guessed progress', async () => { - const { createAssetLoadTask } = load(); - for (const invalid of [[0, 0], [-1, 2], [3, 2], [0.5, 2], [1, Number.NaN]]) { - const progress: number[] = []; - let onProgress!: (completed: number, total: number) => void; - const task = createAssetLoadTask((progressCallback: typeof onProgress) => { onProgress = progressCallback; }, - (value: number) => progress.push(value)); - onProgress(invalid[0], invalid[1]); - await assert.rejects(task.promise, /asset load progress/i); - assert.deepEqual(progress, [0]); - } - - let complete!: (error: unknown, asset?: unknown) => void; - const failed = createAssetLoadTask((_progress: unknown, callback: typeof complete) => { complete = callback; }, () => {}); - complete(new Error('decoder failed')); - await assert.rejects(failed.promise, /decoder failed/); -}); - -test('asset load task cancellation settles pending work and ignores late loader callbacks', async () => { - const { createAssetLoadTask } = load(); - const progress: number[] = []; - let onProgress!: (completed: number, total: number) => void; - let onComplete!: (error: unknown, asset?: unknown) => void; - const task = createAssetLoadTask((progressCallback: typeof onProgress, completeCallback: typeof onComplete) => { - onProgress = progressCallback; onComplete = completeCallback; - }, (value: number) => progress.push(value)); - task.cancel(); - await assert.rejects(task.promise, /cancelled/i); - onProgress(1, 2); onComplete(null, { uuid: 'late' }); - assert.deepEqual(progress, [0]); -}); - -test('failure display preserves nested server and cleanup errors', () => { - const { CocosLocalLoginView } = load(); const f = fixture(); - const view = new CocosLocalLoginView(f.refs); - view.showFailure({ error: { state: 8, msg: '版本拒绝' }, cleanup: new Error('socket close failed') }); - assert.match(f.refs.statusLabel.string, /版本拒绝/); - assert.match(f.refs.statusLabel.string, /socket close failed/); -}); - -test('component rejects non-debug/native before creating host or writing browser storage', async () => { - const path = new URL('../../YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts', import.meta.url); - assert.ok(existsSync(path), 'LocalPlatformLogin component must exist'); - for (const [debug, native] of [[false, false], [true, true]]) { - let hosts = 0; const exports: any = {}; - class Component {} - const decorator = (..._args: unknown[]) => (target: unknown) => target; - runInNewContext(ts.transpileModule(readFileSync(path, 'utf8'), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, experimentalDecorators: true }, - }).outputText, { exports, require: (name: string) => { - if (name === 'cc/env') return { DEBUG: debug }; - if (name === 'cc') return { Component, Node, Label, Prefab: class {}, sys: { isNative: native }, - _decorator: { ccclass: decorator, property: () => () => {} } }; - if (name.endsWith('local-login-host.ts')) return { createLocalLoginHost: () => { hosts++; } }; - if (name.endsWith('local-login-view.ts')) return load(); - if (name.endsWith('remote-config.ts')) return { resolveLaunchWaitTime }; - if (name.endsWith('remote-config-fetcher.ts')) return { HttpConfigFetcher: class {} }; - throw new Error(`Unexpected dependency ${name}`); - }, Error }); - const component = new exports.LocalPlatformLogin(); - await assert.rejects(async () => component.start(), /DEBUG.*browser/); - assert.equal(hosts, 0); - } -}); - -test('component stop cancels pending draw and timer work', async () => { - const path = new URL('../../YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts', import.meta.url); - const exports: any = {}; const timers = new Map void>(); let timerId = 0; - runInNewContext(ts.transpileModule(readFileSync(path, 'utf8'), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, experimentalDecorators: true }, - }).outputText, { exports, require: (name: string) => { - if (name === 'cc/env') return { DEBUG: true }; - if (name === 'cc') return { Component: class {}, Node, Label, Prefab: class {}, ProgressBar, sys: { isNative: false }, - _decorator: { ccclass: () => (target: unknown) => target, property: () => () => {} } }; - if (name.endsWith('local-login-host.ts')) return {}; - if (name.endsWith('local-login-view.ts')) return load(); - if (name.endsWith('remote-config.ts')) return { resolveLaunchWaitTime }; - if (name.endsWith('remote-config-fetcher.ts')) return {}; - throw new Error(`Unexpected dependency ${name}`); - }, Error, performance: { now: () => 123 }, window: { - setTimeout: (callback: () => void) => { timers.set(++timerId, callback); return timerId; }, - clearTimeout: (id: number) => timers.delete(id), - } }); - const component = new exports.LocalPlatformLogin(); - const draw = component.nextDraw(); const timer = component.delay(900); - const drawRejection = assert.rejects(draw, /cancelled/); const timerRejection = assert.rejects(timer, /cancelled/); - component.stop(); await Promise.all([drawRejection, timerRejection]); - assert.equal(timers.size, 0); assert.equal(director.listenerCount('draw'), 0); - assert.equal(component.getDiagnostics().pendingCount, 0); -}); - -test('minimum display ignores an early timer wake and cancellation clears its remaining wait', async () => { - const path = new URL('../../YouleNexus/assets/scripts/local-platform-login/LocalPlatformLogin.ts', import.meta.url); - const exports: any = {}; const timers = new Map void>(); let timerId = 0; let clock = 100; - let hostOptions: any; - runInNewContext(ts.transpileModule(readFileSync(path, 'utf8'), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, experimentalDecorators: true }, - }).outputText, { exports, require: (name: string) => { - if (name === 'cc/env') return { DEBUG: true }; - if (name === 'cc') return { Component: class {}, Node, Label, Sprite, UITransform, ProgressBar, Prefab: class {}, sys: { isNative: false }, - _decorator: { ccclass: () => (target: unknown) => target, property: () => () => {} } }; - if (name.endsWith('local-login-host.ts')) return { createLocalLoginHost: (options: unknown) => { - hostOptions = options; return { start: async () => {}, stop() {}, getSnapshot: () => ({}) }; - } }; - if (name.endsWith('local-login-view.ts')) return load(); - if (name.endsWith('remote-config.ts')) return { resolveLaunchWaitTime }; - if (name.endsWith('remote-config-fetcher.ts')) return { HttpConfigFetcher: class {} }; - throw new Error(`Unexpected dependency ${name}`); - }, Error, performance: { now: () => clock }, window: { - location: { search: '?profile=local' }, localStorage: {}, - setTimeout: (callback: () => void) => { timers.set(++timerId, callback); return timerId; }, - clearTimeout: (id: number) => timers.delete(id), - } }); - async function started(emitDraw = true) { - const component = new exports.LocalPlatformLogin(); const f = fixture(); - const stopButton = new Node('StopButton'), canvas = new Node('Canvas'); - stopButton.parent = canvas; stopButton.components.set(UITransform, new UITransform()); - canvas.components.set(UITransform, new UITransform()); - Object.assign(component, f.refs, { stopButton, loadingPrefab: {}, loginPrefab: {} }); - const logo = new Node('Layer1_Logo'); const group = new Node('group-1'); - const bar = new Node('Spirit254'); bar.components.set(ProgressBar, new ProgressBar()); - const button = new Node('Spirit3103'), text = new Node('Spirit3104'); - for (const node of [button, text, group]) node.components.set(UITransform, new UITransform()); - const label = new Label() as Label & { node: Node }; label.node = text; text.components.set(Label, label); text.parent = group; - group.children.push(bar, button, text); - logo.children.push(group, new Node('group-32')); component.loadingRoot.children.push(logo); - component.loadingRoot.getComponentsInChildren = () => []; - await component.start(); if (emitDraw) director.emit('draw'); await Promise.resolve(); - return component; - } - const drain = async () => { for (let i = 0; i < 5; i++) await Promise.resolve(); }; - function wake(at: number) { - clock = at; const [id, callback] = timers.entries().next().value!; - timers.delete(id); callback(); - } - const component = await started(); let completed = false; - hostOptions.onConfigResolved({ identity: { version: 731 }, remoteConfig: { getValue: (name: string) => name === 'launchWaitTime' ? 500 : null } }); - const minimum = hostOptions.waitForMinimumDisplay().then(() => { completed = true; }); - await drain(); wake(599.8); await drain(); - assert.equal(completed, false, '499.8 ms elapsed must not open minimum display gate'); - assert.equal(component.getDiagnostics().events.some((event: any) => event.kind === 'minimum-display-ready'), false); - assert.equal(timers.size, 1); - wake(600); await minimum; assert.equal(completed, true); assert.equal(timers.size, 0); - component.stop(); - - clock = 1000; const cancelled = await started(); - hostOptions.onConfigResolved({ identity: { version: 731 }, remoteConfig: { getValue: (name: string) => name === 'launchWaitTime' ? 500 : null } }); - const cancelledMinimum = hostOptions.waitForMinimumDisplay(); - const rejection = assert.rejects(cancelledMinimum, /cancelled/); - await drain(); wake(1499.8); await drain(); - assert.equal(timers.size, 1); cancelled.stop(); await rejection; - assert.equal(timers.size, 0); assert.equal(cancelled.getDiagnostics().pendingCount, 0); - - const undrawn = await started(false); let zeroComplete = false; - hostOptions.onConfigResolved({ identity: { version: 731 }, remoteConfig: { getValue: (name: string) => name === 'hall_config' ? { launchWaitTime: 0 } : null } }); - const zeroMinimum = hostOptions.waitForMinimumDisplay().then(() => { zeroComplete = true; }); - await drain(); assert.equal(zeroComplete, false, 'zero display deadline cannot bypass first native frame'); - director.emit('draw'); await zeroMinimum; assert.equal(zeroComplete, true); undrawn.stop(); -}); diff --git a/cocoscreator_projects/framework-tests/local-platform-login/wire.test.ts b/cocoscreator_projects/framework-tests/local-platform-login/wire.test.ts deleted file mode 100644 index 339488a..0000000 --- a/cocoscreator_projects/framework-tests/local-platform-login/wire.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFileSync } from 'node:fs'; -import { test } from 'node:test'; -import { resolveLocalStartup } from '../../YouleNexus/assets/framework/config/local-startup.ts'; -import { RECONNECT_INTERVAL_MS } from '../../YouleNexus/assets/framework/core/constants.ts'; -import type { WireEvent } from '../../YouleNexus/assets/framework/net/wire-client.ts'; -import type { OutboundEnvelope } from '../../YouleNexus/assets/framework/core/types/envelope.ts'; -import { FakeTransport } from '../helpers/fake-transport.ts'; - -const login: OutboundEnvelope = { app: 'youle', route: 'agent', rpc: 'player_login', data: { gameid: 101 } }; -function fixture(name: string) { - return JSON.parse(readFileSync(new URL(`../fixtures/contracts/${name}`, import.meta.url), 'utf8')); -} -async function setup(recordHook?: (event: { kind: string; data: unknown }) => void) { - const api = await import('../../YouleNexus/assets/scripts/local-platform-login/local-login-wire.ts') - .catch(error => assert.fail(`Local login Wire implementation must exist: ${String(error)}`)); - const context = await resolveLocalStartup({ hostKind: 'h5', win: {}, search: '?profile=local', - fetcher: { async fetch() { return { player_server_tcp: 'remote.example:3088' }; } } }); - const transports: FakeTransport[] = []; - const events: WireEvent[] = []; - const evidence: { kind: string; data: unknown }[] = []; - const faults: unknown[] = []; - const order: string[] = []; - const wire = api.createLocalLoginWire({ context, - transportFactory: () => { const transport = new FakeTransport(); transports.push(transport); return transport; }, - record: event => { evidence.push(event); recordHook?.(event); }, - onFault: error => { order.push('fault'); faults.push(error); assert.equal(transports[0]?.closeCalls, 1); }, - }); - wire.subscribe(event => { events.push(event); order.push(event.type === 'message' ? event.message.rpc : event.type); }); - const push = async (message: unknown) => { transports[0]!.serverPush(JSON.stringify(message)); await transports[0]!.flush(); }; - return { wire, context, transports, events, evidence, faults, order, push }; -} - -test('start is not open; only original lobby and ordinary agent/platform events are forwarded', async t => { - const h = await setup(); t.after(() => h.wire.stop()); - h.wire.start(); - assert.equal(h.events.length, 0); - await h.transports[0]!.flush(); - let unsubscribed = 0; - const unsubscribe = h.wire.subscribe(() => unsubscribed++); unsubscribe(); unsubscribe(); - await h.push(fixture('player-login-success.json')); - await h.push({ route: 'agent', rpc: 'show_message', data: { msg: 'hello' } }); - await h.push({ route: 'platform', rpc: 'notice', data: {} }); - assert.equal(unsubscribed, 0); - assert.equal(h.events.filter(event => event.type === 'message').length, 3); - for (const event of h.events) assert.ok(h.evidence.some(item => item.kind === 'wire-event' && item.data === event)); - assert.deepEqual(h.faults, []); -}); - -for (const [name, message, error] of [ - ['room recovery', fixture('player-login-room.json'), /login room recovery/], - ['malformed login', { route: 'agent', rpc: 'player_login', data: { state: 0 } }, /\$\.data\.playerid/], - ['room route', { route: 'room', rpc: 'notice', data: {} }, /room/], - ['game route', { route: 'mahjong', rpc: 'notice', data: {} }, /mahjong/], - ['agent switch', { route: 'agent', rpc: 'connect_agentserver', data: {} }, /connect_agentserver/], - ['platform switch', { route: 'platform', rpc: 'connect_roomserver', data: {} }, /connect_roomserver/], -] as const) test(`${name} stops before downstream delivery and remains terminal`, async t => { - t.mock.timers.enable({ apis: ['setTimeout'] }); - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - await h.push(message); - assert.equal(h.faults.length, 1); assert.match(String(h.faults[0]), error); - assert.equal(h.events.length, 1); - await h.push(fixture('player-login-success.json')); - assert.throws(() => h.wire.reconnectCurrent(), /stopped|terminal/); - assert.throws(() => h.wire.start(), /stopped|terminal/); - h.wire.stop(); h.wire.stop(); t.mock.timers.tick(60000); - assert.equal(h.transports.length, 1); assert.equal(h.events.length, 1); assert.equal(h.faults.length, 1); -}); - -for (const message of [ - { route: 'agent', rpc: 'player_login', data: { state: 1 } }, - { route: 'agent', rpc: 'kick_server', data: { msg: 'bye' } }, -]) test(`${message.rpc} failure reaches existing handlers before host notification`, async t => { - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - h.wire.subscribe(event => { if (event.type === 'message') h.wire.stop(); }); - await h.push(message); - assert.deepEqual(h.order, ['open', message.rpc, 'fault']); - assert.equal(h.faults.length, 1); -}); - -test('outbound scope and explicit switch fail without sending or reconnecting', async t => { - for (const operation of ['send', 'switch'] as const) { - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - assert.throws(() => operation === 'send' - ? h.wire.send({ ...login, rpc: 'create_room' }) : h.wire.switchServer(h.context.config.servers[0]!), /scope|switchServer/); - assert.deepEqual(h.transports[0]!.sent, []); assert.equal(h.faults.length, 1); - } -}); - -test('normal network close reconnects with the same single login envelope', async t => { - t.mock.timers.enable({ apis: ['setTimeout'] }); - const h = await setup(); t.after(() => h.wire.stop()); - h.wire.subscribe(event => { if (event.type === 'open') h.wire.send(login); }); - h.wire.start(); await h.transports[0]!.flush(); - h.transports[0]!.serverError(); await h.transports[0]!.flush(); t.mock.timers.tick(RECONNECT_INTERVAL_MS); - assert.equal(h.transports.length, 2); await h.transports[1]!.flush(); - assert.equal(h.transports[0]!.sent[0], JSON.stringify(login)); - assert.deepEqual(h.transports[1]!.sent, h.transports[0]!.sent); assert.deepEqual(h.faults, []); -}); - -test('scope fault retains its primary error and independent cleanup error', async t => { - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - const cleanup = new Error('cleanup failed'); - h.transports[0]!.close = () => { h.transports[0]!.closeCalls++; throw cleanup; }; - await h.push({ route: 'room', rpc: 'notice', data: {} }); - assert.equal(h.faults.length, 1); - const errors = (h.faults[0] as { errors: unknown[] }).errors; - assert.match(String(errors[0]), /room/); assert.equal(errors[1], cleanup); - h.wire.stop(); assert.equal(h.transports[0]!.closeCalls, 1); -}); - -test('evidence failure preserves the exact thrown value plus cleanup error and prevents delivery', async t => { - const primary = { reason: 'evidence unavailable' }; - const h = await setup(event => { - if (event.kind === 'wire-event' && (event.data as WireEvent).type === 'message') throw primary; - }); - t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - const cleanup = new Error('close failed'); - h.transports[0]!.close = () => { h.transports[0]!.closeCalls++; throw cleanup; }; - await h.push(fixture('player-login-success.json')); - assert.deepEqual((h.faults[0] as { errors: unknown[] }).errors, [primary, cleanup]); - assert.equal(h.events.length, 1); assert.equal(h.faults.length, 1); -}); - -test('listener error is reported unchanged, stops remaining delivery, and stops only once', async t => { - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - const primary = new Error('downstream failed'); - h.wire.subscribe(() => { throw primary; }); - let laterDeliveries = 0; h.wire.subscribe(() => laterDeliveries++); - await h.push(fixture('player-login-success.json')); - assert.equal(h.faults[0], primary); assert.equal(laterDeliveries, 0); - h.wire.stop(); assert.equal(h.transports[0]!.closeCalls, 1); -}); - -test('terminal callback cannot send or reconnect before runtime stop; host notification is single', async t => { - const h = await setup(); t.after(() => h.wire.stop()); h.wire.start(); await h.transports[0]!.flush(); - h.wire.subscribe(event => { - if (event.type !== 'message') return; - assert.throws(() => h.wire.send(login), /terminal/); - assert.throws(() => h.wire.reconnectCurrent(), /terminal/); - }); - await h.push({ route: 'agent', rpc: 'kick_server', data: { msg: 'bye' } }); - assert.equal(h.faults.length, 1); assert.deepEqual(h.transports[0]!.sent, []); -}); diff --git a/cocoscreator_projects/framework-tests/local-platform-login/game-entry.test.ts b/cocoscreator_projects/framework-tests/platform-login/game-entry.test.ts similarity index 93% rename from cocoscreator_projects/framework-tests/local-platform-login/game-entry.test.ts rename to cocoscreator_projects/framework-tests/platform-login/game-entry.test.ts index 68aa08b..e2fca00 100644 --- a/cocoscreator_projects/framework-tests/local-platform-login/game-entry.test.ts +++ b/cocoscreator_projects/framework-tests/platform-login/game-entry.test.ts @@ -4,10 +4,10 @@ import { GameSessionHost } from '../../YouleNexus/assets/framework/sdk/runtime/g import type { GameHost, PlatformToGameEvent, GameServerMessage } from '../../YouleNexus/assets/framework/sdk/contracts/index.ts'; async function setup() { - const api = await import('../../YouleNexus/assets/scripts/local-platform-login/local-login-game-entry.ts') + const api = await import('../../YouleNexus/assets/scripts/platform-login/login-stage-game-entry.ts') .catch(error => assert.fail(`Diagnostic GameEntry implementation must exist: ${String(error)}`)); const evidence: { kind: string; data: unknown }[] = []; - return { entry: api.createDiagnosticGameEntry(101, event => evidence.push(event)), evidence }; + return { entry: api.createLoginStageGameEntry(101, event => evidence.push(event)), evidence }; } test('real GameSessionHost validates and releases one diagnostic instance without entering a game', async () => { diff --git a/cocoscreator_projects/framework-tests/platform-login/loading-animation.test.ts b/cocoscreator_projects/framework-tests/platform-login/loading-animation.test.ts new file mode 100644 index 0000000..2861aec --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/loading-animation.test.ts @@ -0,0 +1,32 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { runInNewContext } from 'node:vm'; +import ts from 'typescript'; + +test('login busy state starts Animation once, hides on completion and restarts on retry', () => { + const exports: any = {}; + const source = readFileSync(new URL('../../YouleNexus/assets/scripts/platform-login/login-panel.ts', import.meta.url), 'utf8'); + runInNewContext(ts.transpileModule(source, { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, + }).outputText, { exports, require(name: string) { + if (name === './node-bindings.ts') return { requireNode(root: any, path: string) { + const result = root.paths[path]; if (!result) throw Error(`Missing ${path}`); return result; + } }; + return {}; + } }); + const children = Object.fromEntries(['登录中遮罩','登录头像背景框','登录中头像','登录中昵称','登录中动画','登录中头像框'] + .map(name => [name, {active:false}])); + const group = {active:false, paths:children}; + let playing = false, starts = 0, stops = 0; + const panel = Object.create(exports.LoginPanel.prototype); + panel.root = {paths:{'group-2/Logining':group}}; + panel.loadingAnimation = {defaultClip:{name:'LoginLoading'}, getState:()=>({isPlaying:playing}), + play(name: string){assert.equal(name,'LoginLoading');playing=true;starts++;}, stop(){playing=false;stops++;}}; + panel.setBusy(true); panel.setBusy(true); + assert.equal(starts,1); assert.equal(group.active,true); + assert.ok(Object.values(children).every(node=>node.active)); + panel.setBusy(false); + assert.equal(stops,1); assert.equal(group.active,false); assert.equal(playing,false); + panel.setBusy(true); assert.equal(starts,2); +}); diff --git a/cocoscreator_projects/framework-tests/platform-login/login-accounts.test.ts b/cocoscreator_projects/framework-tests/platform-login/login-accounts.test.ts new file mode 100644 index 0000000..7133d51 --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/login-accounts.test.ts @@ -0,0 +1,76 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { LoginAccounts, nicknameHash } from '../../YouleNexus/assets/framework/adapters/native/login-accounts.ts'; +import { resolveLoginPolicy } from '../../YouleNexus/assets/framework/presentation/login-policy.ts'; +import { buildLoginRequest } from '../../YouleNexus/assets/framework/protocol/contracts/login-contract.ts'; +test('native authorization preserves provider numbers, agreement guard, callback and cache', () => { + const values=new Map();const calls:number[]=[];const submitted:any[]=[];const tips:string[]=[]; + const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}}, + settings:{accreditlogin:p=>calls.push(p),getcompareCode:()=>0},random:()=>0.2,now:()=>100, + gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}),submit:(a,d)=>submitted.push([a,d]),tip:t=>tips.push(t)}); + service.setAgreement(false);service.authorize('wechat');assert.deepEqual(calls,[]);assert.deepEqual(tips,['未同意协议']); + service.setAgreement(true);service.authorize('wechat');service.authorize('qq');assert.deepEqual(calls,[2,1]); + service.acceptNative({openid:'o',headimgurl:'a',nickname:'n',sex:1,city:'c',Province:'p',unionid:'u'},'bridge'); + assert.equal(submitted[0][0].avatar,'a');assert.equal(submitted[0][0].province,'p'); + assert.ok(values.has('tsgame_wxinfo'));assert.equal(service.cached()?.nickname,'n'); +}); +test('all login paths require the current agreement and rejected paths have no side effects',()=>{ + const values=new Map(),tips:string[]=[],submitted:unknown[]=[],native:number[]=[]; + const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}}, + settings:{getcompareCode:()=>0,accreditlogin:p=>native.push(p)},random:()=>0.2,now:()=>100, + gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}),submit:a=>submitted.push(a),tip:t=>tips.push(t)}); + assert.equal(service.agreed,true);service.setAgreement(false); + service.authorize('wechat');service.authorize('qq');service.visitor();service.device();service.phone('00123','000456');service.autoLogin(); + service.acceptNative({},'global');service.acceptNative({},'bridge'); + for(let i=0;i<5;i++)service.hiddenFirst();service.hiddenSecond(); + assert.equal(tips.length,9);assert.ok(tips.every(t=>t==='未同意协议')); + assert.equal(values.size,0);assert.equal(submitted.length,0);assert.equal(native.length,0); + service.setAgreement(true);service.visitor();assert.equal(submitted.length,1); +}); +test('successful device login persists server identity for the next automatic login', () => { + const values=new Map();const submitted:any[]=[]; + const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}}, + random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),deviceLogin:true}, + submit:(a,d)=>submitted.push([a,d]),tip:()=>{}}); + service.loginSucceeded({state:0,playerid:42,openid:'server-o',avatar:'server-a',nickname:'server-n',sex:2,city:'c',province:'p',unionid:'u',tel:'13800138000'}, {enabled:true,telphone:'13800138000'}); + service.autoLogin();assert.equal(submitted[0][0].openid,'server-o'); + assert.deepEqual(submitted[0][1],{enabled:true,telphone:'13800138000'}); + assert.equal(values.get('tsgame_playerid_ga'),'42');assert.equal(values.get('tsgame_dLogin'),'1'); +}); +test('visitor login and cached visitor stay phone-free when both login buttons are enabled', () => { + const values=new Map();const submitted:any[]=[]; + const options={storage:{getItem:(k:string)=>values.get(k)??null,setItem:(k:string,v:string)=>{values.set(k,v);}}, + random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:true,deviceLogin:true}, + submit:(a:unknown,d:unknown)=>submitted.push([a,d]),tip:()=>{}}; + const service=new LoginAccounts(options); + service.visitor();assert.deepEqual(submitted[0][1],{enabled:false}); + assert.doesNotThrow(()=>service.loginSucceeded({state:0,playerid:42},submitted[0][1])); + assert.equal(values.get('tsgame_playerid_ga'),'42'); + assert.equal(values.has('tsgame_devicePlayer'),false);assert.equal(values.has('tsgame_dLogin'),false); + const reloaded=new LoginAccounts(options);reloaded.autoLogin(); + assert.deepEqual(submitted[1],[submitted[0][0],{enabled:false}]); + assert.doesNotThrow(()=>reloaded.loginSucceeded({state:0,playerid:42},submitted[1][1])); +}); +test('global and WVJB authorization retain their distinct legacy cache destinations', () => { + const values=new Map(); + const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}}, + random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:{...resolveLoginPolicy({raw:{},getValue:()=>null}),showVisitor:true},submit:()=>{},tip:()=>{}}); + const data={openid:'o',headimgurl:'a',nickname:'n',sex:1,city:'c',Province:'p',unionid:'u'}; + service.acceptNative(data,'global');assert.ok(values.has('tsgame_visitorinfo'));assert.equal(values.has('tsgame_wxinfo'),false); + service.acceptNative(data,'bridge');assert.ok(values.has('tsgame_wxinfo')); +}); +test('legacy nickname MD5 matches standard vectors',()=>{ + assert.equal(nicknameHash(''),'d41d8cd98f00b204e9800998ecf8427e'); + assert.equal(nicknameHash('abc'),'900150983cd24fb0d6963f7d28e17f72'); +}); +test('phone login emits original sms fields and does not masquerade as cached auto login', () => { + const values=new Map();const submitted:any[]=[]; + const service=new LoginAccounts({storage:{getItem:k=>values.get(k)??null,setItem:(k,v)=>{values.set(k,v);}}, + random:()=>0.2,now:()=>100,gameid:'g',agentid:'a',policy:resolveLoginPolicy({raw:{},getValue:()=>null}), + submit:(a,d)=>submitted.push([a,d]),tip:()=>{}}); + service.phone('13800138000','123456'); + const [account,mode]=submitted[0];assert.equal(account.unionid,'phone_13800138000'); + const result=buildLoginRequest({identity:{agentid:'a',gameid:'g',channelid:'c',marketid:4,version:1}},account, + {location:null,machineid:'m',machineroom:'',deviceLogin:mode,loginPlayerId:{enabled:false}}); + assert.equal(result.data.telphone,'13800138000');assert.equal(result.data.smmcode,'123456');assert.equal(result.data.telphoneAuto,false); +}); diff --git a/cocoscreator_projects/framework-tests/platform-login/login-policy.test.ts b/cocoscreator_projects/framework-tests/platform-login/login-policy.test.ts new file mode 100644 index 0000000..e487598 --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/login-policy.test.ts @@ -0,0 +1,40 @@ +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { resolveLoginPolicy, loginButtons } from '../../YouleNexus/assets/framework/presentation/login-policy.ts'; +import { resolveRuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts'; + +const remote = (values: Record) => ({raw:values,getValue:(key:string)=>values[key] ?? null}); +test('source defaults and channel overrides retain legacy button order', () => { + const policy = resolveLoginPolicy(remote({})); + assert.deepEqual(loginButtons(policy, 0, false), {wechat:true,visitor:false,phone:false,quick:false,qq:false,account:false}); + assert.deepEqual(loginButtons(policy, 1, false), {wechat:true,visitor:true,phone:false,quick:false,qq:false,account:false}); + const configured = resolveLoginPolicy(remote({deviceLogin:1,hall_config:{showVisitor:1,qqLogin:1}})); + assert.deepEqual(loginButtons(configured, 0, false), {wechat:false,visitor:true,phone:true,quick:false,qq:true,account:false}); + assert.deepEqual(loginButtons(configured, 0, true), {wechat:false,visitor:true,phone:false,quick:false,qq:true,account:true}); +}); +test('invalid upstream login options fail at the config source', () => { + assert.throws(()=>resolveLoginPolicy(remote({deviceLogin:'yes'})), /deviceLogin/); + assert.throws(()=>resolveLoginPolicy(remote({hall_config:{qqLogin:[]}})), /qqLogin/); +}); + +test('build identity selects its own remote agent showVisitor through to login buttons', async () => { + // Relevant hierarchy captured from config_test/update_jsonv2.txt on 2026-09-06. + const config = {agentlist:[ + {agentid:'veRa0qrBf0df2K1G4de2tgfmVxB2jxpv',gamelist:[{gameid:'G2hw0ubng0zcoI0r4mx3H2yr4GejidwO'}]}, + {agentid:'1B2h0ccl205c390Y28m1Ajdplkuu4wgy',showVisitor:1,gamelist:[ + {gameid:'J9r4022t80pxlb01zOK76DN1WD4QewFl',channellist:[ + {channelid:'aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG',marketlist:[{marketid:4,deviceLogin:true}]}]}]}, + ]}; + const options={startupConfig:{...STARTUP_CONFIG,configSource:'remote' as const},hostKind:'h5' as const,win:{},search:'',fetcher:{fetch:async()=>config}}; + const resolved=await resolveRuntimeConfig(options); + assert.equal(resolved.remoteConfig.getValue('showVisitor'),1); + assert.equal(resolved.identity.channelid,'aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG'); + const buttons=loginButtons(resolveLoginPolicy(resolved.remoteConfig),0,false); + assert.equal(buttons.visitor,true);assert.equal(buttons.wechat,false); + assert.equal(buttons.phone,true,'market deviceLogin and agent showVisitor both take effect'); + const other=await resolveRuntimeConfig({...options,search:'?agentid=veRa0qrBf0df2K1G4de2tgfmVxB2jxpv'}); + assert.equal(resolveLoginPolicy(other.remoteConfig).showVisitor,false,'must not read another agent configuration'); +}); diff --git a/cocoscreator_projects/framework-tests/platform-login/logo-countdown.test.ts b/cocoscreator_projects/framework-tests/platform-login/logo-countdown.test.ts new file mode 100644 index 0000000..9b896be --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/logo-countdown.test.ts @@ -0,0 +1,144 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync, existsSync } from 'node:fs'; +import { runInNewContext } from 'node:vm'; +import ts from 'typescript'; +import { EventEmitter } from 'node:events'; +import { resolveLaunchWaitTime } from '../../YouleNexus/assets/framework/config/remote-config.ts'; + +class Sprite {} +class ScrollView { scrollToTop() {} stopAutoScroll() {} } +class Vec3 { constructor(public x = 0, public y = 0, public z = 0) {} } +class Widget { enabled = true; updateAlignment() {} } +class UITransform { + width = 100; height = 30; anchorX = 0.5; anchorY = 0.5; + convertToWorldSpaceAR(value: Vec3) { return new Vec3(value.x + 250, value.y + 70, value.z); } + convertToNodeSpaceAR(value: Vec3) { return new Vec3(value.x - 20, value.y - 10, value.z); } +} +class ProgressBar { progress = 0; } +class Label { + static Overflow = { NONE: 0 }; overflow = 1; + string = ''; updateRenderData() {} +} +class Node extends EventEmitter { + static EventType = { TOUCH_END: 'touch-end' }; + active = true; + children: Node[] = []; + parent: Node | null = null; + position = new Vec3(); + setPosition(position: Vec3) { this.position = position; } + components = new Map(); + constructor(public name = '') { super(); } + getComponent(type: unknown) { return this.components.get(type) ?? null; } + getComponentsInChildren(type: unknown): any[] { + const own = this.getComponent(type); + return [...(own ? [own] : []), ...this.children.flatMap(child => child.getComponentsInChildren(type))]; + } + getChildByPath(path: string): Node | null { + const [head, ...tail] = path.split('/'); + const child = this.children.find(node => node.name === head); + return child ? (tail.length ? child.getChildByPath(tail.join('/')) : child) : null; + } +} +const director = new EventEmitter(); +function countdownFixture() { + const { createLogoCountdown } = load(); + assert.equal(typeof createLogoCountdown, 'function'); + let now = 0; let id = 0; + const timers = new Map void }>(); + const states: { visible: boolean; text: string }[] = []; + const gate = createLogoCountdown({ now: () => now, + schedule: (callback: () => void, ms: number) => { + const key = ++id; timers.set(key, { at: now + ms, callback }); return () => timers.delete(key); + }, render: (state: { visible: boolean; text: string }) => states.push(state) }); + const advance = (ms: number) => { + const end = now + ms; + while (true) { + const next = [...timers].filter(([, timer]) => timer.at <= end).sort((a, b) => a[1].at - b[1].at)[0]; + if (!next) break; + now = next[1].at; timers.delete(next[0]); next[1].callback(); + } + now = end; + }; + return { gate, timers, states, advance, last: () => states[states.length - 1]! }; +} + +test('Logo countdown ticks legacy milliseconds and skip requires config, resource and socket readiness', async () => { + const f = countdownFixture(); let complete = false; void f.gate.promise.then(() => { complete = true; }); + assert.equal(f.last().visible, false); + f.gate.skip(); f.gate.start(2500); + assert.equal(f.last().text, '倒计时3s'); + f.advance(1000); assert.equal(f.last().text, '倒计时2s'); + f.gate.resourcesReady(); assert.equal(f.last().text, '倒计时2s 跳过'); + f.gate.skip(); await Promise.resolve(); assert.equal(complete, false); + f.gate.socketReady(true); await Promise.resolve(); assert.equal(complete, false, 'premature click must not queue'); + f.gate.skip(); await f.gate.promise; + assert.equal(f.last().visible, false); assert.equal(f.timers.size, 0); +}); + +test('Logo exact deadline is independent from resources and socket; terminal cancellation ignores late events', async () => { + const f = countdownFixture(); f.gate.start(10); f.advance(9); + assert.equal(f.last().visible, true); f.advance(1); await f.gate.promise; + assert.equal(f.last().visible, false); assert.equal(f.timers.size, 0); + const cancelled = countdownFixture(); cancelled.gate.start(5000); + const rejection = assert.rejects(cancelled.gate.promise, /cancelled/); + cancelled.gate.cancel(); await rejection; + const count = cancelled.states.length; + cancelled.gate.start(9999); cancelled.gate.resourcesReady(); cancelled.gate.socketReady(true); cancelled.gate.skip(); cancelled.advance(10000); + assert.equal(cancelled.states.length, count); assert.equal(cancelled.timers.size, 0); +}); + +test('Logo render failure at completion rejects the gate and clears all timers', async () => { + const { createLogoCountdown } = load(); let now = 0; let failRender = false; + const callbacks = new Set<() => void>(); + const gate = createLogoCountdown({ now: () => now, + schedule: (callback: () => void) => { callbacks.add(callback); return () => callbacks.delete(callback); }, + render: () => { if (failRender) throw new Error('label rendering failed'); } }); + gate.start(2500); failRender = true; now = 2500; + const rejection = assert.rejects(gate.promise, /label rendering failed/); + assert.doesNotThrow(() => { const callback = [...callbacks][0]; callbacks.delete(callback); callback(); }); + await rejection; assert.equal(callbacks.size, 0); +}); + +test('Logo scheduling and tick render failures settle pending display promises', async () => { + const { createLogoCountdown } = load(); + const failed = createLogoCountdown({ now: () => 0, schedule: () => { throw new Error('timer failed'); }, render() {} }); + const scheduling = assert.rejects(failed.promise, /timer failed/); + assert.doesNotThrow(() => failed.start(2500)); await scheduling; + let failRender = false; const callbacks = new Set<() => void>(); + const gate = createLogoCountdown({ now: () => 0, + schedule: (callback: () => void) => { callbacks.add(callback); return () => callbacks.delete(callback); }, + render: () => { if (failRender) throw new Error('tick rendering failed'); } }); + gate.start(2500); failRender = true; + const rendering = assert.rejects(gate.promise, /tick rendering failed/); + assert.doesNotThrow(() => { const callback = [...callbacks][1]; callbacks.delete(callback); callback(); }); + await rendering; assert.equal(callbacks.size, 0); +}); +const source = new URL('../../YouleNexus/assets/scripts/platform-login/logo-countdown.ts', import.meta.url); +function load() { + assert.ok(existsSync(source), 'Cocos local login view implementation must exist'); + const exports = {}; + runInNewContext(ts.transpileModule(readFileSync(source, 'utf8'), { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, + }).outputText, { exports, require: (name: string) => { + assert.equal(name, 'cc'); + return { Node, Sprite, ScrollView, UITransform, Label, Widget, Vec3, director, Director: { EVENT_AFTER_DRAW: 'draw' } }; + }, Error }); + return exports as any; +} + +test('Logo native binding measures label around button center and releases its touch listener', () => { + const { CocosLogoCountdownView } = load(); assert.equal(typeof CocosLogoCountdownView, 'function'); + const button = new Node('Spirit3103'), text = new Node('Spirit3104'), parent = new Node('group'); + text.parent = parent; + for (const node of [button, text, parent]) node.components.set(UITransform, new UITransform()); + const label = new Label(); text.components.set(Label, label); + const widget = new Widget(); text.components.set(Widget, widget); + let clicks = 0; const view = new CocosLogoCountdownView(button, text, () => clicks++); + assert.equal(button.active, false); assert.equal(text.active, false); + view.render({ visible: true, text: '倒计时3s 跳过' }); + assert.equal(label.string, '倒计时3s 跳过'); assert.equal(label.overflow, Label.Overflow.NONE); + assert.equal(widget.enabled, false); assert.equal(text.position.x, 230); assert.equal(text.position.y, 60); + button.emit('touch-end'); assert.equal(clicks, 1); + view.dispose(); assert.equal(button.listenerCount('touch-end'), 0); assert.equal(button.active, false); +}); diff --git a/cocoscreator_projects/framework-tests/platform-login/phone-code.test.ts b/cocoscreator_projects/framework-tests/platform-login/phone-code.test.ts new file mode 100644 index 0000000..9a8c0fa --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/phone-code.test.ts @@ -0,0 +1,15 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readPhoneCode } from '../../YouleNexus/assets/framework/adapters/native/phone-code.ts'; + +test('original phone cache restores the number and 30-second remainder without auto-filling the code',()=>{ + const raw=JSON.stringify({phoneNumber:'0013800138000',code:'1234',codeTime:1000}); + assert.deepEqual(readPhoneCode(raw,2500),{phoneNumber:'0013800138000',restTime:28}); + assert.deepEqual(readPhoneCode(raw,31000),{phoneNumber:'0013800138000',restTime:0}); + assert.deepEqual(readPhoneCode(raw,32000),{phoneNumber:'0013800138000',restTime:0}); + assert.equal(readPhoneCode(null,32000),null); +}); +test('malformed phone cache exposes the source error',()=>{ + assert.throws(()=>readPhoneCode('{',100),SyntaxError); + assert.throws(()=>readPhoneCode(JSON.stringify({phoneNumber:'123',codeTime:'bad'}),100),/codeTime/); +}); diff --git a/cocoscreator_projects/framework-tests/platform-login/scene-flow.test.ts b/cocoscreator_projects/framework-tests/platform-login/scene-flow.test.ts new file mode 100644 index 0000000..654d48b --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform-login/scene-flow.test.ts @@ -0,0 +1,12 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { SceneFlow } from '../../YouleNexus/assets/scripts/platform-login/scene-flow.ts'; +test('scene transitions are sequential and disposal prevents late scene binding',async()=>{ + const calls:string[]=[];let finish!:()=>void; + const flow=new SceneFlow(async name=>{calls.push(name);if(name==='Loading')await new Promise(r=>finish=r);}); + const loading=flow.enter('Loading',()=>calls.push('bindLoading')); + const login=flow.enter('Login',()=>calls.push('bindLogin')); + await Promise.resolve();assert.deepEqual(calls,['Loading']);finish();await loading;await login; + assert.deepEqual(calls,['Loading','bindLoading','Login','bindLogin']); + flow.dispose();await assert.rejects(flow.enter('Login',()=>{}),/disposed/); +}); diff --git a/cocoscreator_projects/framework-tests/platform/commands.test.ts b/cocoscreator_projects/framework-tests/platform/commands.test.ts index 1748b2f..84899da 100644 --- a/cocoscreator_projects/framework-tests/platform/commands.test.ts +++ b/cocoscreator_projects/framework-tests/platform/commands.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; diff --git a/cocoscreator_projects/framework-tests/platform/game-host-adapter.test.ts b/cocoscreator_projects/framework-tests/platform/game-host-adapter.test.ts index e3e83c8..36bb3a1 100644 --- a/cocoscreator_projects/framework-tests/platform/game-host-adapter.test.ts +++ b/cocoscreator_projects/framework-tests/platform/game-host-adapter.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; diff --git a/cocoscreator_projects/framework-tests/platform/runtime.test.ts b/cocoscreator_projects/framework-tests/platform/runtime.test.ts index a826318..fdef004 100644 --- a/cocoscreator_projects/framework-tests/platform/runtime.test.ts +++ b/cocoscreator_projects/framework-tests/platform/runtime.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import { readFileSync } from 'node:fs'; import assert from 'node:assert/strict'; diff --git a/cocoscreator_projects/framework-tests/platform/startup.test.ts b/cocoscreator_projects/framework-tests/platform/startup.test.ts index 8630652..cdde8b3 100644 --- a/cocoscreator_projects/framework-tests/platform/startup.test.ts +++ b/cocoscreator_projects/framework-tests/platform/startup.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import { test } from 'node:test'; import assert from 'node:assert/strict'; @@ -131,4 +133,4 @@ test('StartupOrchestrator: bootstrap 可独立调用(不依赖 onLogin)', async ready.then(() => { resolved = true; }); await new Promise((r) => setImmediate(r)); assert.equal(resolved, false, '仅 bootstrap 不足以触发 ready'); -}); \ No newline at end of file +}); diff --git a/cocoscreator_projects/framework-tests/sdk/game-contract-harness.test.ts b/cocoscreator_projects/framework-tests/sdk/game-contract-harness.test.ts index 11f20be..9655b32 100644 --- a/cocoscreator_projects/framework-tests/sdk/game-contract-harness.test.ts +++ b/cocoscreator_projects/framework-tests/sdk/game-contract-harness.test.ts @@ -1,4 +1,6 @@ -import { STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +import { STARTUP_CONFIG as EDITABLE_STARTUP_CONFIG } from '../../YouleNexus/assets/framework/config/profiles.ts'; +// 固定测试来源,允许开发者独立切换项目的 local/remote 开关。 +const STARTUP_CONFIG = Object.freeze({...EDITABLE_STARTUP_CONFIG, configSource:'remote' as const}); import { remoteFixture } from '../fixtures/remote-config.ts'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; diff --git a/docs/development-local-config.md b/docs/development-local-config.md new file mode 100644 index 0000000..c96acb1 --- /dev/null +++ b/docs/development-local-config.md @@ -0,0 +1,33 @@ +# 开发测试配置来源 + +唯一开关在 `cocoscreator_projects/YouleNexus/assets/framework/config/profiles.ts` 的 `STARTUP_CONFIG`: + +```ts +mode: 'debug', +configSource: 'local', // local:项目 txt;remote:gameserver 地址 +localConfigResource: 'dev-config/update_jsonv2', // resources 相对路径,不带 .txt +``` + +本地文件为 `cocoscreator_projects/YouleNexus/assets/resources/dev-config/update_jsonv2.txt`。 +初始内容取自 2026-09-06 当前 `gameserver` 的完整配置快照,不会自动与远程同步。 +修改并保存 profiles.ts 或 txt 后,切回 Creator,等待资源导入和脚本编译完成,再启动预览。 +仅刷新浏览器不保证 Creator 已完成文件扫描;如日志仍显示旧 source,需在 Creator +资源管理器中刷新对应 profiles.ts 和 txt,再重新预览。 +文件扩展名是 `.txt`,内容必须是合法 JSON,不能添加注释或尾逗号。 + +默认仍为 `configSource: 'remote'`。本地模式跳过远程请求及 `gameconfig` 地址覆盖; +渠道身份仍按原规则解析。两种来源共用原工程 `get_paravalue` 的层级取值规则, +修改某个键时需留意 agent/game/channel/market 中的对应值。 +控制台 `[PlatformStartup] login-config` 的 `source` 和 `gameserver` 显示实际读取来源。 + +`configSource` 只决定配置内容从哪里读取;`useLocalServer` 独立决定 WebSocket +是否使用 `localServers`,切换配置来源不会隐式切换游戏服务器。 + +本地模式同时要求 `mode: 'debug'` 和 Cocos 开发预览/调试构建。 +发布模式或非调试构建会明确拒绝本地读取;文件缺失、JSON 错误也直接报错,绝不回退远程。 + +## 已验证的预览缓存问题(2026-09-06) + +曾复现磁盘 profiles.ts 已为 local,但新浏览器预览仍输出 remote。通过 Creator +asset-db 刷新 profiles.ts 和本地 txt 后,日志恢复为 local,且读取到 marketid=4 +下的 deviceLogin=true,手机登录按钮正常显示。无需修改配置内容或 get_paravalue。 diff --git a/docs/protocol/07-RPC全量清单.md b/docs/protocol/07-RPC全量清单.md new file mode 100644 index 0000000..5da4f8d --- /dev/null +++ b/docs/protocol/07-RPC全量清单.md @@ -0,0 +1,833 @@ +# 平台模板 RPC 全量清单 + +> 核对日期:2026-09-05;基线:codex/local-platform-login @ 556c47a。本文是后续联调的索引与覆盖快照,不替代 01–05 章的字段契约,也不表示清单内接口已经验收。 + +## 范围与计数 + +- 共 **86 个唯一名称**:旧 RpcList 的 **82 个**(重复赋值按名称去重),加表外 submit_error、show_message、refresh_task_state,以及 h5RpcList.joinRoom。 +- 这是 Game_Surface_3 平台模板的全量:包含大厅、房间、纯推送、历史/保留名、HTTP 特例和外部桥接。具体子游戏自定义 route/rpc 没有在模板中穷举定义,留到子游戏阶段;本轮不选择或接入真实子游戏。 +- 86 是索引名称数,不是 86 个可直接调用的 WebSocket 接口。submit_log 不另计;握手 @toconcon、心跳 @serverheartbeat 和 webserve-服务器未工作 是传输控制数据,不是业务 RPC。 +- platform 是合法路由名称,但本次模板发送扫描未发现独占 route=platform 的命名业务请求;不复制 agent/room 名称来凑第三份清单。纯推送的路由归档不是当前本地服务器抓包证明。 + +## 接入与验收状态口径 + +- **L**:本地真实 Cocos 启动、加载、游客登录已验收;仅 player_login。依据[验收记录](../superpowers/reports/2026-09-05-local-platform-login-acceptance.md)。 +- **R**:新 PlatformRuntime 的严格平台分发表已接入(共 11 个,含 L),仅表示源码处理链存在;除 L 外,当前批次没有真实 RPC 成功验收证据。 +- **N**:不在新 PlatformRuntime 平台分发表中;不代表旧 H5 或旧 PlatformSession 完全没有实现。协议常量存在、旧 room-handlers 存在,均不能算新 Runtime 已接入。 +- **X**:保留、HTTP 特例或本地桥接,不按普通 WS 平台命令判定。具体子游戏生命周期支持也不等于其某个对局 rpc 已实现。 +- 当前 LocalPlatformLogin 联调入口只允许发 agent/player_login;R 中的房间/切服条目仍受测试边界阻断。本文不放宽测试入口。 +- 条目中“旧发送/接收函数”只表示静态定义,不能推断调用点活跃或服务器确实响应。请求/响应摘要取自对应协议章节;未明确之处保留待核实,不补字段。 + +## 发现的差异与注意事项 + +1. refresh_task_state:当前旧源码引用未定义常量;02 章称硬编码字符串,已不符。新 routes.ts 也未收录。 +2. submit_error、show_message:旧路径确有硬编码发送/命名接收器,但不在旧 RpcList,也不在新 routes.ts;不能只扫常量表。 +3. submit_log:实际 rpc 仍为 submit_error;change_room 的响应为 change_seat;quick_enter_share_room 没有同名接收器。 +4. README 仍写 WS 双层包装,而 01 章已更正为单层 {app,route,rpc,data};MessageEvent.data 是浏览器事件壳。以 01 章与已验收传输实现为准。 +5. 02 章登录 version:string 与本地已验收 number 值有差异;openid/unionid 不得依据历史“游客可空”注释省略。 +6. 新 routes.ts 的注释分组不是权威 rpc→route 映射,例如 create_room/self_join_room/get_player_grade1/get_player_grade2 在 room 注释区域,但旧发送实际为 agent;本清单按协议与发送源码归档。 +7. can_award 的旧接收器调用未定义 C_Player.can_award;五个社交保留名没有旧 Net 接收器;不能按完整可用协议验收。 +8. update_roomcard/update_bean:05_Func.js 的原生 yPaytype 与 WVJB yPaytype 回调直接上行,02 章“仅接收、无主动请求”不完整;已在条目中补齐,不能划为只读接口。 +9. 本轮仅整理清单,未修改上述历史文档、业务代码、服务端或任何 Cocos 序列化资源。 + +## 配置来源与批次边界 + +- agentid/gameid/channelid/marketid/version 继续由现有配置来源经 runtime-config/local-startup 解析;各 RPC 只携带它所要求的字段,不能为统一外观给所有请求塞上整套身份。规范字段名为 channelid,不能写 chanelid。 +- openid/unionid 与游客资料由现有 visitor-account 来源读取缓存或生成;登录后的 playerid 来自服务器与 Store。查询目标 playerid 与当前登录 playerid 的语义要逐项区分。 +- roomcode/seat/shortcode/roomtype/deskinfo 等属于后续房间或子游戏范围;短信、原生数据、支付渠道等有各自前置条件,不能生成假值补齐。 +- 后续优先梳理非房间平台 RPC 的只读查询,再逐项处理写操作和推送;房间相关及子游戏最后进行。列入本清单不等于授权执行。 + +## 按功能快速浏览 + +- **大厅 · 登录 / 账号(6)**:`player_login`、`query_player2`、`binding_phone`、`send_phone_checkcode`、`send_phone_code_wechat`、`setSign`。 +- **大厅 · 房间创建 / 进入(大厅侧)(8)**:`create_room`、`self_join_room`、`quick_enter_share_room`、`advanced_roomlist`、`advanced_createroom`、`get_share_room`、`getInfoByShortCode`、`switchRoomList`。 +- **大厅 · 战绩 / 排行 / 财富(5)**:`get_player_grade1`、`get_player_grade2`、`get_treasurelist`、`getShortCodeRankList`、`getVipRankList`。 +- **大厅 · 任务系统(5)**:`get_player_task`、`player_finish_task`、`get_task_award`、`refresh_task_state`、`can_award`。 +- **大厅 · 支付 / 充值 / 资产(4)**:`get_paylist`、`pay_succ`、`topup_card`、`giveCoin`。 +- **大厅 · 仓库 / 星星 / 魅力(4)**:`set_bankpwd`、`change_star`、`update_charm`、`setAllCharm`。 +- **大厅 · 邀请码 / 绑定(2)**:`binding_invitecode`、`get_player_invitecode`。 +- **大厅 · VIP 管理 / 黑白名单(4)**:`optBanList`、`getPlayerWhiteList`、`optWhiteList`、`setVipForbidSelect`。 +- **大厅 · 其它 agent 协议(8)**:`submit_opinion`、`submit_location`、`submit_phoneinfo`、`submit_error`、`kick_server`、`broadcast`、`connect_agentserver`、`playerBehavior`。 +- **房间 · 房间生命周期(8)**:`self_break_room`、`other_break_room`、`self_exit_room`、`other_exit_room`、`player_prepare`、`change_room`、`share_room`、`change_seat`。 +- **房间 · 进房推送(他人视角)(3)**:`other_join_room`、`other_offline`、`other_online`。 +- **房间 · 解散投票(开局后)(8)**:`self_apply_free_room`、`other_apply_free_room`、`self_agree_free_room`、`other_agree_free_room`、`self_refuse_free_room`、`other_refuse_free_room`、`free_room`、`beanroom_surrender`。 +- **房间 · 开局(2)**:`self_makewar`、`other_makewar`。 +- **房间 · 房间内社交(6)**:`send_text`、`send_voice`、`send_gift`、`send_phiz`、`call_phone`、`hangup_phone`。 +- **保留名称 · 房间社交(5)**:`receive_chat`、`play_voice`、`other_send_gift`、`other_callphone`、`other_hangup`。 +- **大厅 · 资产更新与系统推送(4)**:`update_bean`、`update_roomcard`、`kick_offline`、`show_message`。 +- **房间 · 服务器切换(room 侧)(1)**:`connect_roomserver`。 +- **子游戏 · 框架保留名称(2)**:`over_game`、`agentserver_game`。 +- **外部桥接 · 非网络 RPC(1)**:`joinRoom`。 + +## 全量条目 + +### 大厅 · 登录 / 账号(6) + +#### 01. player_login + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**L / R**;新 Rpc 常量:有。 +- 用途:连接建立后 `onopen` 自动发送;断线重连、切服后重发(12_Logic.js:35、:61 构造,09_Net.js:122 `Send_login` 注入)。 +- 请求:构建身份 agentid/gameid/channelid/marketid/version;账号 openid/unionid/nickname/avatar/sex/province/city;设备与环境 machineid/machineroom/location/ip;按来源契约携带设备登录和缓存 playerid 等可选字段。完整结构见登录契约与 02/04 章。 +- 响应/推送:state(0 成功);账号/资产字段,以及可能存在的房间恢复快照。完整结构见 04 章与当前 login-contract;本批只验收不在房的成功登录。 +- 注意:真实本地启动→加载→游客登录已验收;version=1(number)已被本地服务器接受。02 章 version:string、游客 openid 可空的描述不能作为本地联调输入依据;本地服务要求 openid 与 unionid。房间恢复不在已验收范围。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:14);旧函数:[Send_login](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:122)、[player_login](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:240)。 + +#### 02. query_player2 + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:仓库转账前查询目标玩家信息。 +- 请求:`agentid`、`playerid`(目标玩家ID)。 +- 响应/推送:`avatar`(string)、`nickname`(string)、`playerid`(int);昵称/头像均空时提示"未找到对应玩家"。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:44);旧函数:[Send_query_player2](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:869)、[query_player2](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:872)。 + +#### 03. binding_phone + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:绑定手机号。 +- 请求:`agentid`、`playerid`、`phonenum`、`smmcode`(短信验证码)。 +- 响应/推送:`phonenum`(string,回写 `C_Player.tel`)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:51);旧函数:[Send_binding_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:829)、[binding_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:833)。 + +#### 04. send_phone_checkcode + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:发送手机短信验证码。 +- 请求:`agentid`、`phonenum`(构造点未集中定位,至少含手机号)。 +- 响应/推送:无业务字段(`Desk.send_phone_checkcode` 为空实现,07_Desk.js:1342)。 +- 注意:⚠️ 请求字段以后端实现为准。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:58);旧函数:[Send_send_phone_checkcode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:837)、[send_phone_checkcode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:841)。 + +#### 05. send_phone_code_wechat + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:微信渠道发送手机验证码(绑定手机流程的另一入口)。 +- 请求:`agentid`、`phonenum`(唯一调用点 11_GameUI.js:2620-2623 当前被注释,按注释代码为 agentid+phonenum)。 +- 响应/推送:无业务字段(`Desk.send_phone_code_wechat` 为空实现,07_Desk.js:1345)。 +- 注意:⚠️ 框架完整定义并接线,但**当前前端唯一调用点(11_GameUI.js:2623)被注释,实际不发送**;字段以后端实现为准。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:65);旧函数:[Send_send_phone_code_wechat](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:845)、[send_phone_code_wechat](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:849)。 + +#### 06. setSign + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:设置个性签名。 +- 请求:`agentid`、`playerid`、`sign`。 +- 响应/推送:`sign`(string,回显写入 `C_Player.sign`)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:72);旧函数:[Send_setSign](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:735)、[setSign](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:739)。 + +### 大厅 · 房间创建 / 进入(大厅侧)(8) + +#### 07. create_room + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:玩家创建房间。 +- 请求:`agentid`、`playerid`、`gameid`、`roomtype`(房间类型配置数组,见 doc04);`Send_create_room` 自动注入 `ip`=`C_Player.ip`、`location`=`C_Player.addr`(09_Net.js:106-107)。 +- 响应/推送:`state`(0成功)、`roomcode`、`seat`、`roomtype`、`makewar`、`asetcount`、`shortcode`、`infinite`;失败 `showerror`/`error`。详见 [04 → 房间响应公共字段](./04-数据结构.md#房间创建--进入响应公共字段)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:83);旧函数:[Send_create_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:104)、[create_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:111)。 + +#### 08. self_join_room + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**R**;新 Rpc 常量:有。 +- 用途:输入房号 / 快速加入 / H5 唤起 / 进 VIP 配置房。 +- 请求:`agentid`、`playerid`、`gameid`、`roomcode`(房号);`Send_self_join_room` 自动注入 `location`=`C_Player.addr`、`ip`=`C_Player.ip`(09_Net.js:254-255);进 VIP 配置房入口额外带 `vipMatch:1`(12_Logic.js:2171);比赛进房入口可带 `match_id`。 +- 响应/推送:`state`、`roomcode`、`seat`、`isowner`、`players[]`、`roomtype`、`makewar`、`asetcount`、`deskwar`、`deskinfo`(重连快照)。详见 [04 → 房间响应公共字段](./04-数据结构.md#房间创建--进入响应公共字段)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:90);旧函数:[Send_self_join_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:251)、[self_join_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:259)。 + +#### 09. quick_enter_share_room + +- 路由/通道:agent;方向:C→S;结果经其它 rpc 返回;状态:**N**;新 Rpc 常量:有。 +- 用途:快速进入分享/星星场房间。 +- 请求:`agentid`、`playerid`、`gameid`、`type`(房间类型)、`roomtype`(可选)。 +- 响应/推送:走 self_join_room 进房流程(无独立 `Net.quick_enter_share_room` 接收函数,结果通过 self_join_room/show_message 等回包)。 +- 注意:无 Net.quick_enter_share_room;关联 self_join_room/show_message,不能按同名请求响应配对。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:97);旧函数:[Send_quick_enter_share_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:651)。 + +#### 10. advanced_roomlist + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:拉取 VIP/高级房间列表。 +- 请求:`agentid`、`playerid`、`gameid`。 +- 响应/推送:房间列表对象,整包写入 `GameData.snrRoomList` 并渲染(07_Desk.js:1176)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:104);旧函数:[Send_advanced_roomlist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:659)、[advanced_roomlist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:664)。 + +#### 11. advanced_createroom + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:创建 VIP/高级房间。 +- 请求:`agentid`、`gameid`、`playerid`、`tea`(茶水费)、`infinite`(0/1无限局)、`roomtype`、`videoConfig`(可选)、`rebateLimit`(可选)、`rebateType`(可选)。 +- 响应/推送:`tea`、`rebateLimit` 及房间配置(整包写入 `GameData.snrRoomList`,并回拉 advanced_roomlist,07_Desk.js:1180)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:111);旧函数:[Send_advanced_createroom](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:668)、[advanced_createroom](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:673)。 + +#### 12. get_share_room + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:获取分享/星星场房间列表(仅非大厅环境发送)。 +- 请求:`agentid`、`playerid`、`gameid`。 +- 响应/推送:房间数组(`Desk.get_share_room` / `Game_Modify.getShareRoom` 处理)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:118);旧函数:[Send_get_share_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:640)、[get_share_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:647)。 + +#### 13. getInfoByShortCode + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:按短码批量查询房间信息(VIP 房列表)。 +- 请求:`agentid`、`gameid`、`shortcodeList`(短码列表)。 +- 响应/推送:`roomInfo`(短号房间信息) → `GameUI.setVipRoomListData`(07_Desk.js:1284)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:125);旧函数:[Send_getInfoByShortCode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:753)、[getInfoByShortCode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:757)。 + +#### 14. switchRoomList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:开关房间在列表中的可见/可进入状态。 +- 请求:`agentid`、`playerid`、`gameid`、`isClose`(0开/1关)。 +- 响应/推送:`state`(0成功)、`isClose`;失败 `error`(07_Desk.js:1267)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:132);旧函数:[Send_switchRoomList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:743)、[switchRoomList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:747)。 + +### 大厅 · 战绩 / 排行 / 财富(5) + +#### 15. get_player_grade1 + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:拉取战绩(类型1)。 +- 请求:`agentid`、`playerid`、`gameid`、`type`(可选)、`direction`(可选,翻页)、`gradeidx`(可选,分页索引)。 +- 响应/推送:战绩数据(`gameCombat.get_player_grade1` 渲染)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:143);旧函数:[Send_get_player_grade1](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:359)、[get_player_grade1](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:364)。 + +#### 16. get_player_grade2 + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:拉取战绩(类型2)。 +- 请求:透传调用方 `_data`,构造点未集中定位;至少含 `agentid`/`playerid`/`gameid`。 +- 响应/推送:战绩数据(`gameCombat.get_player_grade2` 渲染)。 +- 注意:⚠️ 请求字段构造点未定位,待核对。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:150);旧函数:[Send_get_player_grade2](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:371)、[get_player_grade2](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:376)。 + +#### 17. get_treasurelist + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:财富榜。 +- 请求:`agentid`、`gameid`。 +- 响应/推送:`list`(排行数组) → `Desk.get_treasurelist`。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:157);旧函数:[Send_get_treasurelist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:685)、[get_treasurelist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:690)。 + +#### 18. getShortCodeRankList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:短号场排行榜。 +- 请求:`agentid`、`playerid`、`shortcode`。 +- 响应/推送:成功为排行数据(整包写入 `GameData.vipRank.data`);失败 `error:true` + `message`(07_Desk.js:1311)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:164);旧函数:[Send_getShortCodeRankList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:769)、[getShortCodeRankList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:773)。 + +#### 19. getVipRankList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:VIP 排行榜。 +- 请求:`agentid`、`limit`(条数)。 +- 响应/推送:`list`(VIP排行数组) → `GameData.rankList`(07_Desk.js:1328)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:171);旧函数:[Send_getVipRankList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:787)、[getVipRankList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:791)。 + +### 大厅 · 任务系统(5) + +#### 20. get_player_task + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:获取任务列表。 +- 请求:`agentid`、`playerid`、`gameid`。 +- 响应/推送:`tasks`(任务数组) → `Desk.get_player_task`。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:182);旧函数:[Send_get_player_task](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:408)、[get_player_task](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:413)。 + +#### 21. player_finish_task + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:上报任务完成(如分享成功触发,06_Player.js:414)。 +- 请求:`agentid`、`playerid`、`taskid`。 +- 响应/推送:`state`(int)——`state==1` 且当前 `taskstate==0` 时把 `C_Player.taskstate` 置 1(06_Player.js:476)。 +- 注意:原文档"响应含 `taskstate`"无源码依据,已删除——接收函数仅读 `_msg.data.state`。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:189);旧函数:[Send_player_finish_task](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:420)、[player_finish_task](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:424)。 + +#### 22. get_task_award + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:领取任务奖励。 +- 请求:`agentid`、`playerid`、`gameid`、`taskid`。 +- 响应/推送:`taskid`(对应任务 state 置 2=已领取)、`taskstate`(写入 `C_Player.taskstate`)(06_Player.js:482)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:196);旧函数:[Send_get_task_award](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:428)、[get_task_award](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:434)。 + +#### 23. refresh_task_state + +- 路由/通道:agent;方向:C→S 意图;当前旧代码缺常量,不能认定可发送;状态:**N**;新 Rpc 常量:无。 +- 用途:刷新任务可领取状态。 +- 请求:透传 `_data`(构造点未集中定位,至少含 `agentid`/`playerid`)。 +- 响应/推送:服务器推送 `can_award`(见下条)。 +- 注意:本次核实:09_Net.js:440 用 RpcList.refresh_task_state,但 02_Const.js 无此定义;全模板搜索未找到赋值。未被外部注入时该值为 undefined,JSON.stringify 会省略 rpc。02 章“硬编码字符串”的记载与当前源码不符,待后续专项核对;本轮不修代码。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:203);旧函数:[Send_can_award](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:439)。 + +#### 24. can_award + +- 路由/通道:agent;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:服务器通知有任务可领取。 +- 请求:无(纯推送)。 +- 响应/推送:任务可领取标记(载荷字段待后端确认)。 +- 注意:🐛 `06_Player.js` **未定义** `Player.prototype.can_award`(全文无此方法)。推送一旦到达,`C_Player.can_award` 为 undefined,调用即抛 TypeError。当前为 bug,不可当正常协议使用。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:210);旧函数:[Send_can_award](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:439)、[can_award](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:443)。 + +### 大厅 · 支付 / 充值 / 资产(4) + +#### 25. get_paylist + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:拉取支付项列表。 +- 请求:`agentid`。 +- 响应/推送:`paylist`(支付项数组) → `GameData.payList`,并打开支付界面(09_Net.js:567)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:221);旧函数:[Send_get_paylist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:561)、[get_paylist](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:566)。 + +#### 26. pay_succ + +- 路由/通道:HTTP(保留 agent 信封);方向:C→HTTP;未见同名 Net 接收函数;状态:**X**;新 Rpc 常量:有。 +- 用途:支付成功后通知服务器入账。 +- 请求:`agentid`、`playerid`、`channelid`、`productid`、`payid`、`amount`、`money`、`paytype`(05_Func.js:2240-2254 构造)。 +- 响应/推送:无显式 WS 回包;资产变化通过 `update_bean`/`update_roomcard` 推送(充房卡场景前端还会本地 `UpdateRoomcard`,05_Func.js:2263)。 +- 注意:原工程支付流程使用 HTTP;存在 Send_pay_succ 定义,但调用点被注释。不得仅凭常量/发送函数判为活跃 WS 请求。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:228);旧函数:[Send_pay_succ](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:572)。 + +#### 27. topup_card + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:充值卡兑换。 +- 请求:`agentid`、`playerid`、`cardno`(卡号)。 +- 响应/推送:`Desk.topup_card` 为空实现(07_Desk.js:1365),资产变化经 update_bean/update_roomcard 推送。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:235);旧函数:[Send_topup_card](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:862)、[topup_card](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:866)。 + +#### 28. giveCoin + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:仓库面板向他人转账豆豆/金币。 +- 请求:`agentid`、`playerid`(转出)、`toPlayerid`(转入目标ID)、`gameid`、`count`(数量)、`password`(仓库密码)(11_GameUI.js:2132-2139)。 +- 响应/推送:`state`(0成功)、`star2`(转出后**仓库星星**数 → `setWareHouseStarCOunt`,**非豆豆**);失败 `showerror`/`error`(07_Desk.js:1381)。 +- 注意:响应 `star2` 为仓库星星数,注意区别于豆豆余额。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:242);旧函数:[Send_giveCoin](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:875)、[giveCoin](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:878)。 + +### 大厅 · 仓库 / 星星 / 魅力(4) + +#### 29. set_bankpwd + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:设置仓库密码。 +- 请求:`agentid`、`playerid`、`unionid`、`password`。 +- 响应/推送:`state`(0成功)、`password`;失败 `showerror`/`error`(07_Desk.js:1228)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:253);旧函数:[Send_set_bankpwd](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:696)、[set_bankpwd](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:700)。 + +#### 30. change_star + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:仓库存/取(豆豆 ↔ 仓库星星)。 +- 请求:`agentid`、`playerid`、`mode`(0存入/1取出,源码 `safeInputType-1`)、`password`(仓库密码,字符串)、`count`(数量)(11_GameUI.js:2065-2069 调用点齐全携带此 5 字段)。 +- 响应/推送:`state`(0成功)、`star1`(更新后豆豆余额 → `update_bean2`)、`star2`(更新后仓库星星数 → `setWareHouseStarCOunt`)、`msg`(可选提示)、`count`(可选,回填安全输入);失败 `showerror`/`error`(07_Desk.js:1238)。 +- 注意:原审计疑虑"`mode`/`password` 未见"——经核对仓库存/取调用点(11_GameUI.js:2068-2069)**确含** `mode` 与 `password`,文档正确,疑虑解除。审计提到的"agentid/playerid/toPlayerid/gameid/count"实为相邻的 `giveCoin` 转账包(11_GameUI.js:2132-2139),并非本 rpc。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:260);旧函数:[Send_change_star](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:706)、[change_star](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:710)。 + +#### 31. update_charm + +- 路由/通道:agent;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:座位魅力值更新。 +- 请求:`Net.Send_update_charm`(`09_Net.js:727`) 已定义但**无任何调用点**;实际只作服务器→客户端推送。 +- 响应/推送:`seatlist`:[ {`seat`(int), `charm`(number)} ](07_Desk.js:1260 遍历 setCharm)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:267);旧函数:[Send_update_charm](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:726)、[update_charm](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:730)。 + +#### 32. setAllCharm + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:批量设置总魅力。 +- 请求:`agentid`、`playerid`、`gameid`、`value`(11_GameUI.js:1269-1275)。 +- 响应/推送:无显式业务字段,`Desk.setAllCharm` 仅本地存储并提示成功(07_Desk.js:1321)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:274);旧函数:[Send_setAllCharm](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:778)、[setAllCharm](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:782)。 + +### 大厅 · 邀请码 / 绑定(2) + +#### 33. binding_invitecode + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:绑定邀请码。 +- 请求:`agentid`、`playerid`、`invitecode`(11_GameUI.js:1367-1369)。 +- 响应/推送:`state`(0成功时写入 `invitecode`)、`invitecode`、`error`(提示文案)(06_Player.js:251)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:285);旧函数:[Send_binding_invitecode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:586)、[binding_invitecode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:591)。 + +#### 34. get_player_invitecode + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:获取自己的邀请码(打开绑定界面)。 +- 请求:`agentid`、`playerid`、`unionid`、`openid`(11_GameUI.js:1378-1382)。 +- 响应/推送:`invitecode`(string) → `C_Player.setInvitecod` 并打开绑定界面(09_Net.js:607)。 +- 注意:修正原文档——请求字段为 `agentid/playerid/unionid/openid`,**无 `gameid`**,新增 `unionid`/`openid`(11_GameUI.js:1378)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:292);旧函数:[Send_get_player_invitecode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:602)、[get_player_invitecode](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:606)。 + +### 大厅 · VIP 管理 / 黑白名单(4) + +#### 35. optBanList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:黑名单查看/添加/移除(多入口)。 +- 请求:随入口不同,公共字段 `agentid`、`playerid`、`type`: +- 响应/推送:`type`(1/3/4/5/6)、`banList`(黑名单数组)、`message`(可选提示)(07_Desk.js:1289)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:303);旧函数:[Send_optBanList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:761)、[optBanList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:765)。 + +#### 36. getPlayerWhiteList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:获取白名单。 +- 请求:`agentid`、`playerid`、`gameid`。 +- 响应/推送:`whiteList`(数组) → `GameData.whiteList.data`(07_Desk.js:1412)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:316);旧函数:[Send_getPlayerWhiteList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:881)、[getPlayerWhiteList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:885)。 + +#### 37. optWhiteList + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:白名单添加/修改/删除。 +- 请求:`agentid`、`playerid`、`gameid`、`shortcode`、`mode`、`userid`(目标ID): +- 响应/推送:`whiteList`(更新后数组)、`mode`(可选)、`message`(可选提示)(07_Desk.js:1398)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:323);旧函数:[Send_optWhiteList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:889)、[optWhiteList](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:893)。 + +#### 38. setVipForbidSelect + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:VIP 房禁止玩家选桌开关。 +- 请求:`agentid`、`playerid`、`gameid`、`forbidSelect`(1开/0关,11_GameUI.js:2631-2639)。 +- 响应/推送:`state`(0成功)、`forbidSelect`(1开/0关);失败 `error`(07_Desk.js:1348)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:332);旧函数:[Send_setVipForbidSelect](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:854)、[setVipForbidSelect](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:858)。 + +### 大厅 · 其它 agent 协议(8) + +#### 39. submit_opinion + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:提交反馈/意见。 +- 请求:`agentid`、`playerid`、`gameid`、`content`。 +- 响应/推送:`state`(0成功)(07_Desk.js:1100)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:343);旧函数:[Send_submit_opinion](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:546)、[submit_opinion](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:550)。 + +#### 40. submit_location + +- 路由/通道:agent;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:提交定位信息(仅非大厅环境发送)。 +- 请求:`agentid`、`playerid`、`info`(定位对象)。 +- 响应/推送:经 `Game.submit_location` 处理(09_Net.js:583)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:350);旧函数:[Send_submit_location](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:576)、[submit_location](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:582)。 + +#### 41. submit_phoneinfo + +- 路由/通道:agent;方向:C→S 请求(无响应处理);状态:**N**;新 Rpc 常量:有。 +- 用途:提交手机/通讯录信息(仅非大厅环境发送)。 +- 请求:`agentid`、`playerid`、`info`{ `phoneInfo`(手机信息), `addrBook`(通讯录) }。 +- 响应/推送:`Net.submit_phoneinfo` 接收函数为空实现(09_Net.js:722)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:357);旧函数:[Send_submit_phoneinfo](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:716)、[submit_phoneinfo](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:721)。 + +#### 42. submit_error + +- 路由/通道:agent;方向:C→S;同名回包被旧接收器忽略;状态:**N**;新 Rpc 常量:无。 +- 用途:异常/日志上报(Net.submit_error 与 Net.submit_log 共用同一个实际 rpc)。 +- 请求:`packet`(出错数据包字符串)、`msg`(错误/堆栈信息)、`playerid`、`agentid`、`gameid`(09_Net.js:66-72)。 +- 响应/推送:服务器若回 rpc `"submit_error"`,前端在分发处直接 `return` 忽略(12_Logic.js:235)。 +- 注意:submit_log 是本地函数别名,不是第二个线上 rpc;不能重复计数。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:364);旧函数:[submit_error](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:55)、[submit_log](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:81)。 + +#### 43. kick_server + +- 路由/通道:agent;方向:C→S 请求 + S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:管理端踢出玩家 / 被踢下线弹窗。 +- 请求:`agentid` 等(构造点未集中定位)。 +- 响应/推送:`msg`(踢出提示) → `GameUI.OpenKick`(07_Desk.js:1108)。 +- 注意:⚠️ 请求字段待补。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:371);旧函数:[Send_kick_server](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:554)、[kick_server](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:557)。 + +#### 44. broadcast + +- 路由/通道:agent;方向:C→S 请求 + S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:广播消息/滚动公告(主要为服务器推送)。 +- 请求:`agentid` 等(`Send_broadcast` 存在,09_Net.js:530)。 +- 响应/推送:`msgtype`(0消息框/1滚动公告,可选,缺省 0)、`msgcontent`(内容)(07_Desk.js:1112)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:378);旧函数:[Send_broadcast](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:529)、[broadcast](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:533)。 + +#### 45. connect_agentserver + +- 路由/通道:agent;方向:双向(切服);状态:**R**;新 Rpc 常量:有。 +- 用途:切换到大厅服务器。 +- 请求:`Send_connect_agentserver`(`09_Net.js:515`) 透传 `_data`(切服时使用)。 +- 响应/推送:`agentserver`(新大厅服地址)、`opt`(切换原因,如 `other_break_room`/`free_room`)(09_Net.js:518)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:385);旧函数:[Send_connect_agentserver](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:514)、[connect_agentserver](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:517)。 + +#### 46. playerBehavior + +- 路由/通道:HTTP GET(历史 agent 名称);方向:C→HTTP;状态:**X**;新 Rpc 常量:有。 +- 用途:玩家行为埋点。 +- 请求:原 WS 路径(agentid/gameid/playerid/tag)**被注释**(09_Net.js:799-806),实际改走独立 HTTP GET 上报 `http://test3.1888day.com/api/gamedo/gamedo?agentid=&gameid=&playerid=&tag=`(09_Net.js:807-815)。 +- 响应/推送:HTTP 回调 `playerBehavior_Succ`/`_Fail`(09_Net.js:818/822);WS 接收函数 `Net.playerBehavior`(`09_Net.js:826`) 当前无触发。 +- 注意:当前调用走独立 HTTP 埋点地址,WS 段被注释;不归入本地 WebSocket 验收。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:392);旧函数:[Send_playerBehavior](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:796)、[playerBehavior](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:825)。 + +### 房间 · 房间生命周期(8) + +#### 47. self_break_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:房主在**未开局**前主动解散房间。 +- 请求:仅通用四字段。 +- 响应/推送:`roomcode`(可选,用于从本地"我的房间"列表移除)。处理:清空牌桌、回大厅。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:19);旧函数:[Send_self_break_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:266)、[self_break_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:271)。 + +#### 48. other_break_room + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:他人(房主)解散房间,推送给房内其余玩家。 +- 请求:无(纯推送)。 +- 响应/推送:无业务字段;触发 `Func.exitRoom()`、清桌、回大厅、提示"房主已解散房间!"。 +- 注意:常伴随 **connect_agentserver** 切服包(`data.opt == other_break_room`,见下文)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:26);旧函数:[other_break_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:277)。 + +#### 49. self_exit_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**R**;新 Rpc 常量:有。 +- 用途:自己在**未开局**前退出房间。 +- 请求:仅通用四字段(发送处见 `08_Utl_Output.js:993`)。 +- 响应/推送:`isowner`(可选,==1 且非无限局时把房间加回本地列表), `seat`(可选,传给 `Game_Modify.myExitRoom`), `roomcode`(可选)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:33);旧函数:[Send_self_exit_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:289)、[self_exit_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:294)。 + +#### 50. other_exit_room + +- 路由/通道:room;方向:S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:其他玩家(未开局)退出房间。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`(离开者座位)。处理:清该座位、`playercnt--`;若 `seat==0` 且非无限局,提示房主已离开。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:40);旧函数:[other_exit_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:301)。 + +#### 51. player_prepare + +- 路由/通道:room;方向:C→S 请求 + S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:玩家点击准备(`needprepare==1` 的房间)。 +- 请求:仅通用四字段。 +- 响应/推送:`seat`(准备者座位), `deskwar`(可选;为真表示满足开战条件 → `HideStartScene` + `Game_Modify.StartWar`)。推送给房内所有人。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:47);旧函数:[Send_player_prepare](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:628)、[player_prepare](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:631)。 + +#### 52. change_room + +- 路由/通道:room;方向:C→S;响应 rpc=change_seat;状态:**N**;新 Rpc 常量:有。 +- 用途:玩家请求换座 / 换桌。 +- 请求:仅通用四字段(**不带目标座位**,由服务器决定换到哪个空位)。发送处 `11_GameUI.js:1945`、`08_Utl_Output.js:1006`。 +- 响应/推送:rpc 名改为 **`change_seat`**,`data`:`seat1`, `seat2`(两个互换的座位号)。处理:交换两座 Desk 信息,若自己在其中则 `C_Player.SetSeat` 更新。 +- 注意:上行 rpc=`change_room`,下行 rpc=`change_seat`,二者成对。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:54);旧函数:[Send_change_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:677)。 + +#### 53. share_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:把房间分享到世界房列表。 +- 请求:通用四字段;可选 `roomlist`、`roomtype`、`shareType`(高级房分享时携带,见 `11_GameUI.js:1734`/`11_GameUI.js:1891`)。 +- 响应/推送:无业务字段;提示"已成功分享至平台!"。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:61);旧函数:[Send_share_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:634)、[share_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:637)。 + +#### 54. change_seat + +- 路由/通道:room;方向:S→C(change_room 的响应);状态:**N**;新 Rpc 常量:有。 +- 用途:服务器指定两座互换。 +- 请求:无同名上行;上行是 change_room。 +- 响应/推送:seat1、seat2。 +- 注意:独立列名,不能把 change_room 与 change_seat 合成一条而漏计。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:54);旧函数:[change_seat](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:681)。 + +### 房间 · 进房推送(他人视角)(3) + +#### 55. other_join_room + +- 路由/通道:room;方向:S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:其他玩家加入当前房间。 +- 请求:无(纯推送;自己进房用 `self_join_room`,走 agent 路由,见 02 章)。 +- 响应/推送:`seat`(新玩家座位) + 该玩家完整对象(直接传给 `Player.SetDeskInfo`,见 [04 · Player 座位对象](./04-数据结构.md#player-座位对象)): +- 注意:⚠️ `charm`/`sign` 是否每次必含、其余字段是否完整以 `Player.SetDeskInfo` 实际读取为准,详见 [04 章](./04-数据结构.md#player-座位对象)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:72);旧函数:[other_join_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:281)。 + +#### 56. other_offline + +- 路由/通道:room;方向:S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:其他玩家离线。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`。处理:该座 `onstate=1`,刷新 UI。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:80);旧函数:[other_offline](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:389)。 + +#### 57. other_online + +- 路由/通道:room;方向:S→C 推送;状态:**R**;新 Rpc 常量:有。 +- 用途:其他玩家重新上线。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`, `ip`。处理:该座 `onstate=0`、更新 ip。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:87);旧函数:[other_online](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:394)。 + +### 房间 · 解散投票(开局后)(8) + +#### 58. self_apply_free_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:自己申请解散房间。 +- 请求:仅通用四字段。 +- 响应/推送:`agreefree` { `state`:[各座同意状态数组], `countdown`:倒计时 }。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:101);旧函数:[Send_self_apply_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:308)、[self_apply_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:313)。 + +#### 59. other_apply_free_room + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:他人申请解散。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`(申请者), `agreefree` { `state`[], `countdown` }。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:108);旧函数:[other_apply_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:319)。 + +#### 60. self_agree_free_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:自己同意解散。 +- 请求:仅通用四字段。 +- 响应/推送:无业务字段;本地把自己加入同意列表、刷新投票 UI。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:115);旧函数:[Send_self_agree_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:323)、[self_agree_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:328)。 + +#### 61. other_agree_free_room + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:他人同意解散。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`(同意者)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:122);旧函数:[other_agree_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:333)。 + +#### 62. self_refuse_free_room + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:自己拒绝解散。 +- 请求:仅通用四字段。 +- 响应/推送:无业务字段;清空同意列表、投票结果置不通过、弹"已拒绝"结果。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:129);旧函数:[Send_self_refuse_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:338)、[self_refuse_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:343)。 + +#### 63. other_refuse_free_room + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:他人拒绝解散(任一人拒绝即否决本轮)。 +- 请求:无(纯推送)。 +- 响应/推送:`seat`(拒绝者)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:136);旧函数:[other_refuse_free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:348)。 + +#### 64. free_room + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:解散投票最终结果。 +- 请求:无(纯推送)。 +- 响应/推送:freeNow;deskfree、roomcard、tips、time、seats 按具体分支读取,字段语义见 03 章。 +- 注意:可伴随 **connect_agentserver** 切服包(`data.opt == free_room`,见下文)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:143);旧函数:[free_room](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:353)。 + +#### 65. beanroom_surrender + +- 路由/通道:room;方向:C→S 请求 + S→C 响应;状态:**N**;新 Rpc 常量:有。 +- 用途:豆豆房(金币房)投降。 +- 请求:通用四字段 + `count`(投降数量 = `GameData.surrendCount`,发送处 `11_GameUI.js:1245`)。 +- 响应/推送:`state`(0=成功 → `Game_Modify.onSurrender(_msg)`); 失败时 `showerror`(==1 则弹) / `error`(错误文本)。 +- 注意:⚠️ 未见成对的 `other_xxx` 推送,是否向房内其他玩家广播他人投降待后端确认。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:160);旧函数:[Send_beanroom_surrender](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:612)、[beanroom_surrender](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:616)。 + +### 房间 · 开局(2) + +#### 66. self_makewar + +- 路由/通道:room;方向:C→S 请求 → S→C 响应(self_makewar);状态:**N**;新 Rpc 常量:有。 +- 用途:房主主动开局。 +- 请求:仅通用四字段。 +- 响应/推送:rpc=`self_makewar`,无业务字段;触发 `Desk.self_makewar` → `Game_Modify.StartWar(_msg)`。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:171);旧函数:[Send_self_makewar](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:469)、[self_makewar](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:474)。 + +#### 67. other_makewar + +- 路由/通道:room;方向:S→C 推送;状态:**N**;新 Rpc 常量:有。 +- 用途:开局广播(房主开局或满员自动开局)给房内其他玩家。 +- 请求:无(纯推送)。 +- 响应/推送:开局信息对象 → `Desk.makewar` → `Game_Modify.StartWar(msg)`。 +- 注意:实际发牌等对局数据在此之后通过**子游戏内协议**下发(见 05 章)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:178);旧函数:[other_makewar](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:479)。 + +### 房间 · 房间内社交(6) + +#### 68. send_text + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 文字聊天;状态:**N**;新 Rpc 常量:有。 +- 用途:文字聊天 / 全服公告 / 预定义常用语。 +- 请求:通用四字段 + `text`(内容;点常用语时为 `Game_Config.Info.TextContent[spid-206]` 文本) + `type`(0=普通 / 1=全服公告;按是否勾选公告设 0/1) + `info`(可选;**有 info 时 `type` 改为 2**)。发送处 `11_GameUI.js:1170`(输入框)、`11_GameUI.js:2717`(常用语)。 +- 响应/推送:`type`(0=普通 / 1=全服公告 / 2=机器人 / 3=预定义文字), `text`(内容;**type==3 时为 `Game_Config.Info.TextContent` 的 1 基索引**,客户端按 `(idx-1)%len` 取文本), `seat`(发送者座位,type≠1 时使用), `info`(type==2 时附加)。 +- 注意:百人场(`vipInfinite`)只处理 type==1 公告分支。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:189);旧函数:[Send_send_text](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:399)、[send_text](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:403)。 + +#### 69. send_voice + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 语音;状态:**N**;新 Rpc 常量:有。 +- 用途:发送语音消息。 +- 请求:通用四字段 + `voiceurl`(已上传音频地址) + `time`(时长) + `info`(可选) + `type`(可选,有 info 时为 2)。发送处 `05_Func.js:1714`、`05_Func.js:2984`。 +- 响应/推送:`type`(0=普通 / 2=机器人), `seat`(发送座位), `voiceurl`, `time`, `info`(type==2 时)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:203);旧函数:[Send_send_voice](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:491)、[send_voice](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:494)。 + +#### 70. send_gift + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 互动/送礼;状态:**N**;新 Rpc 常量:有。 +- 用途:向指定座位送互动礼物。 +- 请求:通用四字段 + `giftid`(=`spid_up - 255`,按钮精灵号算出,发送处 `11_GameUI.js:2725`) + `receiveseat`(=`GameData.InteractPlayer`) + `info`(可选) + `type`(有 info 时为 2)。 +- 响应/推送:`type`(0=普通 / 2=机器人), `giftid`(客户端做 `(giftid-1)%4+1` 归一为 1~4 动画), `sendseat`(发送座位), `receiveseat`(接收座位), `info`(type==2 时)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:218);旧函数:[Send_send_gift](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:483)、[send_gift](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:486)。 + +#### 71. send_phiz + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 表情;状态:**N**;新 Rpc 常量:有。 +- 用途:发送表情动画。 +- 请求:通用四字段 + `text`(=`up_id`,表情按钮序号 1~`ConstVal.Emotion.count`,发送处 `11_GameUI.js:815`) + `info`(可选) + `type`(有 info 时为 2)。 +- 响应/推送:`type`(0=普通 / 2=机器人), `text`(表情 ID,客户端按 `(text-1)%ConstVal.Emotion.src_list.length+1` 归一), `seat`(发送座位), `info`(type==2 时)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:232);旧函数:[Send_send_phiz](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:538)、[send_phiz](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:542)。 + +#### 72. call_phone + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 拨打电话;状态:**N**;新 Rpc 常量:有。 +- 用途:拨打/接听电话,置玩家 `onstate=2`(通话中)。 +- 请求:仅通用四字段(发送处 `06_Player.js:380` / `:388` / `:396`,对应接起/电话进来/去电三种触发)。 +- 响应/推送:rpc=`call_phone`,`seat`(拨打者座位 → 该玩家 `onstate=2`)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:239);旧函数:[Send_call_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:451)、[call_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:455)。 + +#### 73. hangup_phone + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 挂断电话;状态:**N**;新 Rpc 常量:有。 +- 用途:挂断电话,置玩家 `onstate=0`。 +- 请求:仅通用四字段(发送处 `06_Player.js:372`)。 +- 响应/推送:rpc=`hangup_phone`,`seat`(挂断者座位 → 该玩家 `onstate=0`)。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:253);旧函数:[Send_hangup_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:460)、[hangup_phone](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:464)。 + +### 保留名称 · 房间社交(5) + +#### 74. receive_chat + +- 路由/通道:room(文档归类,线上未确认);方向:未确认(文档推测为下行);状态:**X**;新 Rpc 常量:有。 +- 用途:聊天推送的另一可能 rpc 名(与 `send_text` 成对)。 +- 请求:未发现同名发送链。 +- 响应/推送:未定义,不能套用其它 rpc 字段。 +- 注意:常量存在,旧 Net 无同名接收器。与实际已实现的 send_voice/send_text/call_phone/hangup_phone/send_gift 区分;不据此断言服务器绝不会发送。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:196);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +#### 75. play_voice + +- 路由/通道:room(文档归类,线上未确认);方向:未确认(文档推测为下行);状态:**X**;新 Rpc 常量:有。 +- 用途:与 `send_voice` 成对,疑为语音**播放**推送。 +- 请求:未发现同名发送链。 +- 响应/推送:未定义,不能套用其它 rpc 字段。 +- 注意:常量存在,旧 Net 无同名接收器。与实际已实现的 send_voice/send_text/call_phone/hangup_phone/send_gift 区分;不据此断言服务器绝不会发送。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:211);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +#### 76. other_send_gift + +- 路由/通道:room(文档归类,线上未确认);方向:未确认(文档推测为下行);状态:**X**;新 Rpc 常量:有。 +- 用途:与 `send_gift` 成对,疑为他人送礼推送。 +- 请求:未发现同名发送链。 +- 响应/推送:未定义,不能套用其它 rpc 字段。 +- 注意:常量存在,旧 Net 无同名接收器。与实际已实现的 send_voice/send_text/call_phone/hangup_phone/send_gift 区分;不据此断言服务器绝不会发送。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:225);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +#### 77. other_callphone + +- 路由/通道:room(文档归类,线上未确认);方向:未确认(文档推测为下行);状态:**X**;新 Rpc 常量:有。 +- 用途:与 `call_phone` 成对,疑为他人拨打电话推送。 +- 请求:未发现同名发送链。 +- 响应/推送:未定义,不能套用其它 rpc 字段。 +- 注意:常量存在,旧 Net 无同名接收器。与实际已实现的 send_voice/send_text/call_phone/hangup_phone/send_gift 区分;不据此断言服务器绝不会发送。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:246);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +#### 78. other_hangup + +- 路由/通道:room(文档归类,线上未确认);方向:未确认(文档推测为下行);状态:**X**;新 Rpc 常量:有。 +- 用途:与 `hangup_phone` 成对,疑为他人挂断电话推送。 +- 请求:未发现同名发送链。 +- 响应/推送:未定义,不能套用其它 rpc 字段。 +- 注意:常量存在,旧 Net 无同名接收器。与实际已实现的 send_voice/send_text/call_phone/hangup_phone/send_gift 区分;不据此断言服务器绝不会发送。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:260);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +### 大厅 · 资产更新与系统推送(4) + +#### 79. update_bean + +- 路由/通道:上行 agent;下行按 agent 归档(实际推送路由待逐项抓包);方向:C→S + S→C;状态:**N**;新 Rpc 常量:有。 +- 用途:豆豆变化(本人或房内座位) +- 请求:原生 yPaytype / WVJB yPaytype 回调直接发送 agentid、playerid、change:num、type:9;这是资产写入路径,不属于只读查询。 +- 响应/推送:bean、change/seat/type(按场景可选)、text;不同分支见原处理器。 +- 注意:02 章“无对应主动请求”与当前 05_Func.js 不符。存在发送代码不代表支付来源可信性、服务端行为或新框架已经验收。 +- 直接上行来源:[原生 yPaytype](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:2191)、[WVJB yPaytype](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:3053)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:406);旧函数:[update_bean](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:597)。 + +#### 80. update_roomcard + +- 路由/通道:上行 agent;下行按 agent 归档(实际推送路由待逐项抓包);方向:C→S + S→C;状态:**N**;新 Rpc 常量:有。 +- 用途:房卡变化 +- 请求:原生 yPaytype / WVJB yPaytype 回调直接发送 agentid、playerid、change:num、type:9;这是资产写入路径,不属于只读查询。 +- 响应/推送:roomcard、text(可选);change 分支条件见原处理器。 +- 注意:02 章“无对应主动请求”与当前 05_Func.js 不符。存在发送代码不代表支付来源可信性、服务端行为或新框架已经验收。 +- 直接上行来源:[原生 yPaytype](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:2188)、[WVJB yPaytype](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:3050)。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:405);旧函数:[update_roomcard](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:382)。 + +#### 81. kick_offline + +- 路由/通道:agent(协议归档;实际推送路由待逐项抓包);方向:S→C;状态:**N**;新 Rpc 常量:有。 +- 用途:被踢下线 +- 请求:无对应主动请求。 +- 响应/推送:fromOther、gameid(可选);旧流程同时上报 submit_error。 +- 注意:仅有旧处理器不能证明新 Runtime 接入或真实服务已覆盖。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:408);旧函数:[kick_offline](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:447)。 + +#### 82. show_message + +- 路由/通道:agent(协议归档;实际推送路由待逐项抓包);方向:S→C;状态:**N**;新 Rpc 常量:无。 +- 用途:通用提示 +- 请求:无对应主动请求。 +- 响应/推送:msg、time。 +- 注意:仅有旧处理器不能证明新 Runtime 接入或真实服务已覆盖。 +- 依据:[02-协议-agent路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/02-协议-agent路由.md:409);旧函数:[show_message](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:655)。 + +### 房间 · 服务器切换(room 侧)(1) + +#### 83. connect_roomserver + +- 路由/通道:room;方向:C→S 请求 + S→C 推送 · 切到房间服;状态:**R**;新 Rpc 常量:有。 +- 用途:从大厅服切换到房间服务器。 +- 请求:由 `Send_connect_roomserver` 发起(走 room 路由)。 +- 响应/推送:`data.roomserver`(新房间服地址)。处理:置 `GameData.ConnectType=true`、`GameData.ConnectRpc=connect_roomserver`、`GameData.Server=data.roomserver`,关闭当前连接并用新地址重连。 +- 依据:[03-协议-room路由.md](G:/Works/YouleGamesCocosCreator/docs/protocol/03-协议-room路由.md:295);旧函数:[Send_connect_roomserver](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:499)、[connect_roomserver](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:502)。 + +### 子游戏 · 框架保留名称(2) + +#### 84. over_game + +- 路由/通道:由具体子游戏契约确定;方向:文档描述 S→C;实际由子游戏确认;状态:**X**;新 Rpc 常量:有。 +- 用途:结算广播保留名;具体载荷由子游戏定义。 +- 请求:模板未定义。 +- 响应/推送:模板未定义。 +- 注意:Game_Surface_3 的 Game_Modify 为模板,不能据名字推导具体子游戏协议。 +- 依据:[05-游戏内协议与桥接.md](G:/Works/YouleGamesCocosCreator/docs/protocol/05-游戏内协议与桥接.md:144);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +#### 85. agentserver_game + +- 路由/通道:由具体子游戏契约确定;方向:未定义;状态:**X**;新 Rpc 常量:有。 +- 用途:对局数据经大厅服中转的保留名。 +- 请求:模板未定义。 +- 响应/推送:模板未定义。 +- 注意:Game_Surface_3 的 Game_Modify 为模板,不能据名字推导具体子游戏协议。 +- 依据:[05-游戏内协议与桥接.md](G:/Works/YouleGamesCocosCreator/docs/protocol/05-游戏内协议与桥接.md:145);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +### 外部桥接 · 非网络 RPC(1) + +#### 86. joinRoom + +- 路由/通道:H5/小程序本地桥接;方向:外部入口→前端确认流程;状态:**X**;新 Rpc 常量:有。 +- 用途:唤起进房确认;不是直接向服务器发送 joinRoom。 +- 请求:{rpc:"joinRoom",data:{roomcode}};H5 来自 URL gameData,小程序来自缓存 openminigamedata。 +- 响应/推送:确认后走 self_join_room;不产生 joinRoom 网络回包。 +- 注意:h5RpcList 唯一名称;旧小程序 checkType=8 与白名单删除撞号,不能沿用。WVJB handler 名不计为服务器 RPC。 +- 依据:[05-游戏内协议与桥接.md](G:/Works/YouleGamesCocosCreator/docs/protocol/05-游戏内协议与桥接.md:229);旧函数:无同名 Net 定义(保留/桥接按所引章节核对)。 + +## 覆盖核对方法 + +- 逐一取 02_Const.js 的 RpcList 字符串赋值去重;核对 09_Net.js 中 RpcList 引用、Net 命名函数以及硬编码 rpc;扫描全模板的直接 _SendData 调用与 rpc 赋值(包含 05_Func.js 支付桥接),补入 h5RpcList。 +- 与 02/03 章标题、02 章末尾推送表以及 05 章保留/桥接名称取并集;按实际名称一条一项,跨章节引用不重复计数。 +- 结果:82 个旧常量名称全部命中;补充 4 个名称后 86 条,无遗漏或重复;11 个现代严格分发名称全部命中。 +- 新框架状态依据 platform/runtime.ts、protocol/platform-handlers.ts、protocol/first-slice-routes.ts;旧 protocol/routes.ts 大常量表仅用于“名称存在”对照。 +- 本轮没有发送网络请求、触发 Cocos 编辑器操作或运行资源生成器;这份静态索引不新增真实验收结论。 diff --git a/docs/superpowers/plans/2026-09-06-login-scenes-and-contract.md b/docs/superpowers/plans/2026-09-06-login-scenes-and-contract.md new file mode 100644 index 0000000..5ee3dec --- /dev/null +++ b/docs/superpowers/plans/2026-09-06-login-scenes-and-contract.md @@ -0,0 +1,67 @@ +# 登录场景与原工程接线 + +用户已确认:启动、加载、登录分别组织;协议由独立 PopupRoot 动态实例化;按钮显示和调用按 Game_Surface_3。 + +最新协议要求覆盖本文早期验收记录:默认已勾选;所有登录方式都要求当前勾选,未勾选统一提示“未同意协议”。LoginAccounts 持有唯一协议状态,授权、回调、游客、手机、快速/隐藏入口和缓存自动登录统一拦截;LocalPlatformLogin 诊断入口同步。175 项相关测试通过。 + +## 实施顺序 + +1. 在 `assets/framework/presentation/login-policy.ts` 集中实现 `JumpWxAuth` 的显示顺序,配置缺省仅在来源定义。测试覆盖审核、设备、游客、QQ、缓存账号组合,并保留 QQ 最后覆盖的源行为。 +2. 在登录适配器实现 `accreditlogin(2/1)`、`sharelogin` 回调、游客/设备/手机账号、缓存与 `player_login`。手机号使用原字段 `telphone/smmcode/telphoneAuto`,不增加服务器接口。验证码按钮沿用“请通过相关渠道获取”。针对接口参数、协议勾选及缓存编写测试。 +3. 创建常驻启动组件与 Loading/Login 场景宿主,保留单个 Runtime/连接;使用 `director.loadScene`,等待场景绑定后展示数据。Logo 继续复用既有倒计时和进度行为。 +4. 使用 Cocos MCP 移除 Login prefab 内协议实例,场景中设置 PopupRoot。协议和手机窗口独立加载、重复点击防重入、场景销毁取消加载与清理监听。 +5. 用原生 Cocos 节点和预览核对启动场景顺序、按钮显示、协议开关和滑动;执行相关测试与类型检查,记录既有失败。对比改动前资源哈希,确认只修改任务资源。 + +## 验收边界 + +- 初始 check=false;点击两次回到 false;微信未同意时不调用授权;QQ/游客遵循原分支。 +- 审核、正式、showVisitor、deviceLogin、qqLogin 和缓存组合符合源码。 +- 授权参数 2/1,原回调字段映射与手机请求字段精确一致。 +- Login prefab 无 Layer10_Protol 子节点;弹层在 PopupRoot 下,关闭后释放。 +- Startup → Loading → Login 使用真实场景切换;Runtime 和网络连接不重复创建。 +- 不修改服务器,不覆盖既有 scene/meta 工作,不提交无关文件。 + +## 本次验证结果 + +- 新入口为 `assets/scenes/PlatformStartup.scene`,实际浏览器日志依次显示 `LoadScene PlatformLoading`、`LoadScene PlatformLogin`。 +- Login prefab 已无协议实例;Startup 引用独立协议/手机 prefab,弹窗归属 PopupRoot。 +- 浏览器实际点击验证 false → true → false、协议内容上下拖动、Spirit253 关闭、遮罩关闭;最终预览 error/warn 为空。 +- Cocos 原生节点验证手机窗口生成两个数字 EditBox,字体取原资源高度 28;协议含原生 ScrollView。 +- 补齐成功设备登录缓存、玩家 ID 写回、global/WVJB 的不同缓存目的地、startlocation(2)/getlocationinfo。正式/审核按钮位置沿用源坐标,关闭对应 Widget 避免静态布局覆盖运行时位置。 +- `node --experimental-transform-types --test framework-tests/local-platform-login/*.test.ts`:97/97 通过。 +- 全工程类型检查仍有既有 5 条错误:login-sources.ts 的 unknown 字段 3 条、room-contracts.ts 的 0/1 类型 1 条、game-contract-harness.ts 的 node:assert 类型 1 条;本次新增代码没有类型错误。 +- 对比本次资源哈希清单,仅既有 Login prefab 因移除嵌套协议而变化;保留其他已存在的 scene/meta 修改。没有修改服务器或原工程,也没有提交无关文件。 +- 真机微信/QQ 授权、手机号登录成功和原生定位尚未进行端到端联调;当前验证涵盖接口参数、缓存/协议契约及 Cocos UI,不代表真实账号已登录。 + +## 远程 showVisitor 排查与修正 + +- 实际 POST `config_test/update_jsonv2.txt` 返回 200。`showVisitor=1` 属于代理 `1B2h0ccl205c390Y28m1Ajdplkuu4wgy`;原构建默认代理错误地选择另一分支,且其下没有当前 gameid。 +- 在 BUILD_IDENTITY 单一来源修正为当前 gameid 所属的代理及渠道 `aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG`,未修改远程文件、服务端或分层配置读取规则。 +- 实际 marketid=4 还配置了 deviceLogin=true。用户确认保留两个按钮同时显示,调整为游客在左、手机在右,解决原坐标重叠。 +- 预览日志确认 showVisitor=true/deviceLogin=true/qqLogin=false;实际画面确认游客与手机并排、微信隐藏,error/warn 为空。 +- 新增远程层级到 UI policy 的回归测试,并同步已有测试中失效的配置 URL 断言;登录、配置和本地适配器合计 170/170 通过。 + +## 游客成功回包 tel 校验修复 + +- 新增的设备缓存校验错误地根据全局 deviceLogin 开关强制要求游客回包包含 tel。改为使用本次请求的 DeviceLoginMode,游客成功只记录 playerid,不写设备账号缓存或伪造手机号。 +- 缓存选择为 visitorinfo 时,自动登录也按游客方式提交;同时启用手机按钮不改变这个请求分支。 +- 回归覆盖“两个按钮开启 → 游客提交 → 无 tel 成功回包 → 刷新缓存自动登录”,并保留手机账号缓存测试。172 项相关测试全部通过。 +- 实际浏览器点击游客显示“登录成功”;刷新后缓存游客自动登录也显示“登录成功”,error/warn 均为空。没有修改服务器。 + +## 手机登录弹窗对齐 + +- 来源:11_GameUI.js 的 3243/3244/3245/3251/3253/3254 分支、openPhoneBind、数字输入面板;12_Logic.js 的 readPhoneCode。 +- 登录入口使用标题第 2 帧“手机登录”;恢复图标和原提示精灵,输入后隐藏对应提示。继续使用原生 EditBox,保留前导零,数字最多 18 位,与原数字面板一致。 +- 使用原 sgame_phoneCode 缓存键恢复手机号和 30 秒剩余时间,验证码不自动回填。倒计时中切等待帧;验证码获取仍仅提示“请通过相关渠道获取”,不发送原工程已注释掉的短信 RPC。 +- 两个输入非空才调用既有 phoneLogin 对应适配器并关闭;Spirit3251 和 Spirit3241 关闭,释放输入监听和定时器。 +- PhoneLoginAssets 通过 MCP 挂载并保存两个帧引用,保证构建资源依赖;没有手改 prefab/meta。 +- 浏览器验证标题、图标、输入位置、前导零、获取提示、空验证码拦截、关闭/重开及遮罩关闭。原生 Cocos 节点验证缓存恢复、倒计时拦截/结束、非空提交一次并关闭、定时器清理。174 项相关测试通过;未发送真实手机号或验证码登录。 + +## 2026-09-06 旧诊断入口退役 + +- 新流程所需倒计时、节点绑定、登录阶段 GameEntry 已迁入 assets/scripts/platform-login,并补充职责、协议状态、缓存优先级及生命周期注释。 +- 使用 Cocos asset-db 删除 LocalPlatformLogin.scene、scripts/local-platform-login 及其 meta。迁移保留行为测试至 framework-tests/platform-login,删除旧诊断入口专属测试。 +- GameEntry 仍是显式拒绝游戏操作的占位实现,保留诊断 key/route;不代表接入大厅或房间。 +- 按现有 Login_Layer 的 Logining 分组修正头像、昵称、遮罩绑定路径,未修改 prefab。 +- 删除后 93 项相关测试通过;浏览器预览正常进入登录界面,无 error/warn。此前已有的 5 项全项目类型错误仍存在。 +- 对比操作前哈希,删除范围之外的已有序列化资源无变化。 diff --git a/docs/superpowers/reports/2026-09-05-platform-ui-function-comparison.md b/docs/superpowers/reports/2026-09-05-platform-ui-function-comparison.md new file mode 100644 index 0000000..0289c99 --- /dev/null +++ b/docs/superpowers/reports/2026-09-05-platform-ui-function-comparison.md @@ -0,0 +1,348 @@ +# 原项目与 Cocos 非房间平台界面功能对照 + +> 2026-09-05;主目录分支 codex/local-platform-login,代码基线 556c47a。目的:在继续非房间 RPC 前明确原入口、交互/状态、资源归属与 Cocos 缺口。本轮仅检查和整理文档。 + +## 结论 + +- 原项目本轮已整理 **22 个功能条目**,覆盖主要非房间平台入口、相关回包与关闭分支;其中黑白名单/VIP 管理只登记边界,房间与真实子游戏后置。文末列出尚未展开的专项项,不把本轮称为全项目每条鼠标分支的完整验收。 +- **Cocos 新项目界面逻辑尚不完整**。已通过真实验收的是 LocalPlatformLogin 诊断场景的本地启动、加载、游客登录。完整大厅、资料/设置/任务/仓库等业务面板仍缺实际接线。 +- 通过 funplay-cocos-mcp 对固定主工程 assets 递归只读检查:**55 个 prefab、3 个 scene**。55 个 prefab 均没有序列化业务组件、cc.Button、clickEvents、cc.EditBox、cc.ScrollView 或 cc.Animation。 +- 上述零计数不等于所有节点都不可点击:诊断入口运行时通过 Node.EventType.TOUCH_END 绑定游客与停止,通过代码驱动加载旋转。已同时搜索 assets/**/*.ts 的组件、实例化和事件绑定;未找到其他非房间业务面板的现代运行时接线。 +- asset-db 仅登记 Login.scene、LocalPlatformLogin.scene、scene-2d.scene;**没有 MainMenu.scene / Loading.scene**。旧 LoginFlow 仍调用 loadScene('MainMenu'),正常生产启动到大厅的链路尚未闭合。 + +## 核对边界与证据 + +- MCP 主工程:G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/,Creator 3.8.8。资源扫描 callId=fp_f377daed9f88526b,时间=2026-09-05T12:09:46.270Z;asset-db 场景查询 callId=fp_88f8733e86ee90e4;group/节点核对 callId=fp_826c54b22021a9fc、fp_f5d7b70d00007965。 +- 编辑器读取前后 query-dirty=false;未打开其它场景、未实例化资源、未触发 UI 点击或网络请求。MCP 执行器初次把路径分隔符误判为外部路径,复核脚本只有固定工程内的目录/JSON 读取后执行只读扫描。 +- 原项目路径为 projects/Game_Surface_3;gameabc_face.mouseup 分别转交 GameUI、Game_Modify、gameCombat。只查 11_GameUI.js 会漏掉模板战绩交互,目录名 01_SubGame 不能替代实际职责判断。 +- 本文中的旧行为依据当前源码;资源结构依据本次 MCP;真实登录结论引用[已保存验收报告](./2026-09-05-local-platform-login-acceptance.md),本轮没有重跑登录验收或测试。 +- RPC 字段索引见 [07-RPC全量清单](../../protocol/07-RPC全量清单.md)。本文记录界面流程,不另建一份运行时配置常量。 + +## 当前 Cocos 入口和核心的实际能力 + +- [LoginFlow.ts](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/LoginFlow.ts:68):旧 PlatformSession/NetClient,硬编码 A/C/G、mock_openid 与连接兜底,成功后跳 MainMenu;这不是新 Runtime 的正式入口。 +- [LaunchFlow.ts](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/LaunchFlow.ts:17):1 秒 scheduleOnce 模拟加载后跳 Login;脚本存在,但三份现有场景未挂它。 +- [SceneStart.ts](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/SceneStart.ts:17):mock playerState 演示,3 秒后写测试资料;现有场景未挂载,不能当服务器状态回显。 +- [两个 PlayerInfoView](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/PlayerInfoView.ts:18) 与 [views/PlayerInfoView.ts](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/views/PlayerInfoView.ts:52):相同 ccclass 名。前者只有 applyPlayer,无注释声称的 bindTo;后者有旧 session binder,但头像加载仍未实现。均未挂到本轮扫描的 prefab/scene;重复类名是后续接管风险,不据静态扫描断言当前已发生运行时冲突。 +- [UI 契约](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/presentation/ui-contracts.ts:1):PageId 只有 loading/login/lobby/room;PlayerModel 为 playerid/nickname/avatar/bean;UiRuntime 命令只有 login/joinRoom/prepare/exitRoom。没有任务、仓库、排行、反馈等页面状态和命令。 +- [诊断 View](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/scripts/local-platform-login/local-login-view.ts:39):游客触摸绑定、版本显示、busy、错误/停止;lobby 分支只是结果 Label。它隐藏其它登录方式,禁止展示 room。 +- 3A 完成的是无引擎接线核心,不包含完整平台各功能面板;上述缺口不等于要重做 3A,而是后续逐项扩展业务契约与 Cocos View。 + +## 资源名不能代替功能名 + +- **Layer7_ewm** 实际包含 group-104 黑名单和 group-105 短号排行榜;当前二维码 group-42 在 **Layer9_Feedback** 中。已有迁移参照手册的名字/功能说明需结合当前 group 核对。 +- **Layer9_Feedback** 同时有 group-74 反馈/兑换码、group-12 通知、group-42 二维码、group-98 房间列表。 +- **Layer29_PhoneInfo** 有 group-106 手机登录/绑定和 group-108 白名单;分享选项 group-107 实际在 **Layer603**。 +- **Layer601** 是 group-102 文本输入;数字输入为 Layer602_inputPannel 的 group-87;通用确认是 Layer420_CheckFree 的 group-36,虽然文件名带 Free,也被管理动作复用。 +- 对后续资源授权与绑定必须使用精确 prefab + group + 节点;不能把旧 spid 数字直接当成 Cocos 节点 ID,也不能只按 prefab 英文名分派业务。 + +## 功能逐项对照 + +下列除第 1 项的诊断边界外,均未完成新 Runtime + 实际 Cocos UI 的闭环验收。资源项标记“存在”仅指本次结构读取成功,不表示视觉、动画或业务已验收。 + +### 01. 启动、加载与游客登录 + +- 原入口与流程:旧 GameUI.utlmouseup case 177 → Func.visitorLogin;读取游客缓存或 simulatePlayerInfo → 登录数据准备 → Net.Send_login。GameUI.StartLoad / JumpWxAuth 管理显示。 +- 数据/配置:统一配置身份、游客账号、设备与环境来源;openid/unionid 必须齐全。 +- RPC/外部能力:player_login +- 关闭、错误及边界:原项目游客入口不等于微信授权入口;诊断场景仅验证本地 H5 游客,不覆盖生产/原生/其它登录。 +- Cocos 资源(存在):[Layer1_Logo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab)、[Layer614_Loading](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer614_Loading.prefab)、[Login_Layer](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab)。 +- Cocos 缺口/状态:诊断闭环已验收;普通 Login.scene 仍挂旧 LoginFlow,尚未切换为新 Runtime 正式入口。 +- 原代码依据:[11_GameUI.js:791](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:791)、[05_Func.js:1233](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:1233)、[09_Net.js:122](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:122)。 + +### 02. 协议同意、微信/手机/快速登录 + +- 原入口与流程:case 248 切换 ProtocolIndex,249 打开协议,250/253 关闭;case 3 在同意协议后走 accreditlogin,否则提示。手机登录 case 3253 按 phoneType=1 调 Func.phoneLogin,3255 调 deviceLogin。 +- 数据/配置:ProtocolIndex、phoneType、设备登录缓存与原生授权返回值。 +- RPC/外部能力:认证来源最终进入 player_login;不能把所有入口当成相同游客按钮。 +- 关闭、错误及边界:本轮只追到来源/调用点;微信、QQ、原生授权全部分支与真实设备回调仍待专项验收。 +- Cocos 资源(存在):[Layer10_Protol](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab)、[Layer18_Auth](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer18_Auth.prefab)、[Login_Layer](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab)、[Layer29_PhoneInfo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab)。 +- Cocos 缺口/状态:诊断 View 主动隐藏微信、手机、QQ、快速登录等节点;协议勾选与授权 UI 尚无现代接线。 +- 原代码依据:[11_GameUI.js:612](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:612)、[11_GameUI.js:1309](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1309)、[11_GameUI.js:2560](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2560)。 + +### 03. 大厅导航、入口开关与玩家头部 + +- 原入口与流程:旧菜单按 versionState、LaunchMode、agentmode、hallConfig/sysConfig 控制入口显隐;设置 7、任务 17、分享 14、个人信息 9 等经统一 spid 事件分发;菜单进入后更新公告和按钮。 +- 数据/配置:C_Player 资料和资产;功能开关、审核版本、入口配置、公告来源。 +- RPC/外部能力:导航本身不发统一 RPC,进入具体功能后才发对应请求。 +- 关闭、错误及边界:按钮有美术节点,不代表在所有模式下都应显示或可点。 +- Cocos 资源(存在):[Layer4_MainMenu](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer4_MainMenu.prefab)、[Layer6](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer6.prefab)、[Layer8_Notice](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer8_Notice.prefab)。 +- Cocos 缺口/状态:MainMenu.scene 不存在;Layer4 未挂业务组件。新 lobby PageModel 只有 self 的 playerid/nickname/avatar/bean,诊断只显示文字结果,不是完整大厅。 +- 原代码依据:[11_GameUI.js:3781](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:3781)、[11_GameUI.js:3915](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:3915)、[11_GameUI.js:4832](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4832)。 + +### 04. 个人资料与个性签名 + +- 原入口与流程:case 9 → OpenInfo_hall 显示昵称、ID、IP、地区、积分、豆豆与签名;116 关闭。3156 → openTextInput → setSignCallBack → Send_setSign;回包 C_Player.setSign 刷新签名。 +- 数据/配置:资料来自 C_Player;输入回调来自 GameData.textCallback;完整资料不等于现有四字段 PlayerModel。 +- RPC/外部能力:setSign;打开本人资料本身复用已登录数据。 +- 关闭、错误及边界:签名编辑取消、长度限制、回包前后状态;旧空签名占位语义需在来源/显示契约明确,不能复制兜底。 +- Cocos 资源(存在):[Layer17_PlayerInfo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer17_PlayerInfo.prefab)、[Layer6](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer6.prefab)、[Layer601](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer601.prefab)。 +- Cocos 缺口/状态:资料弹窗、文本输入、签名命令与响应均未接入;两个旧同名 PlayerInfoView 都不是该弹窗完整实现。 +- 原代码依据:[11_GameUI.js:4545](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4545)、[11_GameUI.js:2350](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2350)、[12_Logic.js:2224](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/12_Logic.js:2224)、[09_Net.js:739](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:739)。 + +### 05. 设置与退出登录 + +- 原入口与流程:case 7 → OpenSetting(0),43/44/357/388 调音乐、音效、摇动、语音开关;Logic.TurnOn/Off 写设置并驱动能力。58 清登录/设备缓存并回授权页,来自大厅时提示去大厅退出。 +- 数据/配置:Utl.Config.bgm/sound/shake/voice、LaunchMode、hallConfig.logoffCallback。 +- RPC/外部能力:本地设置无对应业务 RPC;退出含原生回调条件。 +- 关闭、错误及边界:OpenSetting(mode=1) 含退房/解散行为,单列房间后置。case 47 在同一 switch 重复出现,不能按第二处标签判断必走 CloseSetting。 +- Cocos 资源(存在):[Layer609_Setting](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer609_Setting.prefab)。 +- Cocos 缺口/状态:无设置 Presenter、持久化接线、开关事件及现代退出登录生命周期。 +- 原代码依据:[11_GameUI.js:595](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:595)、[11_GameUI.js:701](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:701)、[11_GameUI.js:4201](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4201)、[12_Logic.js:1094](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/12_Logic.js:1094)。 + +### 06. 更多、公告和通知 + +- 原入口与流程:case 5 → OpenBoard;openPallet/closePallet 管理更多托盘;updateNoticeBoard 更新菜单公告。77/80 关闭通知,但配置允许时转而拉世界房间列表。broadcast 按 msgtype 分配即时消息或滚动公告。 +- 数据/配置:配置公告、通知选择状态与服务器 msgtype/msgcontent。 +- RPC/外部能力:broadcast;关闭通知可能触发 get_share_room(房间后置)。 +- 关闭、错误及边界:配置公告、推送公告、系统通知不是同一个数据源;通知关闭存在条件分支。 +- Cocos 资源(存在):[Layer8_Notice](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer8_Notice.prefab)、[Layer9_Feedback](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer9_Feedback.prefab)、[Layer402_GameNotice](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer402_GameNotice.prefab)。 +- Cocos 缺口/状态:无公告队列、滚动驱动、通知选择/翻页与更多入口接线。 +- 原代码依据:[11_GameUI.js:624](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:624)、[11_GameUI.js:666](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:666)、[11_GameUI.js:3356](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:3356)、[11_GameUI.js:3578](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:3578)、[11_GameUI.js:4357](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4357)、[07_Desk.js:1114](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1114)。 + +### 07. 反馈意见 + +- 原入口与流程:15 设置 feedMode=1 → OpenFeedback;550 取输入,非空时先 CloseFeedback 再 submit_opinion;回包 state=0 成功提示,否则失败提示。548 关闭,CloseFeedback 失焦、清输入、隐藏 group-74。 +- 数据/配置:身份来自 GameData/C_Player,content 来自输入551。 +- RPC/外部能力:submit_opinion +- 关闭、错误及边界:原逻辑发送前关闭并清空输入;失败不会自动恢复草稿。不能宣称原实现已有完整忙碌态或重试保稿。 +- Cocos 资源(存在):[Layer9_Feedback](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer9_Feedback.prefab)。 +- Cocos 缺口/状态:没有输入控件、反馈命令、成功/失败回显、草稿和重复提交策略。 +- 原代码依据:[11_GameUI.js:630](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:630)、[11_GameUI.js:652](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:652)、[11_GameUI.js:6211](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:6211)、[07_Desk.js:1102](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1102)。 + +### 08. 邀请码查询与绑定 + +- 原入口与流程:235 → get_player_invitecode → 回包 setInvitecod 并 OpenBind;366 粘贴过滤数字;367 读取输入365,关闭后 binding_invitecode;362 关闭。OpenBind 按已绑定状态隐藏输入或显示结果。 +- 数据/配置:agentid/playerid/openid/unionid 用于查询;绑定请求只带其协议字段与 invitecode。 +- RPC/外部能力:get_player_invitecode、binding_invitecode +- 关闭、错误及边界:已绑定/未绑定、剪贴板异步路径、空输入与服务器失败需独立核对;原长度检查曾被注释。 +- Cocos 资源(存在):[Layer16_Bind](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer16_Bind.prefab)。 +- Cocos 缺口/状态:无输入/粘贴/已绑定状态呈现,也无现代命令和回包处理。 +- 原代码依据:[11_GameUI.js:1342](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1342)、[11_GameUI.js:1359](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1359)、[11_GameUI.js:1375](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1375)、[11_GameUI.js:6591](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:6591)、[09_Net.js:602](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:602)。 + +### 09. 任务列表、分享任务与领奖 + +- 原入口与流程:17 → get_player_task → Desk 写 TaskInfo → OpenTask 动态复制任务卡,按 state=0/1/其它显示进度、领取、已领取;滑动时抑制点击。去做任务关闭任务并打开分享;sharesuccess 条件满足发送 player_finish_task;get_task_award 回包改匹配任务状态并关闭重建列表。 +- 数据/配置:TaskInfo 的 taskid/title/award/finish/total/state,C_Player.taskstate 与 ConstVal.ShareTaskId。 +- RPC/外部能力:get_player_task、player_finish_task、get_task_award;refresh_task_state/can_award 有旧缺陷。 +- 关闭、错误及边界:CloseTask 清动态项;空列表、total=0、重复领奖均不能凭静态卡片推定正确。旧 UI 领奖构造未带 gameid,与 02 章字段描述存在差异,联调前核对服务器,勿消费端补值。 +- Cocos 资源(存在):[Layer26_Task](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer26_Task.prefab)、[Layer22_Share](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer22_Share.prefab)。 +- Cocos 缺口/状态:无任务列表模型、滚动/动态项、领取状态机、分享成功回调接线。 +- 原代码依据:[11_GameUI.js:727](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:727)、[11_GameUI.js:748](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:748)、[11_GameUI.js:4637](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4637)、[07_Desk.js:942](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:942)、[06_Player.js:424](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/06_Player.js:424)、[06_Player.js:499](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/06_Player.js:499)。 + +### 10. 财富榜与 VIP 排行榜 + +- 原入口与流程:604/3233 按 hallConfig.rankList=1 打开已有榜单,=2 请求 get_treasurelist;Desk 写 GameData.rankList 后 openRankList。3231/3232 请求 getVipRankList(limit),回包同一资源以 true 模式显示;605 关闭。 +- 数据/配置:rankList 开关、sysConfig.vipRankLimit、服务器 list。 +- RPC/外部能力:get_treasurelist、getVipRankList +- 关闭、错误及边界:模式、条目数量、排序/展示字段和空数据需按服务端实际列表验证;短号场榜单另列房间管理。 +- Cocos 资源(存在):[Layer21_RankList](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer21_RankList.prefab)。 +- Cocos 缺口/状态:无列表请求、数据模型、动态行、模式切换和关闭生命周期。 +- 原代码依据:[11_GameUI.js:1853](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1853)、[11_GameUI.js:2540](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2540)、[11_GameUI.js:7628](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:7628)、[07_Desk.js:1225](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1225)、[07_Desk.js:1330](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1330)。 + +### 11. 战绩列表、分页与详情 + +- 原入口与流程:原模板的 gameCombat 位于 01_SubGame_modify.js;84/85 切查询类型,104/105 上下页,55 分级返回、61 关闭。Send_get_player_grade1 构造分页条件;回包解析 gradeinfo/gameinfo1,记录首末 idx,打开战绩。 +- 数据/配置:CombatInfo、CombatCount、combatData 类型与分页游标。 +- RPC/外部能力:get_player_grade1;get_player_grade2 仅有透传发送和空接收体。 +- 关闭、错误及边界:目录叫 SubGame 不等于要选择真实子游戏;本项仅核对原模板自带战绩。具体查牌/回放内容与真实游戏耦合的部分后置。 +- Cocos 资源(存在):[Layer12_CombatLayer](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer12_CombatLayer.prefab)、[Layer13](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer13.prefab)。 +- Cocos 缺口/状态:无现代战绩命令、分页、列表/详情 View;不能把 grade2 算成旧前端完整功能。 +- 原代码依据:[01_SubGame_modify.js:310](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/01_SubGame/01_SubGame_modify.js:310)、[01_SubGame_modify.js:737](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/01_SubGame/01_SubGame_modify.js:737)、[01_SubGame_modify.js:777](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/01_SubGame/01_SubGame_modify.js:777)。 + +### 12. 充值入口、商品金额与确认支付 + +- 原入口与流程:10/286/508 等入口按版本、agentmode、OS、androidPay、TopUpUrl/channelKey 分流外部页、原生或 get_paylist;回包 payList → OpenPay;选择商品与渠道 → OpenCheckPay;金额面板和确认面板分别关闭。 +- 数据/配置:渠道/市场身份、支付配置、payList、payData、原生回调,不能使用测试占位数据。 +- RPC/外部能力:get_paylist;pay_succ 为 HTTP;原生 yPaytype 还可能上行 update_roomcard/update_bean。 +- 关闭、错误及边界:商品查询成功不等于支付成功;下单、取消、回调与资产推送分开验收。 +- Cocos 资源(存在):[Layer11_ChargeInfo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer11_ChargeInfo.prefab)、[Layer14_Pay](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer14_Pay.prefab)。 +- Cocos 缺口/状态:只有资源,无商品/支付状态、渠道分流与原生回调的现代界面接线。 +- 原代码依据:[11_GameUI.js:919](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:919)、[11_GameUI.js:6362](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:6362)、[09_Net.js:566](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/09_Net.js:566)、[05_Func.js:2182](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:2182)、[05_Func.js:2236](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/05_Func.js:2236)。 + +### 13. 兑换码(复用反馈面板) + +- 原入口与流程:3264 设置 feedMode=2 → OpenFeedback(true);3265 粘贴;3266 非空后关闭并发 topup_card。Desk.topup_card 为空体。 +- 数据/配置:cardno 来自输入,不是反馈 content;资产结果依赖后续推送。 +- RPC/外部能力:topup_card、update_bean/update_roomcard +- 关闭、错误及边界:不能仅因面板关闭就判兑换成功;反馈/兑换共享资源,按钮模式不能互相串用。 +- Cocos 资源(存在):[Layer9_Feedback](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer9_Feedback.prefab)。 +- Cocos 缺口/状态:无兑换模式、事件绑定或兑换结果与资产更新接线。 +- 原代码依据:[11_GameUI.js:2648](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2648)、[11_GameUI.js:2656](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2656)、[07_Desk.js:1366](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1366)。 + +### 14. 仓库入口、页签与初始密码 + +- 原入口与流程:3057 → openWareHouse 初始化输入,按 bankpwd 与 hallConfig.wareHouse 切密码/存取/赠送页;3059/3060 切页。3043 在初始化分支验证两次密码一致后 set_bankpwd;成功 setBankPwd(1) 并关仓库,showerror 时提示。 +- 数据/配置:bankpwd 表示是否初始化;wareHouse 的 page/selectType 和输入状态。 +- RPC/外部能力:set_bankpwd +- 关闭、错误及边界:3029/97 关闭全部 group-91/92/93/94;密码与敏感字段不得放诊断输出。 +- Cocos 资源(存在):[Layer25_wareHouse](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer25_wareHouse.prefab)。 +- Cocos 缺口/状态:无页签状态机、输入、密码设置及失败显示。 +- 原代码依据:[11_GameUI.js:1959](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1959)、[11_GameUI.js:2019](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2019)、[11_GameUI.js:8117](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:8117)、[07_Desk.js:1230](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1230)。 + +### 15. 仓库存入与取出 + +- 原入口与流程:3034/3035 选存取,3038/3039 选数量/密码输入,3043 校验后以 mode=safeInputType-1 发 change_star;成功 star1 更新豆豆、star2 更新仓库星星,可回填 count/msg;失败按 showerror 提示。 +- 数据/配置:C_Player.bean、仓库余额、initBean、safeInputCount/password;下游不猜资产语义。 +- RPC/外部能力:change_star +- 关闭、错误及边界:旧代码包含保留赠送金币等数量约束;最终迁移须厘清权威来源,不直接搬运静默修补分支。 +- Cocos 资源(存在):[Layer25_wareHouse](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer25_wareHouse.prefab)。 +- Cocos 缺口/状态:无余额投影、输入限制、提交状态或双资产一致刷新。 +- 原代码依据:[11_GameUI.js:2045](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2045)、[07_Desk.js:1240](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1240)、[11_GameUI.js:8117](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:8117)。 + +### 16. 仓库查询目标玩家与赠送 + +- 原入口与流程:3087/3088/3089 录入目标ID/数量/密码;3096 校验目标ID长度后清旧头像/昵称并 query_player2。回包写 pImg/pNickName/pId,昵称或头像有值时刷新,否则提示未找到。3043 赠送分支发 giveCoin;成功用 star2 更新仓库星星并关闭。 +- 数据/配置:目标玩家ID与当前登录 playerid 区分;query_player2 是嵌在仓库中的查询,不是原项目独立玩家搜索页。 +- RPC/外部能力:query_player2、giveCoin +- 关闭、错误及边界:查询与赠送是不同操作;需防旧查询回包覆盖新目标,查询成功不授权自动赠送。原代码是否处理并发未证明。 +- Cocos 资源(存在):[Layer25_wareHouse](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer25_wareHouse.prefab)。 +- Cocos 缺口/状态:无目标资料 View、查询命令、过期响应处理或赠送流程。 +- 原代码依据:[11_GameUI.js:2000](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2000)、[11_GameUI.js:2119](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2119)、[07_Desk.js:1370](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1370)、[07_Desk.js:1383](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1383)、[11_GameUI.js:8789](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:8789)。 + +### 17. 手机号绑定与验证码面板 + +- 原入口与流程:3256 未绑定时 phoneType=2 → openPhoneBind;3243/3244 打开数字输入,3253 非空后 binding_phone;回包关闭并 setTel。3245 当前只提示通过相关渠道获取,发送微信验证码的代码被注释;3251/3241 关闭。 +- 数据/配置:phoneNumber/phoneCode、缓存验证码剩余时间、C_Player.tel、phoneType。 +- RPC/外部能力:binding_phone;send_phone_checkcode/send_phone_code_wechat 不能据函数存在认定有活跃界面发送。 +- 关闭、错误及边界:同一面板还承担手机登录;倒计时恢复与关闭后的定时器清理需专项验证。 +- Cocos 资源(存在):[Layer29_PhoneInfo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab)。 +- Cocos 缺口/状态:无 EditBox/数字输入绑定、倒计时、登录/绑定分流和回包关闭逻辑。 +- 原代码依据:[11_GameUI.js:2550](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2550)、[11_GameUI.js:2616](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2616)、[11_GameUI.js:9654](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:9654)、[07_Desk.js:1339](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1339)。 + +### 18. 分享、二维码与邀请外部能力 + +- 原入口与流程:14 打开分享,74/75 调好友/朋友圈后关闭,背景/链接来自配置;二维码 Open_ewm/Close_ewm;另一 sharePannel 选择方式后调用 Func.sharefriend。分享成功回调再驱动任务,不等于点击即完成。 +- 数据/配置:Game_Config.Share、hallConfig.shareBgUrl/shareImgUrl、shareFriendInfo 与 WVJB 回调。 +- RPC/外部能力:分享动作本身为原生/外部接口;任务完成再发 player_finish_task。 +- 关闭、错误及边界:二维码内容来源、图片加载失败、取消分享与成功回调须分别验收;不输出渠道密钥。 +- Cocos 资源(存在):[Layer22_Share](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer22_Share.prefab)、[Layer9_Feedback](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer9_Feedback.prefab)、[Layer603](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer603.prefab)。 +- Cocos 缺口/状态:无现代分享/二维码 Presenter、动态图加载、原生结果到任务状态的 UI 接线。 +- 原代码依据:[11_GameUI.js:876](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:876)、[11_GameUI.js:4340](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4340)、[11_GameUI.js:6199](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:6199)、[11_GameUI.js:2669](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2669)、[06_Player.js:424](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/06_Player.js:424)。 + +### 19. 通用提示、断线、踢下线与资产通知 + +- 原入口与流程:show_message → OpenTips,按时间关闭;broadcast 分即时与滚动;kick_server/kick_offline → OpenKick;资产回包/推送经 C_Player 与 UI 通知。OpenDisConnect/CloseDisConnect 管理断线面板。 +- 数据/配置:服务器提示/资产数据、连接状态与显式计时来源。 +- RPC/外部能力:show_message、broadcast、kick_server、kick_offline、update_roomcard、update_bean +- 关闭、错误及边界:弹层优先级、重复推送、关闭计时器、停止后不重连均是生命周期验收点。 +- Cocos 资源(存在):[Layer612_Tips](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer612_Tips.prefab)、[Layer613_RoomCardUpdate](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer613_RoomCardUpdate.prefab)、[Layer615_Reconnect](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer615_Reconnect.prefab)、[Layer616_Kick](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer616_Kick.prefab)、[Layer617_iMessage](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer617_iMessage.prefab)、[Layer402_GameNotice](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer402_GameNotice.prefab)。 +- Cocos 缺口/状态:核心 OverlayModel 已有 reconnect/denial/kicked/fatal;诊断使用自己的文字遮罩。原弹窗资源尚未接管,broadcast/show_message/资产推送等无现代处理链。 +- 原代码依据:[11_GameUI.js:5838](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:5838)、[11_GameUI.js:5920](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:5920)、[11_GameUI.js:5967](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:5967)、[11_GameUI.js:6165](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:6165)、[07_Desk.js:1109](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1109)。 + +### 20. 数字/文本输入、确认框与计算器 + +- 原入口与流程:openInputPanel/openTextInput 保存输入及回调;3155 执行 textCallback 后置空关闭,3159 取消;openCheck 依 checkType 执行对应动作,247/242/246 取消。showCal/hideCal 为本地工具。 +- 数据/配置:inputPanelData/textCallback/checkType 和各业务输入值。 +- RPC/外部能力:组件自身无统一 RPC;确认动作按所属业务发包。 +- 关闭、错误及边界:旧 checkType=8 被小程序进房和白名单删除复用;不能机械照搬。回调、输入模式、键盘事件必须在关闭/销毁时处理。 +- Cocos 资源(存在):[Layer602_inputPannel](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer602_inputPannel.prefab)、[Layer607_Cal](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer607_Cal.prefab)、[Layer601](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer601.prefab)、[Layer420_CheckFree](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer420_CheckFree.prefab)。 +- Cocos 缺口/状态:没有通用输入、确认框组件和业务回调所有权。资源图标不能替代实际输入控件。 +- 原代码依据:[11_GameUI.js:1217](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1217)、[11_GameUI.js:1304](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1304)、[11_GameUI.js:2335](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2335)、[11_GameUI.js:7945](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:7945)、[11_GameUI.js:9142](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:9142)。 + +### 21. 商城、背包、活动、帮助与邻居入口 + +- 原入口与流程:3016/3017/3018 跳 Store/Bag/Act 外部地址;帮助 OpenHelp 调 Game_Modify.onOpenHelp(290),关闭隐藏;showNeighbor 为配置相关入口。 +- 数据/配置:外部 URL/原生打开方式、邻居数据与具体游戏帮助内容。 +- RPC/外部能力:不等于本地平台 RPC 面板;无 RPC 的入口也需要界面迁移。 +- 关闭、错误及边界:帮助正文依赖子游戏钩子,留后;外部页服务不修改、不能伪造链接。邻居只定位到 group-88 的显示/隐藏,具体点击去向尚未展开。 +- Cocos 资源(存在):[Layer8_Notice](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer8_Notice.prefab)、[Layer24_Neighbor](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer24_Neighbor.prefab)、[Layer608_Help](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer608_Help.prefab)。 +- Cocos 缺口/状态:资源存在,但无对应入口、外部能力接线或帮助内容加载。 +- 原代码依据:[11_GameUI.js:1947](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:1947)、[11_GameUI.js:4728](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:4728)、[11_GameUI.js:8027](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:8027)、[12_Logic.js:2054](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/12_Logic.js:2054)。 + +### 22. 黑白名单与 VIP 管理(边界登记,后置) + +- 原入口与流程:3140/3235 按 whiteListManager 分原生管理页或 getPlayerWhiteList;白名单增改删先确认,回包写列表并重开;3214 查询黑名单,3194/3195 增删。短号榜、全体魅力、禁止选桌与房间/VIP 状态相连。 MCP group 对照确认:黑名单 group-104、短号榜 group-105 实际在 Layer7_ewm,不能按文件名把它当二维码页。 +- 数据/配置:whiteList/blackList、shortcode、snrRoomList 与管理配置。 +- RPC/外部能力:getPlayerWhiteList、optWhiteList、optBanList、getShortCodeRankList、setAllCharm、setVipForbidSelect;相关 room 列表 RPC 后置。 +- 关闭、错误及边界:虽然上行 route=agent,仍不自动列入非房间首批;不因位置在大厅就提前执行管理写操作。 +- Cocos 资源(存在):[Layer29_PhoneInfo](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab)、[Layer28_SeniorOptions](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer28_SeniorOptions.prefab)、[Layer7_ewm](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer7_ewm.prefab)。 +- Cocos 缺口/状态:无现代管理面板接线;这里只登记资源和调用链,不验收房间管理。 +- 原代码依据:[11_GameUI.js:2305](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2305)、[11_GameUI.js:2370](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2370)、[11_GameUI.js:2480](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/11_GameUI.js:2480)、[07_Desk.js:1291](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1291)、[07_Desk.js:1400](G:/Works/YouleGamesCocosCreator/projects/Game_Surface_3/js/00_Surface/07_Desk.js:1400)。 + +## 后续实施需要共用的验收条件 + +- 入口与可见性:按同一份模式/配置来源决定入口;资源可见、可点击与权限/前置状态一致。 +- 请求:参数来自权威配置、登录 Store 或明确用户输入;单次操作发出预期包;查询目标与本人身份不混淆。 +- 响应:先校验契约,再更新权威状态、PageModel、实际控件;列表空、拒绝、网络失败、加载失败都可识别。 +- 生命周期:打开/关闭/再次打开不重复监听;未完成请求关闭后不误开弹窗;异步旧响应不覆盖新输入;销毁清理触摸、订阅和定时器。 +- 实际边界:纯 TS 测试与资源结构检查通过后,仍需真实 Creator 预览、真实服务器包和实际界面一致的证据;支付/原生外部流程单独验收。 + +## 尚未展开的专项范围 + +- 每个动态 spid、全部模式组合、拖动裁剪、原工程视觉/声音/动画细节,尚未逐事件运行验证;本轮为入口和数据链审计。 +- 微信/QQ/原生授权、客服电话/剪贴板/分享/支付、邻居去向与外部商城页的全分支;代码来源已定位的部分列在条目中,实际设备效果待对应阶段。 +- 战绩详情里的子游戏解释、回放与帮助正文;创建/加入/房间列表、房内设置、聊天语音、投票、对局与重连快照,留到房间/子游戏阶段。 +- 已发现的旧代码差异(领奖 gameid、refresh_task_state 缺常量、can_award 空缺接收方法、重复 case/确认类型、历史默认值)须逐项核对契约后处理;本轮未修复或替服务器补约定。 + +## 编辑器结构快照 + +以下为本次完整平台 prefab 清点,包含后置的房间资源。节点数含 prefab 根和 group。每项业务组件/按钮事件/输入/滚动/动画组件均为 0;不包含运行时动态绑定。 + +- [Login_Layer.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/Login_Layer.prefab):20 个节点;group-2(登录)。 +- [Layer10_Protol.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer10_Protol.prefab):6 个节点;group-37。 +- [Layer11_ChargeInfo.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer11_ChargeInfo.prefab):6 个节点;group-20。 +- [Layer12_CombatLayer.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer12_CombatLayer.prefab):31 个节点;group-7、group-8、group-9。 +- [Layer13.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer13.prefab):9 个节点;group-7、group-8。 +- [Layer14_Pay.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer14_Pay.prefab):46 个节点;group-53、group-54。 +- [Layer15_JoinRoom.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer15_JoinRoom.prefab):28 个节点;group-5。 +- [Layer16_Bind.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer16_Bind.prefab):9 个节点;group-80。 +- [Layer17_PlayerInfo.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer17_PlayerInfo.prefab):17 个节点;group-18。 +- [Layer18_Auth.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer18_Auth.prefab):11 个节点;group-57。 +- [Layer19_WorldRoomList.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer19_WorldRoomList.prefab):38 个节点;group-82、group-96、group-97。 +- [Layer1_Logo.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer1_Logo.prefab):8 个节点;group-1、group-32。 +- [Layer202_PlayerHeadScore.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer202_PlayerHeadScore.prefab):72 个节点;group-43、group-44、group-45、group-46、group-47、group-48、group-49、group-50、group-51、group-52、group-73。 +- [Layer21_RankList.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer21_RankList.prefab):22 个节点;group-84。 +- [Layer22_Share.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer22_Share.prefab):8 个节点;group-11。 +- [Layer24_Neighbor.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer24_Neighbor.prefab):9 个节点;group-88。 +- [Layer25_wareHouse.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer25_wareHouse.prefab):73 个节点;group-91、group-92、group-93、group-94。 +- [Layer26_Task.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer26_Task.prefab):48 个节点;group-10。 +- [Layer27_CreateRoom.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer27_CreateRoom.prefab):21 个节点;group-4。 +- [Layer28_SeniorOptions.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer28_SeniorOptions.prefab):58 个节点;group-83。 +- [Layer29_PhoneInfo.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer29_PhoneInfo.prefab):41 个节点;group-106、group-108。 +- [Layer402_GameNotice.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer402_GameNotice.prefab):4 个节点;group-56。 +- [Layer403.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer403.prefab):3 个节点;group-81。 +- [Layer404_MainSceneButton.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer404_MainSceneButton.prefab):8 个节点;group-28。 +- [Layer405_MainButton.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer405_MainButton.prefab):8 个节点;group-21。 +- [Layer408_Interact.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer408_Interact.prefab):24 个节点;group-21、group-39。 +- [Layer409_ChatVoice.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer409_ChatVoice.prefab):22 个节点;group-76。 +- [Layer410_ChatText.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer410_ChatText.prefab):22 个节点;group-75。 +- [Layer411.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer411.prefab):8 个节点;group-81。 +- [Layer413_BtnPanel.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer413_BtnPanel.prefab):23 个节点;group-21、group-81、group-99、group-100。 +- [Layer416_PlayerInfo.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer416_PlayerInfo.prefab):19 个节点;group-77、group-38。 +- [Layer418_ChatPannel.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer418_ChatPannel.prefab):56 个节点;group-30。 +- [Layer420_CheckFree.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer420_CheckFree.prefab):9 个节点;group-36。 +- [Layer4_MainMenu.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer4_MainMenu.prefab):34 个节点;group-3。 +- [Layer50_MainScene.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer50_MainScene.prefab):13 个节点;group-21。 +- [Layer6.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer6.prefab):56 个节点;group-103、group-3。 +- [Layer601.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer601.prefab):7 个节点;group-102。 +- [Layer602_inputPannel.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer602_inputPannel.prefab):21 个节点;group-87。 +- [Layer603.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer603.prefab):5 个节点;group-107。 +- [Layer606_Apply.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer606_Apply.prefab):34 个节点;group-29、group-32。 +- [Layer607_Cal.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer607_Cal.prefab):23 个节点;group-78、group-79。 +- [Layer608_Help.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer608_Help.prefab):9 个节点;group-19。 +- [Layer609_Setting.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer609_Setting.prefab):17 个节点;group-6。 +- [Layer612_Tips.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer612_Tips.prefab):8 个节点;group-32、group-89。 +- [Layer613_RoomCardUpdate.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer613_RoomCardUpdate.prefab):5 个节点;group-34。 +- [Layer614_Loading.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer614_Loading.prefab):4 个节点;group-40。 +- [Layer615_Reconnect.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer615_Reconnect.prefab):6 个节点;group-35。 +- [Layer616_Kick.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer616_Kick.prefab):5 个节点;group-33。 +- [Layer617_iMessage.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer617_iMessage.prefab):6 个节点;group-41。 +- [Layer618.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer618.prefab):5 个节点;group-85、group-86。 +- [Layer619_BackHall.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer619_BackHall.prefab):3 个节点;group-55。 +- [Layer620_Record.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer620_Record.prefab):5 个节点;group-101。 +- [Layer7_ewm.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer7_ewm.prefab):33 个节点;group-104、group-105。 +- [Layer8_Notice.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer8_Notice.prefab):12 个节点;group-3、group-95。 +- [Layer9_Feedback.prefab](G:/Works/YouleGamesCocosCreator/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/widgets/Layer9_Feedback.prefab):39 个节点;group-74、group-12、group-42、group-98。 + +场景组件: + +- Login.scene:LoginFlow_Node 上的旧 LoginFlow;未挂现代完整大厅控制器。 +- LocalPlatformLogin.scene:Host 上的 LocalPlatformLogin;游客/停止监听为运行时绑定。 +- scene-2d.scene:2 个节点,无业务组件;这是用户既有未提交资源,本轮未修改。 + +## 本轮文件保护与交付 + +- 全程主目录 Git 分支,未创建 worktree;未合并、提交或推送。 +- 本轮开始有 51 项未提交文件(原 50 项加上上一轮 RPC 清单);结束时核对其状态与 SHA256,保持不变。 +- 本轮只新增本文;无业务代码、测试、配置、scene/prefab/anim/meta 改动。未运行资源生成器或网络请求。