src/worker.js

分割 2 / 2 · 元ファイル 231〜349行付近

原文の連続部分です。長い圧縮行は行の途中で分割する場合があります。全分割を順に連結するとファイル全文になります。

        /* 見た目(アバターと買った物)。数字しか受け取らない */
        p.av = { h:(d.h|0)&7, hc:(d.hc|0)&7, sk:(d.sk|0)%4, w:(d.w|0)%8 };
        p.look = { c:Math.max(0,Math.min(80,d.c|0)), ht:Math.max(0,Math.min(40,d.ht|0)),
                   st:Math.max(0,Math.min(20,d.st|0)) };
        break;
      case 'hit': {
        const k = (d.k==='kill'||d.k==='knock'||d.k==='crow'||d.k==='rock') ? d.k : null;
        if (k) w.hit(a.pid, k, d.i|0);
        break;
      }
      case 'swap': w.swap(); break;
      case 'rock': w.useRock(a.pid); break;
    }
  }

  gone(ws){
    if (!this.socks.has(ws)) return;
    const a = this.info(ws);
    this.socks.delete(ws);
    if (this.w && a.pid) this.w.rmPlayer(a.pid);
    if (this.socks.size === 0){ this.emptyAt = Date.now(); this.stop(); }
    this.report();
  }

  start(){
    if (this.timer) return;
    this.last = Date.now();
    this.timer = setInterval(() => this.tick(), TICK_MS);
    /* ★目覚ましは「畳むため」だけに使う。遊びの進行には使わない */
    this.s.storage.setAlarm(Date.now() + IDLE_MS + 5000).catch(()=>{});
  }
  stop(){ if (this.timer){ clearInterval(this.timer); this.timer = null; } }

  tick(){
    /* ★人が0人なら1コマも進めない */
    if (this.socks.size === 0){ this.stop(); return; }
    const now = Date.now();
    const dt = Math.min(0.25, (now - this.last)/1000);
    this.last = now;
    /* 合図の来なくなった繋がりを切る */
    for (const w of Array.from(this.socks)){
      if (now - (this.info(w).t||now) > STALE_MS){ try{ w.close(1001,'stale'); }catch(e){} this.gone(w); }
    }
    if (this.socks.size === 0){ this.stop(); return; }
    const w = this.world();
    w.step(dt);
    this.all(w.snap());
    if (w.over){
      /* 終わったら部屋は使い回さない。相手探しからも外す */
      this.report(true);
    }
  }

  async report(force){
    if (!this.name) return;
    const n = this.players(), open = this.isOpen();
    const key = n + (open?'o':'x');
    if (!force && key === this.reported) return;
    this.reported = key;
    try{
      await this.env.LOBBY.get(this.env.LOBBY.idFromName('hall'))
        .fetch('https://do/report?r='+this.name+'&n='+n+'&o='+(open?1:0));
    }catch(e){}
  }

  /* ★誰も繋がっていない部屋を畳む門。ここが無いと空の部屋が回り続ける */
  async alarm(){
    if (this.socks.size > 0){
      await this.s.storage.setAlarm(Date.now() + IDLE_MS + 5000).catch(()=>{});
      return;
    }
    if (this.emptyAt && Date.now() - this.emptyAt >= IDLE_MS){
      this.stop(); this.w = null;
      await this.s.storage.deleteAll().catch(()=>{});
      await this.s.storage.deleteAlarm().catch(()=>{});
      return;
    }
    await this.s.storage.setAlarm(Date.now() + 30000).catch(()=>{});
  }
}

/* ===== バグ報告の箱(平成ゾンビ学園から相続した🐛の受け先) ===== */
const BUG_MAX = 900*1024, BUG_KEEP = 200;
export class Bug {
  constructor(state){ this.s = state; }
  async fetch(req){
    const u = new URL(req.url);
    if (req.method === 'POST'){
      const body = await req.text();
      if (body.length > BUG_MAX) return J({ ok:0, why:'big' }, 413);
      const id = Date.now().toString(36) + Math.random().toString(36).slice(2,6);
      await this.s.storage.put('b:'+id, { at:Date.now(), body });
      const list = (await this.s.storage.get('list'))||[];
      list.unshift(id);
      while (list.length > BUG_KEEP){ const old = list.pop(); await this.s.storage.delete('b:'+old); }
      await this.s.storage.put('list', list);
      return J({ ok:1, id });
    }
    const id = u.searchParams.get('id');
    if (id){
      if (u.searchParams.get('del') === '1'){
        await this.s.storage.delete('b:'+id);
        const l = ((await this.s.storage.get('list'))||[]).filter(x=>x!==id);
        await this.s.storage.put('list', l);
        return J({ ok:1 });
      }
      const r = await this.s.storage.get('b:'+id);
      if (!r) return J({ ok:0 }, 404);
      return new Response(r.body, { headers:{'content-type':'application/json; charset=utf-8'} });
    }
    const list = (await this.s.storage.get('list'))||[];
    const out = [];
    for (const x of list.slice(0,60)){
      const r = await this.s.storage.get('b:'+x);
      if (r) out.push({ id:x, at:r.at, bytes:r.body.length });
    }
    return J(out);
  }
}