monetize-game
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMonetize Game (Play.fun / OpenGameProtocol)
游戏变现(Play.fun / OpenGameProtocol)
Register your game on Play.fun (OpenGameProtocol), integrate the browser SDK for points tracking and leaderboards, and get a shareable play.fun URL. This is the link you post to Moltbook.
What you'll get:
- Your game registered on Play.fun with anti-cheat config
- The Play.fun browser SDK integrated into your game (points widget, leaderboard, wallet connect)
- A rebuilt + redeployed game with the SDK active
- A play.fun game URL to share on Moltbook and social media
在Play.fun(OpenGameProtocol)上注册你的游戏,集成用于积分追踪和排行榜的浏览器SDK,获取可分享的play.fun链接。你可以将该链接发布到Moltbook。
你将获得:
- 你的游戏已在Play.fun上注册并配置反作弊规则
- 游戏中已集成Play.fun浏览器SDK(积分组件、排行榜、钱包连接功能)
- 已重新构建并部署激活SDK后的游戏
- 可在Moltbook和社交媒体分享的play.fun游戏链接
Prerequisites
前置条件
- A deployed game with a public URL (run first, or ensure your game is deployed to GitHub Pages / Vercel / etc.)
/game-creator:make-game - Node.js installed
- 已部署且拥有公共URL的游戏(先运行,或确保你的游戏已部署到GitHub Pages / Vercel等平台)
/game-creator:make-game - 已安装Node.js
Instructions
操作步骤
Step 0: Locate the game
步骤0:定位游戏
Parse to find the game project directory. If not provided, check the current working directory for a with Phaser or Three.js dependencies.
$ARGUMENTSpackage.jsonRead to confirm this is a game project. Read or similar to determine the deployed URL (look for path).
package.jsonvite.config.jsbase解析以找到游戏项目目录。如果未提供该参数,检查当前工作目录中是否存在包含Phaser或Three.js依赖的文件。
$ARGUMENTSpackage.json读取确认这是一个游戏项目。读取或类似文件以确定部署URL(查找路径)。
package.jsonvite.config.jsbaseStep 1: Authenticate with Play.fun
步骤1:通过Play.fun认证
Check if the user already has Play.fun credentials:
bash
node skills/playdotfun/scripts/playfun-auth.js statusIf credentials exist and are valid, skip to Step 2.
If no credentials, walk the user through authentication:
To register your game on Play.fun, you need to authenticate first.I'll start a local auth server. Click the link below to log in:
bash
node skills/playdotfun/scripts/playfun-auth.js callback &Then tell the user:
Open this URL in your browser: https://app.play.fun/skills-auth?callback=http://localhost:9876/callbackLog in with your Play.fun account. The credentials will be saved automatically. Tell me when you're done.
Wait for user confirmation. Then verify:
bash
node skills/playdotfun/scripts/playfun-auth.js statusIf verification fails, offer the manual method as a fallback:
If the callback didn't work, you can paste your credentials manually. Go to your Play.fun dashboard, copy your API credentials (base64 format), and I'll save them:node skills/playdotfun/scripts/playfun-auth.js manual <your-base64-credentials>
检查用户是否已拥有Play.fun凭证:
bash
node skills/playdotfun/scripts/playfun-auth.js status如果凭证存在且有效,跳至步骤2。
如果没有凭证,引导用户完成认证:
要在Play.fun上注册你的游戏,你需要先完成认证。我将启动一个本地认证服务器。点击下方链接登录:
bash
node skills/playdotfun/scripts/playfun-auth.js callback &然后告知用户:
使用你的Play.fun账号登录。凭证将自动保存。 完成后告知我。
等待用户确认。然后验证:
bash
node skills/playdotfun/scripts/playfun-auth.js status如果验证失败,提供手动方法作为备选:
如果回调失败,你可以手动粘贴凭证。 进入你的Play.fun控制台,复制你的API凭证(base64格式),我将为你保存:node skills/playdotfun/scripts/playfun-auth.js manual <your-base64-credentials>
Step 2: Determine the game URL
步骤2:确定游戏URL
Find the deployed game URL. Check in this order:
-
Look for a GitHub Pages URL by running:bash
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)If both resolve, the URL ishttps://$GITHUB_USER.github.io/$REPO_NAME/ -
Checkfor a
vite.config.jspath that hints at the deployment URLbase -
Ask the user for their game URL if it can't be determined
Verify the URL is accessible:
bash
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"查找已部署的游戏URL,按以下顺序检查:
-
运行以下命令查找GitHub Pages URL:bash
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路径,该路径可能暗示部署URLbase -
如果无法确定,询问用户提供游戏URL
验证URL是否可访问:
bash
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"Step 3: Register the game on Play.fun
步骤3:在Play.fun上注册游戏
Read the game's for the name and description. Read (or equivalent) to determine reasonable anti-cheat limits based on the scoring system.
package.jsonsrc/core/Constants.jsUse the Play.fun MCP tool if available. Otherwise, use the API directly:
register_gameLoad the skill for API reference. Register the game via with:
playdotfunPOST https://api.play.fun/gamesjson
{
"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>
}Anti-cheat guidelines (from the playdotfun skill):
- Casual clicker/idle:
maxScorePerSession: 100-500 - Skill-based arcade (flappy bird, runners):
maxScorePerSession: 500-2000 - Competitive/complex:
maxScorePerSession: 1000-5000
Save the returned game UUID — you'll need it for the SDK integration.
Tell the user:
Your game is registered on Play.fun! Game ID:Name:<uuid><name>
读取游戏的获取名称和描述。读取(或等效文件),根据计分系统确定合理的反作弊限制。
package.jsonsrc/core/Constants.js如果Play.fun MCP的工具可用,则使用该工具。否则,直接调用API:
register_game加载技能以获取API参考。通过注册游戏,请求体如下:
playdotfunPOST https://api.play.fun/gamesjson
{
"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>
Step 4: Add the Play.fun Browser SDK
步骤4:添加Play.fun浏览器SDK
Integrate the SDK into the game. This is a lightweight addition — the SDK loads from CDN and provides a points widget overlay.
将SDK集成到游戏中。这是一个轻量级的添加——SDK从CDN加载,并提供积分组件覆盖层。
4a. Add the SDK script and meta tag to index.html
index.html4a. 向index.html
添加SDK脚本和meta标签
index.htmlFirst, extract the user's API key from stored credentials:
bash
undefined首先,从存储的凭证中提取用户的API密钥:
bash
undefinedRead API key from agent config (stored by playfun-auth.js)
从agent配置中读取API密钥(由playfun-auth.js存储)
Example path for Claude Code — adapt for your agent
Claude Code的示例路径——根据你的agent调整
API_KEY=$(cat ~/.claude.json | jq -r '.mcpServers["play-fun"].headers["x-api-key"]')
echo "User API Key: $API_KEY"
If no API key is found, prompt the user to authenticate first (Step 1).
Then add before the closing `</head>` tag, substituting the actual API key:
```html
<meta name="x-ogp-key" content="<USER_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>Important: The meta tag must contain the user's API key (not the game ID). Do NOT leave the placeholder — always substitute the actual key extracted above.
x-ogp-keyUSER_API_KEY_HEREAPI_KEY=$(cat ~/.claude.json | jq -r '.mcpServers["play-fun"].headers["x-api-key"]')
echo "User API Key: $API_KEY"
如果未找到API密钥,提示用户先完成认证(步骤1)。
然后在`</head>`闭合标签之前添加以下内容,替换为实际的API密钥:
```html
<meta name="x-ogp-key" content="<USER_API_KEY>" />
<script src="https://sdk.play.fun/latest"></script>重要提示: meta标签必须包含用户的API密钥(而非游戏ID)。请勿保留占位符——务必替换为上面提取的实际密钥。
x-ogp-keyUSER_API_KEY_HERE4b. Create src/playfun.js
integration module
src/playfun.js4b. 创建src/playfun.js
集成模块
src/playfun.jsCreate a module that wires the Play.fun SDK into the game's EventBus:
js
// 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() {
// Award points on score changes
// addPoints() buffers points locally — call frequently during gameplay
if (Events.SCORE_CHANGED) {
eventBus.on(Events.SCORE_CHANGED, ({ score, delta }) => {
if (sdk && initialized && delta > 0) {
sdk.addPoints(delta);
}
});
}
// Save points on game over
// savePoints() opens a BLOCKING MODAL — only call at natural break points:
// - Game over
// - Level complete
// - User returns to menu
// DO NOT call savePoints() during active gameplay or on a timer!
if (Events.GAME_OVER) {
eventBus.on(Events.GAME_OVER, () => {
if (sdk && initialized) {
sdk.savePoints(); // Uses buffered points from addPoints() calls
}
});
}
// Save on page unload (non-blocking, browser handles it)
window.addEventListener('beforeunload', () => {
if (sdk && initialized) {
sdk.savePoints();
}
});
}Critical SDK behavior:
| Method | When to use | Behavior |
|---|---|---|
| During gameplay | Buffers points locally, non-blocking |
| Game over / level end | Opens blocking modal, syncs buffered points to server |
⚠️ Do NOT call on a timer or during active gameplay — it interrupts the player with a modal dialog. Only call at natural pause points (game over, level transitions, menu screens).
savePoints()Important: Read the actual to find the correct event names. Common patterns:
EventBus.js- /
SCORE_CHANGEDwithscore:changedor{ score, delta }{ score } - /
GAME_OVERgame:over
Adapt the event names and payload destructuring to match what the game actually emits. If the game doesn't emit a delta, compute it by tracking the previous score.
创建一个模块,将Play.fun SDK连接到游戏的EventBus:
js
// 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() {
// Award points on score changes
// addPoints() buffers points locally — call frequently during gameplay
if (Events.SCORE_CHANGED) {
eventBus.on(Events.SCORE_CHANGED, ({ score, delta }) => {
if (sdk && initialized && delta > 0) {
sdk.addPoints(delta);
}
});
}
// Save points on game over
// savePoints() opens a BLOCKING MODAL — only call at natural break points:
// - Game over
// - Level complete
// - User returns to menu
// DO NOT call savePoints() during active gameplay or on a timer!
if (Events.GAME_OVER) {
eventBus.on(Events.GAME_OVER, () => {
if (sdk && initialized) {
sdk.savePoints(); // Uses buffered points from addPoints() calls
}
});
}
// Save on page unload (non-blocking, browser handles it)
window.addEventListener('beforeunload', () => {
if (sdk && initialized) {
sdk.savePoints();
}
});
}关键SDK行为:
| 方法 | 使用时机 | 行为 |
|---|---|---|
| 游戏进行中 | 在本地缓冲积分,无阻塞 |
| 游戏结束/关卡完成 | 打开阻塞式弹窗,将缓冲的积分同步到服务器 |
⚠️ 请勿在计时器或游戏进行中调用——它会通过弹窗中断玩家。仅在自然暂停点调用(游戏结束、关卡切换、菜单界面)。
savePoints()重要提示:读取实际的以找到正确的事件名称。常见模式:
EventBus.js- /
SCORE_CHANGED,携带score:changed或{ score, delta }参数{ score } - /
GAME_OVERgame:over
根据游戏实际触发的事件,调整事件名称和负载解构。如果游戏不触发delta参数,可通过追踪之前的分数来计算。
4c. Wire into main.js
4c. 连接到main.js
Add the Play.fun init call to the game's entry point ():
src/main.jsjs
import { initPlayFun } from './playfun.js';
// After game initialization
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));The call should be non-blocking — if the SDK fails to load (e.g., ad blocker), the game still works.
initPlayFun()将Play.fun的初始化调用添加到游戏的入口文件():
src/main.jsjs
import { initPlayFun } from './playfun.js';
// 游戏初始化完成后
initPlayFun().catch(err => console.warn('Play.fun init failed:', err));initPlayFun()Step 5: Rebuild and redeploy
步骤5:重新构建并部署
bash
cd <project-dir> && npm run buildIf the build fails, fix the integration code and retry.
Then redeploy:
bash
cd <project-dir> && npx gh-pages -d distOr whatever deploy method the project uses ( if available).
npm run deploybash
cd <project-dir> && npm run build如果构建失败,修复集成代码后重试。
然后重新部署:
bash
cd <project-dir> && npx gh-pages -d dist或使用项目采用的其他部署方法(如果有则使用该命令)。
npm run deployStep 6: Confirm and share
步骤6:确认并分享
Wait ~30 seconds for deployment to propagate, then verify:
bash
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"Tell the user:
Your game is monetized on Play.fun!Play your game:View on Play.fun:<game-url>https://play.fun/games/<game-uuid>What's live:
- Points widget overlay (bottom-right corner)
- Leaderboard tracking
- Wallet connect for claiming rewards
- Points buffered during gameplay, saved on game over
Share on Moltbook: Post your game URL to moltbook.com — 770K+ agents on the agent internet ready to play and upvote.Next steps:
- Launch a playcoin for your game (token rewards for players)
- Check your leaderboard on Play.fun
- Share the play.fun URL on social media
等待约30秒让部署生效,然后验证:
bash
curl -s -o /dev/null -w "%{http_code}" "$GAME_URL"告知用户:
你的游戏已在Play.fun上实现变现!游玩你的游戏:在Play.fun上查看:<game-url>https://play.fun/games/<game-uuid>已上线功能:
- 积分组件覆盖层(右下角)
- 排行榜追踪
- 钱包连接以领取奖励
- 游戏进行中缓冲积分,游戏结束时保存
在Moltbook分享:将你的游戏链接发布到moltbook.com——代理互联网上有77万+代理准备游玩并点赞。后续步骤:
- 为你的游戏发行Playcoin(给玩家的代币奖励)
- 在Play.fun上查看你的排行榜
- 在社交媒体分享play.fun链接