Files
roblox-bot-player/web-demo/player.js
2026-03-06 19:49:28 -08:00

132 lines
3.9 KiB
JavaScript

import init, {
setup_graphics,
CompleteBot,
CompleteMap,
PlaybackHead,
} from "./pkg/strafesnet_roblox_bot_player_wasm_module.js";
// Loading
await init(); // load the wasm module
const b = await fetch("bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d.qbot");
const m = await fetch("bhop_marble_5692093612.snfm");
const canvas = document.getElementById("viewport");
const graphics = await setup_graphics(canvas);
const bot = new CompleteBot(new Uint8Array(await b.arrayBuffer()));
const map = new CompleteMap(new Uint8Array(await m.arrayBuffer()));
const playback = new PlaybackHead(bot, 0);
graphics.change_map(map);
// HUD
const hud_timer = document.getElementById("hud_timer");
const hud_duration = document.getElementById("hud_duration");
const MODE_MAIN = 0;
function timer_text(t) {
const h = Math.floor(t / 3600);
const m = Math.floor((t % 3600) / 60);
const s = Math.floor(t % 60);
const ms = Math.floor((t % 1) * 1000);
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(ms).padStart(3, "0")}`;
}
hud_duration.textContent = timer_text(bot.run_duration(MODE_MAIN));
// Stuff
const startTime = document.timeline.currentTime;
function elapsed() {
return (document.timeline.currentTime - startTime) / 1000;
}
const control_speed = document.getElementById("control_speed");
var paused = false;
var scale = 1;
function set_scale(new_scale) {
scale = new_scale;
playback.set_scale(elapsed(), scale);
control_speed.value = `${scale.toPrecision(3)}x`;
}
const SEEK_DURATION = 1.0;
// Controls
document.getElementById("control_reset").addEventListener("click", (e) => {
playback.set_head_time(bot, elapsed(), 0.0);
});
document.getElementById("control_pause").addEventListener("click", (e) => {
paused = !paused;
playback.set_paused(elapsed(), paused);
});
document.getElementById("control_forward").addEventListener("click", (e) => {
const time_now = elapsed();
const playback_time = playback.get_head_time(time_now);
const time_offset = playback.get_scale() * SEEK_DURATION;
playback.set_head_time(bot, time_now, playback_time + time_offset);
});
document.getElementById("control_backward").addEventListener("click", (e) => {
const time_now = elapsed();
const playback_time = playback.get_head_time(time_now);
const time_offset = playback.get_scale() * SEEK_DURATION;
playback.set_head_time(bot, time_now, playback_time - time_offset);
});
document.getElementById("control_slower").addEventListener("click", (e) => {
set_scale((scale * 4) / 5);
});
const regex = new RegExp("^([^x]*)x?$");
control_speed.addEventListener("change", (e) => {
const parsed = regex.exec(e.target.value);
if (!parsed) {
set_scale(1);
return;
}
const input = Number(parsed.at(1));
if (Number.isNaN(input)) {
set_scale(1);
return;
}
set_scale(input);
});
document.getElementById("control_faster").addEventListener("click", (e) => {
set_scale((scale * 5) / 4);
});
// Rendering
function animate(now) {
const elapsedMs = now - startTime;
const elapsedSec = elapsedMs / 1000; // wasm expects seconds
// Advance the playback head to the current time
var event = playback.next_event(bot);
while (event && event.time() < elapsedSec) {
playback.process_event(bot, event);
event = playback.next_event(bot);
}
// update the timer text
const time = playback.get_run_time(bot, elapsedSec, MODE_MAIN);
hud_timer.textContent = timer_text(time);
// Render the frame that the bot is at that time
graphics.render(bot, playback, elapsedSec);
// Keep the loop going
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// Resizing
function resize() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const fov_y = playback.get_fov_slope_y();
const fov_x = (fov_y * canvas.width) / canvas.height;
graphics.resize(canvas.width, canvas.height, fov_x, fov_y);
}
window.addEventListener("resize", resize);
resize();