Hi! This is indeed a feature present in the v2 branch, however, implementing it on the stable is not impossible, to do this, you'll need to modify your monogatari.js
file, around line #628 you'll find the saveToSlot
function, this is the function that take cares of saving a game to a "save file" using the browser's local storage.
What you want to modify in it is the calls to Storage.get
and Storage.set
which are the ones retrieving and saving to localStorage. Here's an (untested) example of how you may do that, notice the serverURL
variable has a part that reads :user_identifier
, I added that as an example but how you'll identify your users depends on your server's implementation.
function saveToSlot (id, slot, customName) {
// Check if the player is actually playing
if (playing) {
document.body.style.cursor = "wait";
// Get a list of all the images being shown in screen except the
// face images so they can be shown next time the slot is loaded
let show = "";
$_("#game img:not([data-ui='face']):not([data-visibility='invisible'])").each(function (element) {
show += element.outerHTML.replace(/"/g, "'") + ",";
});
let name;
const serverUrl = `https://yourserver.com/api/:user_identifier/${slot}`;
// Get the name of the Slot if it exists or use the current date.
fetch(serverUrl).then((response) => {
return response.json();
}).then((data) => {
if (data !== null && data !== "") {
if (data.Name !== null && data.Name !== "" && typeof data.Name !== "undefined") {
name = data.Name;
} else {
name = niceDateTime ();
}
} else {
name = niceDateTime ();
}
}).catch(() => {
// The save slot did not exist
}).finally(() => {
if (typeof customName !== "undefined") {
name = customName;
}
// Build the save slot data with the current state of every
// important object
const saveData = {
"Name": name,
"Date": niceDateTime (),
"Engine": engine,
"Show": show,
"Label": engine.Label,
"Storage": storage
};
// Post the data to the server
fetch(serverUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: saveData
}).then (() => {
$_(`[data-menu='load'] [data-ui='saveSlots'] [data-ui='slots'] [data-load-slot='${id}'] small`).text (name);
$_(`[data-menu='save'] [data-ui='slots'] [data-save='${id}'] small`).text (name);
document.body.style.cursor = "auto";
});
});
}
}
And since you're already modifying that, you'll also need to modify the setSlots
function in line #512 to retrieve the slots from your server instead of the localStorage. Here's another (untested) example of how that may look like:
function setSlots () {
$_("[data-menu='load'] [data-ui='saveSlots'] [data-ui='slots']").html("");
$_("[data-menu='save'] [data-ui='slots']").html("");
$_("[data-menu='save'] [data-input='slotName']").value (niceDateTime ());
// Retrieve all slots for an user
const serverUrl = `https://yourserver.com/api/:user_identifier/slots`;
fetch (serverUrl).then((response) => {
return response.json();
}).then((slots) => {
const savedData = Object.keys(slots).filter(function (key) {
return key.indexOf (engine.SaveLabel) == 0;
}).sort (function (a, b) {
const aNumber = parseInt (a.split (engine.SaveLabel)[1]);
const bNumber = parseInt (b.split (engine.SaveLabel)[1]);
if (aNumber > bNumber) {
return 1;
} else if (aNumber < bNumber) {
return -1;
} else {
return 0;
}
});
for (let i = 0; i < savedData.length; i++) {
const label = savedData[i];
const slot = Storage.get(label);
const id = label.split (engine.SaveLabel)[1];
if (slot !== null && slot !== "") {
addSlot (id, slot);
}
}
// Check if there are no Saved games.
if ($_("[data-menu='load'] [data-ui='saveSlots'] [data-ui='slots']").html().trim() == "") {
$_("[data-menu='load'] [data-ui='slots']").html(`<p>${getLocalizedString("NoSavedGames")}</p>`);
}
setAutoSlots ();
});
}
For both functions I assume the server will be returning JSON objects instead of stringified representations of them. I also recommend reading about the fetch function used to do the server calls.
Please let me know if you have any issues or doubts using the functions!