function set_all_url_params(params, { replace_history_state = false } = {}) {

	let new_hash = "";
	for (const [param_name, param_type] of Object.entries(param_types)) {
		if (params[param_name]) {
			if (new_hash.length) {
				new_hash += ",";
			}
			new_hash += encodeURIComponent(param_name);
			if (param_type !== "bool") {
				new_hash += ":" + encodeURIComponent(params[param_name]);
			}
		}
	}
	let query_string = location.search;
	// The Discord Activity needs to preserve the query string, so it's exempt from this.
	if (!query_string.includes("frame_id")) {
		// Omit query string for theoretical backwards compatibility with old URLs.
		// TODO: what were these URLs? do they really still work? are they still relevant? probably not...
		query_string = "";
	}
	const new_url = `${location.origin}${location.pathname}${query_string}#${new_hash}`;
	try {
		// can fail when running from file: protocol
		if (replace_history_state) {
			history.replaceState(null, document.title, new_url);
		} else {
			history.pushState(null, document.title, new_url);
		}
	} catch (_error) {
		location.hash = new_hash;
	}

	$G.triggerHandler("change-url-params");
}
@param {Record<string, string | boolean>} params
@param {object} [options]
@param {boolean} [options.replace_history_state=false]