Skip to content

Instantly share code, notes, and snippets.

@okuoku

okuoku/run.js Secret

Created January 19, 2020 08:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okuoku/e913cf6f132602f46a6178264b289ed9 to your computer and use it in GitHub Desktop.
Save okuoku/e913cf6f132602f46a6178264b289ed9 to your computer and use it in GitHub Desktop.
const d = require("jsdom");
const {JSDOM, ResourceLoader} = d;
const httpServer = require("http-server");
const gl = require("gl");
const WebGLRenderingContext = require("gl/src/javascript/webgl-rendering-context");
const WebGLDebugUtils = require("./webgl-debug.js")();
const Datauri = require("datauri");
const fs = require("fs");
const path = require("path");
const PNG = require("pngjs").PNG;
const indexedDB = require("fake-indexeddb");
const projroot = "c:/Users/oku/WebGLUTSTest/gltest";
const port = 12344;
const url = "http://127.0.0.1:12344";
// Blob server
let blob_count = 0;
let blobs = {};
// Screenshot
let g_ctx = false;
function blob_to_url(blob){
const myuri = "xblob:" + blob_count;
console.log(myuri, blob.type, blob);
blobs[myuri] = blob;
console.log("BLOBURI", myuri);
blob_count ++;
return myuri;
}
function fetch_blob(uri){ // => promise
const r = blobs[uri]();
return r;
}
class MyLoader extends ResourceLoader {
fetch(url, options){
console.log("LOADER",url,options);
if(url == "xblob:1"){
console.log("PATCH!!!");
const buf = fs.readFileSync(path.resolve(__dirname, "patch1.js"));
return Promise.resolve(buf);
}else if(url.indexOf("xblob:") != -1){
return fetch_blob(url);
}else{
return super.fetch(url, options);
}
}
}
function servlog(req, res){
console.log("internalserv", req.method, req.url);
}
class FakeWorker {
constructor(url){
the_loader.fetch(url)
.then(buf => {
const text = buf.toString();
const src = "const obj = function (postMessage) {" + text + "}; obj";
const ex = eval(src);
const home = this;
const recvmsg = function(x,y){
if(home.the_handler){
let ev = {data: x};
console.log("RECVMSG", x, y);
home.the_handler(ev);
}else{
console.log("!! Q RECV MSG", x, y);
home.recvq.push([x,y]);
}
}
this.workerobj = {};
this.workerobj._init = ex;
this.workerobj._init(recvmsg);
console.log("WORKER STANDUP", this.workerobj);
this.sendq.forEach(e => {
// FIXME: this doesn't support Queue message inside
// send handler
console.log("!! DEQ MSG", e[0], e[1]);
let ev = {data: e[0]};
this.workerobj.onmessage(ev);
});
this.sendq = [];
});
};
postMessage(x,y){
if(!this.workerobj){
console.log("!! Q MSG", x);
this.sendq.push([x,y]);
}else{
let e = {data: x};
console.log("SENDMSG", x);
this.workerobj.onmessage(e);
}
};
set onmessage(fn){
console.log("HANDLER SET", fn);
this.the_handler = fn;
this.recvq.forEach(e => {
let ev = {data: e[0]};
fn(ev);
});
this.recvq = [];
}
the_handler = false;
workerobj = false;
sendq = [];
recvq = [];
}
function patchWindow(w){
let dump_count = 0;
const HTMLElement = w.HTMLElement;
const d = w.document;
d.super_createElement = d.createElement;
d.super_addEventListener = d.addEventListener;
w.super_addEventListener = w.addEventListener;
function createObjectURL(blob){
let cache = false;
function cb(){
if(cache){
return Promise.resolve(cache);
}else{
return new Promise((res, rej) => {
const the_reader = new w.FileReader();
the_reader.onload = (e => {
const bv = the_reader.result;
//console.log("DONE", bv);
cache = Buffer.from(bv);
//console.log("RES", cache);
fs.writeFileSync("dump" + dump_count.toString() + ".js",
cache);
dump_count ++;
res(cache);
});
// FIXME: ??? It seems JSDOM ArrayBuffer cannot move to
// Buffer object. Use readAsText instead for now...
the_reader.readAsText(blob);
});
}
}
return blob_to_url(cb);
}
function revokeObjectURL(bogus) {
}
function proxyCEl(nam){
console.log("PROXY CEL", nam);
if(nam == "canvas"){
let cv = d.super_createElement("div");
cv.getContext = function(type, attr){
console.log("PROXY CTX", type, attr);
console.log(attr);
if(type == "2d"){
console.log("???");
return null;
}else if(type == "webgl2"){
return null;
}else if(type == "experimental-webgl2"){
return null;
}else{
function glTrace(name, args){
console.log("GL", name, args.length);
/*
let x = {};
Error.stackTraceLimit = 9999;
Error.captureStackTrace(x);
console.log(x.stack);
*/
}
function errorcheck(err, name, args){
console.log("GLERR", err, name, args);
if(name == "bufferSubData"){
console.log("Q", ctx0._getActiveBuffer(args[0]));
}
process.exit(1);
}
const ctx0 = gl(1920,1080,attr);
const orig_setError = ctx0.setError;
WebGLDebugUtils.init(ctx0);
const ctx = WebGLDebugUtils
.makeDebugContext(ctx0,errorcheck,glTrace);
ctx.canvas = cv;
ctx0.canvas = cv;
g_ctx = ctx0;
return ctx0;
}
}
return cv;
}else{
const el = d.super_createElement(nam);
return el;
}
}
function proxyAlert(msg){
console.log("ALERT", msg);
}
function sleep(ms){
return new Promise((res) => setTimeout(res, ms));
}
function proxyRAF(cb){
//console.log("RAF", cb);
process.nextTick(async function(){
await sleep(100);
const now = w.performance.now();
console.log("RAF", now);
cb(now);
update_screenshot();
});
return 99.99;
}
function proxyAELW(type, cb, options){
let x = {};
console.log("AELW", type, cb, options);
Error.captureStackTrace(x);
console.log(x.stack);
w.super_addEventListener(type, cb, options);
}
function proxyAEL(type, cb, options){
let x = {};
console.log("AEL", type, cb, options);
Error.captureStackTrace(x);
console.log(x.stack);
d.super_addEventListener(type, cb, options);
}
// UnityLoader.js has `(!window.WebGLRenderingContext)` check
w.WebGLRenderingContext = WebGLRenderingContext;
w.requestAnimationFrame = proxyRAF;
w.alert = proxyAlert;
w.URL.createObjectURL = createObjectURL;
w.URL.revokeObjectURL = revokeObjectURL;
w.Worker = FakeWorker;
d.createElement = proxyCEl;
d.addEventListener = proxyAEL;
w.addEventListener = proxyAELW;
w.indexedDB = indexedDB;
}
const pixels0 = new Uint8Array(1920 * 1080 * 4);
const pixels1 = new Uint8Array(1920 * 1080 * 4);
let flip_fb = 0;
let shots = 0;
function update_screenshot(){
let pixels = false;
if(flip_fb == 0){
pixels = pixels0;
flip_fb = 1;
}else{
pixels = pixels1;
flip_fb = 0;
}
//console.log("Pre", g_ctx.getError());
g_ctx.readPixels(0, 0, 1920, 1080, g_ctx.RGBA, g_ctx.UNSIGNED_BYTE, pixels);
//console.log("Post", g_ctx.getError());
shots++;
//console.log("Shot", shots);
let pngout = PNG.sync.write({width: 1920, height: 1080, data: pixels});
fs.writeFileSync("out" + shots.toString() + ".png", pngout);
console.log("Save", shots);
}
const serv = httpServer.createServer({root: projroot, cache: true,
logFn: servlog});
serv.listen(port, "127.0.0.1");
const the_loader = new MyLoader();
const dom = JSDOM.fromURL(url,{
beforeParse: function(w){ patchWindow(w); },
pretendToBeVisual: true,
includeNodelocations: true,
resources: the_loader,
runScripts: "dangerously"
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment