This is a patch you can apply to Profectus to add support for registering query parameters that will either call a function according to their value, or automatically update a ref (so long as that ref is a string, number, or boolean). It also registers /new as a path to automatically create a new save.
An example use case of this is Chromatic Lattice, a multiplayer incremental game where I want users to be able to link others to their board, so I can register a query param by doing this:
const currentBoard = ref();reqisterQueryParam("board", currentBoard);watch(currentBoard, username => { if (username == null) return; // Make a network request to retrieve the board data for the given username});Here's the patch to apply to get this feature:
[details="patch"]
From 0cdc33d0bec6b2248f00e8bb62abc4b11625a448 Mon Sep 17 00:00:00 2001From: thepaperpilot Date: Sun, 11 Feb 2024 13:40:10 -0600Subject: [PATCH 1/2] Added /new and query param handlers--- src/game/routing.ts | 68 +++++++++++++++++++++++++++++++++++++++++++++ src/main.ts | 15 ++++++++-- src/util/save.ts | 5 +--- 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 src/game/routing.tsdiff --git a/src/game/routing.ts b/src/game/routing.tsnew file mode 100644index 0000000..693c1d6--- /dev/null+++ b/src/game/routing.ts@@ -0,0 +1,68 @@+import { globalBus } from "game/events";+import { DecimalSource } from "util/bignum";+import { Ref } from "vue";+import player from "./player";++// https://stackoverflow.com/questions/2090551/parse-query-string-in-javascript+function parseQuery(queryString = window.location.search) {+ const query: Record = {};+ const pairs = (queryString[0] === "?" ? queryString.substring(1) : queryString).split("&");+ for (let i = 0; i < pairs.length; i++) {+ const pair = pairs[i].split("=");+ query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || "");+ }+ return query;+}+const params = parseQuery();++/**+ * Register a handler to be called when creating new saves based on a query param+ * @param key The query param to regster+ * @param handler The callback function when the query param is present+ * @param newSavesOnly If set to true, only call the handler on the /new path+ */+export function registerQueryParam(+ key: string,+ handler: (value: string) => void,+ newSavesOnly?: boolean+): void;+/**+ * Register a ref to have its value set based on a query param+ * @param key The query param to regster+ * @param ref The ref to set the value of+ * @param newSavesOnly If set to true, only overwrite values on the /new path+ * @see {@link numberHandler}.+ */+export function registerQueryParam(+ key: string,+ ref: Ref,+ newSavesOnly?: boolean+): void;+export function registerQueryParam(+ key: string,+ handlerOrRef: ((value: string) => void) | Ref,+ newSavesOnly = false+) {+ globalBus.on("onLoad", () => {+ if (newSavesOnly && player.timePlayed > 0) {+ return;+ }+ if (key in params) {+ if (typeof handlerOrRef === "function") {+ handlerOrRef(params[key]);+ } else {+ if (typeof handlerOrRef.value === "boolean") {+ (handlerOrRef.value as boolean) = params[key].toLowerCase() === "true";+ } else {+ (handlerOrRef.value as string | DecimalSource) = params[key];+ }+ }+ }+ });+}++export function numberHandler(ref: Ref) {+ return function (value: string) {+ ref.value = parseFloat(value);+ };+}diff --git a/src/main.ts b/src/main.tsindex 3b5de9f..f19eea7 100644--- a/src/main.ts+++ b/src/main.ts@@ -3,7 +3,8 @@ import App from "App.vue"; import projInfo from "data/projInfo.json"; import "game/notifications"; import state from "game/state";-import { load } from "util/save";+import { loadSettings } from "game/settings";+import { load, loadSave, newSave } from "util/save"; import { useRegisterSW } from "virtual:pwa-register/vue"; import type { App as VueApp } from "vue"; import { createApp, nextTick } from "vue";@@ -60,7 +61,17 @@ requestAnimationFrame(async () => { "font-weight: bold; font-size: 24px; color: #A3BE8C; background: #2E3440; padding: 4px 8px; border-radius: 8px;", "padding: 4px;" );- await load();++ // Load global settings+ loadSettings();++ if (window.location.pathname === "/new") {+ await loadSave(newSave());+ } else {+ await load();+ }+ window.history.replaceState({}, document.title, "/");+ const { globalBus } = await import("./game/events"); const { startGameLoop } = await import("./game/gameLoop"); diff --git a/src/util/save.ts b/src/util/save.tsindex 54e0e9b..7e8f78c 100644--- a/src/util/save.ts+++ b/src/util/save.ts@@ -3,7 +3,7 @@ import projInfo from "data/projInfo.json"; import { globalBus } from "game/events"; import type { Player } from "game/player"; import player, { stringifySave } from "game/player";-import settings, { loadSettings } from "game/settings";+import settings from "game/settings"; import LZString from "lz-string"; import { ref, shallowReactive } from "vue"; @@ -34,9 +34,6 @@ export function save(playerData?: Player): string { } export async function load(): Promise {- // Load global settings- loadSettings();- try { let save = localStorage.getItem(settings.active); if (save == null) {-- 2.45.2From c21e949722a63b91018dbffda4d135806552d88c Mon Sep 17 00:00:00 2001From: thepaperpilot Date: Thu, 26 Dec 2024 16:38:45 +0000Subject: [PATCH 2/2] Fix typo--- src/game/routing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)diff --git a/src/game/routing.ts b/src/game/routing.tsindex 693c1d6..0d27c8b 100644--- a/src/game/routing.ts+++ b/src/game/routing.ts@@ -28,7 +28,7 @@ export function registerQueryParam( ): void; /** * Register a ref to have its value set based on a query param- * @param key The query param to regster+ * @param key The query param to register * @param ref The ref to set the value of * @param newSavesOnly If set to true, only overwrite values on the /new path * @see {@link numberHandler}.-- 2.45.2[/details]
Discuss this on our forum.