Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

dumduel: smooth interpolation, debug logs, fix stutter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+62 -7
+17
session-server/duel-manager.mjs
··· 161 161 if (input.seq > player.lastInputSeq) { 162 162 player.lastInputSeq = input.seq; 163 163 } 164 + // Log first few inputs 165 + if (input.seq <= 3) { 166 + console.log(`🎯 Input from ${handle}: seq=${input.seq} target=(${input.targetX.toFixed(1)}, ${input.targetY.toFixed(1)})`); 167 + } 164 168 } 165 169 166 170 handlePing(handle, ts, wsId) { ··· 277 281 startFight() { 278 282 this.phase = "fight"; 279 283 this.broadcastWS?.("duel:fight", {}); 284 + console.log(`🎯 Duel fight started! Tick loop active.`); 280 285 } 281 286 282 287 endRound(winnerHandle) { ··· 341 346 ensureTick() { 342 347 if (!this.tickInterval) { 343 348 this.tickInterval = setInterval(() => this.serverTick(), 1000 / TICK_RATE); 349 + console.log(`🎯 Duel tick loop started (${TICK_RATE}Hz, snapshot every ${SNAPSHOT_INTERVAL} ticks)`); 344 350 } 345 351 } 346 352 ··· 348 354 if (this.tickInterval) { 349 355 clearInterval(this.tickInterval); 350 356 this.tickInterval = null; 357 + console.log(`🎯 Duel tick loop stopped`); 351 358 } 352 359 this.resetToWaiting(); 353 360 } ··· 535 542 }; 536 543 537 544 const data = JSON.stringify(snapshot); 545 + 546 + // Log periodic snapshot info 547 + if (this.tick % 300 === 0) { 548 + const channels = []; 549 + for (const [h, p] of this.players) { 550 + if (h === DUMMY_HANDLE) continue; 551 + channels.push(`${h}:${p.udpChannelId ? "UDP" : p.wsId != null ? "WS" : "NONE"}`); 552 + } 553 + console.log(`🎯 Duel snapshot #${this.tick} phase=${this.phase} via [${channels.join(", ")}]`); 554 + } 538 555 539 556 // Send to each player with UDP channel, fallback to WS 540 557 for (const [handle, player] of this.players) {
+45 -7
system/public/aesthetic.computer/disks/dumduel.mjs
··· 25 25 26 26 // Server state (from snapshots) 27 27 let snap = null; // latest snapshot 28 + let snapCount = 0; // total snapshots received 28 29 let roster = []; 29 30 let phase = "waiting"; 30 31 let countdownTimer = 0; 31 32 let roundWinner = null; 32 33 let ping = 0; 34 + 35 + // Opponent interpolation 36 + let opServerX = 0, opServerY = 0; // latest server position 37 + let opDisplayX = 0, opDisplayY = 0; // smoothed display position 38 + const LERP_SPEED = 0.25; // how fast to catch up to server pos 33 39 34 40 // Camera pan 35 41 let camX = 0, camY = 0; ··· 44 50 45 51 function applySnapshot(s) { 46 52 snap = s; 53 + snapCount++; 47 54 phase = s.phase; 48 55 countdownTimer = s.countdownTimer; 49 56 roundWinner = s.roundWinner; 50 57 roster = (s.roster || []).map((h) => ({ handle: h })); 51 58 52 - // Reconcile prediction 59 + // Log first few snapshots + periodic 60 + if (snapCount <= 3 || snapCount % 100 === 0) { 61 + console.log(`📸 snap #${snapCount} phase=${s.phase} players=${s.players?.length} bullets=${s.bullets?.length} tick=${s.tick}`); 62 + } 63 + 64 + // Reconcile own prediction 53 65 const myAck = s.lastInputSeq?.[myHandle] || 0; 54 66 pendingInputs = pendingInputs.filter((inp) => inp.seq > myAck); 55 67 56 68 const meServer = s.players?.find((p) => p.handle === myHandle); 57 69 if (meServer) { 58 - localX = meServer.x; 59 - localY = meServer.y; 70 + // Only correct position if delta is large (>5px) — otherwise let prediction run 71 + const dx = meServer.x - localX; 72 + const dy = meServer.y - localY; 73 + if (dx * dx + dy * dy > 25) { 74 + localX = meServer.x; 75 + localY = meServer.y; 76 + } 77 + 78 + // Always update target from server, then re-apply unacked inputs 60 79 localTargetX = meServer.targetX; 61 80 localTargetY = meServer.targetY; 62 - 63 81 for (const inp of pendingInputs) { 64 82 localTargetX = inp.targetX; 65 83 localTargetY = inp.targetY; ··· 67 85 68 86 ping = meServer.ping || 0; 69 87 } 88 + 89 + // Update opponent server position (for lerp in sim) 90 + const opServer = s.players?.find((p) => p.handle !== myHandle); 91 + if (opServer) { 92 + opServerX = opServer.x; 93 + opServerY = opServer.y; 94 + // On first snapshot, snap display to server pos 95 + if (snapCount === 1) { 96 + opDisplayX = opServerX; 97 + opDisplayY = opServerY; 98 + } 99 + } 70 100 } 71 101 72 102 function boot({ wipe, screen, net: { socket, udp }, handle, sound, send }) { ··· 110 140 // WebSocket — reliable game events 111 141 server = socket((id, type, content) => { 112 142 if (type.startsWith("connected")) { 143 + console.log(`🎯 Connected as ${myHandle}, sending duel:join`); 113 144 server.send("duel:join", { handle: myHandle }); 114 145 return; 115 146 } ··· 125 156 if (type === "duel:joined" || type === "duel:roster") { 126 157 roster = (msg.roster || []).map((h) => ({ handle: h })); 127 158 if (msg.phase) phase = msg.phase; 159 + console.log(`🎯 ${type}: roster=[${msg.roster?.join(", ")}] phase=${msg.phase}`); 128 160 } 129 161 130 162 if (type === "duel:countdown") { 131 163 phase = "countdown"; 132 164 countdownTimer = msg.timer || 180; 165 + console.log(`🎯 Countdown! duelists=${msg.duelists?.join(" vs ")}`); 133 166 } 134 167 135 168 if (type === "duel:fight") { 136 169 phase = "fight"; 170 + console.log(`🎯 Fight!`); 137 171 synth?.({ type: "square", tone: 440, volume: 0.7, attack: 0.01, decay: 0.15, duration: 0.2 }); 138 172 } 139 173 ··· 187 221 } 188 222 localWasMoving = isMoving; 189 223 } 224 + 225 + // Lerp opponent display position toward server position 226 + opDisplayX += (opServerX - opDisplayX) * LERP_SPEED; 227 + opDisplayY += (opServerY - opDisplayY) * LERP_SPEED; 190 228 191 229 // Elastic camera snap-back 192 230 if (!panning) { ··· 322 360 const col = isMe(p.handle) ? [50, 120, 200] : [200, 70, 60]; 323 361 const drawP = isMe(p.handle) 324 362 ? { ...p, x: localX, y: localY, targetX: localTargetX, targetY: localTargetY } 325 - : p; 363 + : { ...p, x: opDisplayX, y: opDisplayY }; 326 364 drawFigure(ink, circle, box, line, ox, oy, drawP, col, frameCount); 327 365 328 366 // Handle label (MatrixChunky8, centered) + ping 329 367 const label = p.handle; 330 368 const pingStr = p.ping > 0 ? ` ${p.ping}` : ""; 331 369 const fullLabel = label + pingStr; 332 - const lx = (isMe(p.handle) ? localX : p.x); 333 - const ly = (isMe(p.handle) ? localY : p.y); 370 + const lx = isMe(p.handle) ? localX : opDisplayX; 371 + const ly = isMe(p.handle) ? localY : opDisplayY; 334 372 ink(...col, 150).write(fullLabel, { 335 373 x: ox + Math.round(lx) - Math.round(fullLabel.length * 2), 336 374 y: oy + Math.round(ly) + 9,