some stuff
This commit is contained in:
parent
f9e21db529
commit
85cbf665f0
@ -16,7 +16,8 @@
|
|||||||
"prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"${DATABASE_URL}\" && echo \"running migrations\" && npm run prod-migrate && echo \"starting server\" && npm run withenv ./src/server/index.ts",
|
"prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"${DATABASE_URL}\" && echo \"running migrations\" && npm run prod-migrate && echo \"starting server\" && npm run withenv ./src/server/index.ts",
|
||||||
"withenv": "tsx ./scripts/run-with-env.ts",
|
"withenv": "tsx ./scripts/run-with-env.ts",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build-p8client": "bun build src/client/pico8-client/veryRawRenderCart.js --outdir src/client/pico8-client/build"
|
"build-p8client": "bun build src/client/pico8-client/veryRawRenderCart.js --outdir src/client/pico8-client/build",
|
||||||
|
"add-pico": "npm run withenv ./scripts/do-release.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
27
scripts/do-release.ts
Normal file
27
scripts/do-release.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { insertRelease } from "../src/server/dbal/dbal";
|
||||||
|
import { ManifestType } from "../src/server/types";
|
||||||
|
import { getCarts } from "../src/server/util/carts";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const doRelease = async (dir: string) => {
|
||||||
|
const manifest = JSON.parse(await fs.readFile(path.join(dir, "picobook.json"), "utf8"));
|
||||||
|
|
||||||
|
if (!ManifestType.Check(manifest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const carts = await getCarts(dir, manifest.carts);
|
||||||
|
|
||||||
|
await insertRelease({
|
||||||
|
manifest,
|
||||||
|
carts,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[2]) {
|
||||||
|
// console.log(process.argv[3]);
|
||||||
|
await doRelease(process.argv[3]);
|
||||||
|
} else {
|
||||||
|
console.log("must pass in a path to a repo");
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { Link, useParams, useSearchParams } from "react-router-dom"
|
import { Link, useParams, useSearchParams } from "react-router-dom"
|
||||||
import { Pico8Console } from "./pico8-client/Pico8Console";
|
import { Pico8Console, Pico8ConsoleImperatives } from "./pico8-client/Pico8Console";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { DbRelease } from "../server/dbal/dbal";
|
import { DbRelease } from "../server/dbal/dbal";
|
||||||
import { css } from "@emotion/css";
|
import { css } from "@emotion/css";
|
||||||
import { useWebsocket } from "./hooks/useWebsocket";
|
import { useWebsocket } from "./hooks/useWebsocket";
|
||||||
@ -14,10 +14,24 @@ export const GamePage = () => {
|
|||||||
const {author, slug} = useParams();
|
const {author, slug} = useParams();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const room = searchParams.get('room');
|
const room = searchParams.get('room');
|
||||||
|
const picoRef = useRef<Pico8ConsoleImperatives>(null);
|
||||||
const socket = useWebsocket({
|
const socket = useWebsocket({
|
||||||
url: `/api/ws/room?room=${room}`,
|
url: `/api/ws/room?room=${room}`,
|
||||||
// url: "wss://echo.websocket.org",
|
// url: "wss://echo.websocket.org",
|
||||||
onMessage({message}) {
|
onMessage({message}) {
|
||||||
|
const msg = message as any;
|
||||||
|
if (msg.type === "gpio") {
|
||||||
|
if (picoRef.current) {
|
||||||
|
const handle = picoRef.current.getPicoConsoleHandle();
|
||||||
|
if (handle) {
|
||||||
|
console.log("updating pico gpio");
|
||||||
|
(handle.gpio as any).dontSend = true;
|
||||||
|
handle.gpio.length = 0;
|
||||||
|
handle.gpio.push(...msg.gpio);
|
||||||
|
(handle.gpio as any).dontSend = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log('message', message);
|
console.log('message', message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -81,7 +95,13 @@ export const GamePage = () => {
|
|||||||
border: 2px solid limegreen;
|
border: 2px solid limegreen;
|
||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
<Pico8Console carts={info.release.carts} />
|
<Pico8Console ref={picoRef} carts={info.release.carts} onGpioChange={(gpio: number[]) => {
|
||||||
|
console.log("sending gpio");
|
||||||
|
socket.sendMessage({
|
||||||
|
type: "gpio",
|
||||||
|
gpio,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
</div>
|
</div>
|
||||||
<div className={css`
|
<div className={css`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -96,11 +116,6 @@ export const GamePage = () => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<button onClick={() => {
|
|
||||||
room && socket.sendMessage({room, type: "hello", name: "world"});
|
|
||||||
}}>Websocket</button>
|
|
||||||
</div>
|
|
||||||
{/* <div>
|
{/* <div>
|
||||||
<p>This is a paragraph about this game. It is a cool game. And a cool website to play it on. It automagically connects from GitHub.</p>
|
<p>This is a paragraph about this game. It is a cool game. And a cool website to play it on. It automagically connects from GitHub.</p>
|
||||||
</div> */}
|
</div> */}
|
||||||
|
@ -2,12 +2,19 @@ import { css } from "@emotion/css";
|
|||||||
import { PicoCart, PicoPlayerHandle, makePicoConsole } from "./renderCart";
|
import { PicoCart, PicoPlayerHandle, makePicoConsole } from "./renderCart";
|
||||||
import { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
|
import { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
|
||||||
|
|
||||||
type Pico8ConsoleImperatives = {
|
export type Pico8ConsoleImperatives = {
|
||||||
getPicoConsoleHandle(): PicoPlayerHandle | null;
|
getPicoConsoleHandle(): PicoPlayerHandle | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Pico8Console = forwardRef((props: { carts: PicoCart[] }, forwardedRef: ForwardedRef<Pico8ConsoleImperatives>) => {
|
export type Pico8ConsoleProps = {
|
||||||
const {carts} = props;
|
carts: PicoCart[],
|
||||||
|
onGpioChange?(gpio: number[]): void,
|
||||||
|
}
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
export const Pico8Console = forwardRef((props: Pico8ConsoleProps, forwardedRef: ForwardedRef<Pico8ConsoleImperatives>) => {
|
||||||
|
const {carts, onGpioChange = noop} = props;
|
||||||
const [playing, setPlaying] = useState(false);
|
const [playing, setPlaying] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const [handle, setHandle] = useState<PicoPlayerHandle | null>(null);
|
const [handle, setHandle] = useState<PicoPlayerHandle | null>(null);
|
||||||
@ -26,6 +33,7 @@ export const Pico8Console = forwardRef((props: { carts: PicoCart[] }, forwardedR
|
|||||||
picoConsole.canvas.focus();
|
picoConsole.canvas.focus();
|
||||||
}
|
}
|
||||||
setHandle(picoConsole);
|
setHandle(picoConsole);
|
||||||
|
picoConsole.gpio.subscribe(onGpioChange);
|
||||||
picoConsole.canvas.addEventListener('keydown',(event) => {
|
picoConsole.canvas.addEventListener('keydown',(event) => {
|
||||||
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) {
|
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -35,7 +35,7 @@ export type PicoPlayerHandle = {
|
|||||||
rightClick: boolean;
|
rightClick: boolean;
|
||||||
}) => void;
|
}) => void;
|
||||||
setGamepadCount: (count: number) => void;
|
setGamepadCount: (count: number) => void;
|
||||||
readonly gpio: number[]; // read + write (should be 256-tuple)
|
gpio: number[] & {subscribe: (f: (gpio: number[]) => void) => void}; // read + write (should be 256-tuple)
|
||||||
|
|
||||||
// state
|
// state
|
||||||
readonly state: {
|
readonly state: {
|
||||||
@ -97,7 +97,8 @@ export const makePicoConsole = async (props: {
|
|||||||
handle.pico8_state = {};
|
handle.pico8_state = {};
|
||||||
handle.pico8_buttons = [0,0,0,0,0,0,0,0];
|
handle.pico8_buttons = [0,0,0,0,0,0,0,0];
|
||||||
handle.pico8_mouse = [0,0,0];
|
handle.pico8_mouse = [0,0,0];
|
||||||
handle.pico8_gpio = [
|
let gpioChanged = (gpio: number[]) => {};
|
||||||
|
const gpioInner = [
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
@ -115,6 +116,27 @@ export const makePicoConsole = async (props: {
|
|||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
];
|
];
|
||||||
|
handle.pico8_gpio = new Proxy(gpioInner, {
|
||||||
|
get(target, prop) {
|
||||||
|
return target[prop as any];
|
||||||
|
},
|
||||||
|
set(target, prop, newValue) {
|
||||||
|
const t = target as any;
|
||||||
|
if (t.setting) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const prev = [...target];
|
||||||
|
target[prop as any] = newValue;
|
||||||
|
const next = [...target];
|
||||||
|
if (!t.dontSend && prev.some((p, i) => p !== next[i])) {
|
||||||
|
gpioChanged(target);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(handle as any).pico8_gpio.subscribe = (f: (gpio: number[]) => void) => {
|
||||||
|
gpioChanged = f;
|
||||||
|
}
|
||||||
handle.pico8_gamepads = {count: 0};
|
handle.pico8_gamepads = {count: 0};
|
||||||
return {
|
return {
|
||||||
raw: handle,
|
raw: handle,
|
||||||
@ -130,7 +152,7 @@ export const makePicoConsole = async (props: {
|
|||||||
shutdownRequested: !!handle.pico8_state.shutdown_requested!,
|
shutdownRequested: !!handle.pico8_state.shutdown_requested!,
|
||||||
soundVolume: handle.pico8_state.sound_volume!,
|
soundVolume: handle.pico8_state.sound_volume!,
|
||||||
},
|
},
|
||||||
gpio: handle.pico8_gpio,
|
gpio: handle.pico8_gpio as PicoPlayerHandle["gpio"],
|
||||||
setMouse({x, y, leftClick, rightClick}) {
|
setMouse({x, y, leftClick, rightClick}) {
|
||||||
handle.pico8_mouse = [x, y, bitfield(leftClick, rightClick)];
|
handle.pico8_mouse = [x, y, bitfield(leftClick, rightClick)];
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
import { FirRouteOptions, FirWebsocketHandler, FirWebsocketInput } from "../util/routewrap.js";
|
import { FirRouteOptions, FirWebsocketHandler, FirWebsocketInput } from "../util/routewrap.js";
|
||||||
import { WebSocket } from "@fastify/websocket";
|
import { WebSocket } from "@fastify/websocket";
|
||||||
|
import { FastifyRequest } from "fastify";
|
||||||
|
|
||||||
const method = "GET";
|
const method = "GET";
|
||||||
const url = "/api/ws/room";
|
const url = "/api/ws/room";
|
||||||
@ -14,9 +15,13 @@ type Room = {
|
|||||||
|
|
||||||
let rooms: Room[] = [];
|
let rooms: Room[] = [];
|
||||||
|
|
||||||
|
const getRoomName = (req: FastifyRequest) => {
|
||||||
|
return (req.query as any).room;
|
||||||
|
}
|
||||||
|
|
||||||
const websocket = {
|
const websocket = {
|
||||||
onOpen({socket, req}) {
|
onOpen({socket, req}) {
|
||||||
const {room: roomName} = req.query as any;
|
const roomName = getRoomName(req);
|
||||||
let room = rooms.find(r => r.name === roomName);
|
let room = rooms.find(r => r.name === roomName);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
console.log("creating room", roomName);
|
console.log("creating room", roomName);
|
||||||
@ -33,7 +38,7 @@ const websocket = {
|
|||||||
console.log('rooms', rooms);
|
console.log('rooms', rooms);
|
||||||
},
|
},
|
||||||
onClose({socket, req}) {
|
onClose({socket, req}) {
|
||||||
const {room: roomName} = req.query as any;
|
const roomName = getRoomName(req);
|
||||||
const room = rooms.find(r => r.name === roomName);
|
const room = rooms.find(r => r.name === roomName);
|
||||||
if (room) {
|
if (room) {
|
||||||
room.sockets = room.sockets.filter(sock => sock !== socket);
|
room.sockets = room.sockets.filter(sock => sock !== socket);
|
||||||
@ -43,8 +48,8 @@ const websocket = {
|
|||||||
}
|
}
|
||||||
console.log('rooms', rooms);
|
console.log('rooms', rooms);
|
||||||
},
|
},
|
||||||
onMessage({socket, payload}) {
|
onMessage({socket, payload, req}) {
|
||||||
const {room: roomName} = payload as any;
|
const roomName = getRoomName(req);
|
||||||
let room = rooms.find(r => r.name === roomName);
|
let room = rooms.find(r => r.name === roomName);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
console.log("creating room", roomName);
|
console.log("creating room", roomName);
|
||||||
@ -58,9 +63,11 @@ const websocket = {
|
|||||||
console.log("adding socket to room", roomName);
|
console.log("adding socket to room", roomName);
|
||||||
room.sockets.push(socket);
|
room.sockets.push(socket);
|
||||||
}
|
}
|
||||||
console.log("replying to everyone in room", roomName);
|
console.log("replying to everyone else in room", roomName);
|
||||||
room.sockets.forEach(sock => {
|
room.sockets.forEach(sock => {
|
||||||
sock.send(JSON.stringify(payload));
|
if (sock !== socket) {
|
||||||
|
sock.send(JSON.stringify(payload));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
console.log('rooms', rooms);
|
console.log('rooms', rooms);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user