import { ContKind, Haku } from "rkgk/haku.js"; import { renderToChunksInArea, dotterRenderArea } from "rkgk/painter.js"; export class User { nickname = ""; brush = ""; reticle = null; isBrushOk = false; simulation = null; constructor(wallInfo, nickname) { this.nickname = nickname; this.haku = new Haku(wallInfo.hakuLimits); } destroy() { this.haku.destroy(); } setBrush(brush) { console.groupCollapsed("setBrush", this.nickname); let compileResult = this.haku.setBrush(brush); console.log("compiling brush complete", compileResult); console.groupEnd(); this.isBrushOk = compileResult.status == "ok"; return compileResult; } renderBrushToChunks(wall, x, y) { console.groupCollapsed("renderBrushToChunks", this.nickname); let result = this.painter.renderBrushToWall(this.haku, x, y, wall); console.log("rendering brush to chunks complete"); console.groupEnd(); return result; } simulate(wall, interactions) { console.group("simulate"); for (let interaction of interactions) { if (interaction.kind == "setBrush") { this.simulation = null; this.setBrush(interaction.brush); } if (this.isBrushOk) { if (this.simulation == null) { console.log("no simulation -- beginning brush"); this.simulation = { renderArea: { left: 0, top: 0, right: 0, bottom: 0 } }; this.haku.beginBrush(); } if (interaction.kind == "dotter" && this.#expectContKind(ContKind.Dotter)) { let dotter = { fromX: interaction.from.x, fromY: interaction.from.y, toX: interaction.to.x, toY: interaction.to.y, num: interaction.num, }; this.haku.contDotter(dotter); this.simulation.renderArea = dotterRenderArea(wall, dotter); } if (interaction.kind == "scribble" && this.#expectContKind(ContKind.Scribble)) { renderToChunksInArea( wall, this.simulation.renderArea, (pixmap, translationX, translationY) => { return this.haku.contScribble(pixmap, translationX, translationY); }, ); console.log("ended simulation"); this.simulation = null; } } } console.groupEnd(); } #expectContKind(kind) { if (this.haku.expectedContKind() == kind) { return true; } else { console.error(`expected cont kind: ${kind}`); return false; } } getStats(wallInfo) { return { astSize: this.haku.astSize, astSizeMax: wallInfo.hakuLimits.ast_capacity, numRefs: this.haku.numRefs, numRefsMax: wallInfo.hakuLimits.ref_capacity, fuel: wallInfo.hakuLimits.fuel - this.haku.remainingFuel, fuelMax: wallInfo.hakuLimits.fuel, memory: wallInfo.hakuLimits.memory - this.haku.remainingMemory, memoryMax: wallInfo.hakuLimits.memory, }; } } export class OnlineUsers extends EventTarget { #wallInfo; #users = new Map(); constructor(wallInfo) { super(); this.#wallInfo = wallInfo; } addUser(sessionId, { nickname, brush }) { if (!this.#users.has(sessionId)) { console.info("user added", sessionId, nickname); let user = new User(this.#wallInfo, nickname); user.setBrush(brush); this.#users.set(sessionId, user); return user; } else { console.info("user already exists", sessionId, nickname); return this.#users.get(sessionId); } } getUser(sessionId) { return this.#users.get(sessionId); } removeUser(sessionId) { if (this.#users.has(sessionId)) { let user = this.#users.get(sessionId); user.destroy(); console.info("user removed", sessionId, user.nickname); // TODO: Cleanup reticles this.#users.delete(sessionId); } } }