Skip to content

Commit

Permalink
Merge branch 'main' into docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonjgardner committed Dec 26, 2022
2 parents ebf48f5 + 39cf83b commit ee4d241
Show file tree
Hide file tree
Showing 29 changed files with 998 additions and 283 deletions.
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"name": "Build",
"type": "pwa-node",
"program": "${workspaceFolder}/src/mod.ts",
"args": ["--DEPLOY true"],
"cwd": "${workspaceFolder}",
"runtimeExecutable": "C:\\Users\\jason\\.deno\\bin\\deno.EXE",
"runtimeArgs": [
Expand Down
3 changes: 2 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"tasks": {
"test": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts --DEPLOY true",
"build": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts",
"art": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts --ART_DIR ./px"
"art": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts --ART_DIR ./px",
"serve": "deno run --allow-read --allow-net --allow-env --allow-write --unstable --allow-ffi ./serve.ts"
},
"fmt": {
"files": {
Expand Down
14 changes: 8 additions & 6 deletions import_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
"nbt_parser/": "https://deno.land/x/nbt_parser@v1.3.0/",
"jszip/": "https://deno.land/x/jszip@0.11.0/",
"canvas/": "https://deno.land/x/canvas@v1.4.1/",
"culori/": "https://deno.land/x/culori@v2.1.0-alpha.0/",
"culori": "https://deno.land/x/culori@v2.1.0-alpha.0/index.js",
"case/": "https://deno.land/x/case@v2.1.0/",
"path/": "https://deno.land/std@0.138.0/path/",
"fs/": "https://deno.land/std@0.138.0/fs/",
"fmt/": "https://deno.land/std@0.138.0/fmt/",
"log/": "https://deno.land/std@0.125.0/log/",
"deno_markdown/": "https://deno.land/x/deno_markdown@v0.2/"
"path/": "https://deno.land/std@0.164.0/path/",
"fs/": "https://deno.land/std@0.164.0/fs/",
"fmt/": "https://deno.land/std@0.164.0/fmt/",
"log/": "https://deno.land/std@0.164.0/log/",
"http/": "https://deno.land/std@0.164.0/http/",
"deno_markdown/": "https://deno.land/x/deno_markdown@v0.2/",
"@minecraft/server": "npm:@minecraft/server@^1.0.0-beta.release.1.19.40"
}
}
215 changes: 137 additions & 78 deletions serve.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Axis } from "./typings/types.ts";
import { decode } from "./src/components/ImagePrinter.ts";
import assemble from "./src/components/_assemble.ts";
import { serve } from "https://deno.land/std@0.164.0/http/server.ts";
import type { Axis, WssParams, WssState } from "./typings/types.ts";
import { serve } from "http/server.ts";
import { join } from "path/win32.ts";
import { ensureDir } from "fs/mod.ts";

type SubscribeEvents =
| "AdditionalContentLoaded"
Expand Down Expand Up @@ -95,20 +95,42 @@ type SubscribeEvents =
const requests: Array<
{ uuid: string; content: string; timestamp: number; result?: boolean }
> = [];
const state = {
idx: 0,
const state: WssState = {
currentRequestIdx: 0,
updatePending: false,
material: "plastic_50",
sendRate: 10,
offset: [8, 0, -16],
useAbsolutePosition: false,
axis: "x" as Axis,
hasFocus: true,
enableBlockHistory: false,
blockHistory: [],
blockHistoryMaxLength: 500,
functionLog: join(Deno.cwd(), 'build', 'wss', 'functions')
};

function getBlockLibrary(material: string) {
return assemble().filter((b) => b.behaviorId.includes(material));
}
const formatPosition = (x: number, y: number, z: number, offsetX?: number, offsetY?: number, offsetZ?: number) => {
const { offset, useAbsolutePosition } = state;
let [ox, oy, oz] = offset || [0, 0, 0];

if (offsetX) {
ox += offsetX;
}

if (offsetY) {
oy += offsetY;
}

if (offsetZ) {
oz += offsetZ;
}

const [nx, ny, nz] = [x - ox, y - oy, z - oz];

return useAbsolutePosition ? `${nx} ${ny} ${nz}` : `~${nx} ~${ny} ~${nz}`;
};



let connectionUpdateInterval: number | undefined;

Expand All @@ -135,13 +157,40 @@ async function watch(fnNameInput: string) {
}
}

/**
* Generate function on-the-fly from a string of commands, and queue it for execution
*/
function logFunction(fnName: string, content: string) {
if (!state.functionLog) {
return;
}

const logPath = join(state.functionLog, `${fnName}.mcfunction`);

Deno.writeTextFileSync(logPath, content, { append: true });

queueCommandRequest(content)

}



async function processMessage(
{ message, sender }: { message: string; sender: string },
) {
const contents = message.replace(`[${sender}] `, "").trim();

// TODO: Add index help function

if (contents.startsWith("history/")) {
const steps = parseInt(contents.replace("history/", ""), 10);

state.blockHistory = [];
state.enableBlockHistory = steps > 0;
state.blockHistoryMaxLength = steps;
return;
}

// Live reload
if (contents.startsWith("lr/")) {
await watch(contents.replace("lr/", "").trim());
Expand All @@ -168,32 +217,14 @@ async function processMessage(
return;
}

if (contents.startsWith("axis/")) {
state.axis = contents.replace("axis/", "").trim() as Axis;
console.log("Axis set to %s", state.axis);
return;
}

if (contents.startsWith("https://") || contents.startsWith("http://")) {
console.log("Image URL updated to", contents);
updateContent(contents);
return;
}
if (contents.startsWith("log/") && state.functionLog) {
const fn = contents.replace("log/", "").trim();

if (contents.startsWith("material/")) {
const material = contents.replace("material/", "").replace(/\s+/g, "_");
state.material = material;
console.log("Material updated to", material);
return;
}
await ensureDir(state.functionLog);

if (contents.startsWith("position/")) {
const position = contents.replace("position/", "").split(/[\s,]+/g, 3).map(
(v) => parseInt(v, 10),
);
state.offset = position;
state.useAbsolutePosition = true;
console.log("Position updated to %o", position);
const [fnName, fnContent] = fn.split("?", 2);
logFunction(fnName, fnContent);
console.info('Logged function "%s" to %s', fnName, state.functionLog);
return;
}

Expand All @@ -204,12 +235,14 @@ async function loadFunctionScript(fnNameInput: string) {
try {
// TODO: Add cache busting to import statement
const { default: mod } = await import(`./src/functions/${scriptFile}.ts`);

mod({
const wssParams: WssParams = {
queueCommandRequest,
parameters: new URLSearchParams(params),
state,
});
formatPosition
}

await mod(wssParams);
} catch (err) {
console.error("Failed loading/executing function script: %s", err);
}
Expand Down Expand Up @@ -248,15 +281,7 @@ async function updateContent(imgUrl: string) {
}

state.updatePending = true;
const commands = await decode(
new URL(imgUrl),
getBlockLibrary(state.material),
state.offset,
state.axis,
state.useAbsolutePosition === true,
);
requests.length = 0;
commands.map((c) => queueCommandRequest(c));

console.info("Queued %d commands", commands.length);
state.updatePending = false;
}
Expand Down Expand Up @@ -302,50 +327,95 @@ function queueCommandRequest(commandLine: string) {
result: false,
});

//sessionStorage.setItem(`request[${uuid}]`, content);

// Speed up rend rate based on number of requests
state.sendRate = requests.length > 100 ? 10 : 2;
state.sendRate = requests.length > 100 ? 3 : 1;
}

async function onOpenHandler(socket: WebSocket) {
console.log("ws:open");

subscribe(socket, ["PlayerMessage", "AppResumed", "AppPaused"]);
subscribe(socket, ["PlayerMessage", "commandResponse"]);

// Send whatever is in queue every #ms
connectionUpdateInterval = setInterval(() => {
const requestsCount = requests.length;

if (!state.hasFocus) {
console.info("Skipping update, no focus");
return;
}

if (
(requestsCount === 0 && !state.updatePending) ||
requests[state.idx].result
requests[state.currentRequestIdx]?.result
) {
return;
}

socket.send(requests[state.idx]?.content ?? "");
state.idx++;
socket.send(requests[state.currentRequestIdx]?.content ?? "");
state.currentRequestIdx++;

if (state.idx >= requestsCount) {
state.idx = 0;
if (state.currentRequestIdx >= requestsCount) {
state.currentRequestIdx = 0;
requests.length = 0;
console.log("Queue cleared");
console.info("Queue cleared");
}
}, state.sendRate);
}

function processCommandResponse({ body }: { body: any }) {
if (body.blockName) {
const pendingRequest = requests.find((r) => r.uuid === body.requestId);
function resetBlocks() {
// queue command to clear a block from state.blockHistory every 10 seconds

if (pendingRequest) {
pendingRequest.result = true;
const q = (sec: number) => {
const pos = state.blockHistory.shift();
if (pos) {
queueCommandRequest(`setblock ${pos.join(" ")} air`);
setTimeout(q, sec * 1000, sec);
return;
}
state.blockHistory.length = 0;
}

q(10);
}

function processCommandResponse(msg: { body: any, header: any }) {
if (!requests) {
return;
}

if (state.enableBlockHistory === true && msg.body.statusMessage === "Block placed" && msg.body.position) {
const pos = Object.values(msg.body.position).map((v) => parseInt(`${v}`, 10)).slice(0, 3) as [number, number, number];
sessionStorage.setItem("lastBlock", JSON.stringify(pos));

state.blockHistory.push(pos);

if (state.blockHistory.length > state.blockHistoryMaxLength ?? 1000) {
resetBlocks();
}
}

const { requestId } = msg.header;

const idx = requests.findIndex((req) => req && req.uuid === requestId);

if (idx === -1) {
//console.warn("Unknown request id: %s", requestId);
return;
}

delete requests[idx];

// const pendingRequest = requests.find((r) => r.uuid === msg.header.requestId);

// const sessionData = sessionStorage.getItem(`request[${msg.body.requestId}]`);

// if (pendingRequest) {
// pendingRequest.result = true;
// } else if (!sessionData) {
// console.warn("Unknown response!\nHeader: %o\nBody: %o", msg.header, msg.body);
// return;
// }

// sessionStorage.removeItem(`request[${msg.body.requestId}]`);
// sessionStorage.setItem(`result[${msg.body.requestId}]`, JSON.stringify(msg.body));
}

export function requestHandler(req: Request) {
Expand All @@ -357,26 +427,16 @@ export function requestHandler(req: Request) {
socket.onmessage = async (e) => {
const msg = JSON.parse(e.data);

if (msg?.header?.eventName === "AppResumed") {
state.hasFocus = true;
return;
}

if (msg?.header?.eventName === "AppPaused") {
state.hasFocus = false;
return;
}

if (msg?.header?.eventName === "PlayerMessage") {
await processMessage(msg.body);
return;
}

if (msg?.header?.messagePurpose === "commandResponse") {
requests[msg.header.requestId] = msg.body.blockName;
processCommandResponse(msg);
return;
}
console.log("On message %o", msg);
console.warn("Unknown message received: %o", msg);
};

socket.onopen = async () => {
Expand All @@ -387,8 +447,7 @@ export function requestHandler(req: Request) {
socket.onclose = () => {
clearInterval(connectionUpdateInterval);
connectionUpdateInterval = undefined;
console.log("ws:close");
console.log("%o", requests);
console.info("ws:close");
};

return response;
Expand Down
Binary file added src/assets/materials/dot_glowing_mer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/materials/dot_mer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/materials/dot_multiply.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/materials/dot_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ee4d241

Please sign in to comment.