Allow downloading savegames

This commit is contained in:
tobspr 2020-05-16 10:05:19 +02:00
parent 2c9867c837
commit a2b1342f55
5 changed files with 61 additions and 6 deletions

BIN
res/ui/icons/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

View File

@ -85,21 +85,22 @@
.savegames { .savegames {
@include S(max-height, 92px); @include S(max-height, 92px);
overflow-y: auto; overflow-y: auto;
@include S(width, 200px); @include S(width, 250px);
pointer-events: all; pointer-events: all;
@include S(padding-right, 5px); @include S(padding-right, 5px);
display: grid; display: grid;
grid-auto-flow: row; grid-auto-flow: row;
@include S(grid-gap, 5px); @include S(grid-gap, 5px);
@include S(margin-top, 10px); @include S(margin-top, 10px);
.savegame { .savegame {
background: #eee; background: #eee;
@include BorderRadius(4px); @include BorderRadius(4px);
@include S(padding, 5px); @include S(padding, 5px);
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto auto;
grid-template-rows: auto auto; grid-template-rows: auto auto;
@include S(grid-column-gap, 15px); @include S(grid-column-gap, 5px);
.internalId { .internalId {
grid-column: 1 / 2; grid-column: 1 / 2;
@ -114,8 +115,9 @@
@include PlainText; @include PlainText;
} }
button.resumeGame { button.resumeGame,
grid-column: 2 / 3; button.downloadGame {
grid-column: 3 / 4;
grid-row: 1 / 3; grid-row: 1 / 3;
@include S(width, 30px); @include S(width, 30px);
@include S(height, 30px); @include S(height, 30px);
@ -123,6 +125,15 @@
align-self: center; align-self: center;
background: #44484a uiResource("icons/play.png") center center / 40% no-repeat; background: #44484a uiResource("icons/play.png") center center / 40% no-repeat;
} }
button.downloadGame {
grid-column: 2 / 3;
background-image: uiResource("icons/download.png");
@include S(width, 15px);
@include S(height, 15px);
align-self: end;
background-size: 60%;
}
} }
} }
} }

View File

@ -79,6 +79,16 @@ export class ReadWriteProxy {
return this.currentData; return this.currentData;
} }
/**
*
* @param {object} obj
*/
static serializeObject(obj) {
const jsonString = JSON_stringify(compressObject(obj));
const checksum = sha1(jsonString + salt);
return compressionPrefix + compressX64(checksum + jsonString);
}
/** /**
* Writes the data asychronously, fails if verify() fails * Writes the data asychronously, fails if verify() fails
* @returns {Promise<string>} * @returns {Promise<string>}

View File

@ -859,3 +859,20 @@ export function formatSecondsToTimeAgo(secs) {
return days + " days ago"; return days + " days ago";
} }
} }
/**
* Generates a file download
* @param {string} filename
* @param {string} text
*/
export function generateFileDownload(filename, text) {
var element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}

View File

@ -1,7 +1,8 @@
import { GameState } from "../core/game_state"; import { GameState } from "../core/game_state";
import { cachebust } from "../core/cachebust"; import { cachebust } from "../core/cachebust";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { makeDiv, formatSecondsToTimeAgo } from "../core/utils"; import { makeDiv, formatSecondsToTimeAgo, generateFileDownload } from "../core/utils";
import { ReadWriteProxy } from "../core/read_write_proxy";
export class MainMenuState extends GameState { export class MainMenuState extends GameState {
constructor() { constructor() {
@ -90,10 +91,15 @@ export class MainMenuState extends GameState {
formatSecondsToTimeAgo((new Date().getTime() - games[i].lastUpdate) / 1000.0) formatSecondsToTimeAgo((new Date().getTime() - games[i].lastUpdate) / 1000.0)
); );
const downloadButton = document.createElement("button");
downloadButton.classList.add("styledButton", "downloadGame");
elem.appendChild(downloadButton);
const resumeBtn = document.createElement("button"); const resumeBtn = document.createElement("button");
resumeBtn.classList.add("styledButton", "resumeGame"); resumeBtn.classList.add("styledButton", "resumeGame");
elem.appendChild(resumeBtn); elem.appendChild(resumeBtn);
this.trackClicks(downloadButton, () => this.downloadGame(games[i]));
this.trackClicks(resumeBtn, () => this.resumeGame(games[i])); this.trackClicks(resumeBtn, () => this.resumeGame(games[i]));
} }
} }
@ -111,6 +117,17 @@ export class MainMenuState extends GameState {
}); });
} }
/**
* @param {object} game
*/
downloadGame(game) {
const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
savegame.readAsync().then(() => {
const data = ReadWriteProxy.serializeObject(savegame.currentData);
generateFileDownload(savegame.filename, data);
});
}
onPlayButtonClicked() { onPlayButtonClicked() {
const savegame = this.app.savegameMgr.createNewSavegame(); const savegame = this.app.savegameMgr.createNewSavegame();