1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-13 18:21:51 +00:00

Merge pull request #40 from tobspr-games/dengr1065/even-more-cleanups

Even more cleanups
This commit is contained in:
Даниїл Григор'єв 2024-07-27 21:59:26 +03:00 committed by GitHub
commit cbd3f20f20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 30 additions and 251 deletions

View File

@ -28,7 +28,6 @@
"debounce-promise": "^3.1.2", "debounce-promise": "^3.1.2",
"howler": "^2.1.2", "howler": "^2.1.2",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"rusha": "^0.8.13",
"semver": "^7.3.5" "semver": "^7.3.5"
}, },
"devDependencies": { "devDependencies": {

View File

@ -36,22 +36,6 @@ import { SettingsState } from "./states/settings";
const logger = createLogger("application"); const logger = createLogger("application");
// Set the name of the hidden property and the change event for visibility
let pageHiddenPropName, pageVisibilityEventName;
if (typeof document.hidden !== "undefined") {
// Opera 12.10 and Firefox 18 and later support
pageHiddenPropName = "hidden";
pageVisibilityEventName = "visibilitychange";
// @ts-ignore
} else if (typeof document.msHidden !== "undefined") {
pageHiddenPropName = "msHidden";
pageVisibilityEventName = "msvisibilitychange";
// @ts-ignore
} else if (typeof document.webkitHidden !== "undefined") {
pageHiddenPropName = "webkitHidden";
pageVisibilityEventName = "webkitvisibilitychange";
}
export class Application { export class Application {
/** /**
* Boots the application * Boots the application
@ -176,7 +160,7 @@ export class Application {
// Unload events // Unload events
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true); window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false); document.addEventListener("visibilitychange", this.handleVisibilityChange.bind(this), false);
// Track touches so we can update the focus appropriately // Track touches so we can update the focus appropriately
document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true); document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
@ -217,7 +201,7 @@ export class Application {
*/ */
handleVisibilityChange(event) { handleVisibilityChange(event) {
window.focus(); window.focus();
const pageVisible = !document[pageHiddenPropName]; const pageVisible = !document.hidden;
if (pageVisible !== this.pageVisible) { if (pageVisible !== this.pageVisible) {
this.pageVisible = pageVisible; this.pageVisible = pageVisible;
logger.log("Visibility changed:", this.pageVisible); logger.log("Visibility changed:", this.pageVisible);

View File

@ -1,4 +1,4 @@
// @ts-ignore // @ts-expect-error FIXME: missing typings
import CompressionWorker from "../webworkers/compression.worker"; import CompressionWorker from "../webworkers/compression.worker";
import { createLogger } from "./logging"; import { createLogger } from "./logging";
@ -6,26 +6,7 @@ import { round2Digits } from "./utils";
const logger = createLogger("async_compression"); const logger = createLogger("async_compression");
export let compressionPrefix = String.fromCodePoint(1); export const compressionPrefix = String.fromCodePoint(1);
function checkCryptPrefix(prefix) {
try {
window.localStorage.setItem("prefix_test", prefix);
window.localStorage.removeItem("prefix_test");
return true;
} catch (ex) {
logger.warn("Prefix '" + prefix + "' not available");
return false;
}
}
if (!checkCryptPrefix(compressionPrefix)) {
logger.warn("Switching to basic prefix");
compressionPrefix = " ";
if (!checkCryptPrefix(compressionPrefix)) {
logger.warn("Prefix not available, ls seems to be unavailable");
}
}
/** /**
* @typedef {{ * @typedef {{

View File

@ -1,6 +1,6 @@
import { globalConfig } from "./config"; import { globalConfig } from "./config";
import { fastArrayDelete } from "./utils";
import { createLogger } from "./logging"; import { createLogger } from "./logging";
import { fastArrayDelete } from "./utils";
const logger = createLogger("buffer_utils"); const logger = createLogger("buffer_utils");
@ -10,9 +10,6 @@ const logger = createLogger("buffer_utils");
*/ */
export function enableImageSmoothing(context) { export function enableImageSmoothing(context) {
context.imageSmoothingEnabled = true; context.imageSmoothingEnabled = true;
context.webkitImageSmoothingEnabled = true;
// @ts-ignore
context.imageSmoothingQuality = globalConfig.smoothing.quality; context.imageSmoothingQuality = globalConfig.smoothing.quality;
} }
@ -22,7 +19,6 @@ export function enableImageSmoothing(context) {
*/ */
export function disableImageSmoothing(context) { export function disableImageSmoothing(context) {
context.imageSmoothingEnabled = false; context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
} }
/** /**

View File

@ -108,24 +108,16 @@ export const globalConfig = {
smoothing: { smoothing: {
smoothMainCanvas: smoothCanvas && true, smoothMainCanvas: smoothCanvas && true,
quality: "low", // Low is CRUCIAL for mobile performance! quality: "low" as ImageSmoothingQuality, // Low is CRUCIAL for mobile performance!
}, },
rendering: {}, rendering: {},
debug, debug,
currentDiscount: 0,
// Secret vars // Secret vars
info: { info: {
// Binary file salt // Binary file salt
file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=", file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=",
// Savegame salt
sgSalt: "}95Q3%8/.837Lqym_BJx%q7)pAHJbF",
// Analytics key
analyticsApiKey: "baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a",
}, },
}; };

View File

@ -41,13 +41,9 @@ export function prepareHighDPIContext(context, smooth = true) {
if (smooth) { if (smooth) {
context.imageSmoothingEnabled = true; context.imageSmoothingEnabled = true;
context.webkitImageSmoothingEnabled = true;
// @ts-ignore
context.imageSmoothingQuality = globalConfig.smoothing.quality; context.imageSmoothingQuality = globalConfig.smoothing.quality;
} else { } else {
context.imageSmoothingEnabled = false; context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
} }
} }

View File

@ -10,79 +10,6 @@ function mathPolyfills() {
}; };
} }
function stringPolyfills() {
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength, padString) {
targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
padString = String(typeof padString !== "undefined" ? padString : " ");
if (this.length >= targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
}
return padString.slice(0, targetLength) + String(this);
}
};
}
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
if (!String.prototype.padEnd) {
String.prototype.padEnd = function padEnd(targetLength, padString) {
targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
padString = String(typeof padString !== "undefined" ? padString : " ");
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
}
return String(this) + padString.slice(0, targetLength);
}
};
}
}
function objectPolyfills() {
// https://github.com/tc39/proposal-object-values-entries/blob/master/polyfill.js
// @ts-ignore
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
// @ts-ignore
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
// @ts-ignore
const concat = Function.bind.call(Function.call, Array.prototype.concat);
const keys = Reflect.ownKeys;
// @ts-ignore
if (!Object.values) {
// @ts-ignore
Object.values = function values(O) {
return reduce(
keys(O),
(v, k) => concat(v, typeof k === "string" && isEnumerable(O, k) ? [O[k]] : []),
[]
);
};
}
if (!Object.entries) {
// @ts-ignore
Object.entries = function entries(O) {
return reduce(
keys(O),
(e, k) => concat(e, typeof k === "string" && isEnumerable(O, k) ? [[k, O[k]]] : []),
[]
);
};
}
}
function domPolyfills() { function domPolyfills() {
// from:https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md // from:https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md
(function (arr) { (function (arr) {
@ -104,18 +31,8 @@ function domPolyfills() {
function initPolyfills() { function initPolyfills() {
mathPolyfills(); mathPolyfills();
stringPolyfills();
objectPolyfills();
domPolyfills(); domPolyfills();
} }
function initExtensions() {
String.prototype.replaceAll = function (search, replacement) {
var target = this;
return target.split(search).join(replacement);
};
}
// Other polyfills // Other polyfills
initPolyfills(); initPolyfills();
initExtensions();

View File

@ -2,21 +2,20 @@
import { Application } from "../application"; import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { sha1, CRC_PREFIX, computeCrc } from "./sensitive_utils.encrypt";
import { createLogger } from "./logging";
import { FILE_NOT_FOUND } from "../platform/storage"; import { FILE_NOT_FOUND } from "../platform/storage";
import { accessNestedPropertyReverse } from "./utils"; import { compressObject, decompressObject } from "../savegame/savegame_compressor";
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";
import { decompressX64, compressX64 } from "./lzstring"; import { createLogger } from "./logging";
import { asyncCompressor, compressionPrefix } from "./async_compression"; import { compressX64, decompressX64 } from "./lzstring";
import { compressObject, decompressObject } from "../savegame/savegame_compressor"; import { computeCrc } from "./sensitive_utils.encrypt";
import debounce from "debounce-promise"; import debounce from "debounce-promise";
const logger = createLogger("read_write_proxy"); const logger = createLogger("read_write_proxy");
const salt = accessNestedPropertyReverse(globalConfig, ["file", "info"]); const salt = globalConfig.info.file;
// Helper which only writes / reads if verify() works. Also performs migration // Helper which only writes / reads if verify() works. Also performs migration
export class ReadWriteProxy { export class ReadWriteProxy {
@ -110,9 +109,7 @@ export class ReadWriteProxy {
const checksum = decompressed.substring(0, 40); const checksum = decompressed.substring(0, 40);
const jsonString = decompressed.substr(40); const jsonString = decompressed.substr(40);
const desiredChecksum = checksum.startsWith(CRC_PREFIX) const desiredChecksum = computeCrc(jsonString + salt);
? computeCrc(jsonString + salt)
: sha1(jsonString + salt);
if (desiredChecksum !== checksum) { if (desiredChecksum !== checksum) {
// Checksum mismatch // Checksum mismatch
@ -199,11 +196,9 @@ export class ReadWriteProxy {
// Compare stored checksum with actual checksum // Compare stored checksum with actual checksum
const checksum = decompressed.substring(0, 40); const checksum = decompressed.substring(0, 40);
const jsonString = decompressed.substr(40); const jsonString = decompressed.slice(40);
const desiredChecksum = checksum.startsWith(CRC_PREFIX) const desiredChecksum = computeCrc(jsonString + salt);
? computeCrc(jsonString + salt)
: sha1(jsonString + salt);
if (desiredChecksum !== checksum) { if (desiredChecksum !== checksum) {
// Checksum mismatch // Checksum mismatch

View File

@ -1,15 +1,4 @@
import { createHash } from "rusha";
import crc32 from "crc/crc32"; import crc32 from "crc/crc32";
import { decompressX64 } from "./lzstring";
export function sha1(str) {
return createHash().update(str).digest("hex");
}
// Window.location.host
export function getNameOfProvider() {
return window[decompressX64("DYewxghgLgliB2Q")][decompressX64("BYewzgLgdghgtgUyA")];
}
// Distinguish legacy crc prefixes // Distinguish legacy crc prefixes
export const CRC_PREFIX = "crc32".padEnd(32, "-"); export const CRC_PREFIX = "crc32".padEnd(32, "-");

View File

@ -40,19 +40,6 @@ export function randomInt(start, end) {
return Math.floor(Math.random() * (end - start + 1) + start); return Math.floor(Math.random() * (end - start + 1) + start);
} }
/**
* Access an object in a very annoying way, used for obsfuscation.
* @param {any} obj
* @param {Array<string>} keys
*/
export function accessNestedPropertyReverse(obj, keys) {
let result = obj;
for (let i = keys.length - 1; i >= 0; --i) {
result = result[keys[i]];
}
return result;
}
/** /**
* Chooses a random entry of an array * Chooses a random entry of an array
* @template T * @template T
@ -406,7 +393,7 @@ export function makeButton(parent, classes = [], innerHTML = "") {
*/ */
export function removeAllChildren(elem) { export function removeAllChildren(elem) {
if (elem) { if (elem) {
var range = document.createRange(); const range = document.createRange();
range.selectNodeContents(elem); range.selectNodeContents(elem);
range.deleteContents(); range.deleteContents();
} }
@ -618,7 +605,7 @@ export function fillInLinkIntoTranslation(translation, link) {
* @param {string} text * @param {string} text
*/ */
export function generateFileDownload(filename, text) { export function generateFileDownload(filename, text) {
var element = document.createElement("a"); const element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text)); element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
element.setAttribute("download", filename); element.setAttribute("download", filename);
@ -634,7 +621,7 @@ export function generateFileDownload(filename, text) {
* @param {string} acceptedType * @param {string} acceptedType
*/ */
export function startFileChoose(acceptedType = ".bin") { export function startFileChoose(acceptedType = ".bin") {
var input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
input.accept = acceptedType; input.accept = acceptedType;

View File

@ -489,7 +489,6 @@ export class Vector {
} }
default: { default: {
assertAlways(false, "Invalid fast inplace rotation: " + angle); assertAlways(false, "Invalid fast inplace rotation: " + angle);
return this;
} }
} }
// return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos); // return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
@ -519,7 +518,6 @@ export class Vector {
} }
default: { default: {
assertAlways(false, "Invalid fast inplace rotation: " + angle); assertAlways(false, "Invalid fast inplace rotation: " + angle);
return new Vector();
} }
} }
} }
@ -593,7 +591,6 @@ export class Vector {
} }
default: default:
assertAlways(false, "Invalid angle: " + angle); assertAlways(false, "Invalid angle: " + angle);
return;
} }
} }
@ -603,7 +600,7 @@ export class Vector {
* @returns {Boolean} * @returns {Boolean}
*/ */
equalsEpsilon(v, epsilon = 1e-5) { equalsEpsilon(v, epsilon = 1e-5) {
return Math.abs(this.x - v.x) < 1e-5 && Math.abs(this.y - v.y) < epsilon; return Math.abs(this.x - v.x) < epsilon && Math.abs(this.y - v.y) < epsilon;
} }
/** /**

9
src/js/globals.d.ts vendored
View File

@ -22,11 +22,6 @@ declare const shapez: any;
declare const ipcRenderer: any; declare const ipcRenderer: any;
// Polyfills
declare interface String {
replaceAll(search: string, replacement: string): string;
}
declare interface ImportMeta { declare interface ImportMeta {
webpackContext( webpackContext(
request: string, request: string,
@ -47,10 +42,6 @@ declare interface ImportMeta {
declare interface CanvasRenderingContext2D { declare interface CanvasRenderingContext2D {
beginRoundedRect(x: number, y: number, w: number, h: number, r: number): void; beginRoundedRect(x: number, y: number, w: number, h: number, r: number): void;
beginCircle(x: number, y: number, r: number): void; beginCircle(x: number, y: number, r: number): void;
msImageSmoothingEnabled: boolean;
mozImageSmoothingEnabled: boolean;
webkitImageSmoothingEnabled: boolean;
} }
// Just for compatibility with the shared code // Just for compatibility with the shared code

View File

@ -1,12 +1,10 @@
import { globalConfig } from "../core/config";
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 { Savegame } from "./savegame"; import { Savegame } from "./savegame";
const logger = createLogger("savegame_manager"); const logger = createLogger("savegame_manager");
import Rusha from "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
@ -217,9 +215,7 @@ export class SavegameManager extends ReadWriteProxy {
* Helper method to generate a new internal savegame id * Helper method to generate a new internal savegame id
*/ */
generateInternalId() { generateInternalId() {
return Rusha.createHash() return self.crypto.randomUUID();
.update(Date.now() + "/" + Math.random())
.digest("hex");
} }
// End // End

View File

@ -2,10 +2,10 @@ import { CHANGELOG } from "../changelog";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { GameState } from "../core/game_state"; import { GameState } from "../core/game_state";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { getLogoSprite, timeoutPromise } from "../core/utils"; import { getLogoSprite } from "../core/utils";
import { getRandomHint } from "../game/hints"; import { getRandomHint } from "../game/hints";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { autoDetectLanguageId, T, updateApplicationLanguage } from "../translations"; import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations";
const logger = createLogger("state/preload"); const logger = createLogger("state/preload");
@ -44,22 +44,6 @@ export class PreloadState extends GameState {
this.startLoading(); this.startLoading();
} }
async fetchDiscounts() {
await timeoutPromise(
fetch("https://analytics.shapez.io/v1/discounts")
.then(res => res.json())
.then(data => {
globalConfig.currentDiscount = Number(
data["1318690"].data.price_overview.discount_percent
);
logger.log("Fetched current discount:", globalConfig.currentDiscount);
}),
2000
).catch(err => {
logger.warn("Failed to fetch current discount:", err);
});
}
async sendBeacon() { async sendBeacon() {
// TODO: Get rid of this analytics stuff // TODO: Get rid of this analytics stuff
} }
@ -78,9 +62,6 @@ export class PreloadState extends GameState {
return this.app.storage.initialize(); return this.app.storage.initialize();
}) })
.then(() => this.setStatus("Connecting to api", 15))
.then(() => this.fetchDiscounts())
.then(() => this.setStatus("Initializing settings", 20)) .then(() => this.setStatus("Initializing settings", 20))
.then(() => { .then(() => {
return this.app.settings.initialize(); return this.app.settings.initialize();
@ -256,7 +237,7 @@ export class PreloadState extends GameState {
subElement.innerHTML = ` subElement.innerHTML = `
<div class="logo"> <div class="logo">
<img src="res/getLogoSprite()" alt="Shapez.io Logo"> <img src="res/${getLogoSprite()}" alt="Shapez.io Logo">
</div> </div>
<div class="failureInner"> <div class="failureInner">
<div class="errorHeader"> <div class="errorHeader">

View File

@ -7,7 +7,7 @@ const logger = createLogger("translations");
// @ts-ignore // @ts-ignore
import baseTranslations from "./built-temp/base-en.json"; import baseTranslations from "./built-temp/base-en.json";
export let T = baseTranslations; export const T = baseTranslations;
if (G_IS_DEV && globalConfig.debug.testTranslations) { if (G_IS_DEV && globalConfig.debug.testTranslations) {
// Replaces all translations by fake translations to see whats translated and what not // Replaces all translations by fake translations to see whats translated and what not
@ -67,18 +67,11 @@ function mapLanguageCodeToId(languageKey) {
* @returns {string} * @returns {string}
*/ */
export function autoDetectLanguageId() { export function autoDetectLanguageId() {
let languages = []; const languages = navigator.languages;
if (navigator.languages) {
languages = navigator.languages.slice();
} else if (navigator.language) {
languages = [navigator.language];
} else {
logger.warn("Navigator has no languages prop");
}
for (let i = 0; i < languages.length; ++i) { for (const language of languages) {
logger.log("Trying to find language target for", languages[i]); logger.log("Trying to find language target for", language);
const trans = mapLanguageCodeToId(languages[i]); const trans = mapLanguageCodeToId(language);
if (trans) { if (trans) {
return trans; return trans;
} }

View File

@ -3,16 +3,6 @@ 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"; import { compressObject } from "../savegame/savegame_compressor";
function accessNestedPropertyReverse(obj, keys) {
let result = obj;
for (let i = keys.length - 1; i >= 0; --i) {
result = result[keys[i]];
}
return result;
}
const salt = accessNestedPropertyReverse(globalConfig, ["file", "info"]);
self.addEventListener("message", event => { self.addEventListener("message", event => {
// @ts-ignore // @ts-ignore
const { jobId, job, data } = event.data; const { jobId, job, data } = event.data;
@ -32,7 +22,7 @@ function performJob(job, data) {
const optimized = compressObject(data.obj); const optimized = compressObject(data.obj);
const stringified = JSON.stringify(optimized); const stringified = JSON.stringify(optimized);
const checksum = computeCrc(stringified + salt); const checksum = computeCrc(stringified + globalConfig.info.file);
return data.compressionPrefix + compressX64(checksum + stringified); return data.compressionPrefix + compressX64(checksum + stringified);
} }
default: default:

View File

@ -8580,11 +8580,6 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
rusha@^0.8.13:
version "0.8.14"
resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68"
integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==
rx-lite@^3.1.2: rx-lite@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"