mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Allow naming savegames
This commit is contained in:
parent
a095cd6324
commit
728f4ae253
File diff suppressed because it is too large
Load Diff
@ -1,218 +1,225 @@
|
|||||||
import { ExplainedResult } from "../core/explained_result";
|
import { ExplainedResult } from "../core/explained_result";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { ReadWriteProxy } from "../core/read_write_proxy";
|
import { ReadWriteProxy } from "../core/read_write_proxy";
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { Savegame } from "./savegame";
|
import { Savegame } from "./savegame";
|
||||||
const logger = createLogger("savegame_manager");
|
const logger = createLogger("savegame_manager");
|
||||||
|
|
||||||
const Rusha = require("rusha");
|
const Rusha = require("rusha");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
|
* @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
|
||||||
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
|
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLocalSavegameStatus = {
|
export const enumLocalSavegameStatus = {
|
||||||
offline: "offline",
|
offline: "offline",
|
||||||
synced: "synced",
|
synced: "synced",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SavegameManager extends ReadWriteProxy {
|
export class SavegameManager extends ReadWriteProxy {
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
super(app, "savegames.bin");
|
super(app, "savegames.bin");
|
||||||
|
|
||||||
this.currentData = this.getDefaultData();
|
this.currentData = this.getDefaultData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RW Proxy Impl
|
// RW Proxy Impl
|
||||||
/**
|
/**
|
||||||
* @returns {SavegamesData}
|
* @returns {SavegamesData}
|
||||||
*/
|
*/
|
||||||
getDefaultData() {
|
getDefaultData() {
|
||||||
return {
|
return {
|
||||||
version: this.getCurrentVersion(),
|
version: this.getCurrentVersion(),
|
||||||
savegames: [],
|
savegames: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentVersion() {
|
getCurrentVersion() {
|
||||||
return 1001;
|
return 1002;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {SavegamesData}
|
* @returns {SavegamesData}
|
||||||
*/
|
*/
|
||||||
getCurrentData() {
|
getCurrentData() {
|
||||||
return super.getCurrentData();
|
return super.getCurrentData();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(data) {
|
verify(data) {
|
||||||
// TODO / FIXME!!!!
|
// TODO / FIXME!!!!
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {SavegamesData} data
|
* @param {SavegamesData} data
|
||||||
*/
|
*/
|
||||||
migrate(data) {
|
migrate(data) {
|
||||||
if (data.version < 1001) {
|
if (data.version < 1001) {
|
||||||
data.savegames.forEach(savegame => {
|
data.savegames.forEach(savegame => {
|
||||||
savegame.level = 0;
|
savegame.level = 0;
|
||||||
});
|
});
|
||||||
data.version = 1001;
|
data.version = 1001;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExplainedResult.good();
|
if (data.version < 1002) {
|
||||||
}
|
data.savegames.forEach(savegame => {
|
||||||
|
savegame.name = null;
|
||||||
// End rw proxy
|
});
|
||||||
|
data.version = 1002;
|
||||||
/**
|
}
|
||||||
* @returns {Array<SavegameMetadata>}
|
|
||||||
*/
|
return ExplainedResult.good();
|
||||||
getSavegamesMetaData() {
|
}
|
||||||
return this.currentData.savegames;
|
|
||||||
}
|
// End rw proxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @returns {Array<SavegameMetadata>}
|
||||||
* @param {string} internalId
|
*/
|
||||||
* @returns {Savegame}
|
getSavegamesMetaData() {
|
||||||
*/
|
return this.currentData.savegames;
|
||||||
getSavegameById(internalId) {
|
}
|
||||||
const metadata = this.getGameMetaDataByInternalId(internalId);
|
|
||||||
if (!metadata) {
|
/**
|
||||||
return null;
|
*
|
||||||
}
|
* @param {string} internalId
|
||||||
return new Savegame(this.app, { internalId, metaDataRef: metadata });
|
* @returns {Savegame}
|
||||||
}
|
*/
|
||||||
|
getSavegameById(internalId) {
|
||||||
/**
|
const metadata = this.getGameMetaDataByInternalId(internalId);
|
||||||
* Deletes a savegame
|
if (!metadata) {
|
||||||
* @param {SavegameMetadata} game
|
return null;
|
||||||
*/
|
}
|
||||||
deleteSavegame(game) {
|
return new Savegame(this.app, { internalId, metaDataRef: metadata });
|
||||||
const handle = new Savegame(this.app, {
|
}
|
||||||
internalId: game.internalId,
|
|
||||||
metaDataRef: game,
|
/**
|
||||||
});
|
* Deletes a savegame
|
||||||
|
* @param {SavegameMetadata} game
|
||||||
return handle.deleteAsync().then(() => {
|
*/
|
||||||
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
deleteSavegame(game) {
|
||||||
const potentialGame = this.currentData.savegames[i];
|
const handle = new Savegame(this.app, {
|
||||||
if (potentialGame.internalId === handle.internalId) {
|
internalId: game.internalId,
|
||||||
this.currentData.savegames.splice(i, 1);
|
metaDataRef: game,
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
}
|
return handle.deleteAsync().then(() => {
|
||||||
|
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
||||||
return this.writeAsync();
|
const potentialGame = this.currentData.savegames[i];
|
||||||
});
|
if (potentialGame.internalId === handle.internalId) {
|
||||||
}
|
this.currentData.savegames.splice(i, 1);
|
||||||
|
break;
|
||||||
/**
|
}
|
||||||
* Returns a given games metadata by id
|
}
|
||||||
* @param {string} id
|
|
||||||
* @returns {SavegameMetadata}
|
return this.writeAsync();
|
||||||
*/
|
});
|
||||||
getGameMetaDataByInternalId(id) {
|
}
|
||||||
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
|
||||||
const data = this.currentData.savegames[i];
|
/**
|
||||||
if (data.internalId === id) {
|
* Returns a given games metadata by id
|
||||||
return data;
|
* @param {string} id
|
||||||
}
|
* @returns {SavegameMetadata}
|
||||||
}
|
*/
|
||||||
logger.error("Savegame internal id not found:", id);
|
getGameMetaDataByInternalId(id) {
|
||||||
return null;
|
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
||||||
}
|
const data = this.currentData.savegames[i];
|
||||||
|
if (data.internalId === id) {
|
||||||
/**
|
return data;
|
||||||
* Creates a new savegame
|
}
|
||||||
* @returns {Savegame}
|
}
|
||||||
*/
|
logger.error("Savegame internal id not found:", id);
|
||||||
createNewSavegame() {
|
return null;
|
||||||
const id = this.generateInternalId();
|
}
|
||||||
|
|
||||||
const metaData = /** @type {SavegameMetadata} */ ({
|
/**
|
||||||
lastUpdate: Date.now(),
|
* Creates a new savegame
|
||||||
version: Savegame.getCurrentVersion(),
|
* @returns {Savegame}
|
||||||
internalId: id,
|
*/
|
||||||
});
|
createNewSavegame() {
|
||||||
|
const id = this.generateInternalId();
|
||||||
this.currentData.savegames.push(metaData);
|
|
||||||
this.sortSavegames();
|
const metaData = /** @type {SavegameMetadata} */ ({
|
||||||
|
lastUpdate: Date.now(),
|
||||||
return new Savegame(this.app, {
|
version: Savegame.getCurrentVersion(),
|
||||||
internalId: id,
|
internalId: id,
|
||||||
metaDataRef: metaData,
|
});
|
||||||
});
|
|
||||||
}
|
this.currentData.savegames.push(metaData);
|
||||||
|
this.sortSavegames();
|
||||||
importSavegame(data) {
|
|
||||||
const savegame = this.createNewSavegame();
|
return new Savegame(this.app, {
|
||||||
const migrationResult = savegame.migrate(data);
|
internalId: id,
|
||||||
if (migrationResult.isBad()) {
|
metaDataRef: metaData,
|
||||||
return Promise.reject("Failed to migrate: " + migrationResult.reason);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
savegame.currentData = data;
|
importSavegame(data) {
|
||||||
const verification = savegame.verify(data);
|
const savegame = this.createNewSavegame();
|
||||||
if (verification.isBad()) {
|
const migrationResult = savegame.migrate(data);
|
||||||
return Promise.reject("Verification failed: " + verification.result);
|
if (migrationResult.isBad()) {
|
||||||
}
|
return Promise.reject("Failed to migrate: " + migrationResult.reason);
|
||||||
|
}
|
||||||
return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames());
|
|
||||||
}
|
savegame.currentData = data;
|
||||||
|
const verification = savegame.verify(data);
|
||||||
/**
|
if (verification.isBad()) {
|
||||||
* Sorts all savegames by their creation time descending
|
return Promise.reject("Verification failed: " + verification.result);
|
||||||
* @returns {Promise<any>}
|
}
|
||||||
*/
|
|
||||||
sortSavegames() {
|
return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames());
|
||||||
this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate);
|
}
|
||||||
let promiseChain = Promise.resolve();
|
|
||||||
while (this.currentData.savegames.length > 30) {
|
/**
|
||||||
const toRemove = this.currentData.savegames.pop();
|
* Sorts all savegames by their creation time descending
|
||||||
|
* @returns {Promise<any>}
|
||||||
// Try to remove the savegame since its no longer available
|
*/
|
||||||
const game = new Savegame(this.app, {
|
sortSavegames() {
|
||||||
internalId: toRemove.internalId,
|
this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate);
|
||||||
metaDataRef: toRemove,
|
let promiseChain = Promise.resolve();
|
||||||
});
|
while (this.currentData.savegames.length > 30) {
|
||||||
promiseChain = promiseChain
|
const toRemove = this.currentData.savegames.pop();
|
||||||
.then(() => game.deleteAsync())
|
|
||||||
.then(
|
// Try to remove the savegame since its no longer available
|
||||||
() => {},
|
const game = new Savegame(this.app, {
|
||||||
err => {
|
internalId: toRemove.internalId,
|
||||||
logger.error(this, "Failed to remove old savegame:", toRemove, ":", err);
|
metaDataRef: toRemove,
|
||||||
}
|
});
|
||||||
);
|
promiseChain = promiseChain
|
||||||
}
|
.then(() => game.deleteAsync())
|
||||||
|
.then(
|
||||||
return promiseChain;
|
() => {},
|
||||||
}
|
err => {
|
||||||
|
logger.error(this, "Failed to remove old savegame:", toRemove, ":", err);
|
||||||
/**
|
}
|
||||||
* Helper method to generate a new internal savegame id
|
);
|
||||||
*/
|
}
|
||||||
generateInternalId() {
|
|
||||||
return Rusha.createHash()
|
return promiseChain;
|
||||||
.update(Date.now() + "/" + Math.random())
|
}
|
||||||
.digest("hex");
|
|
||||||
}
|
/**
|
||||||
|
* Helper method to generate a new internal savegame id
|
||||||
// End
|
*/
|
||||||
|
generateInternalId() {
|
||||||
initialize() {
|
return Rusha.createHash()
|
||||||
// First read, then directly write to ensure we have the latest data
|
.update(Date.now() + "/" + Math.random())
|
||||||
// @ts-ignore
|
.digest("hex");
|
||||||
return this.readAsync().then(() => {
|
}
|
||||||
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
|
|
||||||
return Promise.resolve();
|
// End
|
||||||
}
|
|
||||||
return this.sortSavegames().then(() => this.writeAsync());
|
initialize() {
|
||||||
});
|
// First read, then directly write to ensure we have the latest data
|
||||||
}
|
// @ts-ignore
|
||||||
}
|
return this.readAsync().then(() => {
|
||||||
|
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return this.sortSavegames().then(() => this.writeAsync());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,38 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import("../game/entity").Entity} Entity
|
* @typedef {import("../game/entity").Entity} Entity
|
||||||
*
|
*
|
||||||
* @typedef {{}} SavegameStats
|
* @typedef {{}} SavegameStats
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* camera: any,
|
* camera: any,
|
||||||
* time: any,
|
* time: any,
|
||||||
* entityMgr: any,
|
* entityMgr: any,
|
||||||
* map: any,
|
* map: any,
|
||||||
* hubGoals: any,
|
* hubGoals: any,
|
||||||
* pinnedShapes: any,
|
* pinnedShapes: any,
|
||||||
* waypoints: any,
|
* waypoints: any,
|
||||||
* entities: Array<Entity>,
|
* entities: Array<Entity>,
|
||||||
* beltPaths: Array<any>
|
* beltPaths: Array<any>
|
||||||
* }} SerializedGame
|
* }} SerializedGame
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: number,
|
* version: number,
|
||||||
* dump: SerializedGame,
|
* dump: SerializedGame,
|
||||||
* stats: SavegameStats,
|
* stats: SavegameStats,
|
||||||
* lastUpdate: number,
|
* lastUpdate: number,
|
||||||
* }} SavegameData
|
* }} SavegameData
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* lastUpdate: number,
|
* lastUpdate: number,
|
||||||
* version: number,
|
* version: number,
|
||||||
* internalId: string,
|
* internalId: string,
|
||||||
* level: number
|
* level: number
|
||||||
* }} SavegameMetadata
|
* name: string|null
|
||||||
*
|
* }} SavegameMetadata
|
||||||
* @typedef {{
|
*
|
||||||
* version: number,
|
* @typedef {{
|
||||||
* savegames: Array<SavegameMetadata>
|
* version: number,
|
||||||
* }} SavegamesData
|
* savegames: Array<SavegameMetadata>
|
||||||
*/
|
* }} SavegamesData
|
||||||
|
*/
|
||||||
export default {};
|
|
||||||
|
export default {};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -157,6 +157,7 @@ mainMenu:
|
|||||||
|
|
||||||
savegameLevel: Level <x>
|
savegameLevel: Level <x>
|
||||||
savegameLevelUnknown: Unknown Level
|
savegameLevelUnknown: Unknown Level
|
||||||
|
savegameUnnamed: Unnamed
|
||||||
|
|
||||||
dialogs:
|
dialogs:
|
||||||
buttons:
|
buttons:
|
||||||
@ -274,6 +275,10 @@ dialogs:
|
|||||||
title: Export screenshot
|
title: Export screenshot
|
||||||
desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game!
|
desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game!
|
||||||
|
|
||||||
|
renameSavegame:
|
||||||
|
title: Rename Savegame
|
||||||
|
desc: You can rename your savegame here.
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
|
Loading…
Reference in New Issue
Block a user