mcpField
Y
yandex-js-sdk

yandex-js-sdk

github.com/hungify开发016 次安装1 周前更新
代码仓库

关于 yandex-js-sdk

面向在 Yandex Games 发布游戏的开发者,介绍 JavaScript SDK 的接入,涵盖玩家数据、存储、变现、排行榜、生命周期事件和平台功能。

主要功能

  1. 01SDK 初始化与生命周期处理
  2. 02玩家授权与云存储
  3. 03插屏、激励和横幅广告
  4. 04应用内购买
  5. 05排行榜与远程配置
  6. 06评价提示与游戏发现

适用场景

  1. 01接入 Yandex 玩家资料与存档数据
  2. 02在游戏过程中管理广告
  3. 03提供游戏内购买
  4. 04提交分数并读取远程标记

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/hungify/playables-skills --skill yandex-js-sdk

安装后在 Claude Code 中输入/yandex-js-sdk确认已加载。

详细介绍

Yandex Games JavaScript SDK

概述

Yandex Games 会在 yandex.<tld>/games 的框架中加载游戏。SDK 脚本加载后,宿主会注入全局变量 YaGames。主要功能领域包括:

  • 生命周期与环境:初始化、LoadingAPI.ready()、GameplayAPI.start/stop、暂停/恢复事件、设备信息、语言/TLD
  • 玩家与存储:授权、个人资料数据、按玩家隔离的键值云存储
  • 变现:全屏插屏广告、激励视频、常驻横幅广告、应用内购买
  • 互动:排行榜、远程配置标记、评价提示、添加到主屏幕的快捷方式、服务器时间、剪贴板写入、通过 GamesAPI 进行交叉推广

SDK 不提供社交分享、邀请好友、加入社区、添加到收藏、成就或统计计数器功能。

安装

<script src="https://yandex.ru/games/sdk/v2"></script>

如果动态加载,请轮询,直到定义了 window.YaGames && window.YaGames.init,然后再调用 init()。

快速开始 / 集成工作流

首次接入 SDK 时,请按此顺序操作。每完成一步都先验证,再继续下一步。

async function bootstrap() {
    // 1. Ensure SDK script has loaded
    if (!window.YaGames?.init) {
        throw new Error('YaGames SDK not available — check that the script tag loaded');
    }

    // 2. Initialize — all subsequent SDK calls require ysdk
    const ysdk = await window.YaGames.init();

    // 3. Bind host-driven pause / resume before anything else
    ysdk.on('game_api_pause', () => {
        /* mute audio, pause gameplay */
    });
    ysdk.on('game_api_resume', () => {
        /* unmute, resume */
    });

    // 4. Signal that the loading screen is gone
    ysdk.features.LoadingAPI?.ready();

    // 5. Read environment (language, device type)
    const env = readEnvironment(ysdk);

    // 6. Fetch player — fall back to guest mode if not authorized
    let player = null;
    let storageCache = {};
    try {
        const info = await getPlayer(ysdk);
        player = info.raw;
        if (info.isAuthorized) {
            storageCache = await loadStorage(player);
        }
    } catch (err) {
        console.warn('Player init failed, continuing as guest:', err);
    }

    // 7. Start gameplay tracking
    onGameplayStarted(ysdk);

    return { ysdk, player, storageCache, env };
}
function readEnvironment(ysdk) {
    return {
        language: ysdk.environment.i18n.lang.toLowerCase(),
        tld: ysdk.environment.i18n.tld.toLowerCase(),
        deviceType: ysdk.deviceInfo.type // 'desktop' | 'mobile' | 'tablet' | 'tv'
    };
}

玩家 / 授权

async function getPlayer(ysdk, { useSignedData = false } = {}) {
    const player = await ysdk.getPlayer({ signed: useSignedData });

    return {
        raw: player,
        id: player.getUniqueID(),
        name: player.getName() || null,
        photos: ['small', 'medium', 'large'].map((size) => player.getPhoto(size)).filter(Boolean),
        isAuthorized: player.isAuthorized(),
        payingStatus: player.getPayingStatus(),
        signature: useSignedData ? player.signature : null
    };
}

async function authorizePlayer(ysdk, options) {
    const current = await getPlayer(ysdk, options);
    if (current.isAuthorized) {
        return current;
    }

    await ysdk.auth.openAuthDialog();
    return getPlayer(ysdk, options);
}

存储

每次调用都会写入整个文档——没有按键操作的 API。请在客户端缓存最新快照,并在每次保存前合并数据。访客则使用 localStorage 作为回退方案。

async function loadStorage(player) {
    return player.getData();
}

async function setStorage(player, cache, key, value) {
    const data = { ...(cache || {}) };
    if (Array.isArray(key)) {
        for (let i = 0; i < key.length; i++) {
            data[key[i]] = value[i];
        }
    } else {
        data[key] = value;
    }
    await player.setData(data);
    return data;
}

async function deleteStorage(player, cache, key) {
    const data = { ...(cache || {}) };
    if (Array.isArray(key)) {
        for (let i = 0; i < key.length; i++) {
            delete data[key[i]];
        }
    } else {
        delete data[key];
    }
    await player.setData(data);
    return data;
}

function getCached(cache, key) {
    if (Array.isArray(key)) {
        return key.map((k) => (typeof cache?.[k] === 'undefined' ? null : cache[k]));
    }
    return typeof cache?.[key] === 'undefined' ? null : cache[key];
}

广告

全屏插屏广告

onClose(wasShown) 在正常关闭和 SDK 跳过广告时都会触发。将 wasShown === false 视为失败。

function showInterstitial(ysdk, handlers = {}) {
    ysdk.adv.showFullscreenAdv({
        callbacks: {
            onOpen: () => handlers.onOpen?.(),
            onClose: (wasShown) => handlers.onClose?.(wasShown),
            onError: (err) => handlers.onError?.(err)
        }
    });
}
激励视频

在 onRewarded 中发放奖励。广告界面关闭后会触发 onClose。

function showRewarded(ysdk, handlers = {}) {
    ysdk.adv.showRewardedVideo({
        callbacks: {
            onOpen: () => handlers.onOpen?.(),
            onRewarded: () => handlers.onRewarded?.(),
            onClose: () => handlers.onClose?.(),
            onError: (err) => handlers.onError?.(err)
        }
    });
}
常驻横幅广告
async function getBannerStatus(ysdk) {
    return ysdk.adv.getBannerAdvStatus(); // { stickyAdvIsShowing: boolean, reason?: string }
}

async function showBanner(ysdk) {
    const data = await ysdk.adv.showBannerAdv();
    return data.stickyAdvIsShowing === true;
}

async function hideBanner(ysdk) {
    const data = await ysdk.adv.hideBannerAdv();
    return data.stickyAdvIsShowing === false;
}

支付 / IAP

初始化后获取一次支付对象。传入 signed: true,以便后端验证签名。

async function initPayments(ysdk, { signed = true } = {}) {
    return ysdk.getPayments({ signed });
}
商品目录
async function getCatalog(payments) {
    const products = await payments.getCatalog();
    return products.map((p) => ({
        id: p.id,
        title: p.title,
        description: p.description,
        imageURI: p.imageURI,
        price: p.price,
        priceValue: p.priceValue,
        priceCurrencyCode: p.priceCurrencyCode,
        priceCurrencyImage: p.getPriceCurrencyImage?.('medium')
    }));
}
购买
async function purchase(payments, productId, payload) {
    const result = await payments.purchase({ id: productId, developerPayload: payload });
    if (!result?.purchaseData?.purchaseToken) {
        throw new Error(
            `Purchase succeeded but returned no purchaseToken for product ${productId}`
        );
    }
    return { id: productId, ...result.purchaseData };
}
已拥有的购买项目
async function getPurchases(payments) {
    const list = await payments.getPurchases();
    return list.map((purchase) => ({ id: purchase.productID, ...purchase.purchaseData }));
}
消耗

消耗前请确认已发放物品。消耗失败时,令牌会保留,以便重试。

async function consumePurchase(payments, purchaseToken) {
    if (!purchaseToken) {
        throw new Error('consumePurchase: purchaseToken is required');
    }
    await payments.consumePurchase(purchaseToken);
}

排行榜

排行榜在开发者控制台中配置,并通过字符串 ID 引用。设置分数需要玩家已授权。

async function setLeaderboardScore(ysdk, leaderboardId, score) {
    const lb = await ysdk.getLeaderboards();
    await lb.setLeaderboardScore(leaderboardId, score);
}

async function getLeaderboardEntries(ysdk, leaderboardId, { isPlayerAuthorized = false } = {}) {
    const lb = await ysdk.getLeaderboards();

    const options = { quantityTop: 20 };
    if (isPlayerAuthorized) {
        options.includeUser = true;
        options.quantityAround = 3;
    }

    const result = await lb.getLeaderboardEntries(leaderboardId, options);
    if (!result || result.entries.length === 0) {
        return [];
    }

    return result.entries.map((e) => ({
        id: e.player.uniqueID,
        name: e.player.publicName,
        score: e.score,
        rank: e.rank,
        photo: e.player.getAvatarSrc('large')
    }));
}

远程配置

ysdk.getFlags(options) 会返回一个扁平的 { key: stringValue } 映射。

async function getRemoteConfig(ysdk, options = {}) {
    const params = { clientFeatures: [], ...options };
    return ysdk.getFlags(params);
}

服务器时间

function getServerTime(ysdk) {
    // Returns Unix timestamp in milliseconds
    return ysdk.serverTime();
}

社交

评价提示
async function requestReview(ysdk) {
    const can = await ysdk.feedback.canReview();
    if (!can.value) {
        throw new Error(can.reason || 'review not available');
    }
    const { feedbackSent } = await ysdk.feedback.requestReview();
    return feedbackSent;
}
添加到主屏幕(快捷方式)
async function canAddToHomeScreen(ysdk) {
    const prompt = await ysdk.shortcut.canShowPrompt();
    return prompt.canShow === true;
}

async function addToHomeScreen(ysdk) {
    const result = await ysdk.shortcut.showPrompt();
    return result.outcome === 'accepted';
}

剪贴板

async function clipboardWrite(ysdk, text) {
    await ysdk.clipboard.writeText(text);
}

游戏生命周期、暂停与音频

每次活跃的游戏会话都应以 GameplayAPI.start() 开始、以 GameplayAPI.stop() 结束,以控制广告节奏并进行数据分析。

function onGameplayStarted(ysdk) {
    ysdk.features.GameplayAPI?.start();
}

function onGameplayStopped(ysdk) {
    ysdk.features.GameplayAPI?.stop();
}
function bindPauseHandlers(ysdk, { onPause, onResume }) {
    ysdk.on('game_api_pause', () => onPause?.());
    ysdk.on('game_api_resume', () => onResume?.());
}

对于 Yandex 未通知的可见性/焦点变化,也应结合这些 SDK 回调,使用 document.visibilitychange、window.blur 和 window.focus 进行处理。

其他 Yandex Games 游戏目录(可选)

features.GamesAPI 是否可用取决于具体游戏;调用前请检查该命名空间是否存在。

async function getAllGames(ysdk) {
    const { games } = await ysdk.features.GamesAPI.getAllGames();
    return games;
}

async function getGameById(ysdk, gameId) {
    const { game, isAvailable } = await ysdk.features.GamesAPI.getGameByID(gameId);
    return { ...game, isAvailable };
}

评论

支持 Markdown