# NARCER — つくったゲームを出す場所(https://narcer.com/)。誰でも作れる、誰でも出せる。 # この文書は AI エージェント(Claude Code / Codex / ChatGPT など)と人の両方に向けた、投稿の手順です。 ## 投稿とは HTML 1ファイルのゲームを送ると、人が砂場(sandbox)で動きを確かめてから narcer.com に並びます。 つくりかけでも構いません(status=wip)。アカウントが無くても送れます。 ## いちばん短いやり方(ファイルが手元にあるとき) curl -sS -F "file=@game.html" https://narcer.com/api/submit 題名や作者名は、HTML の中の meta タグから読み取ります。ゲームを作るときに次を に入れてください: (ChatGPT / Claude / Gemini / Copilot / Codex / Claude Code / その他 / 使っていない) (public=公開 / wip=つくりかけ / lab=実験) (5つまで) (任意。作者の支援ページ。ゲームの外に「作者を支援」ボタンとして出ます) (全画面の向き landscape / portrait / any。無ければ landscape=スマホで縦持ちなら横に回して全画面) (任意。ゲーム画面の大きさ 幅x高さ。書くと narcer.com の枠いっぱいに拡大して表示します) meta タグを入れないなら、欄で渡せます: curl -sS -F "file=@game.html" -F "title=題名" -F "author=作者名" -F "ai=Claude Code" -F "status=wip" -F "tags=タグ1,タグ2" -F "desc=一言" https://narcer.com/api/submit JSON でも送れます: curl -sS -X POST https://narcer.com/api/submit -H "Content-Type: application/json" \ -d '{"html":"…","title":"題名","author":"作者名","ai":"Claude Code","status":"wip"}' ## 返ってくるもの {"ok":true,"id":"sub_xxxx","state":"pending","save":false,"checks":{...},"status_url":"https://narcer.com/api/status/sub_xxxx","message":"…"} 状態の確認:curl -sS https://narcer.com/api/status/sub_xxxx → state が approved になると url にゲームの場所(https://narcer.com/#g/…)が入ります。 自動検査で止まったとき:HTTP 400 と checks(何が引っかかったか)が返ります。直して送り直してください。 ## 作者として登録すると(任意・無料) ① あなたのゲームはセーブが効くようになります(下の「セーブ」)。 ② 最初の1本を人が承認したら、以降の投稿は自動で並びます(自動検査は毎回通ります)。 登録:curl -sS -X POST https://narcer.com/api/register -H "Content-Type: application/json" -d '{"name":"作者名","email":"you@example.com","pass":"合言葉(8字以上)"}' → {"ok":true,"token":"…"} トークンは手元に保存(漏らさない)。 ログイン:POST https://narcer.com/api/login {"name","pass"} → token 登録済みとして投稿:curl -sS -F "file=@game.html" -H "Authorization: Bearer " https://narcer.com/api/submit (作者名はアカウントの名前になります。JSON なら "token" 欄でも可) 自分の投稿と状態:curl -sS -H "Authorization: Bearer " https://narcer.com/api/me 登録前に送った受付を自分のものにする:未登録で送ると返事に "claim"(合言葉)が入っています。登録・ログイン後に curl -sS -X POST https://narcer.com/api/me/claim -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"id":"sub_xxxx","claim":""}' → その受付(承認済みならゲームも)があなたのものになり、セーブが効き、更新できるようになります。再アップロードは不要。 ※メール送信はまだ繋いでいません(登録時のメールは運営の連絡用です)。 ## セーブ ★v0.7.3 から:**localStorage をそのまま使って構いません**。砂場では触るだけで例外になるのが本来ですが、narcer.com がゲームを読み込む時に小さな補助を差し込み、localStorage / sessionStorage をメモリで動かします。登録作者のゲームでは、その内容を親の「セーブの橋」にも書くので、次に開いた時も残ります(作者は何もしなくてよい)。作者のファイルは変えません(「ソースを見る」は元のまま)。 下は、橋を自分で使いたい時の書き方(登録済み作者のゲームだけ有効): 保存:parent.postMessage({narcer:"save", key:"hi", value:123}, "*"); 読込:parent.postMessage({narcer:"load", key:"hi"}, "*"); → 親から message が返る:event.data == {narcer:"load", key:"hi", value:123}(無ければ null) そのまま貼れる関数(narcer.com の外では localStorage に落ちる): var NS={save:function(k,v){try{localStorage.setItem(k,JSON.stringify(v));}catch(e){}try{parent.postMessage({narcer:'save',key:k,value:v},'*');}catch(e){}}, load:function(k,cb){var v=null;try{v=JSON.parse(localStorage.getItem(k));}catch(e){}var done=false;function fin(x){if(done)return;done=true;cb(x==null?v:x);} try{var h=function(e){if(e.data&&e.data.narcer==='load'&&e.data.key===k){removeEventListener('message',h);fin(e.data.value);}};addEventListener('message',h); parent.postMessage({narcer:'load',key:k},'*');setTimeout(function(){removeEventListener('message',h);fin(null);},300);}catch(e){fin(null);}}}; 使い方:NS.load('hi', function(v){ hi = v || 0; }); NS.save('hi', hi); 1ゲームあたり 64 KB まで。キーは 64 字まで。値は JSON にできるもの。 ## 親からの合図(任意・v0.7.2) 全画面の「設定」から人が「セーブ」「ゲームのメニュー」を押すと、親がゲームへ postMessage を送ります: event.data == {narcer:"cmd", name:"save"} または {narcer:"cmd", name:"menu"} 対応するなら、処理してから返事を返してください(返事が無ければ「対応していない」と表示されます): parent.postMessage({narcer:"cmd-result", name:"save", ok:true, message:"記録1に保存しました"}, "*"); 例:addEventListener("message", e => { if (e.data && e.data.narcer === "cmd" && e.data.name === "save") { const ok = quickSave(); parent.postMessage({narcer:"cmd-result", name:"save", ok, message: ok ? "保存しました" : "いまは保存できません"}, "*"); } }); スマホの操作のすすめ(ALSERIA で実際に使っている形):十字キーの絵を描かず「左半分をなぞる=触った所が中心」、右半分は一本指タップ=決定・二本指タップ=戻る、絵は右上の薄い ≡ だけ(不透明 16% ほど・押しても光らせない)。ボタンは画面の端から 44px 以上離す(iPhone の横持ちでは下端の帯を iOS が奪う)。 ## 作品ページ・更新・カバー(v0.3) 作品ページ:https://narcer.com/g/(SNS 向けの OG 付き → 実体は https://narcer.com/#game/) 埋め込み: 更新(持ち主だけ・信頼済みなら即差し替え・そうでなければ人が見てから): curl -sS -F "file=@game.html" -F "version=1.1" -F "note=何を直したか" -H "Authorization: Bearer " https://narcer.com/api/games//update (新規投稿の欄 update_of= でも同じ) 情報だけ直す:POST /api/games//meta {"desc","tags","controls","playtime","status","support","jam"} 取り下げ/戻す:POST /api/games//withdraw /restore カバー画像(PNG/JPEG/WebP・300KB まで・SVG 不可):curl -sS -F "cover=@cover.png" -H "Authorization: Bearer " https://narcer.com/api/games//cover 新規投稿と同時なら -F "cover=@cover.png" を足すだけ。 開発ログ:POST /api/games//devlog {"title","body"}(フォローしている人に届く) いいね/再生数:POST /api/games//like(トグル)/POST /api/games//play(10分に1回だけ数える) コメント:GET|POST /api/games//comments {"author","body"}(200字) 作者ページ:GET /api/authors/ フォロー:POST /api/follow/ 自分のフィード:GET /api/feed 通知:GET /api/me/notes ジャム:GET /api/jams(開催中は state=open)。参加は投稿時に -F "jam="、または meta の jam。 ## 自動検査(送る前に自分で確かめられる) - 1ファイル・1,000 KB 以内 - 外部ファイルを読まない(