From fe729f26227b153677e583fa03d1590260343aca Mon Sep 17 00:00:00 2001 From: An Orbit <68935009+orbit-loona@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:44:19 -0400 Subject: [PATCH] fucking hell --- mods/save_loading.js | 688 +++++++++++++++++++++++++------------------ 1 file changed, 403 insertions(+), 285 deletions(-) diff --git a/mods/save_loading.js b/mods/save_loading.js index aa22a0a0..8ccd1af2 100644 --- a/mods/save_loading.js +++ b/mods/save_loading.js @@ -1,333 +1,451 @@ var modName = "mods/save_loading.js"; + try { -function zeroToNull(val) { - if(val === 0) { return null }; - return val; -}; + //https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API + function storageAvailable(type) { + let storage; + try { + storage = window[type]; + const x = "__storage_test__"; + storage.setItem(x, x); + storage.removeItem(x); + return true; + } catch (e) { + return ( + e instanceof DOMException && + // everything except Firefox + (e.code === 22 || + // Firefox + e.code === 1014 || + // test name field too, because code might not be present + // everything except Firefox + e.name === "QuotaExceededError" || + // Firefox + e.name === "NS_ERROR_DOM_QUOTA_REACHED") && + // acknowledge QuotaExceededError only if there's something already stored + storage && + storage.length !== 0 + ); + } + } -if(!localStorage.slSaveSettings) { - localStorage.setItem("slSaveSettings", '{"roundTemps":true}'); -}; - -slSaveSettings = JSON.parse(localStorage.slSaveSettings); - -function epsilonRound(num,precision) { - return Math.round((num + Number.EPSILON) * (10 ** precision)) / (10 ** precision); -}; - -function getSimulationState() { - var simulationState = { - //currentPixels: currentPixels, - pixelMap: structuredClone ? structuredClone(pixelMap) : JSON.parse(JSON.stringify(pixelMap)), - width: width, - height: height, - pixelSize: pixelSize, - settings: settings, - version: 1, - enabledMods: localStorage.enabledMods, + function zeroToNull(val) { + if(val === 0) { return null }; + return val; }; - for(i = 0; i < simulationState.pixelMap.length; i++) { - var column = simulationState.pixelMap[i]; - for(j = 0; j < column.length; j++) { - if(column[j] === null || typeof(column[j]) === "undefined") { - column[j] = 0; - }; + + if(!localStorage.slSaveSettings) { + localStorage.setItem("slSaveSettings", '{"roundTemps":true}'); + }; + + slSaveSettings = JSON.parse(localStorage.slSaveSettings); + + function epsilonRound(num,precision) { + return Math.round((num + Number.EPSILON) * (10 ** precision)) / (10 ** precision); + }; + + function getSimulationState() { + var simulationState = { + //currentPixels: currentPixels, + pixelMap: structuredClone ? structuredClone(pixelMap) : JSON.parse(JSON.stringify(pixelMap)), + width: width, + height: height, + pixelSize: pixelSize, + settings: settings, + version: 1, + enabledMods: localStorage.enabledMods, }; - }; - if(slSaveSettings.roundTemps) { for(i = 0; i < simulationState.pixelMap.length; i++) { var column = simulationState.pixelMap[i]; for(j = 0; j < column.length; j++) { - var pixel = column[j]; - if(pixel?.temp) { - pixel.temp = epsilonRound(pixel.temp,3); + if(column[j] === null || typeof(column[j]) === "undefined") { + column[j] = 0; }; }; }; - }; - return simulationState; -}; - -//https://stackoverflow.com/a/46118025 -function copyToClipboard(text) { - var dummy = document.createElement("textarea"); - // to avoid breaking orgain page when copying more words - // cant copy when adding below this code - // dummy.style.display = 'none' - document.body.appendChild(dummy); - //Be careful if you use textarea. setAttribute('value', value), which works with "input" does not work with "textarea". – Eduard - dummy.value = text; - dummy.select(); - document.execCommand("copy"); - document.body.removeChild(dummy); -} - -const saveTemplateAsFile = (filename, dataObjToWrite) => { //from https://stackoverflow.com/a/65939108 - const blob = new Blob([JSON.stringify(dataObjToWrite)], { type: "text/json" }); - const link = document.createElement("a"); - - link.download = filename; - link.href = window.URL.createObjectURL(blob); - link.dataset.downloadurl = ["text/json", link.download, link.href].join(":"); - - const evt = new MouseEvent("click", { - view: window, - bubbles: true, - cancelable: true, - }); - - link.dispatchEvent(evt); - link.remove() -}; - -function formatCurrentDate() { //derived from https://gist.github.com/Ivlyth/c4921735812dd2c0217a - var d = new Date(); - var year = d.getFullYear().toString(); - - var month = (d.getMonth()+1).toString(); - if(month.length == 1) { month = "0" + month }; - - var day = d.getDate().toString(); - if(day.length == 1) { day = "0" + day }; - - var hour = d.getHours().toString(); - if(hour.length == 1) { hour = "0" + hour }; - - var minute = d.getMinutes().toString(); - if(minute.length == 1) { minute = "0" + minute }; - - var second = d.getSeconds().toString(); - if(second.length == 1) { second = "0" + second }; - - var date_format_str = `${year}-${month}-${day} ${hour}-${minute}-${second}`; - return date_format_str; -}; - -function savePrompt() { - var filename = prompt("Please enter the desired filename, without the .json (defaults to current date)"); - if(filename === null) { - return false; - }; - if(filename === "") { - filename = `Sandboxels save ${formatCurrentDate()}`; - }; - filename += ".json"; - downloadSave(filename) -}; - -function downloadSave(filename=null) { - if(filename === null) { - filename = `Sandboxels save ${formatCurrentDate()}.json`; - }; - saveTemplateAsFile(filename, getSimulationState()); -}; - -function copySaveJSON(doAlert=true) { - copyToClipboard(JSON.stringify(getSimulationState())); - if(doAlert) { alert("Save copied as JSON") }; -}; - -function loadFile() { - //Initialize - var json; - - //load JSON - var file = document.getElementById('myfile').files[0]; - if(file === undefined) { - if(document.getElementById("fileFormStatus") !== "null") { - document.getElementById("fileFormStatus").style.color = "red"; - document.getElementById("fileFormStatus").innerHTML = "No file was uploaded!"; + if(slSaveSettings.roundTemps) { + for(i = 0; i < simulationState.pixelMap.length; i++) { + var column = simulationState.pixelMap[i]; + for(j = 0; j < column.length; j++) { + var pixel = column[j]; + if(pixel?.temp) { + pixel.temp = epsilonRound(pixel.temp,3); + }; + }; + }; }; - throw new Error("No file was uploaded"); + return simulationState; }; - var reader = new FileReader(); - reader.readAsText(file, 'UTF-8'); - //after loading - reader.onload = function(evt) { - json = evt.target.result; + + //https://stackoverflow.com/a/46118025 + function copyToClipboard(text) { + var dummy = document.createElement("textarea"); + // to avoid breaking orgain page when copying more words + // cant copy when adding below this code + // dummy.style.display = 'none' + document.body.appendChild(dummy); + //Be careful if you use textarea. setAttribute('value', value), which works with "input" does not work with "textarea". – Eduard + dummy.value = text; + dummy.select(); + document.execCommand("copy"); + document.body.removeChild(dummy); + } + + const saveTemplateAsFile = (filename, dataObjToWrite) => { //from https://stackoverflow.com/a/65939108 + const blob = new Blob([JSON.stringify(dataObjToWrite)], { type: "text/json" }); + const link = document.createElement("a"); + + link.download = filename; + link.href = window.URL.createObjectURL(blob); + link.dataset.downloadurl = ["text/json", link.download, link.href].join(":"); + + const evt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + + link.dispatchEvent(evt); + link.remove() + }; + + function formatCurrentDate() { //derived from https://gist.github.com/Ivlyth/c4921735812dd2c0217a + var d = new Date(); + var year = d.getFullYear().toString(); + + var month = (d.getMonth()+1).toString(); + if(month.length == 1) { month = "0" + month }; + + var day = d.getDate().toString(); + if(day.length == 1) { day = "0" + day }; + + var hour = d.getHours().toString(); + if(hour.length == 1) { hour = "0" + hour }; + + var minute = d.getMinutes().toString(); + if(minute.length == 1) { minute = "0" + minute }; + + var second = d.getSeconds().toString(); + if(second.length == 1) { second = "0" + second }; + + var date_format_str = `${year}-${month}-${day} ${hour}-${minute}-${second}`; + return date_format_str; + }; + + function savePrompt() { + var filename = prompt("Please enter the desired filename, without the .json (defaults to current date)"); + if(filename === null) { + return false; + }; + if(filename === "") { + filename = `Sandboxels save ${formatCurrentDate()}`; + }; + filename += ".json"; + downloadSave(filename) + }; + + function downloadSave(filename=null) { + if(filename === null) { + filename = `Sandboxels save ${formatCurrentDate()}.json`; + }; + saveTemplateAsFile(filename, getSimulationState()); + }; + + rebuildCurrentPixels ??= function() { + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; + }; + }; + }; + currentPixels = currPix; + }; + + function quicksave(doSuccessAlert=true,doFailAlert=true) { + if(storageAvailable("localStorage")) { + rebuildCurrentPixels(); + localStorage.setItem("quicksave",JSON.stringify(getSimulationState())); + if(doSuccessAlert) { alert("Quicksave saved") }; + return true; + } else { + if(doFailAlert) { alert("Could not save quicksave in localStorage") }; + throw new Error("Could not save quicksave in localStorage"); + }; + }; + + function quickload(pause=true,doSuccessAlert=true,doFailAlert=true) { + clearAll(); + rebuildCurrentPixels(); + var save = localStorage.getItem("quicksave"); + if(!save) { + if(doFailAlert) { alert("No save exists") }; + return false; + } else { + importJsonState(JSON.parse(save)); + if(doSuccessAlert) { alert("Quicksave loaded") }; + if(pause) { + paused = true; + document.getElementById("pauseButton").setAttribute("on","true"); + }; + return true; + }; + rebuildCurrentPixels(); + }; + + function copySaveJSON(doAlert=true) { + copyToClipboard(JSON.stringify(getSimulationState())); + if(doAlert) { alert("Save copied as JSON") }; + }; + + function loadFile() { + //Initialize + var json; + + //load JSON + var file = document.getElementById('myfile').files[0]; + if(file === undefined) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "No file was uploaded!"; + }; + throw new Error("No file was uploaded"); + }; + var reader = new FileReader(); + reader.readAsText(file, 'UTF-8'); + //after loading + reader.onload = function(evt) { + json = evt.target.result; + + //validate + try { + json = JSON.parse(json); + } catch (error) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "The file wasn't valid JSON!"; + }; + throw error; + }; + + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "yellow"; + document.getElementById("fileFormStatus").innerHTML = "JSON was parsed successfully"; + }; + + //return json; + return importJsonState(json); + }; + }; + + function loadText() { + //Initialize + var json; + + //load JSON + var json = document.getElementById('mytext').value; + if(json === "") { + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "red"; + document.getElementById("textFormStatus").innerHTML = "No text was present!"; + }; + throw new Error("No text was present"); + }; //validate try { json = JSON.parse(json); } catch (error) { - if(document.getElementById("fileFormStatus") !== "null") { - document.getElementById("fileFormStatus").style.color = "red"; - document.getElementById("fileFormStatus").innerHTML = "The file wasn't valid JSON!"; + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "red"; + document.getElementById("textFormStatus").innerHTML = "The text wasn't valid JSON!"; }; throw error; }; - if(document.getElementById("fileFormStatus") !== "null") { - document.getElementById("fileFormStatus").style.color = "yellow"; - document.getElementById("fileFormStatus").innerHTML = "JSON was parsed successfully"; + if(document.getElementById("textFormStatus") !== null) { + document.getElementById("textFormStatus").style.color = "yellow"; + document.getElementById("textFormStatus").innerHTML = "JSON was parsed successfully"; }; //return json; return importJsonState(json); }; -}; -function loadText() { - //Initialize - var json; - - //load JSON - var json = document.getElementById('mytext').value; - if(json === "") { - if(document.getElementById("textFormStatus") !== "null") { - document.getElementById("textFormStatus").style.color = "red"; - document.getElementById("textFormStatus").innerHTML = "No text was present!"; - }; - throw new Error("No text was present"); - }; - - //validate - try { - json = JSON.parse(json); - } catch (error) { - if(document.getElementById("textFormStatus") !== "null") { - document.getElementById("textFormStatus").style.color = "red"; - document.getElementById("textFormStatus").innerHTML = "The text wasn't valid JSON!"; - }; - throw error; - }; - - if(document.getElementById("textFormStatus") !== "null") { - document.getElementById("textFormStatus").style.color = "yellow"; - document.getElementById("textFormStatus").innerHTML = "JSON was parsed successfully"; - }; - - //return json; - return importJsonState(json); -}; - -function importJsonState(json) { - //check keys - var jsonKeys = Object.keys(json); - var requiredKeys = [/*"currentPixels", */"pixelMap", "width", "height", "pixelSize"]; - var hasrequiredKeys = true; - for(i = 0; i < requiredKeys.length; i++) { - var key = requiredKeys[i]; - if(!jsonKeys.includes(key)) { - hasrequiredKeys = false; - break; - }; - }; - if(!hasrequiredKeys) { - if(document.getElementById("fileFormStatus") !== "null") { - document.getElementById("fileFormStatus").style.color = "red"; - document.getElementById("fileFormStatus").innerHTML = "JSON is not a valid save!"; - }; - throw new Error("JSON is missing required keys!"); - }; - - //Set values - width = json.width; - height = json.height; - pixelSize = json.pixelSize; - //currentPixels = json.currentPixels; - for(i = 0; i < json.pixelMap.length; i++) { - json.pixelMap[i] = json.pixelMap[i].map(x => zeroToNull(x)); - }; - pixelMap = json.pixelMap; - if(json.settings) { - settings = json.settings; - }; - - //enabledMods handling { - var enMods = "[]"; - if(typeof(json.enabledMods) !== "undefined") { - enMods = json.enabledMods; - }; - enMods = JSON.parse(enMods); - //console.log(enMods); - - var currentEnmods = JSON.parse(localStorage.enabledMods); //should already exist if you're using this mod in the first place - for(emi = 0; emi < enMods.length; emi++) { //load mods additively to prevent self-disabling and the inconvenience of having to readd your mod list when you get bored - var mod = enMods[emi]; - if(!currentEnmods.includes(mod)) { - currentEnmods.push(mod); + function importJsonState(json) { + //check keys + var jsonKeys = Object.keys(json); + var requiredKeys = [/*"currentPixels", */"pixelMap", "width", "height", "pixelSize"]; + var hasrequiredKeys = true; + for(i = 0; i < requiredKeys.length; i++) { + var key = requiredKeys[i]; + if(!jsonKeys.includes(key)) { + hasrequiredKeys = false; + break; }; }; - localStorage.setItem("enabledMods",JSON.stringify(currentEnmods)); - if((enMods.length > 0 && enMods[0] !== modName) || enMods.length > 1) { - alert("Saves with other mods might require a reload (and then importing the save file again).\nIf you see a blank screen, try refreshing and loading the file again before you panic."); + if(!hasrequiredKeys) { + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "red"; + document.getElementById("fileFormStatus").innerHTML = "JSON is not a valid save!"; + }; + throw new Error("JSON is missing required keys!"); }; - //} - - var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug - for(pmi = 0; pmi < pixelMap.length; pmi++) { - var pixelMapPart = pixelMap[pmi]; - for(pmj = 0; pmj < pixelMapPart.length; pmj++) { - var pixelMapUnit = pixelMapPart[pmj]; - if(typeof(pixelMapUnit) === "object") { - if(pixelMapUnit !== null) { - currPix.push(pixelMapUnit); + + //Set values + width = json.width; + height = json.height; + pixelSize = json.pixelSize; + //currentPixels = json.currentPixels; + for(i = 0; i < json.pixelMap.length; i++) { + json.pixelMap[i] = json.pixelMap[i].map(x => zeroToNull(x)); + }; + pixelMap = json.pixelMap; + if(json.settings) { + settings = json.settings; + }; + + //enabledMods handling { + var enMods = "[]"; + if(typeof(json.enabledMods) !== "undefined") { + enMods = json.enabledMods; + }; + enMods = JSON.parse(enMods); + //console.log(enMods); + + var currentEnmods = JSON.parse(localStorage.enabledMods); //should already exist if you're using this mod in the first place + for(emi = 0; emi < enMods.length; emi++) { //load mods additively to prevent self-disabling and the inconvenience of having to readd your mod list when you get bored + var mod = enMods[emi]; + if(!currentEnmods.includes(mod)) { + currentEnmods.push(mod); + }; + }; + localStorage.setItem("enabledMods",JSON.stringify(currentEnmods)); + if((enMods.length > 0 && enMods[0] !== modName) || enMods.length > 1) { + alert("Saves with other mods might require a reload (and then importing the save file again).\nIf you see a blank screen, try refreshing and loading the file again before you panic."); + }; + //} + + var currPix = []; //rebuild currentPixels from pixelMap to try to fix bug + for(pmi = 0; pmi < pixelMap.length; pmi++) { + var pixelMapPart = pixelMap[pmi]; + for(pmj = 0; pmj < pixelMapPart.length; pmj++) { + var pixelMapUnit = pixelMapPart[pmj]; + if(typeof(pixelMapUnit) === "object") { + if(pixelMapUnit !== null) { + currPix.push(pixelMapUnit); + }; }; }; }; - }; - currentPixels = currPix; - - if(document.getElementById("fileFormStatus") !== "null") { - document.getElementById("fileFormStatus").style.color = "green"; - document.getElementById("fileFormStatus").innerHTML = "JSON was loaded successfully."; - }; - return true; -}; - -function setPixelSize(size=null) { - if(size === null) { - if(document.getElementById("pixelSize") !== "null") { - size = document.getElementById("pixelSize").value; - } else { - throw new Error("No size could be read"); + currentPixels = currPix; + + if(document.getElementById("fileFormStatus") !== null) { + document.getElementById("fileFormStatus").style.color = "green"; + document.getElementById("fileFormStatus").innerHTML = "JSON was loaded successfully."; }; + return true; }; - size = parseFloat(size); - if(isNaN(size) || size <= 0) { //NaN check - if(document.getElementById("pixelSizeStatus") !== "null") { - document.getElementById("pixelSizeStatus").style.color = "red"; - document.getElementById("pixelSizeStatus").innerHTML = "Pixel size is empty or invalid"; + function setPixelSize(size=null) { + if(size === null) { + if(document.getElementById("pixelSize") !== null) { + size = document.getElementById("pixelSize").value; + } else { + throw new Error("No size could be read"); + }; }; - throw new Error("NaN or negative size"); + + size = parseFloat(size); + if(isNaN(size) || size <= 0) { //NaN check + if(document.getElementById("pixelSizeStatus") !== null) { + document.getElementById("pixelSizeStatus").style.color = "red"; + document.getElementById("pixelSizeStatus").innerHTML = "Pixel size is empty or invalid"; + }; + throw new Error("NaN or negative size"); + }; + + if(document.getElementById("pixelSizeStatus") !== null) { + document.getElementById("pixelSizeStatus").style.color = "green"; + document.getElementById("pixelSizeStatus").innerHTML = "Pixel size set successfully"; + }; + pixelSize = size; + return true; }; - - if(document.getElementById("pixelSizeStatus") !== "null") { - document.getElementById("pixelSizeStatus").style.color = "green"; - document.getElementById("pixelSizeStatus").innerHTML = "Pixel size set successfully"; - }; - pixelSize = size; - return true; -}; -var saveLoaderDescription = `
-Download simulation -Alternatively, copy simulation JSON + var saveLoaderDescription = `
+ Download simulation + Alternatively, copy simulation JSON -No file loader status -One file, please: - -Or paste JSON -No text loader status - - + No file loader status + One file, please: + + Or paste JSON + No text loader status + + -No size setter status -Pixel size (rendering only): (Use if the save looks cut off) - -
`; + No size setter status + Pixel size (rendering only): (Use if the save looks cut off) + +
`; -runAfterLoad(function() { //somehow it gets defined before elements on xbox - elements ??= {}; elements.save_loader = { - behavior: behaviors.SELFDELETE, - excludeRandom: true, - color: "#FFFFFF", - desc: saveLoaderDescription, + behavior: behaviors.SELFDELETE, + excludeRandom: true, + color: "#FFFFFF", + desc: saveLoaderDescription, }; -}); + + //Somehow, for some illogical reason, quicksaving causes updateStats to somehow disregard its if-statement and fucking TypeError when you mouse over an empty space; this is an attempt to fix it with overkill-level existence checks. + function updateStats() { + var statsDiv = document.getElementById("stats"); + var stats = "x"+mousePos.x+",y"+mousePos.y+""; + stats += "Pxls:" + currentPixels.length+""; + stats += "" + tps+"tps"; + stats += "" + pixelTicks+""; + if ((typeof pixelMap).length === 9) { return; } + if (pixelMap[mousePos.x] !== undefined) { + var currentPixel = pixelMap[mousePos.x][mousePos.y]; + if (typeof(currentPixel) !== "undefined" && currentPixel && currentPixel !== undefined && currentPixel.element) { + stats += "Elem:"+(elements[currentPixel?.element]?.name || currentPixel?.element)+""; + stats += "Temp:"+formatTemp(currentPixel.temp)+""; + if (currentPixel.charge) { + stats += "C"+currentPixel.charge+""; + } + if (currentPixel.burning) { + stats += "Burning"; + } + } + } + if (shiftDown) { + stats += ""+shiftDownTypes[shiftDown]+""; + } + // If the view is not null, show the view in all caps + if (view !== null) { + stats += ""+viewKey[view]+""; + } + statsDiv.innerHTML = stats; + } + + //Element-based quicksave toggle has proven impossible due to numerous irreparable illogic incidents, so have some buttons instead + var qsb = document.createElement("button"); + qsb.setAttribute("id","quicksaveButton"); + qsb.setAttribute("onclick","quicksave()"); + qsb.innerText = "Quicksave"; + document.getElementById("gameDiv").before(qsb); + qsb.after(document.createTextNode(String.fromCharCode(160))); + var qlb = document.createElement("button"); + qlb.setAttribute("id","quickloadButton"); + qlb.setAttribute("onclick","clearAll(); quickload()"); + qlb.innerText = "Quickload"; + document.getElementById("gameDiv").before(qlb); + document.getElementById("gameDiv").before(document.createElement("br")); } catch (error) { alert(`save_loading error: ${error.message}`); };