Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

Commit

Permalink
Huge number of updates for cloud sync!
Browse files Browse the repository at this point in the history
  • Loading branch information
ZudoB committed Feb 16, 2019
1 parent 6ce3a05 commit 9440fff
Show file tree
Hide file tree
Showing 11 changed files with 504 additions and 46 deletions.
13 changes: 13 additions & 0 deletions lang/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@
"button_affirmative": "Rename",
"button_negative": "Cancel"
},
"launch_lock_confirmation": {
"message": "The cloud save file is in use!",
"details": "If you launch now, you may lose your progress.",
"button_affirmative": "Launch Anyway",
"button_negative": "Cancel"
},
"launch_noauth_confirmation": {
"message": "You are not logged in!",
"details": "If you launch now, your save file will not sync to the cloud.",
"button_affirmative": "Launch Anyway",
"button_negative": "Cancel"
},
"mod_delete_confirmation": {
"message": "Are you sure you want to delete the mod?",
"details": "You will need to download another copy of the mod to install it again.",
Expand All @@ -119,6 +131,7 @@
"tag_global_save": "Global Save",
"tag_sdk": "SDK",
"tag_cloud": "Cloud Save",
"description_locked": "You are playing with this cloud save file on a different computer. To continue here, stop playing on the other computer.",
"title_achievements": "Achievements ({0} / {1})",
"description_achievements": "Keep playing to unlock more achievements.",
"description_achievements_complete": "You've unlocked all achievements. Congratulations!",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@fortawesome/fontawesome-free": "^5.5.0",
"@sentry/electron": "0.15.0",
"@zudo/unzipper": "^0.0.4",
"archiver": "^3.0.0",
"chmodr": "^1.2.0",
"discord-rich-presence": "^0.0.8",
"electron-updater": "^4.0.6",
Expand Down
101 changes: 101 additions & 0 deletions src/main/cloud/InstallSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {join as joinPath, sep as pathSep} from "path";
import {app} from "electron";
import {randomBytes} from "crypto";
import * as request from "request";
import * as unzip from "@zudo/unzipper";
import {createWriteStream, readdirSync, emptyDirSync, mkdirsSync, removeSync} from "fs-extra";
import Config from "../utils/Config";
import * as archiver from "archiver";

export default class InstallSync {

/**
* Install the save data from Firebase into an existing install
* @param folderName The folder name
* @param saveDataURL The URL
*/
public static installSaveData(folderName: string, saveDataURL: string): Promise<void> {
return new Promise((ff, rj) => {
const tempZipPath: string = joinPath(app.getPath("temp"), "ddmm" + randomBytes(8).toString("hex") + ".zip");

request(saveDataURL).pipe(createWriteStream(tempZipPath)).on("close", () => {
// mod is downloaded

let saveDataPath: string = joinPath(Config.readConfigValue("installFolder"), "installs", folderName, "appdata");

if (process.platform === "win32") {
saveDataPath = joinPath(saveDataPath, "RenPy");
} else if (process.platform === "darwin") {
saveDataPath = joinPath(saveDataPath, "Library", "RenPy");
} else {
saveDataPath = joinPath(saveDataPath, ".renpy");
}

emptyDirSync(saveDataPath); // clear the folder

const zip = unzip(tempZipPath);

zip.on("file", file => {
const pathParts = file.path.split("/");

const fileName = pathParts.pop();
mkdirsSync(joinPath(saveDataPath, pathParts.join(pathSep)));

console.log(joinPath(saveDataPath, pathParts.join(pathSep)));

// write the file
file.openStream((err, stream) => {
if (err) {
rj(err);
}
stream.pipe(createWriteStream(joinPath(
saveDataPath,
pathParts.join(pathSep), fileName)));
});
});

zip.on("close", () => {
removeSync(tempZipPath);
ff();
});
}).on("error", err => {
rj(err);
});
});
}

/**
* Compresses the save data of a mod into a .zip and returns the path
* @param folderName The folder name
*/
public static compressSaveData(folderName: string): Promise<string> {
return new Promise((ff, rj) => {
const tempZipPath: string = joinPath(app.getPath("temp"), "ddmm" + randomBytes(8).toString("hex") + ".zip");

let saveDataPath: string = joinPath(Config.readConfigValue("installFolder"), "installs", folderName, "appdata");

if (process.platform === "win32") {
saveDataPath = joinPath(saveDataPath, "RenPy");
} else if (process.platform === "darwin") {
saveDataPath = joinPath(saveDataPath, "Library", "RenPy");
} else {
saveDataPath = joinPath(saveDataPath, ".renpy");
}

const output = createWriteStream(tempZipPath);
const zip = archiver("zip");

output.on("close", () => {
ff(tempZipPath);
});

zip.on("error", err => {
rj(err);
});

zip.pipe(output);
zip.directory(saveDataPath, false);
zip.finalize();
});
}
}
62 changes: 56 additions & 6 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ import InstallManager from "./install/InstallManager";
import DiscordManager from "./discord/DiscordManager";
import DownloadManager from "./net/DownloadManager";
import OnboardingManager from "./onboarding/OnboardingManager";
import {unlinkSync} from "fs";
import {readFileSync, unlinkSync} from "fs";
import InstallSync from "./cloud/InstallSync";
import Timeout = NodeJS.Timeout;

const DISCORD_ID = "453299645725016074";

Expand Down Expand Up @@ -89,13 +91,47 @@ function showError(title: string, body: string, stacktrace: string, fatal: boole
appWindow.setClosable(true);
}

function getCloudSaveData(folderName: string): Promise<{url: string, name: string}> {
return new Promise((ff, rj) => {
const installDataFile: string = joinPath(Config.readConfigValue("installFolder"), "installs", folderName, "install.json");
let installData: any;
try {
installData =
JSON.parse(readFileSync(installDataFile).toString("utf8"));

if (installData.cloudSave) {
appWindow.webContents.send("get save url", installData.cloudSave);
ipcMain.once("got save url", (_, url) => {
ff({url, name: installData.cloudSave});
});
} else {
ff(null);
}
} catch (e) {
rj();
return;
}
});
}

/**
* Launches an install, handling frontend functionality automatically
* @param folderName The folder containing the install
*/
function launchInstall(folderName): void {
async function launchInstall(folderName): Promise<void> {
let lockTimer: Timeout;
Config.saveConfigValue("lastLaunchedInstall", folderName);
appWindow.minimize(); // minimise the window to draw attention to the fact another window will be appearing
const saveData: {url: string, name: string} = await getCloudSaveData(folderName);
if (saveData) {
appWindow.webContents.send("lock save", saveData.name);
lockTimer = setInterval(() => {
appWindow.webContents.send("lock save", saveData.name);
}, 30000);
}
if (saveData && saveData.url) {
await InstallSync.installSaveData(folderName, saveData.url);
}
appWindow.webContents.send("running cover", {
display: true,
dismissable: false,
Expand All @@ -108,9 +144,23 @@ function launchInstall(folderName): void {
appWindow.focus();
appWindow.webContents.send("running cover", {display: false});
appWindow.webContents.send("got installs", InstallList.getInstallList());
if (saveData) {
console.log("Compressing save data");
InstallSync.compressSaveData(folderName).then(pathToSave => {
appWindow.webContents.send("upload save", {
localURL: pathToSave,
filename: saveData.name
});
clearTimeout(lockTimer);
}).catch(e => {
// TODO: talk about the error
});
}
}).catch(err => {
appWindow.restore();
appWindow.focus();
clearTimeout(lockTimer);
appWindow.webContents.send("unlock save", saveData.name);
appWindow.webContents.send("running cover", {
display: true,
dismissable: true,
Expand Down Expand Up @@ -224,7 +274,7 @@ ipcMain.on("create install", (ev: IpcMessageEvent, install: { folderName: string
});

// Rename an install
ipcMain.on("rename install", (ev: IpcMessageEvent, options: {folderName: string, newName: string}) => {
ipcMain.on("rename install", (ev: IpcMessageEvent, options: { folderName: string, newName: string }) => {
console.log("[IPC rename install] Renaming " + options.folderName);
InstallManager.renameInstall(options.folderName, options.newName).then(() => {
console.log("[IPC rename install] Renamed " + options.folderName);
Expand Down Expand Up @@ -289,7 +339,7 @@ ipcMain.on("delete save", (ev: IpcMessageEvent, folderName: string) => {
});

// desktop shortcut creation
ipcMain.on("create shortcut", (ev: IpcMessageEvent, options: {folderName: string, installName: string}) => {
ipcMain.on("create shortcut", (ev: IpcMessageEvent, options: { folderName: string, installName: string }) => {
if (process.platform !== "win32") {
dialog.showErrorBox("Shortcut creation is only supported on Windows", "Nice try.");
return;
Expand Down Expand Up @@ -411,7 +461,7 @@ ipcMain.on("onboarding browse", () => {
});

ipcMain.on("download mod", (ev, url) => {
downloadManager.downloadFile(url, joinPath(Config.readConfigValue("installFolder"), "mods"), null,"DOWNLOADED_MOD");
downloadManager.downloadFile(url, joinPath(Config.readConfigValue("installFolder"), "mods"), null, "DOWNLOADED_MOD");
});

// endregion
Expand Down Expand Up @@ -557,7 +607,7 @@ app.on("ready", () => {
});

downloadManager.on("download complete", () => {
appWindow.webContents.send("got modlist", modList.getModList());
appWindow.webContents.send("got modlist", modList.getModList());
});

downloadManager.on("download failed", () => {
Expand Down
8 changes: 8 additions & 0 deletions src/main/install/InstallCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export default class InstallCreator {
mkdirsSync(joinPath(canonicalPath, "appdata"));
mkdirsSync(joinPath(canonicalPath, "install"));

if (process.platform === "win32") {
mkdirsSync(joinPath(canonicalPath, "appdata", "RenPy"));
} else if (process.platform === "darwin") {
mkdirsSync(joinPath(canonicalPath, "appdata", "Library", "RenPy"));
} else {
mkdirsSync(joinPath(canonicalPath, "appdata", ".renpy"));
}

// extract the game from the zip file
const zip = unzip(joinPath(Config.readConfigValue("installFolder"), "ddlc.zip"));

Expand Down
8 changes: 7 additions & 1 deletion src/main/install/InstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ export default class InstallManager {
return new Promise((ff, rj) => {
const dirPath = joinPath(Config.readConfigValue("installFolder"), "installs", folderName);
if (existsSync(dirPath)) {
emptyDir(joinPath(dirPath, "appdata")).then(ff).catch(rj);
if (process.platform === "win32") {
emptyDir(joinPath(dirPath, "appdata")).then(ff).catch(rj);
} else if (process.platform === "darwin") {
emptyDir(joinPath(dirPath, "appdata", "Library", "RenPy")).then(ff).catch(rj);
} else {
emptyDir(joinPath(dirPath, "appdata", ".renpy")).then(ff).catch(rj);
}
} else {
rj(new Error("Install does not exist."))
}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ <h1>{{input_cover.title}}</h1>
<div @click="showHelpMenu" :title="_('renderer.window_controls.help')"><i class="fas fa-question"></i></div>

<div @click="login" v-if="!getLoggedInUsername()">{{_('renderer.window_controls.login')}}</div>
<div @click="showUserMenu" v-else style="max-width: 100px; overflow: hidden; text-overflow: ellipsis;">{{getLoggedInUsername()}}</div>
<div @click="showUserMenu" v-else style="max-width: 200px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">{{getLoggedInUsername()}}</div>

<template v-if="!system_borders">
<div @click="windowMinimise" :title="_('renderer.window_controls.minimise')"><i
Expand Down
49 changes: 45 additions & 4 deletions src/renderer/js-preload/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,37 @@ api.mods.browseForMod = function () {
};

// Launches an install
api.mods.launchInstall = function (folderName) {
ipcRenderer.send("launch install", folderName);
api.mods.launchInstall = function (folderName, locked, loggedIn) {
if (!ready) return;
if (locked) {
api.emit("prompt", {
title: api.translate("renderer.tab_mods.launch_lock_confirmation.message"),
description: api.translate("renderer.tab_mods.launch_lock_confirmation.details"),
affirmative_style: "danger",
button_affirmative: api.translate("renderer.tab_mods.launch_lock_confirmation.button_affirmative"),
button_negative: api.translate("renderer.tab_mods.launch_lock_confirmation.button_negative"),
callback: (launch) => {
if (launch) {
ipcRenderer.send("launch install", folderName);
}
}
});
} else if (loggedIn) {
api.emit("prompt", {
title: api.translate("renderer.tab_mods.launch_lock_confirmation.message"),
description: api.translate("renderer.tab_mods.launch_lock_confirmation.details"),
affirmative_style: "danger",
button_affirmative: api.translate("renderer.tab_mods.launch_lock_confirmation.button_affirmative"),
button_negative: api.translate("renderer.tab_mods.launch_lock_confirmation.button_negative"),
callback: (launch) => {
if (launch) {
ipcRenderer.send("launch install", folderName);
}
}
});
} else {
ipcRenderer.send("launch install", folderName);
}
};

// Creates an install
Expand Down Expand Up @@ -377,11 +406,23 @@ ipcRenderer.on("auth handoff", (_, url) => {

ipcRenderer.on("get save url", (ev, filename) => {
api.emit("get save url", filename);
api.on("got save url", filename => {

api.on("got save url", url => {
ipcRenderer.send("got save url", url);
});
});

ipcRenderer.on("upload save", (ev, data) => {
api.emit("upload save", data);
});

ipcRenderer.on("lock save", (ev, fn) => {
api.emit("lock save", fn);
});

ipcRenderer.on("unlock save", (ev, fn) => {
api.emit("unlock save", fn);
});

// Winstore Appx UI handling
ipcRenderer.on("is appx", (_, is) => {
api.emit("is appx", is);
Expand Down
Loading

0 comments on commit 9440fff

Please sign in to comment.