mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-11 09:11:50 +00:00
Remove savegame object compression
Preparation for further changes. Savegames that are not compressed are much bigger, but this is a rather complex algorithm that shouldn't exist.
This commit is contained in:
parent
2b8709dd3e
commit
c61825e8b3
@ -3,7 +3,6 @@ import { Application } from "../application";
|
|||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { FsError } from "@/platform/fs_error";
|
import { FsError } from "@/platform/fs_error";
|
||||||
import { compressObject, decompressObject } from "../savegame/savegame_compressor";
|
|
||||||
import { asyncCompressor, compressionPrefix } from "./async_compression";
|
import { asyncCompressor, compressionPrefix } from "./async_compression";
|
||||||
import { IS_DEBUG, globalConfig } from "./config";
|
import { IS_DEBUG, globalConfig } from "./config";
|
||||||
import { ExplainedResult } from "./explained_result";
|
import { ExplainedResult } from "./explained_result";
|
||||||
@ -85,7 +84,7 @@ export class ReadWriteProxy {
|
|||||||
* @param {object} obj
|
* @param {object} obj
|
||||||
*/
|
*/
|
||||||
static serializeObject(obj) {
|
static serializeObject(obj) {
|
||||||
const jsonString = JSON.stringify(compressObject(obj));
|
const jsonString = JSON.stringify(obj);
|
||||||
const checksum = computeCrc(jsonString + salt);
|
const checksum = computeCrc(jsonString + salt);
|
||||||
return compressionPrefix + compressX64(checksum + jsonString);
|
return compressionPrefix + compressX64(checksum + jsonString);
|
||||||
}
|
}
|
||||||
@ -117,8 +116,7 @@ export class ReadWriteProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parsed = JSON.parse(jsonString);
|
const parsed = JSON.parse(jsonString);
|
||||||
const decoded = decompressObject(parsed);
|
return parsed;
|
||||||
return decoded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,7 +178,7 @@ export class ReadWriteProxy {
|
|||||||
.then(rawData => {
|
.then(rawData => {
|
||||||
if (rawData == null) {
|
if (rawData == null) {
|
||||||
// So, the file has not been found, use default data
|
// So, the file has not been found, use default data
|
||||||
return JSON.stringify(compressObject(this.getDefaultData()));
|
return JSON.stringify(this.getDefaultData());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawData.startsWith(compressionPrefix)) {
|
if (rawData.startsWith(compressionPrefix)) {
|
||||||
@ -233,9 +231,6 @@ export class ReadWriteProxy {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Decompress
|
|
||||||
.then(compressed => decompressObject(compressed))
|
|
||||||
|
|
||||||
// Verify basic structure
|
// Verify basic structure
|
||||||
.then(contents => {
|
.then(contents => {
|
||||||
const result = this.internalVerifyBasicStructure(contents);
|
const result = this.internalVerifyBasicStructure(contents);
|
||||||
|
|||||||
@ -1,168 +0,0 @@
|
|||||||
const charmap =
|
|
||||||
"!#%&'()*+,-./:;<=>?@[]^_`{|}~¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
|
|
||||||
let compressionCache = {};
|
|
||||||
let decompressionCache = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compresses an integer into a tight string representation
|
|
||||||
* @param {number} i
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function compressInt(i) {
|
|
||||||
// Zero value breaks
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
// save `i` as the cache key
|
|
||||||
// to avoid it being modified by the
|
|
||||||
// rest of the function.
|
|
||||||
const cache_key = i;
|
|
||||||
|
|
||||||
if (compressionCache[cache_key]) {
|
|
||||||
return compressionCache[cache_key];
|
|
||||||
}
|
|
||||||
let result = "";
|
|
||||||
do {
|
|
||||||
result += charmap[i % charmap.length];
|
|
||||||
i = Math.floor(i / charmap.length);
|
|
||||||
} while (i > 0);
|
|
||||||
return (compressionCache[cache_key] = result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decompresses an integer from its tight string representation
|
|
||||||
* @param {string} s
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
function decompressInt(s) {
|
|
||||||
if (decompressionCache[s]) {
|
|
||||||
return decompressionCache[s];
|
|
||||||
}
|
|
||||||
s = "" + s;
|
|
||||||
let result = 0;
|
|
||||||
for (let i = s.length - 1; i >= 0; --i) {
|
|
||||||
result = result * charmap.length + charmap.indexOf(s.charAt(i));
|
|
||||||
}
|
|
||||||
// Fixes zero value break fix from above
|
|
||||||
result -= 1;
|
|
||||||
return (decompressionCache[s] = result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity
|
|
||||||
if (G_IS_DEV) {
|
|
||||||
for (let i = 0; i < 10000; ++i) {
|
|
||||||
if (decompressInt(compressInt(i)) !== i) {
|
|
||||||
throw new Error(
|
|
||||||
"Bad compression for: " +
|
|
||||||
i +
|
|
||||||
" compressed: " +
|
|
||||||
compressInt(i) +
|
|
||||||
" decompressed: " +
|
|
||||||
decompressInt(compressInt(i))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} obj
|
|
||||||
* @param {Map} keys
|
|
||||||
* @param {Map} values
|
|
||||||
* @returns {any[]|object|number|string}
|
|
||||||
*/
|
|
||||||
function compressObjectInternal(obj, keys, values) {
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
let result = [];
|
|
||||||
for (let i = 0; i < obj.length; ++i) {
|
|
||||||
result.push(compressObjectInternal(obj[i], keys, values));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else if (typeof obj === "object" && obj !== null) {
|
|
||||||
let result = {};
|
|
||||||
for (const key in obj) {
|
|
||||||
let index = keys.get(key);
|
|
||||||
if (index === undefined) {
|
|
||||||
index = keys.size;
|
|
||||||
keys.set(key, index);
|
|
||||||
}
|
|
||||||
const value = obj[key];
|
|
||||||
result[compressInt(index)] = compressObjectInternal(value, keys, values);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else if (typeof obj === "string") {
|
|
||||||
let index = values.get(obj);
|
|
||||||
if (index === undefined) {
|
|
||||||
index = values.size;
|
|
||||||
values.set(obj, index);
|
|
||||||
}
|
|
||||||
return compressInt(index);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Map} hashMap
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
function indexMapToArray(hashMap) {
|
|
||||||
const result = new Array(hashMap.size);
|
|
||||||
hashMap.forEach((index, key) => {
|
|
||||||
result[index] = key;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} obj
|
|
||||||
*/
|
|
||||||
export function compressObject(obj) {
|
|
||||||
const keys = new Map();
|
|
||||||
const values = new Map();
|
|
||||||
const data = compressObjectInternal(obj, keys, values);
|
|
||||||
return {
|
|
||||||
keys: indexMapToArray(keys),
|
|
||||||
values: indexMapToArray(values),
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} obj
|
|
||||||
* @param {string[]} keys
|
|
||||||
* @param {any[]} values
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
function decompressObjectInternal(obj, keys = [], values = []) {
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
let result = [];
|
|
||||||
for (let i = 0; i < obj.length; ++i) {
|
|
||||||
result.push(decompressObjectInternal(obj[i], keys, values));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else if (typeof obj === "object" && obj !== null) {
|
|
||||||
let result = {};
|
|
||||||
for (const key in obj) {
|
|
||||||
const realIndex = decompressInt(key);
|
|
||||||
const value = obj[key];
|
|
||||||
result[keys[realIndex]] = decompressObjectInternal(value, keys, values);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else if (typeof obj === "string") {
|
|
||||||
const realIndex = decompressInt(obj);
|
|
||||||
return values[realIndex];
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} obj
|
|
||||||
*/
|
|
||||||
export function decompressObject(obj) {
|
|
||||||
if (obj.keys && obj.values && obj.data) {
|
|
||||||
const keys = obj.keys;
|
|
||||||
const values = obj.values;
|
|
||||||
const result = decompressObjectInternal(obj.data, keys, values);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { compressX64 } from "../core/lzstring";
|
import { compressX64 } from "../core/lzstring";
|
||||||
import { computeCrc } from "../core/sensitive_utils.encrypt";
|
import { computeCrc } from "../core/sensitive_utils.encrypt";
|
||||||
import { compressObject } from "../savegame/savegame_compressor";
|
|
||||||
|
|
||||||
self.addEventListener("message", event => {
|
self.addEventListener("message", event => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -19,8 +18,7 @@ function performJob(job, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "compressObject": {
|
case "compressObject": {
|
||||||
const optimized = compressObject(data.obj);
|
const stringified = JSON.stringify(data.obj);
|
||||||
const stringified = JSON.stringify(optimized);
|
|
||||||
|
|
||||||
const checksum = computeCrc(stringified + globalConfig.info.file);
|
const checksum = computeCrc(stringified + globalConfig.info.file);
|
||||||
return data.compressionPrefix + compressX64(checksum + stringified);
|
return data.compressionPrefix + compressX64(checksum + stringified);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user