SDK p2game

Đăng nhập, bảng xếp hạng và lưu tiến trình đám mây — mà game không bao giờ chạm vào thông tin đăng nhập của người chơi.

Mô hình bảo mật

Game chạy trong iframe trên một tên miền riêng và không giữ token của người chơi. SDK gửi yêu cầu qua postMessage tới khung nền tảng; khung đó mới là nơi cầm phiên đăng nhập và gọi API. Ba hệ quả đáng chú ý:

  • · Một game bị chèn mã độc vẫn không lấy được danh tính người chơi.
  • · Điểm số không bịa được bằng cách gọi thẳng API: mỗi ván có một phiên do nền tảng cấp, có hạn dùng.
  • · Game không cần và không được tự dựng biểu mẫu đăng nhập.

Cài đặt

Cách đơn giản nhất là nhúng bản UMD, phục vụ ngay từ cùng origin với game nên không vướng chính sách bảo mật:

<!-- Nhúng trực tiếp, không cần bundler -->
<script src="https://games.p2game.io/sdk/p2game-sdk.v1.js"></script>
<script src="game.js"></script>

Nếu bạn dùng bundler:

npm install @p2game/sdk

import { p2 } from "@p2game/sdk";

Khởi động

// Gọi sớm nhất có thể, trước khi nạp tài nguyên nặng.
const ctx = await p2.init();

console.log(ctx.player);       // null nếu người chơi chưa đăng nhập
console.log(ctx.leaderboards); // các bảng đã khai trong p2game.json

// Khi khung hình đầu tiên đã vẽ xong — nền tảng gỡ màn hình chờ.
p2.ready();
Quên gọi p2.ready() thì nền tảng phải hiển thị màn hình chờ cho tới khi hết thời gian, và bước chạy thử sẽ ghi cảnh báo SD-004.

Bảng xếp hạng

Bảng phải được khai trong p2game.json trước. Máy chủ kiểm bốn lớp trước khi nhận điểm: phiên chơi hợp lệ, điểm không vượt maxScore, ván đủ dài theo minSessionSeconds, và không vượt hạn mức tần suất.

// Gửi điểm khi kết thúc ván
const result = await p2.leaderboard.submit("high-score", score, {
  level: currentLevel,       // siêu dữ liệu tuỳ ý, tối đa 1 KB
});

if (result.improved) {
  p2.ui.toast(`Kỷ lục mới: ${result.personalBest}`, "success");
}

// Đọc bảng xếp hạng
const top = await p2.leaderboard.top("high-score", { limit: 10, window: "WEEKLY" });
const near = await p2.leaderboard.aroundMe("high-score", { range: 3 });

Đăng nhập

const player = await p2.auth.getUser();

if (!player) {
  // KHÔNG tự dựng form đăng nhập. Nền tảng sẽ mở hộp thoại của nó.
  const loggedIn = await p2.auth.requestLogin();
}

p2.auth.onChange((player) => {
  updateHud(player?.displayName ?? "Khách");
});

Lưu tiến trình đám mây

Tối đa 32 khoá cho mỗi người chơi mỗi game, 64 KB một khoá, tổng 512 KB. Cần khai quyền persistent-storage và bật sdk.cloudSave.

// Lưu tiến trình, đồng bộ giữa mọi thiết bị của người chơi
await p2.storage.set("progress", { level: 7, coins: 1280 });

const saved = await p2.storage.get("progress");
const keys = await p2.storage.keys();
await p2.storage.remove("progress");

Vòng đời

p2.onPause(() => engine.pause());     // người chơi chuyển tab
p2.onResume(() => engine.resume());
p2.onBeforeUnload(() => saveNow());   // cơ hội cuối để lưu

p2.gameOver({ score, level });        // kết thúc một ván

Xử lý lỗi

Mọi lỗi từ SDK là P2GameError với trường code ổn định.

try {
  await p2.leaderboard.submit("high-score", score);
} catch (err) {
  switch (err.code) {
    case "UNAUTHENTICATED": promptLogin(); break;
    case "SCORE_REJECTED":  console.warn(err.message); break;
    case "RATE_LIMITED":    retryAfter(err.retryAfterMs); break;
    default:                console.error(err.message);
  }
}

Chạy thử khi chưa lên nền tảng

Khi mở game ngoài khung nền tảng (ví dụ chạy dev server ở máy bạn), SDK tự chuyển sang chế độ giả lập: điểm số và cloud save lưu vào localStorage, mọi lời gọi vẫn trả về đúng hình dạng dữ liệu. Bạn không phải viết nhánh mã riêng cho lúc phát triển.