← back to kurodenkou__playTogether

Function bodies 123 total

All specs Real LLM only Function bodies
showView function · javascript · L58-L62 (5 LOC)
public/js/app.js
function showView(name) {
  document.querySelectorAll('.view').forEach(v =>
    v.classList.toggle('hidden', v.id !== `view-${name}`)
  );
}
bindLobbyUI function · javascript · L66-L76 (11 LOC)
public/js/app.js
function bindLobbyUI() {
  el('createRoomBtn').addEventListener('click', () => {
    const name = getPlayerName();
    if (!name) return;
    net.send({ type: 'create-room', playerName: name });
    el('createRoomBtn').disabled = true;
  });

  el('joinRoomBtn').addEventListener('click', doJoinRoom);
  el('roomCodeInput').addEventListener('keydown', e => { if (e.key === 'Enter') doJoinRoom(); });
}
doJoinRoom function · javascript · L78-L85 (8 LOC)
public/js/app.js
function doJoinRoom() {
  const code = el('roomCodeInput').value.trim().toUpperCase();
  if (!code) { showStatus('lobby', 'Enter a room code.', true); return; }
  const name = getPlayerName();
  if (!name) return;
  net.send({ type: 'join-room', roomId: code, playerName: name });
  el('joinRoomBtn').disabled = true;
}
getPlayerName function · javascript · L87-L92 (6 LOC)
public/js/app.js
function getPlayerName() {
  const name = el('playerName').value.trim().slice(0, 24);
  if (!name) { showStatus('lobby', 'Enter your name first.', true); return null; }
  localStorage.setItem('playerName', name);
  return name;
}
bindNetworkEvents function · javascript · L96-L115 (20 LOC)
public/js/app.js
function bindNetworkEvents() {
  net
    .on('room-created',  onRoomCreated)
    .on('room-joined',   onRoomJoined)
    .on('player-joined', onPlayerJoined)
    .on('player-left',   onPlayerLeft)
    .on('host-changed',  onHostChanged)
    .on('game-started',  onGameStarted)
    .on('rematch',       onRematch)
    .on('input',         onRemoteInput)
    .on('chat',          onChat)
    .on('error', msg => {
      showStatus('lobby', msg.message, true);
      el('createRoomBtn').disabled = false;
      el('joinRoomBtn').disabled = false;
    })
    .on('disconnect', () => {
      addChatLine('system', 'Disconnected from server.');
    });
}
onRoomCreated function · javascript · L117-L120 (4 LOC)
public/js/app.js
function onRoomCreated(msg) {
  roomState = { roomId: msg.roomId, playerId: msg.playerId, hostId: msg.hostId, players: msg.players };
  enterRoom();
}
onRoomJoined function · javascript · L122-L125 (4 LOC)
public/js/app.js
function onRoomJoined(msg) {
  roomState = { roomId: msg.roomId, playerId: msg.playerId, hostId: msg.hostId, players: msg.players };
  enterRoom();
}
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
onPlayerJoined function · javascript · L127-L131 (5 LOC)
public/js/app.js
function onPlayerJoined(msg) {
  roomState.players = msg.players;
  updatePlayerList();
  addChatLine('system', `${msg.playerName} joined the room.`);
}
onPlayerLeft function · javascript · L133-L138 (6 LOC)
public/js/app.js
function onPlayerLeft(msg) {
  roomState.players = msg.players;
  updatePlayerList();
  const gone = msg.players.find(p => p.id === msg.playerId);
  addChatLine('system', `${gone?.name ?? 'A player'} left the room.`);
}
onHostChanged function · javascript · L140-L145 (6 LOC)
public/js/app.js
function onHostChanged(msg) {
  roomState.hostId = msg.hostId;
  updateHostControls();
  updatePlayerList();
  addChatLine('system', 'Host transferred.');
}
onGameStarted function · javascript · L147-L170 (24 LOC)
public/js/app.js
async function onGameStarted(msg) {
  // msg: { playerOrder, seed, gameType: 'pong'|'nes'|'snes'|'retroarch', romUrl?, coreUrl?, coreWasmUrl? }
  el('preGamePanel').classList.add('hidden');
  el('gamePanel').classList.remove('hidden');
  el('loadingOverlay').classList.remove('hidden');
  el('loadingOverlay').textContent = 'Loading…';

  try {
    if (msg.gameType === 'snes') {
      await startSNESGame(msg.playerOrder, msg.seed, msg.romUrl);
    } else if (msg.gameType === 'nes') {
      await startNESGame(msg.playerOrder, msg.seed, msg.romUrl);
    } else if (msg.gameType === 'retroarch') {
      await startLibretroGame(msg.playerOrder, msg.seed, msg.romUrl, msg.coreUrl, msg.coreWasmUrl);
    } else {
      startPongGame(msg.playerOrder, msg.seed);
    }
    el('loadingOverlay').classList.add('hidden');
  } catch (err) {
    el('loadingOverlay').textContent = `Error: ${err.message}`;
    el('loadingOverlay').classList.add('error');
    console.error('[game-start]', err);
  }
}
onRematch function · javascript · L172-L180 (9 LOC)
public/js/app.js
function onRematch() {
  stopGame();
  el('preGamePanel').classList.remove('hidden');
  el('gamePanel').classList.add('hidden');
  el('loadingOverlay').classList.add('hidden');
  el('loadingOverlay').classList.remove('error');
  updateHostControls();
  addChatLine('system', 'Host started a rematch — waiting for players.');
}
onRemoteInput function · javascript · L182-L184 (3 LOC)
public/js/app.js
function onRemoteInput(msg) {
  engine?.receiveRemoteInput(msg.frame, msg.playerId, msg.input);
}
onChat function · javascript · L186-L189 (4 LOC)
public/js/app.js
function onChat(msg) {
  const isSelf = msg.playerId === roomState?.playerId;
  addChatLine(isSelf ? 'self' : 'other', msg.message, msg.playerName);
}
enterRoom function · javascript · L193-L250 (58 LOC)
public/js/app.js
function enterRoom() {
  showView('room');
  updateRoomInfo();
  updatePlayerList();
  updateHostControls();
  syncGameTypeUI();

  if (!enterRoom._bound) {
    enterRoom._bound = true;

    // Game-type selector — only the host's choice matters (sent with start-game)
    el('gameTypeSelect').addEventListener('change', syncGameTypeUI);
    _wireCoreSelect();

    el('startGameBtn').addEventListener('click', () => {
      const gameType    = el('gameTypeSelect').value;
      const romUrl      = el('romUrlInput').value.trim();
      const coreUrl     = el('coreUrlInput').value.trim();
      const coreWasmUrl = el('coreWasmUrlInput').value.trim();
      if ((gameType === 'nes' || gameType === 'snes') && !romUrl) {
        addChatLine('system', 'Enter a ROM URL before starting.');
        return;
      }
      if (gameType === 'retroarch') {
        if (!coreUrl) { addChatLine('system', 'Enter a Core JS URL before starting.'); return; }
        if (!romUrl)  { addChatLine('system', 'Enter 
Repobility — the code-quality scanner for AI-generated software · https://repobility.com
_populateCoreSelect function · javascript · L257-L280 (24 LOC)
public/js/app.js
async function _populateCoreSelect() {
  try {
    if (_coresCache === null) {
      const resp = await fetch('/api/cores');
      _coresCache = resp.ok ? await resp.json() : [];
    }

    const select = el('coreSelect');
    // Rebuild options (keep the placeholder at index 0)
    while (select.options.length > 1) select.remove(1);

    for (const core of _coresCache) {
      const opt = document.createElement('option');
      // Store both URLs in the value so auto-fill can read them without a second lookup
      opt.value       = JSON.stringify({ jsUrl: core.jsUrl, wasmUrl: core.wasmUrl ?? '' });
      opt.textContent = core.name;
      select.appendChild(opt);
    }

    el('coreSelectRow').classList.toggle('hidden', _coresCache.length === 0);
  } catch {
    el('coreSelectRow').classList.add('hidden');
  }
}
_wireCoreSelect function · javascript · L286-L294 (9 LOC)
public/js/app.js
function _wireCoreSelect() {
  el('coreSelect').addEventListener('change', () => {
    const val = el('coreSelect').value;
    if (!val) return;
    const { jsUrl, wasmUrl } = JSON.parse(val);
    el('coreUrlInput').value     = jsUrl;
    el('coreWasmUrlInput').value = wasmUrl;
  });
}
syncGameTypeUI function · javascript · L296-L327 (32 LOC)
public/js/app.js
function syncGameTypeUI() {
  const gameType      = el('gameTypeSelect').value;
  const isHost        = roomState?.hostId === roomState?.playerId;
  const isRetroarch   = gameType === 'retroarch';
  const isRomEmulator = gameType === 'nes' || gameType === 'snes' || isRetroarch;

  el('coreUrlRow').classList.toggle('hidden', !isRetroarch);
  el('coreWasmUrlRow').classList.toggle('hidden', !isRetroarch);
  el('romUrlRow').classList.toggle('hidden', !isRomEmulator);

  if (isRetroarch) {
    _populateCoreSelect(); // async; shows #coreSelectRow when cores are available
  } else {
    el('coreSelectRow').classList.add('hidden');
  }

  if (gameType === 'snes') {
    el('romUrlLabel').textContent  = 'ROM URL (.sfc / .smc)';
    el('romUrlInput').placeholder  = 'https://example.com/game.sfc';
  } else if (isRetroarch) {
    el('romUrlLabel').textContent  = 'ROM URL';
    el('romUrlInput').placeholder  = 'https://example.com/game.rom';
  } else {
    el('romUrlLabel').textContent  = 'ROM URL 
updateRoomInfo function · javascript · L329-L331 (3 LOC)
public/js/app.js
function updateRoomInfo() {
  el('roomIdDisplay').textContent = roomState.roomId;
}
updatePlayerList function · javascript · L333-L346 (14 LOC)
public/js/app.js
function updatePlayerList() {
  const list = el('playerList');
  list.innerHTML = '';
  for (const p of roomState.players) {
    const li = document.createElement('li');
    li.className = 'player-item' + (p.id === roomState.playerId ? ' self' : '');

    const badge  = p.id === roomState.hostId ? '<span class="badge host">HOST</span>' : '';
    const youTag = p.id === roomState.playerId ? ' <span class="badge you">YOU</span>' : '';

    li.innerHTML = `<span class="player-name">${escHtml(p.name)}${youTag}</span>${badge}`;
    list.appendChild(li);
  }
}
updateHostControls function · javascript · L348-L355 (8 LOC)
public/js/app.js
function updateHostControls() {
  const isHost       = roomState.hostId === roomState.playerId;
  const gameVisible  = !el('gamePanel').classList.contains('hidden');
  el('startGameBtn').classList.toggle('hidden', !isHost || gameVisible);
  el('rematchBtn').classList.toggle('hidden',   !isHost || !gameVisible);
  el('gameTypeSelect').disabled = !isHost;
  el('romUrlInput').disabled    = !isHost;
}
startPongGame function · javascript · L362-L380 (19 LOC)
public/js/app.js
function startPongGame(playerOrder, seed) {
  stopGame();
  const canvas = el('gameCanvas');

  // Reset canvas to Pong's 800×500 logical size
  canvas.width  = DemoGame.W;
  canvas.height = DemoGame.H;
  canvas.style.removeProperty('width');
  canvas.style.removeProperty('height');

  game    = new DemoGame(canvas, playerOrder, seed);
  inputMgr = new InputManager();

  _startEngine(playerOrder);

  const idx = playerOrder.indexOf(roomState.playerId);
  el('playerSlotLabel').textContent = idx >= 0 ? `You are Player ${idx + 1}` : 'Spectating';
  addChatLine('system', `Pong started! Player ${idx + 1}. ↑↓ or W·S to move.`);
}
startSNESGame function · javascript · L386-L411 (26 LOC)
public/js/app.js
async function startSNESGame(playerOrder, seed, romUrl) {
  stopGame();

  if (!romUrl) throw new Error('No ROM URL provided.');

  const canvas = el('gameCanvas');

  // SNES native resolution 256×224; scale up 3× via CSS (pixelated rendering)
  canvas.width  = SNESAdapter.SNES_W;
  canvas.height = SNESAdapter.SNES_H;
  canvas.style.width  = `${SNESAdapter.SNES_W * 3}px`;
  canvas.style.height = `${SNESAdapter.SNES_H * 3}px`;

  el('loadingOverlay').textContent = 'Fetching ROM…';
  game = new SNESAdapter(canvas, playerOrder);
  await game.loadROM(romUrl);

  el('loadingOverlay').textContent = 'Starting…';
  inputMgr = new InputManager();

  _startEngine(playerOrder);

  const idx = playerOrder.indexOf(roomState.playerId);
  el('playerSlotLabel').textContent = idx >= 0 ? `You are Player ${idx + 1}` : 'Spectating';
  addChatLine('system', `SNES game started! You are Player ${idx + 1}. Arrows/WASD · Z=A · X=B · C=X · V=Y · Q=L · E=R · Enter=Start`);
}
All rows above produced by Repobility · https://repobility.com
startNESGame function · javascript · L417-L442 (26 LOC)
public/js/app.js
async function startNESGame(playerOrder, seed, romUrl) {
  stopGame();

  if (!romUrl) throw new Error('No ROM URL provided.');

  const canvas = el('gameCanvas');

  // NES native resolution; scale up via CSS (pixelated rendering)
  canvas.width  = NESAdapter.NES_W;
  canvas.height = NESAdapter.NES_H;
  canvas.style.width  = `${NESAdapter.NES_W * 3}px`;
  canvas.style.height = `${NESAdapter.NES_H * 3}px`;

  el('loadingOverlay').textContent = 'Fetching ROM…';
  game = new NESAdapter(canvas, playerOrder);
  await game.loadROM(romUrl);

  el('loadingOverlay').textContent = 'Starting…';
  inputMgr = new InputManager();

  _startEngine(playerOrder);

  const idx = playerOrder.indexOf(roomState.playerId);
  el('playerSlotLabel').textContent = idx >= 0 ? `You are Player ${idx + 1}` : 'Spectating';
  addChatLine('system', `NES game started! You are Player ${idx + 1}. Arrow keys / W·A·S·D · Z=A · X=B · Enter=Start · Shift=Select`);
}
startLibretroGame function · javascript · L454-L491 (38 LOC)
public/js/app.js
async function startLibretroGame(playerOrder, seed, romUrl, coreUrl, coreWasmUrl) {
  stopGame();

  if (!coreUrl) throw new Error('No Core JS URL provided.');
  if (!romUrl)  throw new Error('No ROM URL provided.');

  const canvas = el('gameCanvas');

  // Set an initial canvas size; LibretroAdapter._resize() will update it
  // once retro_get_system_av_info returns the actual base resolution.
  canvas.width  = 320;
  canvas.height = 240;
  canvas.style.removeProperty('width');
  canvas.style.removeProperty('height');

  el('loadingOverlay').textContent = 'Loading core…';
  const coreModule = await LibretroAdapter.loadCore(
    coreUrl,
    coreWasmUrl || null,
  );

  el('loadingOverlay').textContent = 'Fetching ROM…';
  game = new LibretroAdapter(canvas, playerOrder, coreModule);
  await game.loadROM(romUrl);

  // Apply 3× CSS scaling based on the resolved native resolution.
  canvas.style.width  = `${canvas.width  * 3}px`;
  canvas.style.height = `${canvas.height * 3}px`;

  el('
_startEngine function · javascript · L494-L511 (18 LOC)
public/js/app.js
function _startEngine(playerOrder) {
  engine = new RollbackEngine({
    emulator:      game,
    localPlayerId: roomState.playerId,
    playerIds:     playerOrder,
    readInput:     () => inputMgr.getInput(),
    onStats:       updateStatsHUD,
  });

  engine._sendInput = (frame, input) => {
    net.send({ type: 'input', frame, input });
  };

  engine.start();

  updateHostControls();
  el('rematchBtn').classList.toggle('hidden', roomState.hostId !== roomState.playerId);
}
stopGame function · javascript · L513-L521 (9 LOC)
public/js/app.js
function stopGame() {
  engine?.stop();
  engine = null;
  game?.stopAudio?.();
  game   = null;
  inputMgr?.destroy();
  inputMgr = null;
  clearStatsHUD();
}
updateStatsHUD function · javascript · L525-L530 (6 LOC)
public/js/app.js
function updateStatsHUD(stats) {
  el('hudFrame').textContent     = stats.frame;
  el('hudConfirmed').textContent = stats.confirmedFrame;
  el('hudRollbacks').textContent = stats.rollbacks;
  el('hudMaxDepth').textContent  = stats.maxRollbackDepth;
}
clearStatsHUD function · javascript · L532-L536 (5 LOC)
public/js/app.js
function clearStatsHUD() {
  ['hudFrame', 'hudConfirmed', 'hudRollbacks', 'hudMaxDepth'].forEach(id => {
    el(id).textContent = '—';
  });
}
addChatLine function · javascript · L540-L557 (18 LOC)
public/js/app.js
function addChatLine(kind, text, sender) {
  const box = el('chatLog');
  const div = document.createElement('div');
  div.className = `chat-line chat-${kind}`;

  if (kind === 'system') {
    div.textContent = `⚙ ${text}`;
  } else {
    const nameSpan = document.createElement('span');
    nameSpan.className = 'chat-sender';
    nameSpan.textContent = sender + ': ';
    div.appendChild(nameSpan);
    div.appendChild(document.createTextNode(text));
  }

  box.appendChild(div);
  box.scrollTop = box.scrollHeight;
}
escHtml function · javascript · L563-L567 (5 LOC)
public/js/app.js
function escHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;').replace(/</g, '&lt;')
    .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
showStatus function · javascript · L569-L575 (7 LOC)
public/js/app.js
function showStatus(view, msg, isError = false) {
  const el_ = document.getElementById(`status-${view}`);
  if (!el_) return;
  el_.textContent = msg;
  el_.className = `status ${isError ? 'error' : 'info'}`;
  el_.classList.remove('hidden');
}
DemoGame class · javascript · L18-L300 (283 LOC)
public/js/demo-game.js
class DemoGame {
  // ── Canvas dimensions (logical pixels) ──────────────────────────────────
  static W = 800;
  static H = 500;

  // ── Geometry constants ───────────────────────────────────────────────────
  static PADDLE_W = 12;
  static PADDLE_H = 90;
  static PADDLE_SPEED = 7;
  static PADDLE_LEFT_X = 40;
  static PADDLE_RIGHT_X = DemoGame.W - 40 - DemoGame.PADDLE_W;

  static BALL_R = 9;
  static BALL_INIT_VX = 5;
  static BALL_INIT_VY = 3;
  static BALL_MAX_SPEED = 15;
  static BALL_ACCEL = 0.4;       // speed increase on each paddle hit

  static WIN_SCORE = 7;

  // ─────────────────────────────────────────────────────────────────────────

  /**
   * @param {HTMLCanvasElement} canvas
   * @param {string[]} playerIds  ordered player IDs (index 0 = left, 1 = right)
   * @param {number}   seed       shared PRNG seed from server
   */
  constructor(canvas, playerIds, seed) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.playerIds = playerIds;
    th
constructor method · javascript · L45-L55 (11 LOC)
public/js/demo-game.js
  constructor(canvas, playerIds, seed) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.playerIds = playerIds;
    this.seed = seed >>> 0;

    canvas.width  = DemoGame.W;
    canvas.height = DemoGame.H;

    this.state = this._initialState();
  }
step method · javascript · L59-L133 (75 LOC)
public/js/demo-game.js
  step(inputMap) {
    const s = this.state;
    if (s.winner !== null) return; // game over — freeze

    s.frame++;
    if (s.hitFlash > 0) s.hitFlash--;

    // Move paddles ────────────────────────────────────────────────────────
    for (let i = 0; i < 2; i++) {
      const pid = this.playerIds[i];
      if (!pid) continue;
      const input = inputMap[pid] ?? 0;
      const pad = s.paddles[i];
      if (input & InputBits.UP)   pad.y = Math.max(0, pad.y - DemoGame.PADDLE_SPEED);
      if (input & InputBits.DOWN) pad.y = Math.min(DemoGame.H - DemoGame.PADDLE_H, pad.y + DemoGame.PADDLE_SPEED);
    }

    // Move ball ───────────────────────────────────────────────────────────
    s.ball.x += s.ball.vx;
    s.ball.y += s.ball.vy;

    // Top / bottom wall bounce
    if (s.ball.y - DemoGame.BALL_R <= 0) {
      s.ball.y = DemoGame.BALL_R;
      s.ball.vy = Math.abs(s.ball.vy);
    } else if (s.ball.y + DemoGame.BALL_R >= DemoGame.H) {
      s.ball.y = DemoGame.H - DemoGame.BALL_R;
   
saveState method · javascript · L135-L146 (12 LOC)
public/js/demo-game.js
  saveState() {
    const s = this.state;
    return {
      frame: s.frame,
      rngState: s.rngState,
      ball: { ...s.ball },
      paddles: s.paddles.map(p => ({ ...p })),
      scores: [...s.scores],
      winner: s.winner,
      hitFlash: s.hitFlash,
    };
  }
loadState method · javascript · L148-L158 (11 LOC)
public/js/demo-game.js
  loadState(snap) {
    this.state = {
      frame: snap.frame,
      rngState: snap.rngState,
      ball: { ...snap.ball },
      paddles: snap.paddles.map(p => ({ ...p })),
      scores: [...snap.scores],
      winner: snap.winner,
      hitFlash: snap.hitFlash,
    };
  }
render method · javascript · L160-L254 (95 LOC)
public/js/demo-game.js
  render() {
    const ctx = this.ctx;
    const s = this.state;
    const { W, H, PADDLE_W, PADDLE_H, PADDLE_LEFT_X, PADDLE_RIGHT_X, BALL_R } = DemoGame;

    // Background ─────────────────────────────────────────────────────────
    ctx.fillStyle = '#080810';
    ctx.fillRect(0, 0, W, H);

    // Hit flash ──────────────────────────────────────────────────────────
    if (s.hitFlash > 0) {
      ctx.fillStyle = `rgba(255,255,180,${s.hitFlash * 0.025})`;
      ctx.fillRect(0, 0, W, H);
    }

    // Center dashed divider ──────────────────────────────────────────────
    ctx.save();
    ctx.strokeStyle = '#1c1c2e';
    ctx.lineWidth = 2;
    ctx.setLineDash([14, 14]);
    ctx.beginPath();
    ctx.moveTo(W / 2, 0);
    ctx.lineTo(W / 2, H);
    ctx.stroke();
    ctx.restore();

    // Scores ─────────────────────────────────────────────────────────────
    ctx.save();
    ctx.font = 'bold 54px "Courier New", monospace';
    ctx.textAlign = 'center';
    // P1 score
    ctx.fillStyle =
_initialState method · javascript · L258-L272 (15 LOC)
public/js/demo-game.js
  _initialState() {
    const rngState = this.seed;
    return {
      frame: 0,
      rngState,
      ball:    { x: DemoGame.W / 2, y: DemoGame.H / 2, vx: DemoGame.BALL_INIT_VX, vy: DemoGame.BALL_INIT_VY },
      paddles: [
        { y: DemoGame.H / 2 - DemoGame.PADDLE_H / 2 },
        { y: DemoGame.H / 2 - DemoGame.PADDLE_H / 2 },
      ],
      scores:   [0, 0],
      winner:   null,
      hitFlash: 0,
    };
  }
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
_resetBall method · javascript · L275-L286 (12 LOC)
public/js/demo-game.js
  _resetBall(s, lastScorer) {
    // LCG: next = (a * x + c) mod m   (Numerical Recipes constants)
    s.rngState = ((Math.imul(1664525, s.rngState) + 1013904223) >>> 0);
    const vy = (((s.rngState >>> 16) % 5) - 2) * 1.2;          // -2.4 … 2.4
    const vx = DemoGame.BALL_INIT_VX * (lastScorer === 0 ? -1 : 1);
    s.ball = {
      x: DemoGame.W / 2,
      y: DemoGame.H / 2,
      vx,
      vy,
    };
  }
_drawPaddle method · javascript · L288-L299 (12 LOC)
public/js/demo-game.js
  _drawPaddle(ctx, x, y, color) {
    ctx.save();
    ctx.shadowColor = color;
    ctx.shadowBlur  = 14;
    ctx.fillStyle   = color;
    ctx.fillRect(x, y, DemoGame.PADDLE_W, DemoGame.PADDLE_H);
    // Lighter highlight stripe
    ctx.fillStyle = 'rgba(255,255,255,0.18)';
    ctx.fillRect(x, y, 3, DemoGame.PADDLE_H);
    ctx.shadowBlur = 0;
    ctx.restore();
  }
InputManager class · javascript · L35-L184 (150 LOC)
public/js/input.js
class InputManager {
  // Analog stick dead zone — ignore tilt below this threshold
  static DEAD_ZONE = 0.5;

  constructor() {
    this._held = new Set();

    this._onKeyDown = (e) => {
      // Prevent arrow keys / space from scrolling the page
      if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
        e.preventDefault();
      }
      this._held.add(e.code);
    };

    this._onKeyUp = (e) => {
      this._held.delete(e.code);
    };

    window.addEventListener('keydown', this._onKeyDown);
    window.addEventListener('keyup',   this._onKeyUp);

    // Track connected gamepads for the UI indicator
    this._connectedGamepads = new Set();

    this._onGamepadConnected = (e) => {
      this._connectedGamepads.add(e.gamepad.index);
      this._updateGamepadIndicator();
    };

    this._onGamepadDisconnected = (e) => {
      this._connectedGamepads.delete(e.gamepad.index);
      this._updateGamepadIndicator();
    };

    window.addEventListener('ga
constructor method · javascript · L39-L72 (34 LOC)
public/js/input.js
  constructor() {
    this._held = new Set();

    this._onKeyDown = (e) => {
      // Prevent arrow keys / space from scrolling the page
      if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
        e.preventDefault();
      }
      this._held.add(e.code);
    };

    this._onKeyUp = (e) => {
      this._held.delete(e.code);
    };

    window.addEventListener('keydown', this._onKeyDown);
    window.addEventListener('keyup',   this._onKeyUp);

    // Track connected gamepads for the UI indicator
    this._connectedGamepads = new Set();

    this._onGamepadConnected = (e) => {
      this._connectedGamepads.add(e.gamepad.index);
      this._updateGamepadIndicator();
    };

    this._onGamepadDisconnected = (e) => {
      this._connectedGamepads.delete(e.gamepad.index);
      this._updateGamepadIndicator();
    };

    window.addEventListener('gamepadconnected',    this._onGamepadConnected);
    window.addEventListener('gamepaddisconnected', this._onGamep
_getGamepadBits method · javascript · L81-L133 (53 LOC)
public/js/input.js
  _getGamepadBits() {
    if (!navigator.getGamepads) return 0;

    let bits = 0;
    const DEAD = InputManager.DEAD_ZONE;

    for (const gp of navigator.getGamepads()) {
      if (!gp) continue;

      const btn = gp.buttons;
      const ax  = gp.axes;

      if (gp.mapping === 'standard') {
        // ── Standard layout (Xbox / PlayStation / Switch Pro) ──────────────
        // D-pad
        if (btn[12]?.pressed) bits |= InputBits.UP;
        if (btn[13]?.pressed) bits |= InputBits.DOWN;
        if (btn[14]?.pressed) bits |= InputBits.LEFT;
        if (btn[15]?.pressed) bits |= InputBits.RIGHT;
        // Face buttons (full SNES mapping)
        if (btn[0]?.pressed)  bits |= InputBits.A;   // A / Cross
        if (btn[1]?.pressed)  bits |= InputBits.B;   // B / Circle
        if (btn[2]?.pressed)  bits |= InputBits.X;   // X / Square  (SNES X)
        if (btn[3]?.pressed)  bits |= InputBits.Y;   // Y / Triangle (SNES Y)
        // Shoulder buttons
        if (btn[4]?.pressed)  bit
_updateGamepadIndicator method · javascript · L137-L148 (12 LOC)
public/js/input.js
  _updateGamepadIndicator() {
    const el = document.getElementById('gamepadIndicator');
    if (!el) return;
    const count = this._connectedGamepads.size;
    if (count === 0) {
      el.textContent = '';
      el.classList.add('hidden');
    } else {
      el.textContent = `Gamepad (${count})`;
      el.classList.remove('hidden');
    }
  }
getInput method · javascript · L153-L175 (23 LOC)
public/js/input.js
  getInput() {
    let bits = 0;
    const h = this._held;

    // Keyboard
    if (h.has('ArrowUp')    || h.has('KeyW')) bits |= InputBits.UP;
    if (h.has('ArrowDown')  || h.has('KeyS')) bits |= InputBits.DOWN;
    if (h.has('ArrowLeft')  || h.has('KeyA')) bits |= InputBits.LEFT;
    if (h.has('ArrowRight') || h.has('KeyD')) bits |= InputBits.RIGHT;
    if (h.has('KeyZ') || h.has('KeyJ'))       bits |= InputBits.A;
    if (h.has('KeyX') || h.has('KeyK'))       bits |= InputBits.B;
    if (h.has('KeyC'))                        bits |= InputBits.X;   // SNES X
    if (h.has('KeyV'))                        bits |= InputBits.Y;   // SNES Y
    if (h.has('KeyQ'))                        bits |= InputBits.L;   // SNES L
    if (h.has('KeyE'))                        bits |= InputBits.R;   // SNES R
    if (h.has('Enter') || h.has('KeyP'))      bits |= InputBits.START;
    if (h.has('ShiftLeft') || h.has('ShiftRight') || h.has('KeyO')) bits |= InputBits.SELECT;

    // Gamepad (ORed in — key
destroy method · javascript · L178-L183 (6 LOC)
public/js/input.js
  destroy() {
    window.removeEventListener('keydown', this._onKeyDown);
    window.removeEventListener('keyup',   this._onKeyUp);
    window.removeEventListener('gamepadconnected',    this._onGamepadConnected);
    window.removeEventListener('gamepaddisconnected', this._onGamepadDisconnected);
  }
Repobility — the code-quality scanner for AI-generated software · https://repobility.com
loadCore method · javascript · L108-L163 (56 LOC)
public/js/libretro-adapter.js
  static loadCore(jsUrl, wasmUrl = null, globalName = 'LibretroCore') {
    return new Promise(async (resolve, reject) => {
      const timeout = setTimeout(
        () => reject(new Error('Libretro core initialization timed out (30 s)')),
        30_000
      );

      // Pre-configure the Emscripten Module before the script runs.
      // Standard Emscripten glue checks for a pre-existing global named 'Module'
      // (or occasionally the build-configured name); we set both so either works.
      const modCfg = {
        noInitialRun: true,
        onRuntimeInitialized() {
          clearTimeout(timeout);
          resolve(modCfg);
        },
        print:    (msg) => console.log('[libretro]', msg),
        printErr: (msg) => console.warn('[libretro]', msg),
      };

      if (wasmUrl) {
        // Override Emscripten's WASM resolver so the binary is fetched through
        // our proxy regardless of the base URL Emscripten thinks the .js lives at.
        modCfg.locateFile = (pat
onRuntimeInitialized method · javascript · L120-L123 (4 LOC)
public/js/libretro-adapter.js
        onRuntimeInitialized() {
          clearTimeout(timeout);
          resolve(modCfg);
        },
constructor method · javascript · L172-L200 (29 LOC)
public/js/libretro-adapter.js
  constructor(canvas, playerIds, coreModule) {
    this.canvas    = canvas;
    this.playerIds = playerIds;
    this.M         = coreModule;
    this.ctx       = canvas.getContext('2d');

    this._imageData   = null;
    this._frameBuffer = null;   // Uint32Array view into _imageData.data
    this._width       = 0;
    this._height      = 0;
    this._pixelFormat = LibretroAdapter.PIXEL_FORMAT.RGB1555;
    this._dirty       = false;

    this._romLoaded   = false;
    this._audioMuted  = false;
    this._audioCtx    = null;
    this._workletNode = null;

    // Per-port button state: port index → Uint8Array(16) where 1 = pressed
    this._inputState = [new Uint8Array(16), new Uint8Array(16)];

    // Reusable buffer for the single-sample audio callback path
    this._singleSampleBuf = new Int16Array(2);

    // Keep all registered function pointers alive (prevents GC of the JS closure)
    this._callbacks = {};

    this._registerCallbacks();
  }
page 1 / 3next ›