Phoenix · Block Club · Skin Remote

Export & Load Bundle Skin Remote

Tổng hợp luồng tách skin ra khỏi client, đóng gói thành bundle tải từ server, và cách client Cocos tải lại với retry / timeout / fallback. Mô phỏng đúng luồng remote của game gốc Block Blast Classic.

00 Bức tranh tổng thể

Skin không nhúng hết vào client. Mỗi skin remote (nhóm id 2xxx/3xxx/4xxx/5xxx) là một bundle Cocos riêng nằm trên server. Client chỉ giữ skin cơ bản 10xx; khi cần skin remote thì hỏi server để biết tải skin nào, rồi assetManager.loadBundle kéo bundle về.

1 · EXPORTBuild bundle trong Cocos EditorMỗi skin = 1 bundle. Editor sinh config.json + import/ + native/.
2 · ĐÓNG GÓICopy / zip bundleĐưa bundle vào thư mục server hoặc nén thành .zip cho bản native.
3 · SERVERCấp config + phục vụ fileGET /config trả pool skin; GET /remote/... phục vụ file tĩnh.
4 · CLIENTTải & áp dụngHỏi config → chọn skin → loadBundle có retry/timeout → fallback 1000 nếu lỗi.

01 Export bundle — làm gì

Bước build bundle thật sự chỉ Cocos Editor làm được. Các script trong repo chỉ sắp xếp asset và đóng gói quanh bước đó.

ViệcCông cụKết quả
Tách asset per-skin khỏi resources/ vào thư mục bundle riêng remote-bundles/Remote_Skin_Material_<id>/ build-remote-bundles.mjs Mỗi id một thư mục, đủ format 1 skin: bg + board + shadow + cube_1..7 (skin mượn bg/board của skin khác thì copy kèm)
Đánh dấu thư mục là Asset Bundle và build web-mobile Cocos Editor Sinh bundle hợp lệ: config.json, import/, native/, index.js
Đóng gói mỗi bundle thành Remote_Skin_Material_<id>.zip (cho bản native tải zip về giải nén) zip-remote-bundles.mjs Zip chứa nội dung bundle ở gốc zip → giải nén ra là bundle dùng ngay
Lưu ý: build-remote-bundles.mjs chỉ di chuyển asset đúng chỗ kèm .meta, chạy lại idempotent. Nó KHÔNG tạo được bundle Cocos — phải mở Editor đánh dấu Asset Bundle rồi build mới ra config.json/import/native.

# Sắp xếp asset per-skin thành thư mục bundle (2/3/4/5xxx)
node tools/skin-test-server/build-remote-bundles.mjs
node tools/skin-test-server/build-remote-bundles.mjs 2006 3012 4001   # chỉ vài id
node tools/skin-test-server/build-remote-bundles.mjs --dry            # xem kế hoạch

# → Mở Cocos Editor: đánh dấu từng thư mục là Asset Bundle, build web-mobile

# Nén bundle đã build thành .zip (cho bản native)
node tools/skin-test-server/zip-remote-bundles.mjs
node tools/skin-test-server/zip-remote-bundles.mjs 2006 3012

02 Hai phía: Server và Client

Server cần làm

tools/skin-test-server/server.mjs · cổng 8099

  • GET /config?device=<id> — mô phỏng server quyết định: trả { remoteBaseUrl, changeSkinEnabled, rotation, abTest, variant, pool[] }.
  • GET /remote/Remote_Skin_Material_<id>/... — phục vụ file tĩnh của bundle.
  • GET /health → { ok: true }.
  • Lọc pool skin theo catalog.available + A/B test (giống trait engine gốc).
  • Đọc lại config.json mỗi request → sửa config có hiệu lực ngay, không restart.
  • Giả lập lỗi (fault.mode) để test client: hang, error500, notfound.

Thứ tự tìm thư mục bundle

  • REMOTE_DIR (env)
  • bundles/ trong folder server (tự đủ, mang đi đâu cũng chạy)
  • game/build/web-mobile/remote (build repo chính)
  • mock/ (config giả, chỉ để test probe)

Client cần làm

SkinServerClient.ts + SkinBundleLoader.ts

  • Bật chế độ remote: web thêm ?bundle=1 vào URL, hoặc set window.__SKIN_REMOTE__ = true. Mặc định TẮT (SKIN_REMOTE_ENABLED).
  • Hỏi config: SkinServerClient gọi GET /config?device=<id> → nhận pool skin + rotation + cờ đổi skin.
  • Chọn skin từ pool theo rotation (random no-repeat / sequence).
  • Tải bundle: SkinBundleLoader gọi assetManager.loadBundle(<base>/Remote_Skin_Material_<id>) với retry + timeout.
  • Native: tải zip → giải nén ra đĩa (fflate unzipSync) → loadBundle(thư mục giải nén). Web: loadBundle thẳng từ URL thư mục.
  • Áp dụng skin; key thiếu trong bundle → fallback trong GameView._applyThemeCore.

03 Error-handling — điểm cốt lõi

Mô phỏng đúng game gốc: tải / áp dụng skin thất bại không crash, không chặn người chơi, revert về skin default 1000.

Cơ chếMặc địnhHành vi
maxRetryCount2 (tổng 3 lần)Số lần thử lại khi engine báo lỗi
retryIntervalMs2000 msKhoảng cách giữa các lần thử
timeoutMs8000 msTimeout thủ công cả quá trình — tránh treo khi server không đóng kết nối
Set _failedper-sessionId đã fail không thử lại ngay → tránh spam server mỗi lần clear bàn
Fallbackskin 1000Hết retry / timeout / lỗi → callback(null) → caller giữ hoặc revert skin default

Giả lập lỗi để test (config.fault.mode)

modeTest điều gì
noneLuồng bình thường
hangServer treo → kiểm tra timeout thủ công của client
error500Lỗi server → kiểm tra retry + fallback
notfoundBundle không tồn tại → kiểm tra fallback skin 1000

04 Catalog hiện tại & chạy thử

Mẫu đã dựng: 2006 (DragonBones), 3012 + 4001 (Spine). Chỉ skin có asset trong resources và map được THEMES mới đổi nhìn thấy được.

# 1. Chạy server
node tools/skin-test-server/server.mjs            # http://127.0.0.1:8099
PORT=8099 node tools/skin-test-server/server.mjs  # đổi cổng

# 2. Mở client web kèm cờ bật remote
#    <url game>?bundle=1            → tải 3 skin remote 2006/3012/4001
#    <url game>?bundle=1&device=deviceA  → nhánh A/B test

# 3. Sửa config.json → F5 client (server đọc lại mỗi request, không cần restart)
Checklist báo cáo: (1) Export cần Cocos Editor build từng bundle; script chỉ sắp xếp + zip. (2) Server trả config (pool/A-B) và file tĩnh. (3) Client bật bằng ?bundle=1, hỏi config, chọn skin, loadBundle có retry/timeout, fallback 1000 khi lỗi. (4) Native tải zip giải nén, web load thẳng URL.