Bỏ qua tới nội dung
Các trang tài liệu

Nhà phát triển · Nâng cao và hỏi đáp

Câu hỏi thường gặp

Những lỗi hay gặp khi tích hợp và cách xử lý — rơi về standalone, chưa init, CSP chặn nhúng, mixed content, âm thanh, điểm bị từ chối.

Trên trang này

Khởi tạo và kết nối

Trong portal mà game vẫn chạy standalone

WSG.environment là standalone dù game đang nằm trong portal thường do một trong các lý do sau:

  • Đọc quá sớm. Trước khi init() xong, WSG.environment luôn là standalone. Đọc sau await WSG.init(), hoặc dùng ctx.environment.
  • Bắt tay quá 3 giây. SDK gửi init tới portal và chờ 3 giây; không có trả lời thì chuyển sang standalone. Khởi tạo với debug: true sẽ thấy cảnh báo trong console khi điều này xảy ra.
  • Mất query của portal. SDK cần wsg=1 và host=<origin portal> trong URL. Game chuyển hướng sang trang khác trong bundle, hoặc xoá query bằng history.replaceState() trước khi init(), sẽ làm SDK không nhận ra portal.
  • Iframe lồng nhau. Nếu engine tự tạo thêm một iframe con và nạp SDK trong đó, window.parent là trang game chứ không phải portal. Nạp SDK và gọi init() ở trang index.html gốc của bundle.
  • Hai bản SDK. Nạp cả thẻ script lẫn một bản đóng gói khác của SDK tạo ra hai instance; chỉ instance bạn gọi init() mới kết nối. Chỉ dùng thẻ script.

API bị từ chối với not_initialized hoặc bị bỏ qua

Lời gọi trước khi init() xong (kể cả trong lúc init() đang chạy) không có tác dụng: hàm bất đồng bộ reject với not_initialized, còn loadingFinished(), gameplayStart(), track() bị bỏ qua lặng lẽ. Hậu quả hay gặp là điểm bị từ chối no_gameplay vì gameplayStart() đã bị bỏ qua. Luôn await WSG.init() trước, và bật debug để thấy cảnh báo.

WSG is not defined

Thẻ script SDK chưa chạy khi code game chạy: thiếu thẻ, đặt sau script game, có thuộc tính async, hoặc file SDK không tải được (404). Đặt <script src="/sdk/v1/wsg-sdk.js"></script> trước script game, không dùng async. Khi chạy trên máy, nhớ có bản SDK ở đúng /sdk/v1/ (xem Kiểm thử).

Nhúng và tải

Console báo "Refused to frame… frame-ancestors"

Origin game gửi Content-Security-Policy: frame-ancestors <origin portal>, nên chỉ portal được nhúng game. Nhúng URL game vào trang thử nghiệm của bạn hay website khác sẽ bị trình duyệt chặn. Để kiểm thử, mở thẳng URL game (chạy standalone) hoặc dùng server riêng trên máy.

Mixed content: tài nguyên http:// bị chặn

Portal và origin game chạy https, nên mọi tài nguyên http:// bị chặn. Lỗi hay gặp nhất là bản build còn trỏ SDK tới http://localhost:4000/... (giá trị mặc định khi chạy Vite của template). Bản nộp phải dùng /sdk/v1/wsg-sdk.js và đường dẫn tương đối cho mọi asset.

Overlay "Đang tải game" không biến mất, hoặc 30 giây sau mới biến mất

Portal chỉ gỡ overlay khi nhận loadingFinished(), hoặc tự gỡ sau 30 giây. Kiểm tra game có gọi WSG.loadingFinished() sau khi init() xong, khi màn hình đầu tiên đã sẵn sàng.

Asset 404 trên portal nhưng chạy tốt ở máy

Thường do đường dẫn tuyệt đối (/assets/... trỏ về gốc origin thay vì thư mục của phiên bản), sai hoa thường (origin game phân biệt hoa thường), hoặc file nằm ngoài thư mục build. Kiểm thử với cấu trúc giống production để bắt sớm.

Âm thanh và hiển thị

Không có âm thanh, nhất là trên iPhone

  • Trình duyệt chỉ cho phát âm thanh sau một thao tác của người chơi. Tạo hoặc resume() AudioContext ngay trong xử lý chạm đầu tiên.
  • Kiểm tra game có đang tắt tiếng vì audio(false) hay pause mà chưa bật lại khi audio(true)/resume.
  • Trên iPhone, khi gạt công tắc im lặng, âm thanh Web Audio cũng bị tắt.

Trang bị cuộn, zoom hoặc chọn chữ khi chạm

Thêm meta viewport user-scalable=no và CSS overflow: hidden; touch-action: none; user-select: none như ở Yêu cầu với game.

Game không dừng khi chuyển tab

requestAnimationFrame tự ngừng khi tab ẩn, nhưng setInterval, setTimeout và âm thanh thì không. Nghe WSG.on('pause') và WSG.on('resume') để dừng mọi thứ; SDK phát hai sự kiện này cả trong portal lẫn ở standalone.

Điểm

Điểm bị từ chối no_gameplay

Server không thấy gameplay đang chạy, hoặc gameplayStop() đã quá 10 giây. Kiểm tra: gameplayStart() được gọi sau khi init() xong; điểm được nộp ngay sau gameplayStop(); game không gửi quá nhiều track() (server nhận tối đa 60 sự kiện mỗi phút cho mỗi phiên, kể cả gameplay_start).

Điểm bị từ chối too_fast

Thời gian từ lần gameplayStart() gần nhất tới lúc nộp nhỏ hơn limits.minDurationMs. Đặt minDurationMs bằng thời gian nhanh nhất một lượt hợp lệ có thể kết thúc, và đừng gọi lại gameplayStart() ngay trước khi nộp (ví dụ sau khi hồi sinh rồi chết ngay).

Điểm bị từ chối rate_too_high

Điểm chia cho số giây chơi vượt limits.maxScorePerSecond. Tính lại tốc độ ghi điểm tối đa từ thiết kế và nhân 1,5. Nếu chỉ bị sau khi hồi sinh hay tạm dừng, đó là vì thời gian được tính từ lần gameplayStart() gần nhất: xem Thời gian chơi khi tạm dừng và hồi sinh.

Điểm bị từ chối invalid_score hoặc over_max

invalid_score: điểm có phần thập phân, âm hoặc quá 2^53 — làm tròn bằng Math.floor() trước khi nộp. over_max: điểm lớn hơn limits.maxScore — kẹp điểm trong game, hoặc nâng maxScore ở phiên bản sau nếu thiết kế cho phép.

Standalone nhận điểm nhưng portal từ chối

Chế độ standalone không chạy luật chống gian lận nào. Luôn kiểm tra điểm trên portal thử nghiệm, xem Luật chống gian lận.

Nộp điểm thành công mà bảng xếp hạng không đổi

Mỗi người chơi chỉ có một dòng mỗi kỳ là điểm tốt nhất, nên điểm thấp hơn kỷ lục không thay đổi gì (improved: false). Với scoreOrder: "asc", điểm thấp hơn mới là tốt hơn. Kỳ ngày và tuần đặt lại lúc 00:00 giờ Việt Nam.

Khác

Dữ liệu WSG.storage biến mất

Dữ liệu gắn với tài khoản khách, mà tài khoản khách gắn với cookie của trình duyệt. Xoá cookie, dùng chế độ ẩn danh hoặc đổi máy là một người chơi mới với kho trống.

share() trả ok: false

Người chơi đã huỷ bảng chia sẻ, trình duyệt không có navigator.share và không cho ghi clipboard, hoặc lời gọi không nằm ngay trong xử lý chạm/click. Nếu text dài quá 280 ký tự hay url không phải URL tuyệt đối, Promise reject với invalid_argument thay vì trả ok: false.

getToken() trả null

Token chỉ có trong portal. Ở standalone và disabled, getToken() luôn trả null.

Game có được gọi server riêng không?

Mặc định là không: bundle chỉ được tải tài nguyên từ chính nó và file SDK. Nếu game cần server riêng, hãy thoả thuận trước với đội vận hành và xác thực người chơi bằng token của portal; xem Yêu cầu với game và Xác thực người chơi.