diff --git a/lith/Caddyfile b/lith/Caddyfile
index 911a505b44..f164cc63c8 100644
--- a/lith/Caddyfile
+++ b/lith/Caddyfile
@@ -1057,7 +1057,7 @@ oskiewar.com, midi.oskiewar.com {
# answers 304 against its ETag and loads from disk. Without the header the
# browser's heuristic kept whole modules stale across deploys — a returning
# visitor once ran a months-old round-room.mjs against the current relay.
- @oskiewarmodules path /oskiewar.js /oskiewar-sfx.mjs /oskiewar-voice.mjs /oskiewar-midi.mjs /oskiewar-wizard.mjs
+ @oskiewarmodules path /oskiewar.js /oskiewar-sfx.mjs /oskiewar-voice.mjs /oskiewar-midi.mjs /oskiewar-wizard.mjs /oskiewar-map.mjs /oskiewar-workshop.mjs
handle @oskiewarmodules {
root * /opt/ac/xbox/live
header Cache-Control no-cache
diff --git a/session-server/oskiewar-live-manager.mjs b/session-server/oskiewar-live-manager.mjs
index a82bfd9076..6416fbfbd9 100644
--- a/session-server/oskiewar-live-manager.mjs
+++ b/session-server/oskiewar-live-manager.mjs
@@ -6,6 +6,7 @@ import {
oskiewarEvent,
oskiewarSurface,
} from "../system/public/aesthetic.computer/lib/oskiewar-analytics.mjs";
+import { validateMap } from "../xbox/live/oskiewar-map.mjs";
import { createPostHogEventCapture } from "../shared/posthog-event-capture.mjs";
const MATCH_WORD = "[bdfgklmnprstvz][aeiou][bdfgklmnprstvz][aeiou][bdfgklmnprstvz][aeiou]";
@@ -14,7 +15,7 @@ const MATCH_NAME = new RegExp(
const MATCH_ID = new RegExp(
`^ow-(?:${MATCH_WORD}-${MATCH_WORD}-${MATCH_WORD}|[a-z]{4,7}[0-9]{1,3})$`);
const PHASES = new Set(["select", "intro", "fight", "round", "match", "replay"]);
-const MAX_MESSAGE_BYTES = 8192;
+const MAX_MESSAGE_BYTES = 16384; // Includes bounded workshop geometry for spectators.
const MAX_VIEWERS = 64;
// Agents watch the same fan-out as a phone but are counted and capped on their
// own, so a room full of spectators can never lock a maintainer out of the
@@ -129,6 +130,9 @@ function perf(value) {
}
export function validateOskiewarLiveState(value) {
+ if (value?.map?.workshop !== undefined) {
+ try { validateMap(value.map.workshop); } catch { return "Invalid workshop map"; }
+ }
if (!value || value.format !== "ac.oskiewar.live" || value.version !== 1)
return "Unsupported live state";
if (!integer(value.seq, 0, 2147483647) || !finite(value.at, 10000000000000))
@@ -358,7 +362,7 @@ export class OskiewarLiveManager {
// wants a remote nudge. The relay forwards the bare instruction and
// nothing else, at most once per five seconds per room, and the shell
// decides when reloading is actually safe.
- ws.on("message", (data) => this.nudge(room, data));
+ ws.on("message", (data) => this.nudge(room, data, ws));
const remove = () => {
room.agents.delete(ws);
room.updatedAt = this.now();
@@ -477,10 +481,28 @@ export class OskiewarLiveManager {
send(target, "oskiewar:net", content);
}
- nudge(room, data) {
- if (Buffer.byteLength(data) > 512) return;
+ nudge(room, data, ws) {
+ if (Buffer.byteLength(data) > 16384) return;
let message;
try { message = JSON.parse(data.toString()); } catch { return; }
+ if (message.type === "oskiewar:workshop") {
+ const content = message.content;
+ if (!content || typeof content.id !== "string" || content.id.length > 80 ||
+ !content.command || typeof content.command !== "object") return;
+ const now = this.now();
+ room.workshopPending ||= new Map();
+ for (const [id, entry] of room.workshopPending)
+ if (entry.expires < now) room.workshopPending.delete(id);
+ const refuse = error => send(ws, "oskiewar:workshop-result",
+ { id: content.id, ok: false, error });
+ if (!room.publisher) return refuse("No live publisher");
+ if (room.workshopPending.size >= 8) return refuse("Workshop is busy");
+ const id = String(room.workshopSequence = (room.workshopSequence || 0) + 1);
+ room.workshopPending.set(id, { ws, id: content.id, expires: now + 15000 });
+ send(room.publisher, "oskiewar:workshop", { id, command: content.command });
+ return;
+ }
+ if (Buffer.byteLength(data) > 512) return;
if (message.type === "oskiewar:reload") {
const now = this.now();
if (room.nudgedAt && now - room.nudgedAt < 5000) return;
@@ -511,8 +533,21 @@ export class OskiewarLiveManager {
publish(room, ws, data) {
if (room.publisher !== ws) return;
+ if (Buffer.byteLength(data) <= 32768) {
+ let reply;
+ try { reply = JSON.parse(data.toString()); } catch {}
+ if (reply?.type === "oskiewar:workshop-result") {
+ const entry = room.workshopPending?.get(reply.content?.id);
+ if (entry) {
+ room.workshopPending.delete(reply.content.id);
+ if (entry.expires >= this.now() && room.agents.has(entry.ws))
+ send(entry.ws, reply.type, { ...reply.content, id: entry.id });
+ }
+ return;
+ }
+ }
if (Buffer.byteLength(data) > MAX_MESSAGE_BYTES) {
- send(ws, "oskiewar:error", { message: "Live state exceeds 8 KiB" });
+ send(ws, "oskiewar:error", { message: "Live state exceeds 16 KiB" });
return;
}
let message;
diff --git a/session-server/oskiewar-live-manager.test.mjs b/session-server/oskiewar-live-manager.test.mjs
index a93a13a87d..a3792eda1f 100644
--- a/session-server/oskiewar-live-manager.test.mjs
+++ b/session-server/oskiewar-live-manager.test.mjs
@@ -617,3 +617,30 @@ test("the rollback lane's packets pass seat to seat and never reach the grandsta
assert.equal(host.sent.length, 2);
assert.equal(host.sent[1].type, "oskiewar:input");
});
+
+test('workshop routes acknowledgements only to the requesting agent', () => {
+ const manager = new OskiewarLiveManager();
+ const host = new FakeSocket(), agent = new FakeSocket(), other = new FakeSocket(), viewer = new FakeSocket();
+ for (const [ws, role] of [[host, 'publisher'], [agent, 'agent'], [other, 'agent'], [viewer, 'viewer']])
+ manager.handleConnection(ws, { url: '/oskiewar-live?match=sezzi7&role=' + role });
+ const packet = Buffer.from(JSON.stringify({ type: 'oskiewar:workshop',
+ content: { id: 'same', command: { op: 'inspect' } } }));
+ const count = host.sent.length;
+ viewer.emit('message', packet);
+ assert.equal(host.sent.length, count);
+ agent.emit('message', packet);
+ const first = host.sent.at(-1).content.id;
+ other.emit('message', packet);
+ const second = host.sent.at(-1).content.id;
+ assert.notEqual(first, second);
+ const reply = id => Buffer.from(JSON.stringify({ type: 'oskiewar:workshop-result',
+ content: { id, ok: true, result: { revision: 7 } } }));
+ agent.emit('message', reply(first)); // Agents cannot forge publisher replies.
+ assert.notEqual(agent.sent.at(-1).type, 'oskiewar:workshop-result');
+ host.emit('message', reply(first));
+ assert.equal(agent.sent.at(-1).content.id, 'same');
+ assert.equal(agent.sent.at(-1).content.result.revision, 7);
+ assert.notEqual(other.sent.at(-1).type, 'oskiewar:workshop-result');
+ host.emit('message', reply(second));
+ assert.equal(other.sent.at(-1).content.id, 'same');
+});
diff --git a/system/netlify/functions/oskiewar-maps.mjs b/system/netlify/functions/oskiewar-maps.mjs
new file mode 100644
index 0000000000..5f1265129e
--- /dev/null
+++ b/system/netlify/functions/oskiewar-maps.mjs
@@ -0,0 +1,61 @@
+// Private immutable drafts and public immutable map versions, owned by AC accounts.
+import { createHash } from 'node:crypto';
+import { validateMap } from '../../../xbox/live/oskiewar-map.mjs';
+
+const headers = { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' };
+const reply = (statusCode, value) => ({ statusCode, headers, body: JSON.stringify(value) });
+const idPattern = /^[a-f0-9]{64}$/;
+
+export function createMapsHandler({ authorize, connect }) {
+ return async event => {
+ if (!['GET', 'POST'].includes(event.httpMethod)) return reply(405, { message: 'GET or POST only' });
+ let database;
+ try {
+ const query = event.queryStringParameters || {};
+ const user = await authorize(event.headers || {});
+ const owner = user?.sub;
+ let map;
+ let body;
+ if (event.httpMethod === 'POST') {
+ if (!owner) return reply(401, { message: 'Sign in to save maps' });
+ if (typeof event.body !== 'string' || Buffer.byteLength(event.body) > 16384)
+ return reply(413, { message: 'Map exceeds 16 KiB' });
+ try { body = JSON.parse(event.body); map = validateMap(body.map); }
+ catch (error) { return reply(400, { message: error.message }); }
+ } else {
+ if (query.id !== undefined && !idPattern.test(query.id)) return reply(400, { message: 'Invalid map id' });
+ if (query.mine === '1' && !owner) return reply(401, { message: 'Sign in to list drafts' });
+ }
+ database = await connect();
+ const collection = database.db.collection('oskiewar-maps');
+ if (event.httpMethod === 'POST') {
+ const published = body.publish === true;
+ const id = createHash('sha256').update(JSON.stringify([owner, published, map])).digest('hex');
+ await collection.updateOne({ _id: id }, { $setOnInsert: {
+ owner, published, map, createdAt: new Date().toISOString(),
+ } }, { upsert: true });
+ return reply(201, { id, published, map,
+ ...(published ? { url: 'https://oskiewar.com/?workshop=' + id } : {}) });
+ }
+ if (query.id) {
+ const row = await collection.findOne({ _id: query.id,
+ $or: [{ published: true }, ...(owner ? [{ owner }] : [])] });
+ if (!row) return reply(404, { message: 'Map not found' });
+ return reply(200, { id: row._id, published: row.published, map: row.map });
+ }
+ const rows = await collection.find(query.mine === '1' ? { owner } : { published: true },
+ { projection: { _id: 1, 'map.name': 1, published: 1, createdAt: 1 } })
+ .sort({ createdAt: -1 }).limit(50).toArray();
+ return reply(200, { maps: rows.map(row => ({ id: row._id, name: row.map.name,
+ published: row.published, createdAt: row.createdAt })) });
+ } catch {
+ return reply(503, { message: 'Map storage unavailable' });
+ } finally { if (database) await database.disconnect(); }
+ };
+}
+export async function handler(event) {
+ const [{ authorize }, { connect }] = await Promise.all([
+ import('../../backend/authorization.mjs'), import('../../backend/database.mjs'),
+ ]);
+ return createMapsHandler({ authorize, connect })(event);
+}
diff --git a/xbox/live/coach.html b/xbox/live/coach.html
index 6f5362ac00..4170beef53 100644
--- a/xbox/live/coach.html
+++ b/xbox/live/coach.html
@@ -135,6 +135,22 @@ claude mcp add coach -- node ./coach.mjs
+
Build a map while playing
+ Open the map workshop, enable
+ Coach editing, and connect your coach to the room shown
+ on the title. coach_workshop can reshape terrain and platforms,
+ move and highlight spawns, drop items, undo edits, reset the round, or
+ restart the level. The edited map stays in place between rounds.
+ Ask: Raise the middle platform, put a rocket launcher on the left,
+ highlight the spawns, and restart so I can try it. Then:
+ Save this as Moon Yard. Publish it and give me the play link.
+ Sign in to save private drafts or publish. Each save keeps a separate
+ version; published versions have a permanent play link. The coach can
+ list and load them later. Editing currently works in local practice in
+ the browser. Edited sessions do not upload competitive replays; reload
+ before returning to recorded matches. Turning Coach editing off prevents
+ further commands; an already submitted save may still finish.
+
Things to say to it
- Sit in on regga890 and tell me after each round what to change.
@@ -149,7 +165,9 @@ claude mcp add coach -- node ./coach.mjs
watch it, the same way anyone with the address can join the second chair.
A coach reads fighter positions, states, scores and the moves on screen.
It does not see your keyboard, your account, or anything outside the room.
- It cannot press a button, and the game counts it apart from human
+ Editing is off by default. When you enable it, attached coaches may edit
+ the practice map and save or publish it through your signed-in account.
+ The game counts coaches apart from human
spectators — the antenna on the title means a machine is reading, not one
more person in the stands. Up to four coaches can share one room.
diff --git a/xbox/live/coach.mjs b/xbox/live/coach.mjs
index b324d61b4a..8bd0cefdb0 100644
--- a/xbox/live/coach.mjs
+++ b/xbox/live/coach.mjs
@@ -7,7 +7,7 @@
// curl -fsSLO https://oskiewar.com/coach.mjs
// claude mcp add coach -- node ./coach.mjs
//
-// The coach takes the relay's read-only `agent` seat on a match room — the one
+// The coach takes the relay's `agent` seat on a match room — the one
// the title screen prints under START — and reads the same frames every phone
// in the grandstand reads. It never presses a button. From those frames it
// keeps a ledger a model can coach from: every hit, who threw it, what you
@@ -16,7 +16,7 @@
// them; `coach_record` pulls your track record from the replay ledger. While
// it is seated the title screen says "coach linked".
//
-// Runs on Node 22+ (global WebSocket and fetch). Nothing here writes anywhere.
+// Runs on Node 22+. Workshop writes require the player to enable Coach editing.
import { createInterface } from "node:readline";
import { pathToFileURL } from "node:url";
@@ -269,6 +269,10 @@ export function createLedger() {
// The seat. One attachment at a time; a round room hands off to the next and
// the session room is the forwarding address when a round goes quiet.
+const workshopPending = new Map();
+let workshopSequence = 0;
+let workshopNextRoom = null;
+
const state = {
socket: null, room: null, wantRoom: null, sessionRoom: "", label: "coach",
generation: 0, quietTimer: null, retryTimer: null, status: null,
@@ -325,6 +329,19 @@ function open(room) {
if (mine !== state.generation) return;
let message;
try { message = JSON.parse(event.data); } catch { return; }
+ if (message.type === "oskiewar:workshop-result") {
+ const pending = workshopPending.get(message.content?.id);
+ if (pending) {
+ workshopPending.delete(message.content.id);
+ clearTimeout(pending.timer);
+ message.content.ok ? pending.resolve(text(message.content.result))
+ : pending.reject(new Error(message.content.error || "Workshop command failed"));
+ }
+ if (workshopNextRoom) {
+ const next = workshopNextRoom; workshopNextRoom = null; follow(next);
+ }
+ return;
+ }
if (message.type === "oskiewar:error") {
done(new Error(String(message.content?.message || "relay error")));
return;
@@ -343,8 +360,10 @@ function open(room) {
state.ledger.observe(frame);
done();
armQuiet();
- if (frame.nextRoundId && frame.nextRoundId !== state.room)
- follow(frame.nextRoundId);
+ if (frame.nextRoundId && frame.nextRoundId !== state.room) {
+ if (workshopPending.size) workshopNextRoom = frame.nextRoundId;
+ else follow(frame.nextRoundId);
+ }
});
socket.addEventListener("close", () => {
if (mine !== state.generation) return;
@@ -365,6 +384,7 @@ function open(room) {
}
function detach() {
+ workshopNextRoom = null;
state.wantRoom = null;
state.generation++;
stopTimers();
@@ -515,9 +535,51 @@ function toolOut() {
note: `Left ${room}. The title screen's "coach linked" goes dark.` });
}
+function toolWorkshop(command) {
+ if (!state.socket || state.socket.readyState !== 1)
+ throw new Error("Call coach_in and wait for a live connection first");
+ if (workshopPending.size) throw new Error("Wait for the previous workshop command");
+ const id = String(++workshopSequence);
+ const packet = JSON.stringify({ type: "oskiewar:workshop", content: { id, command } });
+ if (Buffer.byteLength(packet) > 16384) throw new Error("Workshop command exceeds 16 KiB");
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => {
+ workshopPending.delete(id);
+ if (workshopNextRoom) { const next = workshopNextRoom; workshopNextRoom = null; follow(next); }
+ reject(new Error("No workshop acknowledgement. Inspect before retrying; the operation may have completed."));
+ }, 15000);
+ workshopPending.set(id, { resolve, reject, timer });
+ try { state.socket.send(packet); } catch (error) {
+ clearTimeout(timer); workshopPending.delete(id); reject(error);
+ }
+ });
+}
+
const TOOLS = [
+ { name: "coach_workshop",
+ description: "Edit a local practice map while the player plays. Player must enable Coach editing. " +
+ "Start with inspect: returns the complete map, revision and player positions. " +
+ "apply replaces the map with your edited document; drop adds an item; undo restores the previous map. " +
+ "reset-round preserves round wins; restart-level clears them. Both keep the edited map. " +
+ "highlight shows spawn markers. save stores a named private draft; publish creates an immutable public version " +
+ "using the player's signed-in account. list lists public maps (mine:true lists own drafts and publications); " +
+ "load applies a saved map by id. Use the latest revision for every mutation/save/publish/load. " +
+ "Map version 1: format ac.oskiewar.map, name, features (ordered contiguous from/to covering 0–40, " +
+ "kind flat/bank/transition, lift -450–720, rise 0–720, dir -1/1), decks (col,cols,row), " +
+ "two spawns (columns 0–39), pickups (kind,col,amount), skateboard boolean. " +
+ "Item kinds: HANDGUN, SPACE LASER, RUBBER SMG, ROCKET LAUNCHER, LIGHT SABER, GRENADE. " +
+ "Read the returned map after changes, then watch play and iterate. Network/survival/replay editing is unavailable.",
+ inputSchema: { type: "object", additionalProperties: false, required: ["op"], properties: {
+ op: { type: "string", enum: ["inspect", "apply", "drop", "undo", "reset-round", "restart-level",
+ "highlight", "save", "publish", "load", "list"] },
+ revision: { type: "integer", minimum: 0 }, map: { type: "object" },
+ item: { type: "object", required: ["kind", "col", "amount"], properties: {
+ kind: { type: "string" }, col: { type: "number" }, amount: { type: "integer" } } },
+ enabled: { type: "boolean" }, name: { type: "string", maxLength: 60 },
+ id: { type: "string" }, mine: { type: "boolean" },
+ } } },
{ name: "coach_in",
- description: "Add your coach: take the read-only agent seat on an " +
+ description: "Add your coach: take the agent seat on an " +
"oskiewar match room so this session can watch the fight. Give the " +
"room name printed under START on the title screen, or a handle to " +
"sit in their most recent versus room. The title screen shows " +
@@ -571,6 +633,7 @@ const TOOLS = [
async function callTool(name, args = {}) {
switch (name) {
+ case "coach_workshop": return toolWorkshop(args);
case "coach_in": return toolIn(args);
case "coach_status": return toolStatus();
case "coach_watch": return toolWatch(args);
@@ -590,13 +653,13 @@ export async function handleMessage(message) {
return { jsonrpc: "2.0", id, result: {
protocolVersion: params?.protocolVersion || "2024-11-05",
capabilities: { tools: {} },
- serverInfo: { name: "oskiewar-coach", version: "1.0.0" },
+ serverInfo: { name: "oskiewar-coach", version: "1.1.0" },
instructions: "Claude Coach for oskiewar. coach_in seats this " +
- "session as a read-only agent on a match room (the name under " +
+ "session as an agent on a match room (the name under " +
"START on the title screen); coach_watch streams hits, blocks, " +
"swings and deaths as they happen; coach_analyze folds them into " +
"numbers to coach from; coach_record and coach_replay read the " +
- "replay ledger. The coach never presses a button." } };
+ "replay ledger. coach_workshop edits local practice maps and saves or publishes them when the player enables Coach editing. Inspect before editing and use the returned revision." } };
case "initialized":
case "notifications/initialized": return null;
case "ping": return { jsonrpc: "2.0", id, result: {} };
diff --git a/xbox/live/mac-test.html b/xbox/live/mac-test.html
index d61161261c..5ecab24406 100644
--- a/xbox/live/mac-test.html
+++ b/xbox/live/mac-test.html
@@ -23,7 +23,7 @@
{
const harness = new URLSearchParams(location.search);
globalThis.__oskiewarVersusCapable = !harness.has("social-preview") &&
- !harness.has("replay-oven") && !harness.has("self-play");
+ !harness.has("replay-oven") && !harness.has("self-play") && !harness.has("workshop");
}
// A bare front door asks the relay for one open room, and the first
// frame waits on the answer. The ask used to leave only after the whole
@@ -242,7 +242,8 @@
// Pal select is intentionally retired; this keeps Spiderdummy reachable
// without restoring a screen that no longer matches the front door.
globalThis.__oskiewarOpponent =
- new URLSearchParams(location.search).get("opponent") || "";
+ new URLSearchParams(location.search).get("opponent") ||
+ (new URLSearchParams(location.search).has("workshop") ? "dummy" : "");
// `__oskiewarVersusCapable` is raised in the head, beside the fetches
// that depend on it.
let titleOpen = true;
@@ -1198,6 +1199,18 @@
resizeCanvas();
}
}
+ if (message.type === "oskiewar:workshop") {
+ const { id, command } = message.content || {};
+ Promise.resolve().then(() => {
+ if (!globalThis.__oskiewarWorkshopRequest) throw new Error("Workshop unavailable");
+ return globalThis.__oskiewarWorkshopRequest(command);
+ }).then(result => ({ id, ok: true, result }),
+ error => ({ id, ok: false, error: error.message }))
+ .then(content => {
+ if (socket.readyState === WebSocket.OPEN)
+ socket.send(JSON.stringify({ type: "oskiewar:workshop-result", content }));
+ });
+ }
if (message.type === "oskiewar:reload") {
if (globalThis.__oskiewarTouch?.screen === "title") {
document.querySelector("#status").textContent = "updating";
@@ -1439,6 +1452,8 @@
import createFrameDriver from "/frame-driver.mjs";
import mountAccount from "/account.mjs";
import mountWizard from "/oskiewar-wizard.mjs";
+ import { mountWorkshop } from "/oskiewar-workshop.mjs";
+ if (!socialPreview) await mountWorkshop();
import { qrcode } from "/aesthetic.computer/dep/@akamfoad/qr/qr.mjs";
import {
captureOskiewarAction,
diff --git a/xbox/live/oskiewar-map.mjs b/xbox/live/oskiewar-map.mjs
new file mode 100644
index 0000000000..91e7670ee2
--- /dev/null
+++ b/xbox/live/oskiewar-map.mjs
@@ -0,0 +1,42 @@
+// Portable workshop maps. Only data crosses the coach connection.
+export const MAP_FORMAT = 'ac.oskiewar.map';
+export const ITEM_KINDS = ['HANDGUN', 'SPACE LASER', 'RUBBER SMG',
+ 'ROCKET LAUNCHER', 'LIGHT SABER', 'GRENADE'];
+export function validateMap(value) {
+ const fail = message => { throw new Error(message); };
+ const number = (n, lo, hi) => Number.isFinite(n) && n >= lo && n <= hi;
+ if (!value || value.format !== MAP_FORMAT || value.version !== 1)
+ fail('Expected ac.oskiewar.map version 1');
+ if (typeof value.name !== 'string' || !value.name.trim() || value.name.length > 60)
+ fail('Name must contain 1–60 characters');
+ if (!Array.isArray(value.features) || !value.features.length || value.features.length > 24)
+ fail('Use 1–24 terrain segments');
+ let edge = 0;
+ const features = value.features.map(f => {
+ if (!f || f.from !== edge || !number(f.to, f.from + .25, 40) ||
+ !['flat', 'bank', 'transition'].includes(f.kind) ||
+ !number(f.lift ?? 0, -450, 720) || !number(f.rise ?? 0, 0, 720) ||
+ (f.kind !== 'flat' && ![-1, 1].includes(f.dir)))
+ fail('Terrain must cover columns 0–40 in order; lift -450–720, rise 0–720');
+ edge = f.to;
+ return { from: f.from, to: f.to, kind: f.kind, lift: f.lift ?? 0,
+ rise: f.rise ?? 0, dir: f.dir === -1 ? -1 : 1 };
+ });
+ if (edge !== 40) fail('Terrain must end at column 40');
+ if (!Array.isArray(value.spawns) || value.spawns.length !== 2 ||
+ !value.spawns.every(col => number(col, 0, 39))) fail('Supply two spawn columns (0–39)');
+ if (!Array.isArray(value.decks) || value.decks.length > 16) fail('Use at most 16 decks');
+ const decks = value.decks.map(d => {
+ if (!d || !number(d.col, 0, 39) || !number(d.cols, .5, 40 - d.col) ||
+ !number(d.row, 1, 14)) fail('Invalid deck col/cols/row');
+ return { col: d.col, cols: d.cols, row: d.row };
+ });
+ if (!Array.isArray(value.pickups) || value.pickups.length > 32) fail('Use at most 32 pickups');
+ const pickups = value.pickups.map(p => {
+ if (!p || !ITEM_KINDS.includes(p.kind) || !number(p.col, 0, 39) ||
+ !Number.isInteger(p.amount) || !number(p.amount, 0, 99)) fail('Invalid pickup kind/col/amount');
+ return { kind: p.kind, col: p.col, amount: p.amount };
+ });
+ return { format: MAP_FORMAT, version: 1, name: value.name.trim(), features,
+ decks, spawns: value.spawns.slice(), pickups, skateboard: value.skateboard === true };
+}
diff --git a/xbox/live/oskiewar-workshop.mjs b/xbox/live/oskiewar-workshop.mjs
new file mode 100644
index 0000000000..a4955f82be
--- /dev/null
+++ b/xbox/live/oskiewar-workshop.mjs
@@ -0,0 +1,66 @@
+import { validateMap } from './oskiewar-map.mjs';
+
+export async function mountWorkshop() {
+ globalThis.__oskiewarValidateMap = validateMap;
+ const label = document.createElement('label');
+ label.style.cssText = 'position:fixed;bottom:8px;left:8px;z-index:30;background:#111;color:white;padding:8px;font:14px sans-serif';
+ const toggle = document.createElement('input');
+ toggle.type = 'checkbox';
+ label.append(toggle, ' Coach editing');
+ label.title = 'Allow attached coaches to edit this local game and save or publish maps with your account. Network matches are excluded.';
+ const practice = document.createElement('a');
+ practice.href = '/?workshop=1';
+ practice.textContent = ' · Map workshop';
+ practice.style.color = '#8eeaff';
+ label.append(practice);
+ for (const event of ['pointerdown', 'keydown', 'keyup'])
+ label.addEventListener(event, e => e.stopPropagation());
+ document.body.append(label);
+ const mapId = new URLSearchParams(location.search).get('workshop');
+ if (mapId && mapId !== '1') {
+ try {
+ if (!/^[a-f0-9]{64}$/.test(mapId)) throw new Error('Invalid map link');
+ const response = await fetch('/api/oskiewar-maps?id=' + mapId,
+ { signal: AbortSignal.timeout(10000) });
+ const body = await response.json();
+ if (!response.ok) throw new Error(body.message || 'Map unavailable');
+ globalThis.__oskiewarPublishedMap = validateMap(body.map);
+ } catch (error) { label.append(' · ' + error.message); }
+ }
+ toggle.addEventListener('change', () => {
+ globalThis.__oskiewarWorkshopEnabled = toggle.checked;
+ });
+ let busy = false;
+ globalThis.__oskiewarWorkshopRequest = async command => {
+ if (!toggle.checked) throw new Error('Turn on Coach editing in the game first');
+ if (busy) throw new Error('Another workshop command is running');
+ busy = true;
+ try {
+ const game = globalThis.__oskiewarWorkshopCommand;
+ if (!game) throw new Error('Game is not ready');
+ const current = game({ op: 'inspect' }); // Also checks play mode.
+ if (!['save', 'publish', 'load', 'list'].includes(command.op)) return game(command);
+ if (command.op === 'load') {
+ const saved = await api('GET', { id: command.id });
+ return game({ op: 'apply', revision: command.revision, map: saved.map });
+ }
+ if (command.op === 'list') return await api('GET', { mine: command.mine === true ? '1' : '0' });
+ if (command.revision !== current.revision) throw new Error('Map changed; inspect before saving');
+ const map = validateMap({ ...current.map, name: command.name ?? current.map.name });
+ return await api('POST', {}, { map, publish: command.op === 'publish' });
+ } finally { busy = false; }
+ };
+ async function api(method, query, body) {
+ const token = await globalThis.__oskiewarAccount?.bearer();
+ if ((method === 'POST' || query.mine === '1') && !token) throw new Error('Sign in to save maps');
+ if (!toggle.checked) throw new Error('Coach editing was turned off');
+ const response = await fetch('/api/oskiewar-maps?' + new URLSearchParams(query), {
+ method, headers: { 'Content-Type': 'application/json',
+ ...(token ? { authorization: 'Bearer ' + token } : {}) },
+ ...(body ? { body: JSON.stringify(body) } : {}), signal: AbortSignal.timeout(10000),
+ });
+ const result = await response.json();
+ if (!response.ok) throw new Error(result.message || 'Map request failed');
+ return result;
+ }
+}
diff --git a/xbox/live/oskiewar.js b/xbox/live/oskiewar.js
index 5641eb65e6..dbd095d1d6 100644
--- a/xbox/live/oskiewar.js
+++ b/xbox/live/oskiewar.js
@@ -81,7 +81,7 @@ if (hostAnalytics)
};
// Monotonic count of committed revisions to this piece (next revision included).
-const buildVersion = 128;
+const buildVersion = 129;
const floorY = 1800;
// Oskiewar now opens as a versus game. An ordinary web visit hosts a room —
// the URL becomes the invitation — and until a friend opens it, all you can
@@ -229,7 +229,7 @@ const parkRight = parkSegments[parkSegments.length - 1].right;
// span stops at it. A bowl dug three tiles into the floor makes every one of
// those assumptions wrong by exactly the depth of the bowl, and the symptom
// is a fighter riding out of frame at the bottom of the pipe.
-const parkDeepest = floorY + Math.max(0,
+let parkDeepest = floorY + Math.max(0,
...parkSegments.map((segment) => -segment.lift));
// Enough samples that an arc reads as an arc. One per tile drew the halfpipe
// as a staircase — 90 units is most of a fighter wide, and a transition turns
@@ -1336,12 +1336,144 @@ const ball = balls[0];
// The board is not gated by `ballEnabled`. That switch is the ball's — it
// takes the serve, the cross-wack and the BALLED death off the map together —
// and the board shares none of them.
-const skateBoardEnabled = true;
+let skateBoardEnabled = true;
// The ball is out of the round — @jeffrey asked for the cube bare. All of
// the ball's machinery (serve, boot, carry, cross-wack, the BALLED death)
// sleeps behind this switch exactly as it always did for the test harness;
// flipping it back on is the whole re-installation.
let ballEnabled = false;
+
+function mapHash(text) {
+ let hash = 2166136261;
+ for (const character of String(text || "oskiewar")) {
+ hash ^= character.charCodeAt(0);
+ hash = Math.imul(hash, 16777619);
+ }
+ return hash >>> 0;
+}
+let currentMapName = "HALFPIPE";
+let currentMapId = "halfpipe";
+let workshopBase = null;
+// Workshop edits are local practice state. They never enter rollback matches
+// or the competitive replay ledger; documents are saved separately.
+let workshopMap = null;
+let workshopRevision = 0;
+let workshopHistory = [];
+let workshopHighlight = false;
+let workshopTainted = false;
+
+function workshopSnapshot() {
+ return { format: "ac.oskiewar.map", version: 1,
+ name: workshopMap?.name || currentMapName,
+ features: parkSegments.map(({ from, to, kind, lift, rise, dir }) =>
+ ({ from, to, kind, lift: lift || 0, rise: rise || 0, dir: dir || 1 })),
+ decks: parkDecks.map(d => ({ col: (d.left - gridLeft) / tileSize,
+ cols: (d.right - d.left) / tileSize, row: (floorY - d.y) / tileSize })),
+ spawns: players.map(p => (p.spawnX - gridLeft) / tileSize - .5),
+ pickups: workshopMap?.pickups.map(p => ({ ...p })) ||
+ [...gunPickups, ...saberPickups, ...grenadePickups].filter(p => p.startsActive)
+ .map(p => ({ kind: p.kind || "GRENADE", col: tileCol(p.x), amount: p.amount || 0 })),
+ skateboard: skateBoardEnabled };
+}
+
+function installWorkshopMap(map) {
+ workshopMap = map;
+ parkSegments.splice(0, parkSegments.length, ...map.features.map(f => ({ ...f,
+ left: gridLeft + f.from * tileSize, right: gridLeft + f.to * tileSize })));
+ parkDeepest = floorY + Math.max(0, ...parkSegments.map(s => -s.lift));
+ rebuildTerrainProfile();
+ parkDecks.splice(0, parkDecks.length, ...map.decks.map((d, i) => ({ level: i + 1,
+ left: gridLeft + d.col * tileSize, right: gridLeft + (d.col + d.cols) * tileSize,
+ y: floorY - d.row * tileSize })));
+ players.forEach((p, i) => { p.spawnX = tileCenterX(map.spawns[i]); });
+ installMapPickups(gunPickups, map.pickups.filter(p =>
+ !["GRENADE", "LIGHT SABER"].includes(p.kind)), "gun");
+ installMapPickups(saberPickups, map.pickups.filter(p => p.kind === "LIGHT SABER"), "saber");
+ installMapPickups(grenadePickups, map.pickups.filter(p => p.kind === "GRENADE"), "grenade");
+ skateBoardEnabled = map.skateboard;
+ currentMapName = map.name;
+ currentMapId = "workshop-" + mapHash(JSON.stringify(map)).toString(36);
+ // Reconcile newly raised floors without restarting the players' exchange.
+ for (const p of players) {
+ const ground = terrainFloorAt(p.x);
+ if (p.y > ground || p.grounded) { p.y = ground; p.vy = 0; p.grounded = true; }
+ }
+ renderPreviousState = null;
+}
+
+function workshopCommand(command) {
+ if (!globalThis.__oskiewarWorkshopEnabled) throw new Error("Coach editing is off");
+ if (netSession || roundViewer || versusLane() || survivalActive() || resimActive)
+ throw new Error("Use local practice for map editing; leave the network/survival/replay lane first");
+ if (!command || typeof command !== "object") throw new Error("Expected a workshop command");
+ const { op } = command;
+ if (op === "inspect") return { revision: workshopRevision, map: workshopSnapshot(),
+ highlights: workshopHighlight, undo: workshopHistory.length,
+ units: { columns: gridCols, tileSize, floorY },
+ players: players.map(p => ({ x: p.x, y: p.y, spawnX: p.spawnX })) };
+ if (op === "highlight") {
+ if (typeof command.enabled !== "boolean") throw new Error("Supply enabled: true or false");
+ workshopHighlight = command.enabled;
+ return workshopCommand({ op: "inspect" });
+ }
+ if (command.revision !== workshopRevision) throw new Error("Map changed; inspect and use its revision");
+ if (!["apply", "drop", "undo", "reset-round", "restart-level"].includes(op))
+ throw new Error("Unknown workshop operation");
+ let map;
+ if (op === "undo") {
+ if (!workshopHistory.length) throw new Error("Nothing to undo");
+ map = workshopHistory[workshopHistory.length - 1];
+ } else {
+ map = op === "apply" ? command.map : workshopSnapshot();
+ if (op === "drop") map.pickups.push(command.item);
+ }
+ map = globalThis.__oskiewarValidateMap(map);
+ if (op === "undo") workshopHistory.pop();
+ else if (!["reset-round", "restart-level"].includes(op)) {
+ workshopHistory.push(workshopSnapshot());
+ if (workshopHistory.length > 20) workshopHistory.shift();
+ }
+ workshopTainted = true;
+ workshopRevision++;
+ installWorkshopMap(map);
+ if (op === "reset-round" || op === "restart-level")
+ resetRound(runtime().monotonicUs, op === "restart-level");
+ return workshopCommand({ op: "inspect" });
+}
+
+function installMapPickups(target, authored, type) {
+ target.length = 0;
+ for (const entry of authored) {
+ const x = tileCenterX(entry.col);
+ target.push({
+ ...(type === "gun" ? { kind: entry.kind, amount: entry.amount,
+ cycle: Boolean(entry.cycle) } : {}),
+ ...(type === "saber" ? { kind: "LIGHT SABER" } : {}),
+ ...(type === "grenade" ? { amount: entry.amount } : {}),
+ x, y: terrainFloorAt(x) - 70, z: 0,
+ active: true, startsActive: true, respawnAt: 0,
+ });
+ }
+}
+
+function resetWorkshopMap() {
+ if (!workshopMap) return;
+ if ((globalThis.__oskiewarWorkshopEnabled || globalThis.__oskiewarPublishedMap) &&
+ !versusLane() && !survivalActive() && !roundViewer && !resimActive) {
+ installWorkshopMap(workshopMap);
+ return;
+ }
+ const spawns = players.map(p => p.spawnX);
+ installWorkshopMap(workshopBase);
+ workshopMap = null;
+ workshopHistory = [];
+ workshopRevision++;
+ currentMapName = "HALFPIPE";
+ currentMapId = "halfpipe";
+ if (versusLane() || survivalActive()) players.forEach((p, i) => { p.spawnX = spawns[i]; });
+}
+
+
// Physics remains an exact 60 Hz story. Rendering may happen between those
// authored instants—especially during slow motion—so retain the state that
// entered each tick and blend only presentation coordinates toward the state
@@ -1996,6 +2128,8 @@ function spectatorState(now, nextRoundId = "") {
Math.round((roundDurationUs - roundElapsedUs) / 1000));
const state = {
format: "ac.oskiewar.live", version: 1, seq: liveSequence++,
+ map: { id: currentMapId, name: currentMapName,
+ ...(workshopMap ? { workshop: workshopMap } : {}) },
at: run.unixMs || 0, phase,
previousRoundId: previousRoundName ? "ow-" + previousRoundName : "",
fighters: players.map((player) => ({
@@ -2414,7 +2548,7 @@ function flushPendingRoundReplay(now) {
}
function uploadRoundReplay(now) {
- if (!replay) return;
+ if (!replay || workshopTainted) return;
recordReplayCheckpoint(now, true);
const demo = JSON.parse(JSON.stringify(replay));
demo.matchId = "ow-" + matchName;
@@ -4167,6 +4301,17 @@ function roundDemoState(demo, now) {
function applyRoundViewerState(state, now, dt = 1 / 60) {
if (!state?.fighters?.length || !state.camera || !state.round) return;
+ if (state.map?.id && state.map.id !== currentMapId) {
+ if (state.map.workshop && globalThis.__oskiewarValidateMap) {
+ try { installWorkshopMap(globalThis.__oskiewarValidateMap(state.map.workshop)); }
+ catch { return; }
+ } else if (workshopMap) {
+ installWorkshopMap(workshopBase);
+ workshopMap = null;
+ currentMapId = "halfpipe";
+ currentMapName = "HALFPIPE";
+ }
+ }
for (let index = 0; index < players.length; index++) {
const source = state.fighters[index];
const player = players[index];
@@ -5592,6 +5737,12 @@ function replayViewerImpacts(tick, dt) {
}
function gameBoot() {
+ globalThis.__oskiewarWorkshopCommand = workshopCommand;
+ workshopBase ||= workshopSnapshot();
+ if (globalThis.__oskiewarPublishedMap && globalThis.__oskiewarValidateMap) {
+ workshopMap = globalThis.__oskiewarValidateMap(globalThis.__oskiewarPublishedMap);
+ workshopTainted = true;
+ }
syncGameView();
// The reel harness may boot with the debug overlay lit — safe-zone crops,
// stat chassis, input read-outs — to diagnose framing on a rendered reel.
@@ -5656,6 +5807,7 @@ function gameBoot() {
}
function resetRound(now, resetMatch = false) {
+ resetWorkshopMap();
if (replay) {
const nextRoundName = pronounceableMatchName();
// A versus room is one address for a whole match — the link a friend was
@@ -12795,7 +12947,8 @@ function worldQuad(a, b, c, d, color) {
}
// Preserve the sampled silhouette, merging only collinear edges.
-const terrainProfile = (() => {
+const terrainProfile = [];
+function rebuildTerrainProfile() {
const points = [];
const step = (worldRight - worldLeft) / terrainSamples;
for (let i = 0; i <= terrainSamples; i++) {
@@ -12809,8 +12962,9 @@ const terrainProfile = (() => {
}
points.push(point);
}
- return points;
-})();
+ terrainProfile.splice(0, terrainProfile.length, ...points);
+}
+rebuildTerrainProfile();
function drawTerrainSurface(left, right, near, far, color) {
for (let index = 1; index < terrainProfile.length; index++) {
@@ -14802,6 +14956,15 @@ function gamePaint() {
worldLine(ledgeLeft, ledge.y, platformNear,
ledgeRight, ledge.y, platformNear, 5, ledgeInk);
}
+ if (workshopHighlight && globalThis.__oskiewarWorkshopEnabled) {
+ for (const player of players) {
+ const x = player.spawnX, y = terrainFloorAt(x);
+ const ink = player.pad === 0 ? [80, 240, 255] : [255, 100, 210];
+ worldLine(x - 55, y - 4, -50, x + 55, y - 4, -50, 6, ink);
+ worldLine(x, y, 0, x, y - 240, 0, 6, ink);
+ worldLine(x, y - 240, 0, x + 65, y - 210, 0, 6, ink);
+ }
+ }
drawSurvivalLava(t);
const shadowInk = mixColor([3, 5, 14], [92, 99, 101],
visualTheme.light * .72);
diff --git a/xbox/live/render-social-preview.mjs b/xbox/live/render-social-preview.mjs
index 748740f633..c04af3255b 100644
--- a/xbox/live/render-social-preview.mjs
+++ b/xbox/live/render-social-preview.mjs
@@ -44,7 +44,7 @@ function fileFor(pathname) {
// and the capture comes out empty rather than loudly wrong.
if (["/oskiewar.js", "/oskiewar-sfx.mjs", "/oskiewar-voice.mjs",
"/oskiewar-midi.mjs", "/frame-driver.mjs", "/account.mjs",
- "/oskiewar-wizard.mjs",
+ "/oskiewar-wizard.mjs", "/oskiewar-workshop.mjs", "/oskiewar-map.mjs",
"/round-room.mjs"].includes(pathname)) return join(live, pathname.slice(1));
if (pathname === "/aesthetic.computer/lib/auth0-otp.mjs")
return join(repo, "system/public/aesthetic.computer/lib/auth0-otp.mjs");
diff --git a/xbox/live/social/manifest.json b/xbox/live/social/manifest.json
index afe20324b2..755a2f9705 100644
--- a/xbox/live/social/manifest.json
+++ b/xbox/live/social/manifest.json
@@ -1,7 +1,7 @@
{
"format": "ac.oskiewar.social-preview",
"version": 2,
- "build": "513dca2c7287b14e",
+ "build": "92356e840496391d",
"theme": "light",
"imageWidth": 1200,
"imageHeight": 630,
diff --git a/xbox/live/social/oskiewar-title.jpg b/xbox/live/social/oskiewar-title.jpg
index bf05c121ec05c5b9b60c37e48d4176d0bb31922a..0082dbbe7d3d4cbf0e236e13978eb2bde65ca093 100644
GIT binary patch
literal 66174
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 TI`a^G@bT@=p7nw|2dE
z@uj)z+ai`0%Y4hZ{%1)@@9#Iemz}&A7#dV+lVY&Wbm3m^qW!s2?+SKpyvY-H`O=Z6
z67Sdy{Vw;KTw0y}*3Ijb?y0ku@w1V@Gnzg|eZojXa!}YFO^;d2Wy7h6ES=iy6
z)vI&0_58!%tgD{8Nhfeom9M*LXiER8tuozfp4A^#=NHe@EN#2rb~EdRMt1h&S@9Zc
zsTFmNv!Q
zj(c)lujXg*#yxW~%eLRz^6titurAe||NOZwpR8_TTPn*`;*zRTU9>lN{j!xO=2`~c
zemC{<#3!XcTmNl(UAkuFj+k}AR*RM`TjV+^@=4LV=a#3hhKAg$|H-!L{-^IU{~6vD
z*FVc#w(X_vaVOKHza6n@OIGZN(3}El(`hvMjw05P+^u{GfDLH%SF?UPj)-Lc)sYV
zK6gj$d7T&6O_ezLv)0qvE7t7!gCAK_^(K9cTr
n%
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 |