From a879519b8ab873ec4b3b75aab4e01a1d116dcb8c Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Sat, 24 Feb 2024 20:21:36 -0800 Subject: [PATCH] refactor: overhaul sequences --- .../lib/Apps/GBAEmulator/GBAEmulator.svelte | 20 +- client/src/lib/Apps/appList.ts | 63 ++-- client/src/lib/Sequences/sequences.ts | 300 ++++++++++-------- client/src/lib/stores/sequenceStore.ts | 30 +- client/src/lib/stores/settingsStore.ts | 28 +- 5 files changed, 250 insertions(+), 191 deletions(-) diff --git a/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte index a4295c6..c6362de 100644 --- a/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte +++ b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte @@ -1,18 +1,18 @@ diff --git a/client/src/lib/Apps/appList.ts b/client/src/lib/Apps/appList.ts index 2a8da94..dcf9be5 100644 --- a/client/src/lib/Apps/appList.ts +++ b/client/src/lib/Apps/appList.ts @@ -1,46 +1,53 @@ -import Camera from './Camera/Camera.svelte' -import MusicBrowser from './MusicBrowser/MusicBrowser.svelte' -import Settings from './Settings/Settings.svelte' +import Camera from "./Camera/Camera.svelte"; +import MusicBrowser from "./MusicBrowser/MusicBrowser.svelte"; +import Settings from "./Settings/Settings.svelte"; -type Format = 'png' | 'jpg' | 'webp' +type Format = "png" | "jpg" | "webp"; const resolveIconPath = (slug: keyof typeof appList, format: Format) => { - return `/static/app-icons/${slug}.${format}` -} + return `/static/app-icons/${slug}.${format}`; +}; -import GBAEmulator from './GBAEmulator/GBAEmulator.svelte' +import GBAEmulator from "./GBAEmulator/GBAEmulator.svelte"; +import JsDoom from "./JSDoom/JSDoom.svelte"; interface AppList { [key: string]: { - name: string - component: any - icon: string - external: boolean - } + name: string; + component: any; + icon: string; + external: boolean; + }; } export const appList: AppList = { - 'camera': { - name: 'Camera', + camera: { + name: "Camera", component: Camera, - icon: resolveIconPath('camera', 'png'), + icon: resolveIconPath("camera", "png"), external: false, }, - 'media-player': { - name: 'Media Player', + "media-player": { + name: "Media Player", component: MusicBrowser, - icon: resolveIconPath('media-player', 'png'), + icon: resolveIconPath("media-player", "png"), external: false, }, - 'settings': { - name: 'Settings', - component: Settings, - icon: resolveIconPath('settings', 'webp'), - external: false, - }, - 'gba-emulator': { - name: 'GBA Emulator', + "gba-emulator": { + name: "GBA Emulator", component: GBAEmulator, - icon: resolveIconPath('gba-emulator', 'png'), + icon: resolveIconPath("gba-emulator", "png"), external: true, }, -} + jsdoom: { + name: "Doom", + component: JsDoom, + icon: resolveIconPath("jsdoom", "png"), + external: true, + }, + settings: { + name: "Settings", + component: Settings, + icon: resolveIconPath("settings", "webp"), + external: false, + }, +}; diff --git a/client/src/lib/Sequences/sequences.ts b/client/src/lib/Sequences/sequences.ts index e07712a..72840de 100644 --- a/client/src/lib/Sequences/sequences.ts +++ b/client/src/lib/Sequences/sequences.ts @@ -14,42 +14,47 @@ Sequences should be either event-driven or periodic. In the case of periodic sequences, invoke them in the periodicSequence function */ -import { Notifications } from '../Notifications/notifications' -import { sequenceStore } from '../stores/sequenceStore' -import { settingsStore } from '../stores/settingsStore' -import { get } from 'svelte/store' -import getVoicePath from '../utils/getVoicePath' -import { tick } from 'svelte' +import { Notifications } from "../Notifications/notifications"; +import { sequenceStore } from "../stores/sequenceStore"; +import { settingsStore } from "../stores/settingsStore"; +import { get } from "svelte/store"; +import getVoicePath from "../utils/getVoicePath"; +import { tick } from "svelte"; // await a "tick" (a svelte update frame) at the start of every sequence so that // state is synced and no weird side effects occur export const initializationSequence = async () => { - await tick() - Notifications.info('Jankboard initialized!', { + await tick(); + Notifications.info("Jankboard initialized!", { withAudio: true, - src: getVoicePath('jankboard-initialized', 'en'), - }) + src: getVoicePath("jankboard-initialized", "en"), + }); setTimeout(() => { - if (get(settingsStore).goWoke) return - Notifications.success('LittenOS is online', { + if (get(settingsStore).goWoke) return; + Notifications.success("LittenOS is online", { withAudio: true, - src: getVoicePath('littenos-is-online', 'en'), - }) + src: getVoicePath("littenos-is-online", "en"), + }); setTimeout(() => { - Notifications.warn('Breaching Monte Vista codebase', { + Notifications.warn("Breaching Monte Vista codebase", { withAudio: true, - src: getVoicePath('breaching-monte-vista', 'en'), - }) + src: getVoicePath("breaching-monte-vista", "en"), + }); setTimeout(() => { - Notifications.playAudio(getVoicePath('hello-virtual-assistant', 'en')) - periodicSequence() - }, 3000) - }, 3000) - }, 3000) -} + Notifications.playAudio( + getVoicePath("hello-virtual-assistant", "en"), + () => { + sequenceStore.update("initializationComplete", true); + periodicSequence(); + } + ); + }, 3000); + }, 3000); + }, 3000); +}; -let counter = 1 +let counter = 1; /** * Special sequence that plays invokes itself periodically, started automatically * at the end of the initializationSequence @@ -59,7 +64,7 @@ let counter = 1 * @return void */ const periodicSequence = async () => { - await tick() + await tick(); /** * Returns either true or false based on the provided probability @@ -69,11 +74,11 @@ const periodicSequence = async () => { */ const chance = (probability: number) => { if (probability < 0 || probability > 1) { - throw new Error('Probability must be between 0 and 1') + throw new Error("Probability must be between 0 and 1"); } - return Math.random() < probability - } + return Math.random() < probability; + }; /** * Calls a callback function at regular intervals. @@ -82,119 +87,134 @@ const periodicSequence = async () => { * @param callback - the function to call */ const every = (seconds: number, callback: () => void) => { - if (counter % seconds === 0) callback() - } + if (counter % seconds === 0) callback(); + }; // add your periodic sequences here - every(10, () => { - if (chance(0.2)) breaching1323Sequence() - else if (chance(0.2)) breaching254Sequence() - else if (chance(0.05)) bullyingRohanSequence() - }) + every(15, () => { + if (chance(0.2)) breaching1323Sequence(); + else if (chance(0.2)) breaching254Sequence(); + else if (chance(0.05)) bullyingRohanSequence(); + else if (chance(0.1)) bypassCoprocessorRestrictionsSequence(); + }); // Dont touch - counter++ - setTimeout(periodicSequence, 1000) -} + counter++; + setTimeout(periodicSequence, 1000); +}; export const criticalFailureIminentSequence = async () => { - await tick() - Notifications.error('Critical robot failure imminent', { + await tick(); + Notifications.error("Critical robot failure imminent", { withAudio: true, - src: getVoicePath('critical-robot-failure', 'en'), - }) -} + src: getVoicePath("critical-robot-failure", "en"), + }); +}; export const collisionDetectedSequence = async () => { - await tick() - Notifications.error('Collision detected', { + await tick(); + Notifications.error("Collision detected", { withAudio: true, - src: getVoicePath('collision-detected', 'en'), - }) -} + src: getVoicePath("collision-detected", "en"), + }); +}; export const collisionImminentSequence = async () => { - await tick() - Notifications.error('Collision imminent', { + await tick(); + Notifications.error("Collision imminent", { withAudio: true, - src: getVoicePath('collision-imminent', 'en'), - }) -} + src: getVoicePath("collision-imminent", "en"), + }); +}; export const cruiseControlEngagedSequence = async () => { - if (get(settingsStore).disableAnnoyances) return - await tick() - Notifications.success('Cruise control engaged', { + if (get(settingsStore).disableAnnoyances) return; + await tick(); + Notifications.success("Cruise control engaged", { withAudio: true, - src: getVoicePath('cruise-control-engaged', 'en'), - }) -} + src: getVoicePath("cruise-control-engaged", "en"), + }); +}; export const retardSequence = async () => { - if (get(settingsStore).goWoke) return - await tick() - Notifications.warn('Retard', { + if (get(settingsStore).goWoke) return; + await tick(); + Notifications.warn("Retard", { withAudio: true, - src: getVoicePath('retard', 'en'), - }) -} + src: getVoicePath("retard", "en"), + }); +}; const breaching254Sequence = async () => { - if (get(settingsStore).disableAnnoyances) return - await tick() - Notifications.warn('Breaching 254 mainframe', { + if (get(settingsStore).disableAnnoyances) return; + await tick(); + Notifications.warn("Breaching 254 mainframe", { withAudio: true, - src: getVoicePath('breaching-254-mainframe', 'en'), - }) -} + src: getVoicePath("breaching-254-mainframe", "en"), + }); +}; const breaching1323Sequence = async () => { - if (get(settingsStore).disableAnnoyances) return - await tick() - Notifications.warn('Breaching 1323 mainframe', { + if (get(settingsStore).disableAnnoyances) return; + await tick(); + Notifications.warn("Breaching 1323 mainframe", { withAudio: true, - src: getVoicePath('breaching-1323-mainframe', 'en'), - }) -} + src: getVoicePath("breaching-1323-mainframe", "en"), + }); +}; const bullyingRohanSequence = async () => { - if (get(settingsStore).disableAnnoyances) return - await tick() - Notifications.info('Bullying Rohan', { + if (get(settingsStore).disableAnnoyances) return; + await tick(); + Notifications.info("Bullying Rohan", { withAudio: true, - src: getVoicePath('bullying-rohan', 'en'), - }) -} + src: getVoicePath("bullying-rohan", "en"), + }); +}; export const userErrorDetectedSequence = async () => { - await tick() - Notifications.error('User error detected', { + await tick(); + Notifications.error("User error detected", { withAudio: true, - src: getVoicePath('user-error-detected', 'en'), - }) -} + src: getVoicePath("user-error-detected", "en"), + }); +}; export const infotainmentBootupSequence = async () => { if ( get(sequenceStore).infotainmentStartedFirstTime || get(settingsStore).disableAnnoyances ) - return + return; - await tick() + await tick(); - sequenceStore.update('infotainmentStartedFirstTime', true) - - Notifications.info('Infotainment system buffering', { - withAudio: true, - src: getVoicePath('infotainment-system-buffering', 'en'), - }) - setTimeout(() => { - Notifications.success('Infotainment system online', { + const sequence = () => { + Notifications.info("Infotainment system buffering", { withAudio: true, - src: getVoicePath('infotainment-system-online', 'en'), - }) - }, 3000) -} + src: getVoicePath("infotainment-system-buffering", "en"), + }); + setTimeout(() => { + Notifications.success("Infotainment system online", { + withAudio: true, + src: getVoicePath("infotainment-system-online", "en"), + onComplete: () => { + sequenceStore.update("infotainmentStartedFirstTime", true); + }, + }); + }, 3000); + }; + + if (!get(sequenceStore).initializationComplete) { + const unsubscribe = sequenceStore.subscribe((data) => { + if (data.initializationComplete) { + sequence(); + unsubscribe(); + } + }); + } else { + sequence(); + } +}; /** * Waits for the infotainment system to boot up before executing the given sequence. @@ -202,52 +222,80 @@ export const infotainmentBootupSequence = async () => { * If it's already booted, the sequence will be executed immediately. * * @param sequence - The sequence to execute after infotainment bootup, or immediately it already booted. - * @param delay? - The delay in milliseconds to wait if infotainment system is currently booting. Defaults to 5000ms */ -const waitForInfotainmentBootup = ( - sequence: () => void, - delay: number = 5000 -) => { +const waitForInfotainmentBootup = (sequence: () => void) => { if (!get(sequenceStore).infotainmentStartedFirstTime) { - setTimeout(sequence, delay) + const unsubscribe = sequenceStore.subscribe((data) => { + if (data.infotainmentStartedFirstTime) { + sequence(); + unsubscribe(); + } + }); } else { - sequence() + sequence(); } -} +}; export const musicPlayerBootupSequence = async () => { if ( get(sequenceStore).musicStartedFirstTime || get(settingsStore).disableAnnoyances ) - return + return; - await tick() + await tick(); - sequenceStore.update('musicStartedFirstTime', true) + sequenceStore.update("musicStartedFirstTime", true); waitForInfotainmentBootup(() => { - Notifications.info('Downloading copyrighted music...', { + Notifications.info("Downloading copyrighted music...", { withAudio: true, - src: getVoicePath('downloading-copyrighted-music', 'en'), - }) - }) -} + src: getVoicePath("downloading-copyrighted-music", "en"), + }); + }); +}; export const gbaEmulatorBootupSequence = async () => { if ( get(sequenceStore).gbaEmulatorStartedFirstTime || get(settingsStore).disableAnnoyances ) - return + return; - await tick() - sequenceStore.update('gbaEmulatorStartedFirstTime', true) + await tick(); + sequenceStore.update("gbaEmulatorStartedFirstTime", true); waitForInfotainmentBootup(() => { - Notifications.info('Loading pirated Nintendo ROMs', { + Notifications.info("Loading pirated Nintendo ROMs", { withAudio: true, - src: getVoicePath('loading-pirated-nintendo', 'en'), - }) - }) -} + src: getVoicePath("loading-pirated-nintendo", "en"), + }); + }); +}; + +export const doomBootupSequence = async () => { + if ( + get(sequenceStore).doomStartedFirstTime || + get(settingsStore).disableAnnoyances + ) + return; + + await tick(); + sequenceStore.update("doomStartedFirstTime", true); + + waitForInfotainmentBootup(() => { + Notifications.success("Doom Engaged", { + withAudio: true, + src: getVoicePath("doom-engaged", "en"), + }); + }); +}; + +const bypassCoprocessorRestrictionsSequence = async () => { + if (get(settingsStore).disableAnnoyances) return; + await tick(); + Notifications.warn("Bypassing coprocessor restrictions", { + withAudio: true, + src: getVoicePath("bypassing-coprocessor-restrictions", "en"), + }); +}; diff --git a/client/src/lib/stores/sequenceStore.ts b/client/src/lib/stores/sequenceStore.ts index fd17ad3..60f6adb 100644 --- a/client/src/lib/stores/sequenceStore.ts +++ b/client/src/lib/stores/sequenceStore.ts @@ -1,34 +1,38 @@ /* in this store, put stateful variables that sequences need to access */ -import { writable } from 'svelte/store' +import { writable } from "svelte/store"; interface SequenceStoreData { - infotainmentStartedFirstTime: boolean - musicStartedFirstTime: boolean - gbaEmulatorStartedFirstTime: boolean + initializationComplete: boolean; + infotainmentStartedFirstTime: boolean; + musicStartedFirstTime: boolean; + gbaEmulatorStartedFirstTime: boolean; + doomStartedFirstTime: boolean; } let defaults: SequenceStoreData = { + initializationComplete: false, infotainmentStartedFirstTime: false, // for infotainment bootup sequence musicStartedFirstTime: false, gbaEmulatorStartedFirstTime: false, -} + doomStartedFirstTime: false, +}; const createSequenceStore = () => { - const { subscribe, set, update } = writable(defaults) + const { subscribe, set, update } = writable(defaults); return { subscribe, update: ( data: keyof SequenceStoreData, newValue: SequenceStoreData[typeof data] ) => { - update(store => { - store[data] = newValue - return store - }) + update((store) => { + store[data] = newValue; + return store; + }); }, reset: () => set(defaults), - } -} + }; +}; -export const sequenceStore = createSequenceStore() +export const sequenceStore = createSequenceStore(); diff --git a/client/src/lib/stores/settingsStore.ts b/client/src/lib/stores/settingsStore.ts index 7599db7..917bcfd 100644 --- a/client/src/lib/stores/settingsStore.ts +++ b/client/src/lib/stores/settingsStore.ts @@ -1,35 +1,35 @@ /* stores global app wide settings */ -import { writable } from 'svelte/store' +import { writable } from "svelte/store"; export interface SettingsStoreData { - disableAnnoyances: boolean - goWoke: boolean - fastStartup: boolean + disableAnnoyances: boolean; + goWoke: boolean; + fastStartup: boolean; } export const defaults: SettingsStoreData = { disableAnnoyances: false, // disable non-critical notifications goWoke: false, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences - fastStartup: true, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.) -} + fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.) +}; const createSequenceStore = () => { - const { subscribe, set, update } = writable(defaults) + const { subscribe, set, update } = writable(defaults); return { subscribe, update: ( data: keyof SettingsStoreData, newValue: SettingsStoreData[typeof data] ) => { - update(store => { - store[data] = newValue - return store - }) + update((store) => { + store[data] = newValue; + return store; + }); }, reset: () => set(defaults), set: (data: SettingsStoreData) => set(data), - } -} + }; +}; -export const settingsStore = createSequenceStore() +export const settingsStore = createSequenceStore();