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

Start to make mods safer

This commit is contained in:
tobspr 2022-01-15 10:49:58 +01:00
parent e12ed11c3e
commit a0071681e7
14 changed files with 42 additions and 64 deletions

View File

@ -54,8 +54,11 @@ function createWindow() {
// fullscreen: true, // fullscreen: true,
autoHideMenuBar: true, autoHideMenuBar: true,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: false,
webSecurity: false, webSecurity: false,
// sandbox: true,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
}, },
allowRunningInsecureContent: false, allowRunningInsecureContent: false,
}); });

3
electron/preload.js Normal file
View File

@ -0,0 +1,3 @@
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer);

View File

@ -19,7 +19,8 @@
"publishStandalone": "yarn publishOnItch && yarn publishOnSteam", "publishStandalone": "yarn publishOnItch && yarn publishOnSteam",
"publishWeb": "cd gulp && yarn main.deploy.prod", "publishWeb": "cd gulp && yarn main.deploy.prod",
"publish": "yarn publishStandalone && yarn publishWeb", "publish": "yarn publishStandalone && yarn publishWeb",
"syncTranslations": "node sync-translations.js" "syncTranslations": "node sync-translations.js",
"buildTypes": "tsc src/js/application.js --declaration --allowJs --emitDeclarationOnly --skipLibCheck --out types.js"
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.5.4", "@babel/core": "^7.5.4",

View File

@ -1,7 +1,6 @@
import { BaseItem } from "../game/base_item"; import { BaseItem } from "../game/base_item";
import { ClickDetector } from "./click_detector"; import { ClickDetector } from "./click_detector";
import { Signal } from "./signal"; import { Signal } from "./signal";
import { getIPCRenderer } from "./utils";
/* /*
* *************************************************** * ***************************************************
@ -113,13 +112,11 @@ export class FormElementInput extends FormElement {
if (G_WEGAME_VERSION) { if (G_WEGAME_VERSION) {
const value = String(this.element.value); const value = String(this.element.value);
getIPCRenderer() ipcRenderer.invoke("profanity-check", value).then(newValue => {
.invoke("profanity-check", value) if (value !== newValue && this.element) {
.then(newValue => { this.element.value = newValue;
if (value !== newValue && this.element) { }
this.element.value = newValue; });
}
});
} }
} }

View File

@ -42,21 +42,6 @@ export function getPlatformName() {
return "unknown"; return "unknown";
} }
/**
* Returns the IPC renderer, or null if not within the standalone
* @returns {object|null}
*/
let ipcRenderer = null;
export function getIPCRenderer() {
if (!G_IS_STANDALONE) {
return null;
}
if (!ipcRenderer) {
ipcRenderer = eval("require")("electron").ipcRenderer;
}
return ipcRenderer;
}
/** /**
* Makes a new 2D array with undefined contents * Makes a new 2D array with undefined contents
* @param {number} w * @param {number} w

View File

@ -602,7 +602,7 @@ export class RegularGameMode extends GameMode {
/** @type {(typeof MetaBuilding)[]} */ /** @type {(typeof MetaBuilding)[]} */
this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding]; this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding];
// @ts-expect-error // @ts-ignore
if (!(G_IS_DEV || window.sandboxMode || queryParamOptions.sandboxMode)) { if (!(G_IS_DEV || window.sandboxMode || queryParamOptions.sandboxMode)) {
this.hiddenBuildings.push(MetaItemProducerBuilding); this.hiddenBuildings.push(MetaItemProducerBuilding);
} }

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

@ -24,6 +24,8 @@ declare const G_WEGAME_VERSION: boolean;
declare const shapez: any; declare const shapez: any;
declare const ipcRenderer: any;
// Polyfills // Polyfills
declare interface String { declare interface String {
replaceAll(search: string, replacement: string): string; replaceAll(search: string, replacement: string): string;

View File

@ -1,10 +1,8 @@
/* typehints:start */ /* typehints:start */
import { Application } from "../application"; import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { getIPCRenderer } from "../core/utils";
import { Mod } from "./mod"; import { Mod } from "./mod";
import { ModInterface } from "./mod_interface"; import { ModInterface } from "./mod_interface";
import { MOD_SIGNALS } from "./mod_signals"; import { MOD_SIGNALS } from "./mod_signals";
@ -76,12 +74,17 @@ export class ModLoader {
this.exposeExports(); this.exposeExports();
window.registerMod = mod => {
this.modLoadQueue.push(mod);
};
if (G_IS_STANDALONE || G_IS_DEV) { if (G_IS_STANDALONE || G_IS_DEV) {
try { try {
let mods = []; let mods = [];
if (G_IS_STANDALONE) { if (G_IS_STANDALONE) {
mods = await getIPCRenderer().invoke("get-mods"); mods = await ipcRenderer.invoke("get-mods");
} else if (G_IS_DEV && globalConfig.debug.externalModUrl) { }
if (G_IS_DEV && globalConfig.debug.externalModUrl) {
const mod = await ( const mod = await (
await fetch(globalConfig.debug.externalModUrl, { await fetch(globalConfig.debug.externalModUrl, {
method: "GET", method: "GET",
@ -91,12 +94,8 @@ export class ModLoader {
} }
mods.forEach(modCode => { mods.forEach(modCode => {
window.registerMod = mod => { const func = new Function(modCode);
this.modLoadQueue.push(mod); func();
};
// ugh
eval(modCode);
delete window.registerMod;
}); });
} catch (ex) { } catch (ex) {
alert("Failed to load mods: " + ex); alert("Failed to load mods: " + ex);
@ -111,6 +110,8 @@ export class ModLoader {
}); });
this.modLoadQueue = []; this.modLoadQueue = [];
this.signals.postInit.dispatch(); this.signals.postInit.dispatch();
delete window.registerMod;
} }
} }

View File

@ -3,7 +3,6 @@ import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { compressX64 } from "../core/lzstring"; import { compressX64 } from "../core/lzstring";
import { getIPCRenderer } from "../core/utils";
import { T } from "../translations"; import { T } from "../translations";
const logger = createLogger("puzzle-api"); const logger = createLogger("puzzle-api");
@ -113,9 +112,7 @@ export class ClientAPI {
return Promise.resolve({ token }); return Promise.resolve({ token });
} }
const renderer = getIPCRenderer(); return ipcRenderer.invoke("steam:get-ticket").then(
return renderer.invoke("steam:get-ticket").then(
ticket => { ticket => {
logger.log("Got auth ticket:", ticket); logger.log("Got auth ticket:", ticket);
return this._request("/v1/public/login", { return this._request("/v1/public/login", {

View File

@ -4,7 +4,6 @@ import { GameRoot } from "../../game/root";
/* typehints:end */ /* typehints:end */
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { getIPCRenderer } from "../../core/utils";
import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider"; import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider";
const logger = createLogger("achievements/steam"); const logger = createLogger("achievements/steam");
@ -109,9 +108,7 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
return Promise.resolve(); return Promise.resolve();
} }
this.ipc = getIPCRenderer(); return ipcRenderer.invoke("steam:is-initialized").then(initialized => {
return this.ipc.invoke("steam:is-initialized").then(initialized => {
this.initialized = initialized; this.initialized = initialized;
if (!this.initialized) { if (!this.initialized) {
@ -136,7 +133,7 @@ export class SteamAchievementProvider extends AchievementProviderInterface {
if (!this.initialized) { if (!this.initialized) {
promise = Promise.resolve(); promise = Promise.resolve();
} else { } else {
promise = this.ipc.invoke("steam:activate-achievement", ACHIEVEMENT_IDS[key]); promise = ipcRenderer.invoke("steam:activate-achievement", ACHIEVEMENT_IDS[key]);
} }
return promise return promise

View File

@ -1,5 +1,4 @@
import { StorageInterface } from "../storage"; import { StorageInterface } from "../storage";
import { getIPCRenderer } from "../../core/utils";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
const logger = createLogger("electron-storage"); const logger = createLogger("electron-storage");
@ -12,7 +11,7 @@ export class StorageImplElectron extends StorageInterface {
this.jobs = {}; this.jobs = {};
this.jobId = 0; this.jobId = 0;
getIPCRenderer().on("fs-response", (event, arg) => { ipcRenderer.on("fs-response", (event, arg) => {
const id = arg.id; const id = arg.id;
if (!this.jobs[id]) { if (!this.jobs[id]) {
logger.warn("Got unhandled FS response, job not known:", id); logger.warn("Got unhandled FS response, job not known:", id);
@ -37,7 +36,7 @@ export class StorageImplElectron extends StorageInterface {
const jobId = ++this.jobId; const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject }; this.jobs[jobId] = { resolve, reject };
getIPCRenderer().send("fs-job", { ipcRenderer.send("fs-job", {
type: "write", type: "write",
filename, filename,
contents, contents,
@ -52,7 +51,7 @@ export class StorageImplElectron extends StorageInterface {
const jobId = ++this.jobId; const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject }; this.jobs[jobId] = { resolve, reject };
getIPCRenderer().send("fs-job", { ipcRenderer.send("fs-job", {
type: "read", type: "read",
filename, filename,
id: jobId, id: jobId,
@ -65,7 +64,7 @@ export class StorageImplElectron extends StorageInterface {
// ipcMain // ipcMain
const jobId = ++this.jobId; const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject }; this.jobs[jobId] = { resolve, reject };
getIPCRenderer().send("fs-job", { ipcRenderer.send("fs-job", {
type: "delete", type: "delete",
filename, filename,
id: jobId, id: jobId,

View File

@ -1,6 +1,5 @@
import { NoAchievementProvider } from "../browser/no_achievement_provider"; import { NoAchievementProvider } from "../browser/no_achievement_provider";
import { PlatformWrapperImplBrowser } from "../browser/wrapper"; import { PlatformWrapperImplBrowser } from "../browser/wrapper";
import { getIPCRenderer } from "../../core/utils";
import { createLogger } from "../../core/logging"; import { createLogger } from "../../core/logging";
import { StorageImplElectron } from "./storage"; import { StorageImplElectron } from "./storage";
import { SteamAchievementProvider } from "./steam_achievement_provider"; import { SteamAchievementProvider } from "./steam_achievement_provider";
@ -71,15 +70,13 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
} }
initializeDlcStatus() { initializeDlcStatus() {
const renderer = getIPCRenderer();
if (G_WEGAME_VERSION) { if (G_WEGAME_VERSION) {
return Promise.resolve(); return Promise.resolve();
} }
logger.log("Checking DLC ownership ..."); logger.log("Checking DLC ownership ...");
// @todo: Don't hardcode the app id // @todo: Don't hardcode the app id
return renderer.invoke("steam:check-app-ownership", 1625400).then( return ipcRenderer.invoke("steam:check-app-ownership", 1625400).then(
res => { res => {
logger.log("Got DLC ownership:", res); logger.log("Got DLC ownership:", res);
this.dlcs.puzzle = Boolean(res); this.dlcs.puzzle = Boolean(res);
@ -106,7 +103,7 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
} }
setFullscreen(flag) { setFullscreen(flag) {
getIPCRenderer().send("set-fullscreen", flag); ipcRenderer.send("set-fullscreen", flag);
} }
getSupportsAppExit() { getSupportsAppExit() {
@ -115,6 +112,6 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
exitApp() { exitApp() {
logger.log(this, "Sending app exit signal"); logger.log(this, "Sending app exit signal");
getIPCRenderer().send("exit-app"); ipcRenderer.send("exit-app");
} }
} }

View File

@ -405,12 +405,6 @@ export class MainMenuState extends GameState {
makeButton(outerDiv, ["newGameButton", "styledButton"], T.mainMenu.newGame), makeButton(outerDiv, ["newGameButton", "styledButton"], T.mainMenu.newGame),
this.onPlayButtonClicked this.onPlayButtonClicked
); );
// Mods
this.trackClicks(
makeButton(outerDiv, ["modsButton", "styledButton"], " "),
this.onModsClicked
);
} else { } else {
// New game // New game
this.trackClicks( this.trackClicks(
@ -419,6 +413,9 @@ export class MainMenuState extends GameState {
); );
} }
// Mods
this.trackClicks(makeButton(outerDiv, ["modsButton", "styledButton"], " "), this.onModsClicked);
buttonContainer.appendChild(outerDiv); buttonContainer.appendChild(outerDiv);
} }

View File

@ -1,6 +1,5 @@
import { THIRDPARTY_URLS } from "../core/config"; import { THIRDPARTY_URLS } from "../core/config";
import { TextualGameState } from "../core/textual_game_state"; import { TextualGameState } from "../core/textual_game_state";
import { getIPCRenderer } from "../core/utils";
import { MODS } from "../mods/modloader"; import { MODS } from "../mods/modloader";
import { T } from "../translations"; import { T } from "../translations";
@ -117,7 +116,7 @@ export class ModsState extends TextualGameState {
this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone); this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone);
return; return;
} }
getIPCRenderer().send("open-mods-folder"); ipcRenderer.send("open-mods-folder");
} }
onSteamLinkClicked() { onSteamLinkClicked() {