From 851f8a1d7e10174c1e5a72e0f570db75a1073c0e Mon Sep 17 00:00:00 2001 From: Joey Wunderlich Date: Thu, 20 Oct 2022 14:27:19 -0700 Subject: [PATCH] only post screen when at end of render, incl palette in sim<->host messages (#1380) * only post screen when at end of render, incl palette in sim<->host messages * use throttledmsg * don't send image message unless host * pass around palette as 48b uint8array instead of 96b string * do not export getOrigin / postImage * bump pxt version --- libs/game/multiplayer.cpp | 5 +---- libs/game/multiplayer.ts | 13 +++++++++++++ libs/game/scene.ts | 2 ++ libs/game/sim/multiplayer.ts | 35 ++++++++++++++++++----------------- libs/screen/sim/state.ts | 32 +++++++++++++++++++++++++++----- package.json | 2 +- 6 files changed, 62 insertions(+), 27 deletions(-) diff --git a/libs/game/multiplayer.cpp b/libs/game/multiplayer.cpp index 3a45d4377..6e5b8613e 100644 --- a/libs/game/multiplayer.cpp +++ b/libs/game/multiplayer.cpp @@ -2,18 +2,15 @@ namespace multiplayer { //% - void postImage(Image_ im, String goal) { - // no support >:( + void postImage(Image_ im) { } //% void setOrigin(String origin) { - // no } //% Image_ getCurrentImage() { - // nah return NULL; } diff --git a/libs/game/multiplayer.ts b/libs/game/multiplayer.ts index 0c7afa7c0..d4bc627bc 100644 --- a/libs/game/multiplayer.ts +++ b/libs/game/multiplayer.ts @@ -2,6 +2,9 @@ namespace multiplayer { //% shim=multiplayer::getCurrentImage declare function getCurrentImage(): Image; + //% shim=multiplayer::postImage + declare function postImage(im: Image): void; + //% shim=multiplayer::setOrigin declare function setOrigin(origin: string): void; @@ -21,4 +24,14 @@ namespace multiplayer { }); game.pushScene(); } + + export function initServer() { + if (getOrigin() === "server") { + game.eventContext().registerFrameHandler(scene.MULTIPLAYER_POST_SCREEN_PRIORITY, () => { + if (getOrigin() === "server") { + postImage(screen); + } + }) + } + } } \ No newline at end of file diff --git a/libs/game/scene.ts b/libs/game/scene.ts index 35a30c63f..b73d6553e 100644 --- a/libs/game/scene.ts +++ b/libs/game/scene.ts @@ -65,6 +65,7 @@ namespace scene { export const RENDER_DIAGNOSTICS_PRIORITY = 150; export const MULTIPLAYER_SCREEN_PRIORITY = 190; export const UPDATE_SCREEN_PRIORITY = 200; + export const MULTIPLAYER_POST_SCREEN_PRIORITY = 210; // default rendering z indices export const ON_PAINT_Z = -20; @@ -179,6 +180,7 @@ namespace scene { }); // update screen this.eventContext.registerFrameHandler(UPDATE_SCREEN_PRIORITY, control.__screen.update); + multiplayer.initServer(); // register additional components Scene.initializers.forEach(f => f(this)); } diff --git a/libs/game/sim/multiplayer.ts b/libs/game/sim/multiplayer.ts index 55ada256d..153bfc777 100644 --- a/libs/game/sim/multiplayer.ts +++ b/libs/game/sim/multiplayer.ts @@ -1,19 +1,28 @@ namespace pxsim.multiplayer { - export function postImage(im: pxsim.RefImage, goal: string) { + const throttledImgPost = pxsim.U.throttle((msg: MultiplayerImageMessage) =>{ + getMultiplayerState().send(msg); + }, 50, true); + + export function postImage(im: pxsim.RefImage) { + if (getMultiplayerState().origin !== "server") + return; const asBuf = pxsim.image.toBuffer(im); - getMultiplayerState().send({ + const sb = board() as ScreenBoard; + throttledImgPost({ content: "Image", image: asBuf, - goal + palette: sb?.screenState?.paletteToUint8Array(), }); } + export function getCurrentImage(): pxsim.RefImage { return getMultiplayerState().backgroundImage; } export function setOrigin(origin: "client" | "server" | undefined) { getMultiplayerState().origin = origin; + } export function getOrigin(): string { @@ -41,8 +50,9 @@ namespace pxsim { export interface MultiplayerImageMessage extends SimulatorMultiplayerMessage { content: "Image"; - goal: string; // goal of message; e.g. "broadcast-screen" image: RefBuffer; + // 48bytes, [r0,g0,b0,r1,g1,b1,...] + palette: Uint8Array; } export interface MultiplayerButtonEvent extends SimulatorMultiplayerMessage { @@ -51,13 +61,11 @@ namespace pxsim { state: "Pressed" | "Released" | "Held"; } - let postScreenInterval: any; export class MultiplayerState { lastMessageId: number; origin: string; backgroundImage: RefImage; - constructor() { this.lastMessageId = 0; } @@ -76,17 +84,6 @@ namespace pxsim { init(origin: string) { this.origin = origin; runtime.board.addMessageListener(msg => this.messageHandler(msg)); - if (postScreenInterval) { - clearInterval(postScreenInterval) - } - postScreenInterval = setInterval(() => { - if (this.origin === "server") { - const b = board() as ScreenBoard; - const screenState = b && b.screenState; - const lastImage = screenState && screenState.lastImage; - lastImage && pxsim.multiplayer.postImage(lastImage, "broadcast-screen"); - } - }, 50); } setButton(key: number, isPressed: boolean) { @@ -111,6 +108,10 @@ namespace pxsim { msg.image.data = new Uint8Array(msg.image.data); } this.backgroundImage = pxsim.image.ofBuffer(msg.image); + if (msg.palette?.length === 48) { + const palBuffer = new pxsim.RefBuffer(msg.palette) + pxsim.pxtcore.setPalette(palBuffer); + } } } else if (isButtonMessage(msg)) { if (this.origin === "server") { diff --git a/libs/screen/sim/state.ts b/libs/screen/sim/state.ts index 89be3b004..09985c449 100644 --- a/libs/screen/sim/state.ts +++ b/libs/screen/sim/state.ts @@ -1,7 +1,6 @@ namespace pxsim { function htmlColorToUint32(hexColor: string) { const ca = new Uint8ClampedArray(4) - const ui = new Uint32Array(ca.buffer) const v = parseInt(hexColor.replace(/#/, ""), 16) ca[0] = (v >> 16) & 0xff; ca[1] = (v >> 8) & 0xff; @@ -11,6 +10,13 @@ namespace pxsim { return new Uint32Array(ca.buffer)[0] } + function UInt32ToRGB(col: number): number[] { + const ui = new Uint32Array(1); + ui[0] = col; + const ca = new Uint8ClampedArray(ui.buffer); + return [ca[0], ca[1], ca[2]]; + } + export class ScreenState { width = 0 height = 0 @@ -25,10 +31,8 @@ namespace pxsim { constructor(paletteSrc: string[], w = 0, h = 0) { if (!paletteSrc) paletteSrc = ["#000000", "#ffffff"] - this.palette = new Uint32Array(paletteSrc.length) - for (let i = 0; i < this.palette.length; ++i) { - this.palette[i] = htmlColorToUint32(paletteSrc[i]) - } + this.palette = new Uint32Array(paletteSrc.length); + this.setPaletteFromHtmlColors(paletteSrc); if (w) { this.width = w this.height = h @@ -41,6 +45,24 @@ namespace pxsim { this.brightness = b | 0; } + paletteToUint8Array() { + const out = new Uint8Array(this.palette.length * 3); + for (let i = 0; i < this.palette.length; ++i) { + const [r, g, b] = UInt32ToRGB(this.palette[i]); + const s = 3 * i; + out[s] = r; + out[s + 1] = g; + out[s + 2] = b; + } + return out; + } + + setPaletteFromHtmlColors(src: string[]) { + for (let i = 0; i < this.palette.length; ++i) { + this.palette[i] = htmlColorToUint32(src[i]) + } + } + setPalette(buf: RefBuffer) { const ca = new Uint8ClampedArray(4) const rd = new Uint32Array(ca.buffer) diff --git a/package.json b/package.json index 6e5734202..0fc081200 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "libs/**/*" ], "dependencies": { - "pxt-core": "8.2.4" + "pxt-core": "8.4.8" }, "devDependencies": { "typescript": "^4.2.3"