Skip to content

Instantly share code, notes, and snippets.

@mgiachetti
Created February 4, 2021 13:00
Show Gist options
  • Save mgiachetti/644f92a8b4b1c56f729239f650ccaf5b to your computer and use it in GitHub Desktop.
Save mgiachetti/644f92a8b4b1c56f729239f650ccaf5b to your computer and use it in GitHub Desktop.
Script to run doom on Mural.
// @ts-check
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function avgColor(data, x0, y0, width, height, dist) {
const color = [0, 0, 0, 0];
for (let y = 0; y < dist; y++) {
for (let x = 0; x < dist; x++) {
const offset = ((y0 * dist + y) * width + x0 * dist + x) * 4;
color[0] += data[offset + 0];
color[1] += data[offset + 1];
color[2] += data[offset + 2];
color[3] += data[offset + 3];
}
}
color[0] = Math.round(color[0] / (dist * dist));
color[1] = Math.round(color[1] / (dist * dist));
color[2] = Math.round(color[2] / (dist * dist));
color[3] = Math.round(color[3] / (dist * dist));
return color;
}
var boardId = window.location.pathname.match(/([^\/]+\/[^\/]+)\/[a-z0-9]*$/)[1].replace('/', '.');
var userId = app.me.get('username');
async function createScreen() {
const TEMPLATE = { "height": 138, "hidden": false, "id": "0-1600259662016", "instruction": "", "locked": false, "lockedByFacilitator": false, "owner": "martingiachetti3508", "parentId": null, "rotation": 0, "stackingOrder": 0, "title": "", "type": "murally.widget.TextWidget", "width": 138, "x": 330, "y": 87, "fontFamily": "proxima-nova", "textAlign": "center", "fontSize": 23, "minLines": 6, "backgroundColor": "#fcfe7d", "textType": "realNote", "bold": false, "italic": false, "strike": false, "underline": false, "text": "", "border": false, "round": false, "create": true, "recover": false };
const mwidth = 80;
const mheight = 40;
const widgets = [];
const sep = 1;
const swidth = TEMPLATE.width;
const sheight = TEMPLATE.height;
for (let y = 0; y < mheight; y++) {
for (let x = 0; x < mwidth; x++) {
widgets.push({
...TEMPLATE,
x: x * (swidth + sep),
y: y * (sheight + sep),
id: `pixel-${x}-${y}`,
});
}
}
for (let i = 0; i < widgets.length / 50; i++) {
const widgetsMap = widgets.slice(i * 50, (i + 1) * 50).reduce((acc, w) => ({ ...acc, [w.id]: w }), {});
// post to mural
const authorization = `Bearer ${window.app.getToken().jwt}`
const [, , , , workspace, muralId] = location.pathname.split('/');
const url = `https://app.mural.co/api/v2/murals/${workspace}/${muralId}?acceptBadRequest=true`
await fetch(url, {
"headers": {
"accept": "application/json",
"accept-language": "en-US,en;q=0.9",
authorization,
"cache-control": "no-cache",
"content-type": "application/json; charset=utf-8",
"pragma": "no-cache",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-mural-ly-version": "1.0.0",
},
"body": JSON.stringify(widgetsMap),
"method": "POST",
"mode": "cors",
"credentials": "include"
});
await delay(2000);
}
}
async function updateScreen() {
const mwidth = 80;
const mheight = 40;
const widgets = [];
const sep = 0;
const swidth = 138;
const sheight = 138;
const x0 = 138 * 20;
const y0 = 138 * 20;
for (let y = 0; y < mheight; y++) {
for (let x = 0; x < mwidth; x++) {
widgets.push({
backgroundColor: '#ffffff',
x: x0 + x * (swidth + sep),
y: y0 + y * (sheight + sep),
id: `pixel-${x}-${y}`,
});
}
}
const sliceSize = 5000;
for (let i = 0; i < widgets.length / sliceSize; i++) {
const widgetsMap = widgets.slice(i * sliceSize, (i + 1) * sliceSize).reduce((acc, w) => ({ ...acc, [w.id]: w }), {});
// post to mural
const authorization = `Bearer ${window.app.getToken().jwt}`
const [, , , , workspace, muralId] = location.pathname.split('/');
const url = `https://app.mural.co/api/v2/murals/${workspace}/${muralId}?acceptBadRequest=true`
await fetch(url, {
"headers": {
"accept": "application/json",
"accept-language": "en-US,en;q=0.9",
authorization,
"cache-control": "no-cache",
"content-type": "application/json; charset=utf-8",
"pragma": "no-cache",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-mural-ly-version": "1.0.0",
},
"body": JSON.stringify(widgetsMap),
"method": "POST",
"mode": "cors",
"credentials": "include"
});
await delay(2000);
}
}
// updateScreen()
function updateColor(widgetId, color) {
// const boardId = window.location.pathname.match(/([^\/]+\/[^\/]+)\/[a-z0-9]*$/)[1].replace('/', '.');
// const userId = app.me.get('username');
const operation = {
boardId,
operation: {
boardId,
action: 'UPDATE_WIDGET',
userId,
widgetId,
validatePayload: false,
values: {
backgroundColor: color,
}
},
};
// send remote
// _socket.emit('broadcastP2P', JSON.stringify(operation));
// send local
// _socket._callbacks.$remoteOperations[0](JSON.stringify(operation));
// const w = window.engine.widgets.get(widgetId);
// window.engine.widgets.update({ ...w, properties: { ...w.properties, backgroundColor: color } });
return operation;
}
function setPixel(x, y, color) {
return updateColor(`pixel-${x}-${y}`, `rgb(${color[0]},${color[1]},${color[2]})`);
}
function clearScreen(color) {
const mwidth = 80;
const mheight = 40;
for (let y = 0; y < mheight; y++) {
for (let x = 0; x < mwidth; x++) {
setPixel(x, y, color);
}
}
}
async function initDoom() {
let iframe = document.querySelector('#doom_iframe');
if (!iframe) {
iframe = document.createElement('iframe');
iframe.id = 'doom_iframe';
document.body.appendChild(iframe);
iframe = document.querySelector('#doom_iframe');
const ibody = iframe.contentDocument.body;
const div = document.createElement('div');
div.id = 'dosbox';
ibody.appendChild(div);
let script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = await (await fetch('https://code.jquery.com/jquery-3.5.1.slim.min.js')).text();
ibody.appendChild(script);
script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = await (await fetch('https://js-dos.com/cdn/js-dos-api.js')).text();
ibody.appendChild(script);
script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = `
var dosbox = new Dosbox({
id: "dosbox",
onload: function (dosbox) {
dosbox.run('https://js-dos.com/cdn/upload/DOOM-@evilution.zip', "./DOOM/DOOM.EXE");
//dosbox.run('https://drive.google.com/uc?export=view&id=1XVOF8ktihBL-RP0vqGZMBMC1dpdCfXHZ', "./DOOM/DOOM.EXE");
},
onrun: function (dosbox, app) {
console.log("App '" + app + "' is runned");
const canvas = $('.dosbox-container canvas')[0];
const ctx = canvas.getContext("2d");
const cwidth = 640;
const cheight = 320;
setInterval(() => {
console.time('frame');
const img = ctx.getImageData(0, 0, cwidth, cheight);
window.top.postMessage({cmd: 'frame', data: img.data}, '*');
}, 50);
}
});
$(".dosbox-start").click();
`;
ibody.appendChild(script);
await delay(10000);
}
screenCastRun();
}
function screenCastRun() {
const cwidth = 640;
const cheight = 320;
const downscaleFactor = 8;
const mwidth = cwidth / downscaleFactor;
const mheight = cheight / downscaleFactor;
window.onmessage = function (e) {
if (e.data.cmd === 'frame') {
console.time('frame');
const data = e.data.data;
const operations = [];
for (let y = 0; y < mheight; y++) {
for (let x = 0; x < mwidth; x++) {
const color = avgColor(data, x, y, cwidth, cheight, downscaleFactor);
operations.push(setPixel(x, y, color));
}
}
console.timeEnd('frame');
console.time('render');
const widgets = operations.map((o, i) => {
const w = window.engine.widgets.get(o.operation.widgetId);
const color = o.operation.values.backgroundColor;
const nw = { ...w, properties: { ...w.properties, backgroundColor: color } };
return nw;
})
window.engine.widgets.update(...widgets);
console.timeEnd('render');
}
};
}
var _renderParams = {
x0: 100,
y0: 100,
scale: 1,
};
function screenCastRun2() {
const cwidth = 640;
const cheight = 320;
const downscaleFactor = 4;
const mwidth = cwidth / downscaleFactor;
const mheight = cheight / downscaleFactor;
const canvas = /** @type {HTMLCanvasElement} */(document.querySelector('.render-engine'));
const ctx = canvas.getContext("2d");
const w = canvas.clientWidth;
const h = canvas.clientHeight;
// clearColor
ctx.fillStyle = '#dddddd';
ctx.fillRect(0, 0, w * 2, h * 2);
window.onmessage = function (e) {
if (e.data.cmd === 'frame') {
console.time('frame');
console.time()
const data = e.data.data;
// clearColor
ctx.fillStyle = '#dddddd';
ctx.fillRect(0, 0, w, h);
for (let y = 0; y < mheight; y++) {
for (let x = 0; x < mwidth; x++) {
const x0 = _renderParams.x0;
const y0 = _renderParams.y0;
const scale = _renderParams.scale;
const dp = 138
const sep = 1
const color = avgColor(data, x, y, cwidth, cheight, downscaleFactor);
const c = `rgb(${color[0]},${color[1]},${color[2]})`;
ctx.fillStyle = c;
ctx.fillRect(x0 + x * (dp + sep) * scale, y0 + y * (dp + sep) * scale, dp * scale, dp * scale);
}
}
console.timeEnd('frame');
}
};
}
async function run() {
if (!window.engine.widgets.get('pixel-0-0')) {
await createScreen();
await delay(2000);
}
initDoom()
}
run()
@mgiachetti
Copy link
Author

To RUN

just paste it on a developer console after an empty mural is loaded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment