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.environmentluôn làstandalone. Đọc sauawait WSG.init(), hoặc dùngctx.environment. - Bắt tay quá 3 giây. SDK gửi
inittới portal và chờ 3 giây; không có trả lời thì chuyển sang standalone. Khởi tạo vớidebug: truesẽ 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=1vàhost=<origin portal>trong URL. Game chuyển hướng sang trang khác trong bundle, hoặc xoá query bằnghistory.replaceState()trước khiinit(), 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.parentlà trang game chứ không phải portal. Nạp SDK và gọiinit()ở trangindex.htmlgố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()AudioContextngay trong xử lý chạm đầu tiên. - Kiểm tra game có đang tắt tiếng vì
audio(false)haypausemà chưa bật lại khiaudio(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.