mirror of
https://codeberg.org/vlw/still-alive.git
synced 2025-09-14 00:13:41 +02:00
Fix Monkeydo events and #3
Monkeydo events will now be executed through the Proxy created in 8b659066cc
This commit also implements a solution for #3 by having each window report to its PlayerManager when it has been closed.
This commit is contained in:
parent
8b659066cc
commit
7a6ed67d92
8 changed files with 1784 additions and 1702 deletions
|
@ -3,9 +3,9 @@ import { default as Player } from "./PlayerWindow.mjs";
|
|||
export default class PlayerManager {
|
||||
constructor() {
|
||||
this.players = {
|
||||
"lyrics": new Player("lyrics","monkeydo_lyrics.json")//,
|
||||
//"credits": new Player("credits","monkeydo_credits.json"),
|
||||
//"art": new Player("art")
|
||||
"lyrics": new Player("lyrics","monkeydo_lyrics.json"),
|
||||
"credits": new Player("credits","monkeydo_credits.json"),
|
||||
"art": new Player("art")
|
||||
};
|
||||
|
||||
this.channels = new WeakMap();
|
||||
|
@ -20,16 +20,20 @@ export default class PlayerManager {
|
|||
}
|
||||
}
|
||||
|
||||
// Close all opened windows (some browsers allow one window to open)
|
||||
closeAll() {
|
||||
for(const player of Object.values(this.players)) {
|
||||
player.window?.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Window blocked from opening, show "allow popups" message
|
||||
windowOpenFailed() {
|
||||
// Close all opened windows (some browsers allow one window to open)
|
||||
//for(const player of Object.values(this.players)) {
|
||||
// player.window?.close();
|
||||
//}
|
||||
console.log("failed to open a window");
|
||||
this.closeAll();
|
||||
throw new Error("WINDOW_OPEN_FAILED");
|
||||
}
|
||||
|
||||
message(event) {
|
||||
console.log(event);
|
||||
if(event.data === "PLAYER_CLOSED") return this.closeAll();
|
||||
}
|
||||
}
|
|
@ -34,16 +34,18 @@ export default class PlayerWindow {
|
|||
this.url = new URL(window.location.href + "player");
|
||||
this.url.hash = name;
|
||||
|
||||
// Path to Monkeydo manifest to load in this window
|
||||
if(manifest) this.url.searchParams.append("manifest",manifest);
|
||||
|
||||
// Copy window size rect into windowFeatures
|
||||
Object.assign(this.features,windowPositions[this.url.hash]);
|
||||
Object.assign(this.features,windowPositions[name]);
|
||||
}
|
||||
|
||||
// Convert windowFeatures object into a CSV DOMString
|
||||
getWindowFeatures() {
|
||||
let output = [];
|
||||
for(let [key,value] of Object.entries(this.features)) {
|
||||
// Coercive boolean
|
||||
if(typeof key === "boolean") {
|
||||
value = value ? "yes" : "no";
|
||||
}
|
||||
|
@ -52,6 +54,11 @@ export default class PlayerWindow {
|
|||
return output.join(",");
|
||||
}
|
||||
|
||||
// Close this window
|
||||
close() {
|
||||
return this.window?.close() ?? false;
|
||||
}
|
||||
|
||||
// Compile windowFeatures and open the window
|
||||
open() {
|
||||
const features = this.getWindowFeatures();
|
||||
|
|
|
@ -19,6 +19,7 @@ export default class StillAlivePlayer {
|
|||
const self = this;
|
||||
|
||||
this.name = name;
|
||||
this.player = element;
|
||||
|
||||
// Open a BroadcastChannel to listen to messages for me
|
||||
this.channel = new BroadcastChannel(this.name);
|
||||
|
@ -28,45 +29,98 @@ export default class StillAlivePlayer {
|
|||
const methods = {
|
||||
// Clear the screen from elements
|
||||
blank: () => {
|
||||
while(this.player.firstChild) {
|
||||
this.player.removeChild(this.player.lastChild);
|
||||
while(self.firstChild) {
|
||||
self.removeChild(self.lastChild);
|
||||
}
|
||||
},
|
||||
// Create a new paragraph and make it the target for textFeed calls
|
||||
lineFeed: () => {
|
||||
this.target = document.createElement("p");
|
||||
this.player.appendChild(this.target);
|
||||
self.target = document.createElement("p");
|
||||
self.player.appendChild(self.target);
|
||||
},
|
||||
// Append text to the current target element
|
||||
textFeed: (text) => {
|
||||
this.target.innerText = this.target.innerText + text;
|
||||
self.target.innerText = self.target.innerText + text;
|
||||
},
|
||||
// Decode and draw art from artset by key
|
||||
drawArt: (index) => {
|
||||
// Decode and draw art from artset by index key
|
||||
drawArt: (idx) => {
|
||||
this.blank();
|
||||
self.target = document.createElement("pre");
|
||||
self.target.innerText = window.decodeURIComponent(artset[key]);
|
||||
self.target.innerText = window.decodeURIComponent(artset[idx]);
|
||||
self.player.appendChild(self.target);
|
||||
},
|
||||
playCredits: () => {
|
||||
self.players.credits.play();
|
||||
play: () => {
|
||||
if(!self.monkey) return console.warn("This player has no manifest loaded");
|
||||
self.monkey.play();
|
||||
}
|
||||
}
|
||||
|
||||
// Execute or relay Monkeydo methods
|
||||
const proxiedMethods = this.getMethods(methods);
|
||||
|
||||
console.log(proxiedMethods.lineFeed("lyrics"));
|
||||
this.player = element;
|
||||
// Monkeydo
|
||||
const manifest = this.getManifest();
|
||||
if(manifest) {
|
||||
this.monkey = new Monkeydo(proxiedMethods);
|
||||
this.monkey.load(manifest).then(() => {
|
||||
// Start playback of the first manifest
|
||||
if(this.name === "lyrics") this.host();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get parent directory of this file
|
||||
getParentURL() {
|
||||
let pathname = window.location.pathname.split("/");
|
||||
pathname.pop();
|
||||
pathname = pathname[pathname.length - 1];
|
||||
return pathname;
|
||||
}
|
||||
|
||||
// This player is the "host", it will play the music and send events to other players
|
||||
host() {
|
||||
const parent = this.getParentURL();
|
||||
const pathname = [parent,"assets","media","still-alive.webm"].join("/");
|
||||
|
||||
const musicURL = new URL(window.location.origin);
|
||||
musicURL.pathname = pathname;
|
||||
|
||||
// Load and play the Still Alive when ready
|
||||
const stillalive = new Audio(musicURL.toString());
|
||||
stillalive.addEventListener("canplaythrough",() => {
|
||||
this.monkey.play(); // Start the visuals
|
||||
stillalive.play(); // Start the music
|
||||
},{ once: true });
|
||||
}
|
||||
|
||||
getManifest() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if(!searchParams.has("manifest")) return false;
|
||||
|
||||
// Relative pathname to manifest JSON
|
||||
const parent = this.getParentURL();
|
||||
const pathname = [parent,searchParams.get("manifest")].join("/");
|
||||
|
||||
// Create a URL to the manifest
|
||||
const manifest = new URL(window.location.origin);
|
||||
manifest.pathname = pathname;
|
||||
|
||||
return manifest.toString();
|
||||
}
|
||||
|
||||
getMethods(methods) {
|
||||
const handler = {
|
||||
name: this.name,
|
||||
relay: (...args) => this.relay(...args),
|
||||
// Get and call methods from object
|
||||
get(target,propKey,receiver) {
|
||||
const origMethod = target[propKey];
|
||||
return function (...args) {
|
||||
console.log(this);
|
||||
let result = origMethod.apply(this, args);
|
||||
// Relay function call if it's not for me
|
||||
const channel = args[args.length - 1] ?? null;
|
||||
if(channel && channel !== handler.name) return handler.relay(args);
|
||||
|
||||
let result = origMethod.apply(this,args);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
@ -74,7 +128,7 @@ export default class StillAlivePlayer {
|
|||
return new Proxy(methods,handler);
|
||||
}
|
||||
|
||||
// Open a channel to a different player to relay a task
|
||||
// Open a channel to a different player to forward a task
|
||||
relay(channelName,message) {
|
||||
const channel = new BroadcastChannel(channelName);
|
||||
channel.postMessage(message);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 22eda9780099b77bfd755f214534bba24a881894
|
||||
Subproject commit 7ca095a0d7a91aff50be42efb380fdbda05b2320
|
|
@ -11,4 +11,7 @@ const name = window.location.hash.substring(1);
|
|||
document.body.className = name;
|
||||
|
||||
const target = document.getElementById("player");
|
||||
new Player(target,name);
|
||||
const player = new Player(target,name);
|
||||
|
||||
// Notify PlayerManager that I was closed
|
||||
window.addEventListener("beforeunload",() => player.channel.postMessage("PLAYER_CLOSED"));
|
|
@ -2,11 +2,24 @@ import { default as Player } from "./modules/PlayerManager.mjs";
|
|||
|
||||
const play = document.getElementById("play");
|
||||
|
||||
if(typeof BroadcastChannel !== "function") {
|
||||
const message = document.getElementById("message");
|
||||
play.classList.add("unsupported");
|
||||
play.innerText = "Your browser can not play this demo";
|
||||
message.innerText = error;
|
||||
function info(text = "") {
|
||||
const element = document.getElementById("message");
|
||||
element.innerText = text;
|
||||
}
|
||||
|
||||
play.addEventListener("click",() => new Player());
|
||||
if(typeof BroadcastChannel !== "function") {
|
||||
play.classList.add("unsupported");
|
||||
info("Your browser can not play this demo");
|
||||
}
|
||||
|
||||
play.addEventListener("click",() => {
|
||||
try {
|
||||
info();
|
||||
new Player();
|
||||
}
|
||||
catch(except) {
|
||||
if(except.message === "WINDOW_OPEN_FAILED") return info("Yikes! Player windows could not be created. Allow this page to open pop-ups, it will be worth it!");
|
||||
// Cannot handle this exception, rethrow
|
||||
throw except;
|
||||
}
|
||||
});
|
|
@ -7,6 +7,7 @@
|
|||
<meta name="description" content="End credits from 'Portal' - the video game, recreated with JS and browser windows.">
|
||||
<title>Still Alive</title>
|
||||
<link rel="stylesheet" href="assets/css/style.css">
|
||||
<style>body { text-align: center; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
|
|
3336
monkeydo_lyrics.json
3336
monkeydo_lyrics.json
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue