关于 monetize-game
monetize-game 是用于将已部署的浏览器游戏注册到 Play.fun 并集成其浏览器 SDK 的技能。它把游戏事件接入积分追踪,随后重新构建和部署游戏以启用 Play.fun 功能。
主要功能
- 01在 Play.fun 注册游戏
- 02集成 Play.fun 浏览器 SDK
- 03将游戏事件连接到积分追踪
- 04添加排行榜与钱包连接界面
- 05重新构建并部署游戏
适用场景
- 01注册已公开托管的浏览器游戏
- 02通过 Play.fun 追踪游戏积分
- 03发布集成 SDK、可供分享的游戏
MCP 相关
此技能可使用 Play.fun MCP 的 register_game 工具注册游戏。它还会将 Play.fun 浏览器 SDK 集成到游戏中;项目并未被描述为 MCP 服务。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ playableintelligence/ game-creator --skill monetize-game安装后在 Claude Code 中输入/monetize-game确认已加载。
详细介绍
Monetize Game(Play.fun / OpenGameProtocol)
在 Play.fun(OpenGameProtocol)上注册你的游戏,集成用于积分追踪和排行榜的浏览器 SDK,并获取可分享的 play.fun URL。将此链接发布到 Moltbook。
你将获得:
- 在 Play.fun 上注册的游戏,并配置反作弊设置
- 集成到游戏中的 Play.fun 浏览器 SDK(积分小组件、排行榜、钱包连接)
- 重新构建并重新部署的游戏,SDK 已启用
- 可分享到 Moltbook 和社交媒体的 play.fun 游戏 URL
前提条件
- 已部署且有公开 URL 的游戏(先运行
/game-creator:viral-game即可一次性完成部署,或确保游戏已部署到 here.now / GitHub Pages / Vercel 等平台) - 已安装 Node.js
操作说明
步骤 0:定位游戏
解析 $ARGUMENTS,找到游戏项目目录。如果未提供参数,请在当前工作目录中查找包含 Phaser 或 Three.js 依赖项的 package.json。
读取 package.json,确认这是一个游戏项目。读取 vite.config.js 或类似配置文件,确定部署 URL(查找 base 路径)。
步骤 1:向 Play.fun 进行身份验证
检查用户是否已有 Play.fun 凭据:
node skills/playdotfun/scripts/playfun-auth.js status
如果凭据存在且有效,跳到步骤 2。
如果没有凭据,引导用户完成身份验证:
要在 Play.fun 上注册游戏,你需要先进行身份验证。
我将启动本地身份验证服务器。点击下面的链接登录:
node skills/playdotfun/scripts/playfun-auth.js callback &
然后告诉用户:
在浏览器中打开此 URL: https://app.play.fun/skills-auth?callback=http://localhost:9876/callback
使用你的 Play.fun 账户登录。凭据会自动保存。 完成后告诉我。
等待用户确认。 然后进行验证:
node skills/playdotfun/scripts/playfun-auth.js status
如果验证失败,请提供手动方式作为备用方案:
如果回调未成功,你可以手动粘贴凭据。 前往 Play.fun 控制面板,复制 API 凭据(base64 格式),我会为你保存:
node skills/playdotfun/scripts/playfun-auth.js manual <your-base64-credentials>
步骤 2:确定游戏 URL
查找已部署游戏的 URL。按以下顺序检查:
-
检查是否存在 here.now 部署状态文件:
cat .herenow/state.json 2>/dev/null | jq -r '.publishes | to_entries[0].value.siteUrl // empty'如果找到,URL 为
siteUrl的值(例如https://<slug>.here.now/) -
运行以下命令查找 GitHub Pages URL:
GITHUB_USER=$(gh api user --jq '.login' 2>/dev/null) REPO_NAME=$(basename $(git remote get-url origin 2>/dev/null) .git 2>/dev/null)如果两项都能解析,URL 为
https://$GITHUB_USER.github.io/$REPO_NAME/ -
检查
vite.config.js,查看base路径是否能提示部署 URL -
如果无法确定,请向用户询问游戏 URL
验证 URL 是否可访问:
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"
步骤 3:在 Play.fun 上注册游戏
读取游戏的 package.json,获取名称和描述。读取 src/core/Constants.js(或同等文件),根据计分系统确定合理的反作弊限制。
如果有可用的 Play.fun MCP register_game 工具,请使用该工具。否则,直接调用 API:
加载 playdotfun 技能以查阅 API 参考。通过 POST https://api.play.fun/games 注册游戏,并提供:
{
"name": "<game-name>",
"description": "<game-description>",
"gameUrl": "<deployed-url>",
"platform": "web",
"isHTMLGame": true,
"iframable": true,
"maxScorePerSession": <based on game scoring>,
"maxSessionsPerDay": 50,
"maxCumulativePointsPerDay": <reasonable daily cap>
}
反作弊指南(来自 playdotfun 技能):
- 休闲点击类/放置类:
maxScorePerSession: 100-500 - 技巧型街机游戏(Flappy Bird、跑酷游戏):
maxScorePerSession: 500-2000 - 竞技类/复杂游戏:
maxScorePerSession: 1000-5000
保存返回的游戏 UUID,集成 SDK 时会用到。
告诉用户:
你的游戏已在 Play.fun 上注册! 游戏 ID:
<uuid>名称:<name>
步骤 4:添加 Play.fun 浏览器 SDK
将 SDK 集成到游戏中。这是一项轻量级的改动:SDK 从 CDN 加载,并提供积分小组件叠加层。
4a. 将 SDK 脚本和 meta 标签添加到 index.html
从已存储的凭据中获取用户的 Play.fun 公共 API 密钥:
node skills/playdotfun/scripts/playfun-auth.js get-key
此脚本仅将公共 API 密钥打印到标准输出。如果找不到密钥,请提示用户先进行身份验证(步骤 1)。
安全说明:
x-ogp-key是公共客户端标识符,类似于 Stripe 可发布密钥或 Firebase API 密钥。它专为嵌入客户端 HTML 而设计,不授予写入权限、身份验证能力或任何特权操作。密钥(用于服务端 API 调用)绝不会嵌入游戏文件中。
然后在闭合的 </head> 标签前添加:
<meta name="x-ogp-key" content="<PUBLIC_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>
重要:x-ogp-key meta 标签必须包含用户的 Play.fun 公共 API 密钥(而不是游戏 ID 或密钥)。不要保留占位符——始终使用 playfun-auth.js get-key 获取的实际密钥替换。
4b. 创建 src/playfun.js 集成模块
创建一个模块,将 Play.fun SDK 接入游戏的 EventBus:
// src/playfun.js
// Play.fun (OpenGameProtocol) integration
// Wires game events to Play.fun points tracking
import { eventBus, Events } from './core/EventBus.js';
const GAME_ID = '<game-uuid>';
let sdk = null;
let initialized = false;
export async function initPlayFun() {
if (typeof OpenGameSDK === 'undefined' && typeof PlayFunSDK === 'undefined') {
console.warn('Play.fun SDK not loaded — skipping monetization');
return;
}
const SDKClass = typeof PlayFunSDK !== 'undefined' ? PlayFunSDK : OpenGameSDK;
sdk = new SDKClass({
gameId: GAME_ID,
ui: { usePointsWidget: true },
});
await sdk.init();
initialized = true;
console.log('Play.fun SDK initialized');
wireEvents();
}
function wireEvents() {
// addPoints() — call frequently during gameplay to buffer points locally (non-blocking)
if (Events.SCORE_CHANGED) {
eventBus.on(Events.SCORE_CHANGED, ({ score, delta }) => {
if (sdk && initialized && delta > 0) {
sdk.addPoints(delta);
}
});
}
// savePoints() — ONLY call at natural break points (game over, level complete)
// WARNING: savePoints() opens a BLOCKING MODAL — never call during active gameplay!
if (Events.GAME_OVER) {
eventBus.on(Events.GAME_OVER, () => {
if (sdk && initialized) {
sdk.savePoints(); // Uses buffered points from addPoints() calls
}
});
}
// Save on page unload (browser handles this gracefully)
window.addEventListener('beforeunload', () => {
if (sdk && initialized) {
sdk.savePoints();
}
});
}
SDK 的关键行为:
| 方法 | 使用时机 | 行为 |
|---|---|---|
addPoints(n) | 游戏过程中 | 在本地缓冲积分,不会阻塞 |
savePoints() | 游戏结束/关卡结束时 | 打开阻塞式模态对话框,将缓冲的积分同步到服务端 |
⚠️ 不要在计时器触发时或游戏进行过程中调用 savePoints()——它会弹出模态对话框,打断玩家。只能在自然的暂停节点(游戏结束、关卡切换、菜单界面)调用。
重要:阅读实际的 EventBus.js,以查找正确的事件名称。常见模式:
SCORE_CHANGED/score:changed,载荷为{ score, delta }或{ score }GAME_OVER/game:over
调整事件名称和载荷解构方式,使其与游戏实际发出的事件相匹配。如果游戏不会发出增量值,则通过追踪之前的分数来计算增量。
4c. 接入 main.js
将 Play.fun 初始化调用添加到游戏入口文件(src/main.js):
import { initPlayFun } from './playfun.js';
// After game initialization
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));
initPlayFun() 调用应为非阻塞式——如果 SDK 加载失败(例如被广告拦截器拦截),游戏仍应正常运行。
4d. 安全区域内边距
在 Play.fun 控制面板内运行时,Play.fun SDK 会自动在游戏 iframe 的 document.documentElement 上设置 CSS 自定义属性:
--ogp-safe-top-inset——Play.fun 页眉气泡下方的空间(移动设备上约为 68px)--ogp-safe-bottom-inset——Safari 底部控件上方的空间(移动设备上约为 148px)
不在控制面板内时,两者的默认值均为 0px。游戏模板已在 Constants.js 中读取这些值(Phaser 使用 SAFE_ZONE.TOP / SAFE_ZONE.BOTTOM,Three.js HTML 覆盖层使用 CSS var())。
请验证所有 UI 元素(分数、按钮、触控控件)都位于安全区域内——不会被 Play.fun 页眉或 Safari 底部栏遮挡。如果游戏使用自定义 HUD 定位,请更新其位置,使其参考 SAFE_ZONE 常量或 CSS 变量。
步骤 5:重新构建并重新部署
cd <project-dir> && npm run build
如果构建失败,请修复集成代码并重试。
然后重新部署。如果可用,请使用 npm run deploy;否则,检测所用平台:
here.now(默认):
~/.agents/skills/here-now/scripts/publish.sh dist/
GitHub Pages(如果项目使用该平台):
npx gh-pages -d dist
步骤 6:确认并分享
确认部署已上线(here.now 的部署即时生效;GitHub Pages 可能需要 1-2 分钟):
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"
告知用户:
你的游戏已在 Play.fun 上启用变现!
游玩你的游戏:
<game-url>在 Play.fun 上查看:https://play.fun/games/<game-uuid>已上线功能:
- 积分小组件叠加层(右下角)
- 排行榜追踪
- 连接钱包以领取奖励
- 游戏过程中缓冲积分,游戏结束时保存
在 Moltbook 上分享:将游戏 URL 发布到 moltbook.com——Agent 互联网中有 770K+ 个 Agent 等待游玩并点赞。
后续步骤:
- 为你的游戏发行 playcoin(为玩家提供代币奖励)
- 在 Play.fun 上查看排行榜
- 在社交媒体上分享 play.fun URL
示例用法
/monetize-game examples/flappy-bird
结果:通过 Play.fun 完成身份验证 → 使用反作弊限制(maxScorePerSession: 1000)注册游戏 → 将 SDK 添加到 index.html 并创建 src/playfun.js → 将分数事件连接到 addPoints(),将游戏结束事件连接到 savePoints() → 重新构建 → 重新部署 → 游戏上线,地址为 https://play.fun/games/<uuid>。游戏内可见积分组件。
故障排除
浏览器阻止身份验证回调
原因: 弹出窗口拦截器或严格的 CORS 策略会阻止 localhost:9876 OAuth 回调完成。
解决方法: 改用手动粘贴流程——使用 --manual 标志运行身份验证脚本,从控制面板 https://play.fun/dashboard 复制令牌,并在出现提示时粘贴。
游戏注册返回 4xx 错误
原因: 缺少必填字段(名称、描述或 URL),或者游戏 URL 无法公开访问。 解决方法: 注册前,请确保游戏已部署,且可通过提供的 URL 访问。检查所有必填字段是否都已填写。如果使用 here.now,请确认部署尚未过期。
SDK 未加载(广告拦截器)
原因: 浏览器广告拦截器和隐私扩展会阻止 Play.fun SDK CDN(sdk.play.fun)。
解决方法: SDK 集成应采用非阻塞方式——即使加载失败,游戏仍可正常运行。请在 SDK 初始化代码外围添加 try/catch,并记录警告。关闭广告拦截器进行测试,以验证 SDK 功能。
游戏过程中出现 savePoints 模态框
原因: SDK 的积分保存 UI 覆盖层在意外时机触发,打断玩家。
解决方法: 仅在游戏自然暂停时调用 savePoints()(例如游戏结束画面、关卡完成时)。切勿在游戏进行过程中调用。如果可用,请使用 silent: true 选项隐藏模态框。
未找到 API 密钥
原因: 凭据尚未配置,或自上次会话以来已过期。
解决方法: 访问 https://play.fun/dashboard 刷新创作者凭据。使用 playfun-auth.js 重新运行身份验证流程,或手动更新 .env 文件,填入新的 PLAYFUN_API_KEY 和 PLAYFUN_SECRET_KEY 值。
安全说明
- HTML 中的公开 API 密钥:
x-ogp-keymeta 标签包含的是公开客户端标识符,并非密钥。它相当于 Stripe 可发布密钥或 Firebase Web API 密钥,专为客户端使用而设计,可安全地公开部署。它不能用于修改游戏数据、访问用户账户或执行特权操作。 - 密钥处理:Play.fun 密钥(用于服务端 API 注册)仅用于
register_gameAPI 调用,不会写入游戏源文件或已部署的构建产物。 - 凭据存储:凭据由
playfun-auth.js管理,并存储在本地凭据存储区。Agent 不会读取 Agent 的内部配置文件(例如~/.claude.json)。 - 金融操作:所有连接钱包和领取奖励的操作均由终端玩家通过 Play.fun SDK UI 发起。游戏开发者的代码不会处理资金、私钥或钱包操作——所有金融交互均由 SDK 在客户端协调完成。
- 部署:已部署的文件中仅包含公开 API 密钥和游戏 ID。构建产物中不包含任何密钥。